/* ==========================================================================
   LinguaRamp — landing page styles
   "A dojo, not a textbook." Self-contained, no external requests.
   ========================================================================== */

:root {
  /* Palette (from the app's design system) */
  --ink: #0b1220;
  --ink-2: #111a2e;
  --blue: #4168f7;
  --blue-dark: #3253d8;
  --blue-tint: #d6edf8;
  --orange: #ff8c42;
  --teal: #01b4a2;
  --green: #1e9a4c;
  --green-bright: #7bd46e;
  --learning: #ef7a14; /* VocabularyStatusDesign.learningColor */

  --bg-light: #ffffff;
  --bg-offwhite: #f6f8fb;
  --text: #1a2233;
  --muted: #5a6577;

  --on-dark: #f2f5fa;
  --on-dark-muted: #9aa7bd;

  /* Spacing scale */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4rem;
  --s-9: 6rem;

  --radius: 16px;
  --radius-lg: 24px;
  --wrap: 1120px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  --shadow-sm: 0 1px 2px rgba(11, 18, 32, 0.06),
    0 4px 14px rgba(11, 18, 32, 0.06);
  --shadow-md: 0 8px 30px rgba(11, 18, 32, 0.12);
  --shadow-phone: 0 24px 60px rgba(6, 12, 26, 0.45);
}

* {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1.06rem;
  line-height: 1.65;
  color: var(--text);
  background: var(--bg-light);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a {
  color: var(--blue);
  text-decoration: none;
}
a:hover {
  color: var(--blue-dark);
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.08;
}

strong {
  font-weight: 700;
}

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

/* --------------------------- Layout helpers --------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--s-5);
}
.wrap--narrow {
  max-width: 760px;
}

/* Visible to screen readers, not to eyes. */
.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;
}

.section {
  position: relative;
  padding-block: clamp(3.5rem, 8vw, 6rem);
  overflow: hidden;
}

.section--light {
  background: var(--bg-light);
  color: var(--text);
}
.section--tint {
  background: var(--bg-offwhite);
  color: var(--text);
}
.section--dark {
  background: var(--ink);
  color: var(--on-dark);
}

.section__head {
  max-width: 720px;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.section__title {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
}
.section__lede {
  margin-top: var(--s-4);
  font-size: 1.12rem;
  color: var(--muted);
}
.section--dark .section__lede {
  color: var(--on-dark-muted);
}

.eyebrow {
  margin: 0 0 var(--s-3);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.eyebrow--blue {
  color: var(--blue);
}
.eyebrow--orange {
  color: var(--orange);
}
/* The battles section's eyebrow, in the fluency strand's green so the two
   sections read as one idea. About 5:1 against --ink. */
.eyebrow--green {
  color: var(--green);
}

.grad-text {
  background: linear-gradient(100deg, var(--on-dark) 0%, var(--orange) 45%, var(--blue) 95%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* --------------------------- Buttons --------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  border: 1.5px solid transparent;
  border-radius: 14px;
  padding: 0.85rem 1.4rem;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease,
    box-shadow 0.15s ease;
  line-height: 1.1;
}
.btn:hover {
  transform: translateY(-1px);
}
.btn:active {
  transform: translateY(0);
}

.btn--primary {
  background: var(--blue);
  color: #fff;
  box-shadow: 0 8px 22px rgba(65, 104, 247, 0.32);
}
.btn--primary:hover {
  background: var(--blue-dark);
  color: #fff;
}

.btn--pill {
  border-radius: 999px;
  padding: 0.6rem 1.2rem;
  font-size: 0.95rem;
}

/* Hero-weight CTA — matches the visual mass of the App Store badge it stands
   in for while the app is pre-launch. */
.btn--lg {
  padding: 0.95rem 1.7rem;
  font-size: 1.05rem;
}

.btn--ghost {
  background: transparent;
  color: var(--on-dark);
  border-color: rgba(242, 245, 250, 0.28);
}
.btn--ghost:hover {
  color: var(--on-dark);
  border-color: rgba(242, 245, 250, 0.6);
  background: rgba(242, 245, 250, 0.06);
}

.btn--ghost-dark {
  background: transparent;
  color: var(--text);
  border-color: rgba(26, 34, 51, 0.2);
}
.btn--ghost-dark:hover {
  color: var(--text);
  border-color: var(--blue);
  background: rgba(65, 104, 247, 0.05);
}

/* App Store style button (custom-drawn — swap for official badge at launch) */
.btn--appstore {
  background: #000;
  color: #fff;
  border-radius: 14px;
  padding: 0.6rem 1.3rem;
}
.btn--appstore:hover {
  background: #14161c;
  color: #fff;
}
.appstore__label {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.05;
  text-align: left;
}
.appstore__small {
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  opacity: 0.9;
}
.appstore__big {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.apple-glyph {
  flex: none;
}
.btn--appstore-lg {
  padding: 0.75rem 1.6rem;
}

/* --------------------------- Nav --------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(11, 18, 32, 0.72);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid rgba(242, 245, 250, 0.08);
}
.nav {
  max-width: var(--wrap);
  margin-inline: auto;
  /* Thin top/bottom padding: the 66px brand mark is the tallest item and
     everything else centres on it, so this only trims the mark's margin. */
  padding: 0.1875rem var(--s-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--on-dark);
  font-weight: 800;
  letter-spacing: -0.01em;
}
.brand:hover {
  color: var(--on-dark);
}
.brand__icon {
  border-radius: 22.5%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.brand__name {
  font-size: 1.25rem;
}
/* The inline-SVG nav mark (index.html #navLogo). */
.brand__logo {
  display: block;
  flex-shrink: 0;
  width: 66px;
  height: 66px;
  overflow: hidden;
}
.brand__logo svg {
  display: block;
  width: 100%;
  height: 100%;
}
/* With JS the nav glyphs stay hidden until main.js places them
   (navLogoClimb); reduced motion keeps the plain icon. */
.js .brand__logo [class^="nav-logo__glyph-"] {
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .js .brand__logo [class^="nav-logo__glyph-"] {
    opacity: 1;
  }
}
/* "Ramp" takes the warm half of the icon's ramp gradient; the violet-to-blue
   end disappears into the navy header. */
.brand__ramp {
  background: linear-gradient(90deg, #ff8c42 0%, #f4566a 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* The footer sits on the solid dark section, where the full ramp gradient
   reads (as in the final CTA headline). */
.footer .brand__ramp {
  background-image: linear-gradient(90deg, #ff8c42 0%, #f4566a 35%, #9b4fe0 65%, #4168f7 100%);
}

.nav__menu {
  display: flex;
  align-items: center;
  gap: var(--s-6);
}
.nav__links {
  display: flex;
  align-items: center;
  gap: var(--s-6);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__links a {
  color: var(--on-dark-muted);
  font-size: 0.96rem;
  font-weight: 600;
}
.nav__links a:hover {
  color: var(--on-dark);
}

.nav__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 10px;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.nav__toggle-bar {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--on-dark);
  border-radius: 2px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(2) {
  opacity: 0;
}
.nav__toggle[aria-expanded="true"] .nav__toggle-bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* 1100, not 760: below it the seven nav links wrap to two rows (measured
   2026-11-06; six already wrapped below ~960). Keep in step with main.js. */
@media (max-width: 1100px) {
  .nav__toggle {
    display: flex;
  }
  .nav__menu {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-2);
    padding: var(--s-4) var(--s-5) var(--s-6);
    background: rgba(11, 18, 32, 0.97);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid rgba(242, 245, 250, 0.08);
    transform: translateY(-8px);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.2s ease, opacity 0.2s ease;
  }
  .nav__menu.is-open {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }
  .nav__links {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  .nav__links a {
    display: block;
    padding: 0.85rem 0.25rem;
    font-size: 1.05rem;
    border-bottom: 1px solid rgba(242, 245, 250, 0.08);
  }
  .nav__cta {
    margin-top: var(--s-3);
    align-self: flex-start;
  }
}

/* --------------------------- Hero --------------------------- */

.hero {
  position: relative;
  padding-block: clamp(3.5rem, 7vw, 6rem) clamp(4rem, 8vw, 7rem);
  background: var(--ink);
  color: var(--on-dark);
  overflow: hidden;
}
.hero__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
      60% 55% at 78% 12%,
      rgba(65, 104, 247, 0.28),
      transparent 70%
    ),
    radial-gradient(50% 50% at 10% 90%, rgba(255, 140, 66, 0.1), transparent 70%);
  pointer-events: none;
}

.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}

.hero__title {
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  margin-bottom: var(--s-5);
}
/* The ramp's gradient climbs from Hear's white through orange into Battle's solid blue —
   you ramp to the battle. */
.hero__title-battle {
  color: var(--blue);
}
.hero__sub {
  font-size: clamp(1.08rem, 1.6vw, 1.28rem);
  color: var(--on-dark-muted);
  max-width: 34ch;
}
.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-6);
}
.hero__reassure {
  margin-top: var(--s-5);
  font-size: 0.9rem;
  color: var(--on-dark-muted);
}

/* Hero art — holds the reel phone (see "Hero reel" near the end). */
.hero__art {
  position: relative;
  min-height: 460px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.phone {
  position: relative;
  width: 232px;
  aspect-ratio: 402 / 874;
  background: #05070d;
  border-radius: 40px;
  padding: 10px;
  box-shadow: var(--shadow-phone);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.phone__notch {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  width: 92px;
  height: 20px;
  background: #05070d;
  border-radius: 999px;
  z-index: 3;
}
.phone__shot {
  width: 100%;
  height: 100%;
  border-radius: 30px;
  object-fit: cover;
  object-position: top center;
  background: #fff;
}


/* --------------------------- Language strip --------------------------- */

.lang-strip {
  background: var(--ink);
  padding-block: var(--s-6) var(--s-7);
  border-top: 1px solid rgba(242, 245, 250, 0.06);
}
.lang-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.5rem 0.95rem;
  border-radius: 999px;
  background: rgba(242, 245, 250, 0.06);
  border: 1px solid rgba(242, 245, 250, 0.12);
  color: var(--on-dark);
  font-size: 0.92rem;
  font-weight: 600;
}
.chip--soft {
  color: var(--on-dark-muted);
  background: transparent;
  border-style: dashed;
}
.chip__flag {
  font-size: 1.05rem;
  line-height: 1;
}

/* --------------------------- Belt divider --------------------------- */

.belt-divider {
  height: 4px;
  border: 0;
  margin: 0;
  background: linear-gradient(
    90deg,
    var(--blue) 0%,
    var(--teal) 52%,
    var(--orange) 100%
  );
  opacity: 0.55;
}

/* --------------------------- How it works --------------------------- */

.phase-tag {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
}
.phase-tag--blue {
  color: var(--blue);
  background: rgba(65, 104, 247, 0.12);
}
.phase-tag--teal {
  color: #078a7d;
  background: rgba(1, 180, 162, 0.14);
}
.phase-tag--orange {
  color: #d96b1f;
  background: rgba(255, 140, 66, 0.16);
}
.phase-tag--green {
  color: #15733a;
  background: rgba(30, 154, 76, 0.14);
}
.phase-tag--violet {
  color: #5b45c9;
  background: rgba(110, 86, 230, 0.12);
}
/* Read and Write (off by default; steps a learner adds): same hue, dashed
   outline instead of a fill. The copy never calls them "optional" — every step
   is the learner's choice. */
.phase-tag--optional {
  background: transparent;
  border: 1.5px dashed currentColor;
  padding: calc(0.22rem - 1.5px) calc(0.6rem - 1.5px);
}

/* Four-strands grid: 2×2 on desktop, stacked on narrow. Uses .feature-card for
   the card chrome, so only the layout lives here. */
.strands {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.strands .feature__title {
  margin: var(--s-3) 0 var(--s-2);
}

.how__close {
  margin-top: var(--s-7);
  text-align: center;
  font-size: 1.15rem;
  color: var(--muted);
  max-width: 62ch;
  margin-inline: auto;
}
.how__close strong {
  color: var(--text);
}

/* --------------------------- Features --------------------------- */

.features {
  display: flex;
  flex-direction: column;
  gap: clamp(2.5rem, 5vw, 4rem);
}

.feature-row {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.feature-row--reverse .feature-row__text {
  order: 2;
}
.feature-row__media {
  display: flex;
  justify-content: center;
}

.feature__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 14px;
  background: var(--blue-tint);
  color: var(--blue-dark);
  margin-bottom: var(--s-4);
}
.feature__title {
  font-size: 1.5rem;
  margin-bottom: var(--s-3);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.feature__body {
  color: var(--muted);
  font-size: 1.06rem;
  max-width: 46ch;
}

.badge {
  font-size: 0.66rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
}
.badge--plus {
  color: #fff;
  background: var(--blue);
}

/* Mini phone frame for feature rows */
.phone--mini {
  width: 220px;
}

.feature-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.feature-card {
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.07);
  border-radius: var(--radius-lg);
  padding: var(--s-6) var(--s-5);
  box-shadow: var(--shadow-sm);
}
.feature-card .feature__body {
  max-width: none;
}

/* In your corner strip */
.corner {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding: var(--s-6);
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.07);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.corner__label {
  margin: 0 0 var(--s-4);
  font-weight: 800;
  letter-spacing: -0.01em;
  font-size: 1.05rem;
}
.corner__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-5);
}
.corner__item {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  color: var(--muted);
  font-size: 0.98rem;
}
.corner__item strong {
  color: var(--text);
}
.corner__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--bg-offwhite);
  color: var(--blue);
  margin-bottom: var(--s-2);
}

/* --------------------------- Dark band (on-device) --------------------------- */

.band {
  text-align: center;
}
.band__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    55% 60% at 50% 0%,
    rgba(1, 180, 162, 0.14),
    transparent 70%
  );
  pointer-events: none;
}
.band__inner {
  position: relative;
  max-width: 720px;
}
.band__title {
  font-size: clamp(1.9rem, 4vw, 2.7rem);
}
.band__copy {
  margin: var(--s-4) auto 0;
  color: var(--on-dark-muted);
  font-size: 1.12rem;
  max-width: 60ch;
}
.band__chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-3);
  list-style: none;
  margin: var(--s-6) 0 0;
  padding: 0;
}
.pill-chip {
  padding: 0.5rem 1rem;
  border-radius: 999px;
  background: rgba(242, 245, 250, 0.07);
  border: 1px solid rgba(242, 245, 250, 0.16);
  color: var(--on-dark);
  font-size: 0.9rem;
  font-weight: 600;
}

/* --------------------------- Companion --------------------------- */

.companion-panel {
  position: relative;
  overflow: hidden;
  background: linear-gradient(150deg, var(--ink) 0%, var(--ink-2) 100%);
  color: var(--on-dark);
  border-radius: 28px;
  padding: clamp(2rem, 5vw, 4rem);
  box-shadow: var(--shadow-md);
}
.companion__body {
  position: relative;
  z-index: 1;
  max-width: 640px;
}
.companion__lede {
  margin: var(--s-4) 0 var(--s-5);
  color: var(--on-dark-muted);
  font-size: 1.12rem;
}
.companion__lede strong {
  color: var(--on-dark);
}
.companion__bullets {
  list-style: none;
  margin: 0 0 var(--s-5);
  padding: 0;
  display: grid;
  gap: var(--s-3);
}
.companion__bullets li {
  position: relative;
  padding-left: 1.9rem;
  color: var(--on-dark);
}
.companion__bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 10px;
  height: 10px;
  border-radius: 3px;
  background: linear-gradient(135deg, var(--blue), var(--orange));
}
.companion__sites-label {
  margin: 0 0 var(--s-3);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--on-dark-muted);
}
.companion__sites-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--on-dark-muted);
}
.companion__sites-list li {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}
.companion__sites-list svg {
  flex: none;
  width: 1.25em;
  height: 1.25em;
  fill: currentColor;
}
/* iQIYI and Rakuten Viki are wordmarks, so they stand in for the name. */
.companion__sites-list svg.is-wordmark {
  width: auto;
  height: 1em;
}
/* Rakuten Viki's swoosh sits under the letters; size up so the letters
   match the other names. */
.companion__sites-list svg.is-wordmark--swoosh {
  height: 1.35em;
}
/* The heading sits above the video band, so the lede opens the panel. */
.companion__body > :first-child {
  margin-top: 0;
}
/* Full-bleed band between the section heading and the panel: outside .wrap,
   edge to edge, no radius. Capped at 85vh so a wide monitor doesn't get a
   band taller than the window; contain letterboxes the sides onto the ink
   ground rather than cropping away the transcript panel (right) or the
   subtitle (bottom). */
.companion__figure {
  margin: 0 0 clamp(2.5rem, 6vw, 4rem);
  background: var(--ink);
}
.companion__shot {
  display: block;
  width: 100%;
  height: auto;
  max-height: 85vh;
  object-fit: contain;
}
.companion__caption {
  padding-block: var(--s-4) var(--s-6);
  color: var(--on-dark-muted);
  font-size: 0.95rem;
}
.companion__caption-link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
.companion__caption-link:hover {
  color: #fff;
}

/* --------------------------- Lessons (coming soon) ---------------------------
   Teaser for a feature that is NOT in the beta. Step chips are coloured by the
   four-strands scheme used higher up the page: teal = study (words, patterns,
   sentences), blue = input (Library, Journeys), orange = output (scenario
   battles). Every text colour here clears 4.5:1 on its own background. */

.lessons__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(2.5rem, 5vw, 3.5rem);
  align-items: center;
}

.lessons__tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-3);
}
.soon-tag {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  color: #5b45c9;
  background: rgba(110, 86, 230, 0.12);
}
.lessons__eyebrow {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--blue);
}

.lessons__steps {
  list-style: none;
  margin: var(--s-6) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.lessons__step {
  display: flex;
  gap: var(--s-4);
  align-items: flex-start;
}
.lessons__num {
  flex: 0 0 36px;
  height: 36px;
  border-radius: 999px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lessons__num--blue {
  color: var(--blue);
  background: rgba(65, 104, 247, 0.12);
}
.lessons__num--teal {
  color: #078a7d;
  background: rgba(1, 180, 162, 0.14);
}
.lessons__num--green {
  color: #15733a;
  background: rgba(30, 154, 76, 0.14);
}
.lessons__step-title {
  font-size: 1.12rem;
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.lessons__step-body {
  margin: var(--s-1) 0 0;
  color: var(--muted);
}
.lessons__step-body--quote {
  font-style: italic;
}

/* Bring-your-own-AI callout: deliberately quieter than the steps above it. */
.lessons__byo {
  margin-top: var(--s-6);
  max-width: 520px;
  display: flex;
  gap: var(--s-4);
  align-items: flex-start;
  padding: var(--s-4) 1.1rem;
  border-radius: var(--radius);
  background: var(--bg-offwhite);
  border: 1px solid rgba(26, 34, 51, 0.07);
}
.lessons__byo-icon {
  flex: 0 0 36px;
  height: 36px;
  box-sizing: border-box;
  border-radius: 10px;
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.1);
  color: var(--blue);
  display: flex;
  align-items: center;
  justify-content: center;
}
.lessons__byo-title {
  font-size: 1rem;
  letter-spacing: -0.01em;
  line-height: 1.35;
}
.lessons__byo-body {
  margin: var(--s-1) 0 0;
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--muted);
}

.lessons__cta {
  margin-top: var(--s-6);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
}
.lessons__note {
  margin: 0;
  font-size: 0.9rem;
  color: var(--muted);
  max-width: 40ch;
}

/* Example course */
.lessons__demo {
  background: var(--bg-offwhite);
  border-radius: var(--radius-lg);
  padding: clamp(1rem, 3vw, 1.75rem);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.lessons__ask {
  align-self: flex-end;
  max-width: 86%;
  margin: 0;
  background: var(--blue);
  color: #fff;
  padding: 0.85rem 1.1rem;
  border-radius: 20px 20px 6px 20px;
  line-height: 1.5;
}

.course {
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.07);
  border-radius: 20px;
  box-shadow: var(--shadow-sm);
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.course__head > * {
  margin: 0;
}
.course__kicker {
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}
.course__title {
  margin-top: var(--s-1);
  font-size: 1.5rem;
  line-height: 1.15;
}
.course__goal {
  margin-top: var(--s-2);
  font-size: 0.94rem;
  color: var(--muted);
  line-height: 1.5;
}
.course__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}
.course__pill {
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.2rem 0.65rem;
  border-radius: 999px;
  background: var(--bg-offwhite);
  color: var(--text);
}
.course__pill--teal {
  background: rgba(1, 180, 162, 0.14);
  color: #076e64;
}

.course__lessons {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.course__lesson {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-3);
  border-radius: 14px;
}
.course__lesson--done {
  background: var(--bg-offwhite);
}
.course__lesson--current {
  border: 1.5px solid var(--blue);
}
.course__lesson-head {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.course__lesson-mark {
  flex: 0 0 26px;
  height: 26px;
  box-sizing: border-box;
  border-radius: 999px;
  border: 1.5px solid rgba(26, 34, 51, 0.2);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
.course__lesson--done .course__lesson-mark {
  border: 0;
  background: var(--green);
  color: #fff;
}
.course__lesson--current .course__lesson-mark {
  border: 0;
  background: var(--blue);
  color: #fff;
}
.course__lesson-title {
  flex: 1 1 auto;
  font-weight: 700;
  line-height: 1.3;
}
.course__lesson-state {
  white-space: nowrap;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--blue-dark);
}

.course__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.step-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 3px;
  border-radius: 10px;
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.3;
}
.step-chip__icon {
  flex: 0 0 22px;
  height: 22px;
  border-radius: 7px;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}
.step-chip--study {
  background: rgba(1, 180, 162, 0.14);
  color: #076e64;
}
.step-chip--input {
  background: rgba(65, 104, 247, 0.12);
  color: var(--blue-dark);
}
.step-chip--battle {
  background: rgba(255, 140, 66, 0.16);
  color: #a84d00;
}
/* The step the learner is on: outlined, same footprint (padding minus border). */
.step-chip--current {
  border: 1.5px solid #078a7d;
  padding: 1.5px 7.5px 1.5px 1.5px;
}
.step-chip__done {
  color: #15733a;
}

.course__progress {
  height: 8px;
  border-radius: 999px;
  background: rgba(65, 104, 247, 0.14);
  overflow: hidden;
}
.course__progress > span {
  display: block;
  height: 100%;
  background: var(--blue);
  border-radius: 999px;
}
.course__next {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
}
.course__next-label {
  color: var(--muted);
}

.course-key {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  text-align: center;
}
.course-key__label,
.course-key__note {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
}
.course-key__label {
  font-weight: 700;
}
.course-key__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 14px;
  font-size: 0.82rem;
}
.course-key__item {
  display: flex;
  align-items: center;
  gap: 5px;
}
.course-key__item--study svg {
  color: #078a7d;
}
.course-key__item--input svg {
  color: var(--blue);
}
.course-key__item--battle svg {
  color: #c45a12;
}

@media (max-width: 960px) {
  .lessons__inner {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --------------------------- Pricing --------------------------- */

.plans {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
  align-items: stretch;
}
.plan {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.1);
  border-radius: var(--radius-lg);
  padding: clamp(1.75rem, 3vw, 2.5rem);
  box-shadow: var(--shadow-sm);
}
.plan--featured {
  border: 2px solid var(--blue);
  box-shadow: 0 18px 44px rgba(65, 104, 247, 0.18);
  position: relative;
}
/* The same Cloud badge (.badge--plus) the page puts on every Cloud feature,
   at card scale — so each badge elsewhere points back to this plan. */
.plan__badge {
  align-self: flex-start;
  font-size: 0.72rem;
  padding: 0.3rem 0.7rem;
  margin-bottom: var(--s-3);
}
.plan__name {
  font-size: 1.5rem;
}
.plan__price {
  margin: var(--s-3) 0 var(--s-2);
}
.plan__amount {
  font-size: 2.6rem;
  font-weight: 800;
  letter-spacing: -0.03em;
}
.plan__per {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--muted);
}
.plan__tagline {
  margin: 0 0 var(--s-5);
  color: var(--muted);
}
.checklist {
  list-style: none;
  margin: 0 0 var(--s-6);
  padding: 0;
  display: grid;
  gap: var(--s-3);
  flex: 1;
  /* The list stretches to push the CTA down; its rows must not stretch with it,
     or a short Free list spreads out to match the taller Cloud card. */
  align-content: start;
}
.checklist li {
  position: relative;
  padding-left: 1.9rem;
  color: var(--text);
}
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.28em;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--green);
  /* white check drawn with a data URI (no external request) */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round' d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
  background-size: 14px 14px;
  background-position: center;
  background-repeat: no-repeat;
}
.plan__cta {
  width: 100%;
}
.plans__foot {
  margin-top: var(--s-5);
  text-align: center;
  font-size: 0.9rem;
  color: var(--muted);
}

/* --------------------------- Beta --------------------------- */
/* The #beta section reuses the pricing cards; these only add what they lack. */

/* Small blue dots instead of the pricing cards' green checks: these lists are
   requirements and notes, not plan features. Dot centred on the first line
   (body line-height 1.65 → first-line middle at 0.825em). */
.beta__cards .checklist li {
  padding-left: 1.3rem;
}
.beta__cards .checklist li::before {
  top: calc(0.825em - 4px);
  left: 0.15rem;
  width: 8px;
  height: 8px;
  background: var(--blue);
}

.beta__note {
  margin: var(--s-3) 0 0;
  font-size: 0.88rem;
  color: var(--muted);
  text-align: center;
}
.beta__feedback {
  max-width: 760px;
  margin: var(--s-6) auto 0;
  padding: var(--s-5);
  background: var(--bg-light); /* white box on the tinted #beta section */
  border: 1px solid rgba(26, 34, 51, 0.08);
  border-radius: var(--radius);
}
.beta__feedback-title {
  font-size: 1.1rem;
  margin: 0 0 var(--s-3);
}
.beta__feedback-list {
  margin: 0;
  padding-left: 1.2rem;
  display: grid;
  gap: var(--s-2);
  color: var(--text);
}

/* --------------------------- FAQ --------------------------- */

.faq {
  display: grid;
  gap: var(--s-3);
}
.faq__item {
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.1);
  border-radius: var(--radius);
  overflow: hidden;
}
.faq__item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: 1.15rem 1.3rem;
  font-weight: 700;
  font-size: 1.06rem;
  color: var(--text);
}
.faq__item summary::-webkit-details-marker {
  display: none;
}
.faq__chevron {
  flex: none;
  width: 12px;
  height: 12px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
  margin-top: -4px;
}
.faq__item[open] .faq__chevron {
  transform: rotate(-135deg);
  margin-top: 3px;
}
.faq__answer {
  padding: 0 1.3rem 1.25rem;
  color: var(--muted);
}
.faq__answer p {
  margin: 0;
}
/* Answers that need a second paragraph; single-paragraph ones are unaffected. */
.faq__answer p + p {
  margin-top: var(--s-3);
}

/* --------------------------- Contact form ---------------------------
   Foot of the FAQ, on the light section. Tinted card rather than the white of
   .faq__item so it reads as a different kind of thing than one more question.

   Unlike .waitlist this is NOT pre-launch scaffolding — it stays when the app
   ships. The two share only .waitlist__hp (the honeypot), which is positioning,
   not appearance. */

.contact {
  margin-top: var(--s-7);
  padding: var(--s-6);
  background: var(--bg-offwhite);
  border: 1px solid rgba(26, 34, 51, 0.1);
  border-radius: var(--radius);
}

.contact__title {
  margin: 0 0 var(--s-2);
  font-size: 1.25rem;
  font-weight: 800;
  color: var(--text);
}

.contact__sub {
  margin: 0 0 var(--s-5);
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.55;
}
.contact__sub a {
  color: var(--blue);
  text-decoration: underline;
  text-underline-offset: 2px;
  /* The address is long enough to overflow a narrow phone on its own line. */
  word-break: break-word;
}

.contact__label {
  display: block;
  margin-bottom: var(--s-2);
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--text);
}
/* The email label follows the message field, so it needs the gap the first
   one doesn't. */
.contact__input + .contact__label {
  margin-top: var(--s-4);
}

.contact__input {
  display: block;
  width: 100%;
  font-family: inherit;
  font-size: 1rem;
  color: var(--text);
  background: var(--bg-light);
  border: 1.5px solid rgba(26, 34, 51, 0.16);
  border-radius: 14px;
  padding: 0.8rem 0.95rem;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.contact__input--message {
  /* Vertical only: a horizontal drag would break the card's layout. */
  resize: vertical;
  min-height: 7.5rem;
  line-height: 1.55;
}
.contact__input::placeholder {
  color: #9aa3b4;
}
.contact__input:hover {
  border-color: rgba(26, 34, 51, 0.3);
}
.contact__input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(65, 104, 247, 0.15);
}
.contact__input:disabled {
  /* Both fields are disabled on success — this must read as "done", not
     "broken". */
  opacity: 0.65;
  cursor: default;
}
/* Only after a submit attempt — see main.js. Styling :invalid directly would
   paint the field red before anyone has finished typing the first character. */
.contact__form.is-validated .contact__input:invalid {
  border-color: var(--orange);
}

.contact__hint {
  margin: var(--s-2) 0 0;
  color: var(--muted);
  font-size: 0.82rem;
}

/* Turnstile injects a fixed-size iframe. Reserve nothing until it exists —
   the widget is interaction-only, so most visitors never see a box. */
.contact__widget:not(:empty) {
  margin-top: var(--s-4);
}

.contact__actions {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-top: var(--s-5);
}

.contact__status {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
}
.contact__status.is-error {
  color: #c2410c; /* --orange is tuned for dark sections; it fails contrast here. */
}
.contact__status.is-ok {
  color: var(--green);
}

.contact__noscript {
  margin: var(--s-4) 0 0;
  color: var(--muted);
  font-size: 0.9rem;
}

@media (max-width: 520px) {
  .contact {
    padding: var(--s-5);
  }
  .contact__actions {
    /* Stacked, so the status line sits under the button rather than being
       squeezed into a two-word column beside it. */
    align-items: stretch;
    gap: var(--s-3);
  }
  .contact__actions .btn {
    width: 100%;
  }
}

/* --------------------------- Final CTA --------------------------- */

.final-cta {
  text-align: center;
}
.final-cta__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
      50% 60% at 50% 0%,
      rgba(65, 104, 247, 0.22),
      transparent 70%
    ),
    radial-gradient(40% 50% at 80% 100%, rgba(255, 140, 66, 0.12), transparent 70%);
  pointer-events: none;
}
.final-cta__inner {
  position: relative;
  max-width: 640px;
}
/* The app icon, large: the same mark people will look for on the App Store. */
.final-cta__logo {
  width: 138px;
  height: 138px;
  margin: 0 auto var(--s-5);
  border-radius: 22.5%;
  overflow: hidden;
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(242, 245, 250, 0.08);
}
.final-cta__logo svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* Two waves of hops up the slope when the logo scrolls in, timed like the
   iOS splash's loading wave (LaunchSplashLayerView): each glyph, foot to
   tip, rises and lands over 0.36s, 0.12s after the one below it. The second
   wave starts 1s after the first. Translate units are the SVG's 1024-unit
   canvas: 36 units is ~5px at 138px. */
@keyframes cta-glyph-hop {
  0% {
    transform: translateY(0);
    animation-timing-function: ease-out;
  }
  18% {
    transform: translateY(-36px);
    animation-timing-function: ease-in;
  }
  36%,
  100% {
    transform: translateY(0);
  }
}
.final-cta__logo.is-hopping [class^="cta-logo__glyph-"] {
  animation: cta-glyph-hop 1s 2 both;
}
.final-cta__logo.is-hopping .cta-logo__glyph-1 { animation-delay: 0.3s; }
.final-cta__logo.is-hopping .cta-logo__glyph-2 { animation-delay: 0.42s; }
.final-cta__logo.is-hopping .cta-logo__glyph-3 { animation-delay: 0.54s; }
.final-cta__logo.is-hopping .cta-logo__glyph-4 { animation-delay: 0.66s; }
/* Hover / tap: one wave, straight away. */
.final-cta__logo.is-hopping-once [class^="cta-logo__glyph-"] {
  animation: cta-glyph-hop 1s 1 both;
}
.final-cta__logo.is-hopping-once .cta-logo__glyph-2 { animation-delay: 0.12s; }
.final-cta__logo.is-hopping-once .cta-logo__glyph-3 { animation-delay: 0.24s; }
.final-cta__logo.is-hopping-once .cta-logo__glyph-4 { animation-delay: 0.36s; }
@media (prefers-reduced-motion: reduce) {
  .final-cta__logo.is-hopping [class^="cta-logo__glyph-"],
  .final-cta__logo.is-hopping-once [class^="cta-logo__glyph-"] {
    animation: none;
  }
}
.final-cta__title {
  font-size: clamp(2rem, 5vw, 3rem);
}
/* "ramp" in the icon's full ramp gradient, foot (orange) to tip (blue). */
.final-cta__ramp {
  background: linear-gradient(90deg, #ff8c42 0%, #f4566a 35%, #9b4fe0 65%, #4168f7 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.final-cta__sub {
  margin: var(--s-3) auto var(--s-6);
  color: var(--on-dark-muted);
  font-size: 1.15rem;
}
.final-cta .btn--appstore {
  margin-inline: auto;
}

/* --------------------------- Waitlist form ---------------------------
   Pre-launch signup, living inside the final CTA (`#waitlist`). Removed with
   that section when the app ships — see index.html. */

.waitlist {
  max-width: 460px;
  margin: 0 auto;
  text-align: left;
}

.waitlist__row {
  display: flex;
  gap: var(--s-3);
}

.waitlist__input {
  flex: 1 1 auto;
  min-width: 0; /* Or a long placeholder pushes the button off-canvas. */
  font-family: inherit;
  font-size: 1rem;
  color: var(--on-dark);
  background: rgba(242, 245, 250, 0.07);
  border: 1.5px solid rgba(242, 245, 250, 0.22);
  border-radius: 14px;
  padding: 0.85rem 1rem;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.waitlist__input::placeholder {
  color: var(--on-dark-muted);
}
.waitlist__input:hover {
  border-color: rgba(242, 245, 250, 0.4);
}
.waitlist__input:focus {
  outline: none;
  border-color: var(--blue);
  background: rgba(242, 245, 250, 0.1);
}
/* Only after a submit attempt — see main.js. Styling :invalid directly would
   paint the field red before anyone has finished typing the first character. */
.waitlist.is-validated .waitlist__input:invalid {
  border-color: var(--orange);
}

.waitlist__submit {
  flex: none;
}

.waitlist__consent {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin-top: var(--s-4);
  color: var(--on-dark-muted);
  font-size: 0.88rem;
  line-height: 1.5;
  cursor: pointer;
}
.waitlist__consent input {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.2rem;
  accent-color: var(--blue);
  cursor: pointer;
}
.waitlist__consent a {
  color: var(--on-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.waitlist__consent a:hover {
  color: #fff;
}

/* Turnstile injects a fixed-size iframe. Reserve nothing until it exists —
   the widget is interaction-only, so most visitors never see a box. */
.waitlist__widget:not(:empty) {
  margin-top: var(--s-4);
}

.waitlist__status {
  margin: var(--s-4) 0 0;
  min-height: 1.5em; /* Holds the line so the layout doesn't jump on submit. */
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--on-dark-muted);
}
.waitlist__status.is-error {
  color: var(--orange);
}
.waitlist__status.is-ok {
  color: var(--green-bright);
}

/* Honeypot. Off-canvas rather than display:none — some bots skip fields that
   are explicitly hidden, which defeats the point. */
.waitlist__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.waitlist__noscript {
  max-width: 460px;
  margin: var(--s-5) auto 0;
  color: var(--on-dark-muted);
  font-size: 0.92rem;
}
.waitlist__noscript a {
  color: var(--on-dark);
  text-decoration: underline;
}

@media (max-width: 520px) {
  .waitlist__row {
    flex-direction: column;
  }
  .waitlist__submit {
    width: 100%;
  }
}

/* --------------------------- Footer --------------------------- */

.footer {
  background: var(--ink);
  border-top: 1px solid rgba(242, 245, 250, 0.08);
  padding-block: var(--s-7);
}
.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-6);
}
.footer__copy {
  margin: 0;
  color: var(--on-dark-muted);
  font-size: 0.92rem;
}
.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer__links a {
  color: var(--on-dark-muted);
  font-size: 0.92rem;
  font-weight: 600;
}
.footer__links a:hover {
  color: var(--on-dark);
}

/* --------------------------- Hanzi watermark --------------------------- */

.hanzi-watermark {
  position: absolute;
  font-weight: 800;
  color: var(--on-dark);
  opacity: 0.05;
  line-height: 0.8;
  pointer-events: none;
  user-select: none;
  z-index: 0;
  white-space: nowrap;
}
.hanzi-watermark--hero {
  font-size: 30vw;
  top: -6%;
  right: -4%;
}
.hanzi-watermark--band {
  font-size: 28vw;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.hanzi-watermark--panel {
  font-size: 22rem;
  top: -8%;
  right: -3%;
  opacity: 0.05;
}
.hanzi-watermark--final {
  font-size: 30vw;
  bottom: -12%;
  left: -4%;
}
/* Bottom-left and bleeding off-canvas: the battles grid fills the middle of
   its section, so a centred mark would sit behind the cards. */
.hanzi-watermark--battles {
  font-size: 26vw;
  bottom: -12%;
  left: -3%;
}

/* --------------------------- Scroll reveal --------------------------- */

.js .reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.6s ease, transform 0.6s ease;
  will-change: opacity, transform;
}
.js .reveal.in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .btn:hover {
    transform: none;
  }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 960px) {
  .corner__list {
    grid-template-columns: 1fr;
    gap: var(--s-4);
  }
}

@media (max-width: 840px) {
  .hero__grid {
    grid-template-columns: 1fr;
    text-align: center;
  }
  .hero__sub {
    margin-inline: auto;
  }
  .hero__cta {
    justify-content: center;
  }
  .hero__art {
    order: -1;
    min-height: 420px;
    margin-bottom: var(--s-5);
  }

  .feature-row,
  .feature-row--reverse {
    grid-template-columns: 1fr;
  }
  .feature-row__text,
  .feature-row--reverse .feature-row__text {
    order: 0;
  }
  .feature__body {
    max-width: none;
  }
  .feature-pair,
  .strands {
    grid-template-columns: 1fr;
  }

  .plans {
    grid-template-columns: 1fr;
    max-width: 460px;
    margin-inline: auto;
  }
}

@media (max-width: 520px) {
  body {
    font-size: 1.02rem;
  }
  .phone {
    width: 200px;
  }
  .hanzi-watermark--panel {
    font-size: 14rem;
  }
}

@media (max-width: 380px) {
  .wrap {
    padding-inline: var(--s-4);
  }
  .phone {
    width: 172px;
  }
  .hero__art {
    min-height: 360px;
  }
  .btn {
    width: 100%;
  }
  .hero__cta .btn {
    width: 100%;
  }
}

/* A corner strip nested inside .features (the engine-choice note under the
   Scenarios / Ask AI rows) — the flex gap already spaces it, so drop the
   standalone margin that the section-level strips need. */
.features > .corner {
  margin-top: 0;
}

/* --------------------------- Flow (features) ---------------------------
   The review engine leads the section; below it, the section layout IS the
   diagram of what feeds it: a boxed group of content sources, an arrow down to
   the Library hub, down through the ramp, into a small cap that points back up
   to the engine, and a dashed rail up the left side for the feedback edge.
   Plain CSS so every node stays real text that reflows and is selectable. */

/* Sits between the engine card and the diagram, bridging the two. */
.flow__lede {
  max-width: 720px;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.flow {
  position: relative;
  padding-left: 58px;
}

/* The feedback edge: engine cap -> up the left -> back into the sources box.
   bottom = half the one-row cap (~37px), so the rail leaves from the cap's
   middle; the cap is the last thing in the flow (.flow__return rides beside
   the rail, out of flow). */
.flow__rail {
  position: absolute;
  left: 8px;
  top: 34px;
  bottom: 37px;
  width: 36px;
  border: 2px dashed var(--blue);
  border-right: 0;
  border-radius: 16px 0 0 16px;
  opacity: 0.75;
  pointer-events: none;
}
.flow__rail::after {
  content: "";
  position: absolute;
  top: -7px;
  right: -3px;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 9px solid var(--blue);
}

/* Stage 1 — the sources, fenced to read as one group. */
.flow__group {
  border: 1.5px dashed rgba(26, 34, 51, 0.22);
  border-radius: var(--radius-lg);
  padding: var(--s-5) var(--s-5) var(--s-6);
  background: rgba(255, 255, 255, 0.55);
}
.flow__label {
  margin: 0 0 var(--s-5);
  font-size: 0.76rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--muted);
}
.flow__group-inner {
  display: grid;
  grid-template-columns: 1fr 186px;
  gap: var(--s-6);
  align-items: center;
}
.flow__sources {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.flow__group-media {
  display: flex;
  justify-content: center;
}

.src-card {
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.07);
  border-radius: var(--radius);
  padding: var(--s-5);
  box-shadow: var(--shadow-sm);
}
.src-card .feature__icon {
  width: 42px;
  height: 42px;
  border-radius: 12px;
  margin-bottom: var(--s-3);
}
.src-card__title {
  font-size: 1.16rem;
  margin-bottom: var(--s-2);
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.src-card__body {
  margin: 0;
  color: var(--muted);
  font-size: 0.97rem;
}
/* A zoomable screenshot (the Battles cards). Thumbs render small, so they lean
   on the lightbox (data-full) to be readable. */
.shot__zoom {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  border-radius: 12px;
}
.shot__zoom:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
}
.shot__zoom img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
  border: 1px solid rgba(26, 34, 51, 0.12);
  box-shadow: var(--shadow-sm);
}

/* In-page jump link, e.g. "Back up to the engine ↑" in the diagram's end cap. */
.flow__more-link {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--blue);
  text-decoration: none;
}
.flow__more-link:hover {
  text-decoration: underline;
}

/* Connector between stages. */
.flow__arrow {
  width: 2px;
  height: 46px;
  margin: var(--s-4) auto;
  background: var(--muted);
  position: relative;
  opacity: 0.55;
}
.flow__arrow::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -2px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 11px solid var(--muted);
}

/* Stages 2 and 3 — the hub and the engine. */
.flow__node {
  background: #fff;
  border: 1px solid rgba(26, 34, 51, 0.07);
  border-radius: var(--radius-lg);
  padding: var(--s-6);
  box-shadow: var(--shadow-sm);
}
.flow__node--engine {
  display: grid;
  /* Wide enough for the three fanned drills (354px) plus the bleed the
     rotation adds to their bounding box. */
  grid-template-columns: 1fr 390px;
  gap: var(--s-6);
  align-items: center;
}
.flow__node-title {
  font-size: 1.5rem;
  margin-bottom: var(--s-3);
}
.flow__node-body {
  margin: 0;
  color: var(--muted);
}
.flow__node-media {
  display: flex;
  justify-content: center;
}
/* Listen → Speak → Mixed, fanned as a staircase so the sequence reads left to
   right. Each card steps up and rotates further clockwise, and z-index rises
   with it so Mixed — the phase they're climbing toward — finishes on top.
   Kept in normal flow (the hero absolutely positions its phones, which needs a
   fixed-height parent); the overlap comes from negative margins instead.
   Every transform lists translateX, rotate, translateY in that order, fanned
   and stacked alike, so the fan-out interpolates per function rather than
   through a matrix. */
.flow__node-media--fan {
  --fan-overlap: 30px;
  gap: 0;
  align-items: center;
}
.flow__node-media--fan .phone--mini {
  width: 138px;
  padding: 6px;
  border-radius: 24px;
  transition: transform 0.9s cubic-bezier(0.2, 1.15, 0.3, 1) 0.15s;
}
.flow__node-media--fan .phone--mini:nth-child(1) {
  transform: translateX(0) rotate(-6deg) translateY(14px);
  margin-right: calc(-1 * var(--fan-overlap));
  z-index: 1;
  filter: saturate(0.94);
}
.flow__node-media--fan .phone--mini:nth-child(2) {
  transform: translateX(0) rotate(-1deg) translateY(2px);
  margin-right: calc(-1 * var(--fan-overlap));
  z-index: 2;
  filter: saturate(0.97);
}
.flow__node-media--fan .phone--mini:nth-child(3) {
  transform: translateX(0) rotate(5deg) translateY(-12px);
  z-index: 3;
}
/* Before the fan scrolls into view (main.js adds .is-fanned), all three sit
   squared up in the middle slot with Write on top, the two beneath peeking
   out like a deck. One slot is the frame's width less the overlap, and a
   translate's % is of the frame's own width. */
.js .flow__node-media--fan:not(.is-fanned) .phone--mini:nth-child(1) {
  transform: translateX(calc(100% - var(--fan-overlap))) rotate(1deg) translateY(-8px);
}
.js .flow__node-media--fan:not(.is-fanned) .phone--mini:nth-child(2) {
  transform: translateX(0) rotate(3deg) translateY(-10px);
}
.js .flow__node-media--fan:not(.is-fanned) .phone--mini:nth-child(3) {
  transform: translateX(calc(-100% + var(--fan-overlap))) rotate(5deg) translateY(-12px);
}
@media (prefers-reduced-motion: reduce) {
  .flow__node-media--fan .phone--mini {
    transition: none;
  }
}
/* Must follow the rules above, not ride in the earlier 520px block — same
   specificity, so source order decides. */
@media (max-width: 520px) {
  .flow__node-media--fan {
    --fan-overlap: 24px;
  }
  .flow__node-media--fan .phone--mini {
    width: 112px;
  }
}
/* The bezel details are sized for a 232px frame; scale them to match. */
.flow__node-media--fan .phone__notch {
  top: 9px;
  width: 50px;
  height: 12px;
}
.flow__node-media--fan .phone__shot,
.flow__node-media--fan .phone__zoom {
  border-radius: 18px;
}
.flow__chips {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-5) 0 0;
  flex-wrap: wrap;
}
.flow__chip-sep {
  color: var(--muted);
  font-weight: 700;
}
/* Under the engine's step chips: what the pipeline can be shaped into. */
.engine-caption {
  margin: var(--s-3) 0 0;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--muted);
}
.engine-caption strong {
  color: var(--text);
}
.engine-caption + .flow__node-body {
  margin-top: var(--s-4);
}

/* Stage 2 is two pools side by side. The incoming arrow is offset left so it
   lands on Library only — word lists are built in, not fed by the sources
   above. */
.flow__arrow--left {
  margin-inline: 25% auto;
}
/* The sources arrow's caption, beside the stem like the feed labels below.
   No padding or border here, so the arrow's vertical margins collapse through
   and this box is exactly the arrow's height: top 50% centres on the stem. */
.flow__intake {
  position: relative;
}
.flow__intake .flow__conn-label {
  left: calc(25% + 14px);
  top: 50%;
  transform: translateY(-50%);
}

.flow__pools {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.flow__node--pool {
  padding: var(--s-5);
}
.flow__node--pool .flow__node-title {
  font-size: 1.3rem;
  margin-bottom: var(--s-2);
}
.flow__pool-tag {
  margin: 0 0 var(--s-3);
  font-size: 0.74rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--blue);
}

/* The Y-merge: a stub up into each pool, a bar across, one stem down. */
.flow__merge {
  position: relative;
  height: 68px;
  /* No top margin: the stubs must start at the pool cards' bottom edge or the
     merge reads as a floating bar rather than a connection. */
  margin-top: 0;
}
.flow__merge-bar {
  position: absolute;
  left: 25%;
  right: 25%;
  top: 30px;
  border-top: 2px solid var(--muted);
  opacity: 0.55;
}
.flow__merge-bar::before,
.flow__merge-bar::after {
  content: "";
  position: absolute;
  top: -30px;
  width: 2px;
  height: 30px;
  background: var(--muted);
}
.flow__merge-bar::before {
  left: -1px;
}
.flow__merge-bar::after {
  right: -1px;
}
.flow__merge::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 30px;
  width: 2px;
  height: 26px;
  transform: translateX(-50%);
  background: var(--muted);
  opacity: 0.55;
}
.flow__merge::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 54px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 11px solid var(--muted);
  opacity: 0.55;
}
.flow__merge-label {
  margin: 0 0 var(--s-5);
  text-align: center;
  color: var(--muted);
  font-size: 0.95rem;
}
.flow__merge-label strong {
  color: var(--text);
}

/* Stage 4 — the diagram's end: a slim stand-in for the engine card, which
   leads the section. Both the words stem and the lines lane land on it. */
.flow__endcap {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
}
.flow__endcap .feature__icon {
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  margin: 0;
}
.flow__endcap-title {
  flex: 1;
  margin: 0;
  font-size: 1.16rem;
  font-weight: 800;
}
/* One row squeezed the title onto three lines on phones: give the link its own
   row, lined up under the title (icon 42px + gap). */
@media (max-width: 600px) {
  .flow__endcap {
    flex-wrap: wrap;
    row-gap: var(--s-1);
  }
  .flow__endcap .flow__more-link {
    flex-basis: 100%;
    padding-left: calc(42px + var(--s-4));
  }
}

/* The feedback edge's caption, running up beside the dashed rail and read
   bottom to top, the way the edge flows. Sits in the gap between the rail
   (8-10px) and the cards (58px), starting just above the rail's bottom curve.
   No .reveal on it: .reveal.in's transform: none would undo the rotation. */
.flow__return {
  position: absolute;
  left: 18px;
  bottom: 64px;
  margin: 0;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  line-height: 1.3;
  color: var(--blue);
  font-size: 0.98rem;
  font-style: italic;
}

.flow__node-body + .flow__node-body {
  margin-top: var(--s-3);
}

/* --------------------------- Stage 5: battle ---------------------------
   The diagram's way out: a stem down from the engine cap into a dark card
   that previews #battles. Both sit after .flow (so the rail still ends on
   the cap) and borrow its 58px inset to line up with the cards above. */
.flow-exit {
  position: relative;
  height: 84px;
  margin-left: 58px;
}
.flow-exit__stem {
  position: absolute;
  left: 50%;
  top: 0;
  width: 2px;
  height: 74px;
  transform: translateX(-50%);
  background: var(--orange);
}
.flow-exit__stem::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -10px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 10px solid var(--orange);
}
.flow-exit__label {
  left: calc(50% + 16px);
  top: 30px;
}

.battle-step {
  position: relative;
  overflow: hidden;
  margin-left: 58px;
  padding: var(--s-7) clamp(1.5rem, 3.5vw, 2.5rem);
  border-radius: var(--radius-lg);
  background: var(--ink);
  color: var(--on-dark);
  box-shadow: var(--shadow-md);
}
.battle-step__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(50% 60% at 15% 0%, rgba(255, 140, 66, 0.16), transparent 70%),
    radial-gradient(45% 55% at 90% 100%, rgba(65, 104, 247, 0.18), transparent 70%);
  pointer-events: none;
}
.hanzi-watermark--step {
  font-size: 18rem;
  right: -1rem;
  bottom: -4rem;
  opacity: 0.04;
}
.battle-step__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-6) clamp(1.5rem, 4vw, 2.5rem);
  align-items: center;
}
.battle-step__icon {
  background: rgba(255, 140, 66, 0.16);
  color: var(--orange);
}
/* --green is tuned for the battles section's eyebrow; the lighter green holds
   up better at this card's smaller eyebrow size on ink. */
.eyebrow--green-bright {
  color: var(--green-bright);
}
.battle-step__title {
  margin: 0 0 var(--s-3);
  font-size: clamp(1.6rem, 3vw, 2rem);
  line-height: 1.15;
}
.battle-step__body {
  margin: 0 0 var(--s-5);
  color: var(--on-dark-muted);
  font-size: 1.06rem;
}
/* --blue sits near 4:1 on --ink; lift it for the dark card. */
.battle-step__link {
  color: #8ea4ff;
}
.battle-step__types {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-3);
}
/* Near-opaque for the same reason as .battles .feature-card: at low alpha the
   戰 watermark showed through the rows as grey blocks. */
.battle-step__type {
  display: block;
  padding: var(--s-4) 1.25rem;
  border-radius: var(--radius);
  background: rgba(19, 28, 48, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.09);
  color: var(--on-dark);
  text-decoration: none;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
.battle-step__type:hover,
.battle-step__type:focus-visible {
  color: var(--on-dark); /* outranks the global a:hover blue */
  border-color: rgba(255, 140, 66, 0.55);
  background: rgba(27, 38, 62, 0.96);
}
.battle-step__type:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}
.battle-step__types strong {
  display: block;
  margin-bottom: var(--s-1);
}
.battle-step__types span {
  color: var(--on-dark-muted);
  font-size: 0.92rem;
}
@media (max-width: 840px) {
  /* .flow drops its inset here, so these follow. */
  .flow-exit,
  .battle-step {
    margin-left: 0;
  }
  .battle-step__inner {
    grid-template-columns: 1fr;
  }
  .hanzi-watermark--step {
    font-size: 12rem;
  }
}

/* --------------------------- The ramp ---------------------------
   A third row under the pools. Library items and single sentences drop into
   the ramp; words leave it through the dial, and lines take their own lane
   straight to the engine. Word lists skip the ramp entirely. Every connector
   sits on a quarter of the two-column pools grid (gap --s-5 = 24px), so it
   lines up with the cards above and below it:
     12.5% - 3px   left column, 1st quarter: sentences in, lines out
     37.5% - 9px   left column, 3rd quarter: Library items in, words out
     75% + 6px     right column centre: the word-list line, past the ramp */
.flow__feed {
  position: relative;
  height: 64px;
}
.flow__drop,
.flow__through,
.flow__bypass-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--muted);
  opacity: 0.55;
  transform: translateX(-1px);
}
.flow__drop--a {
  left: calc(12.5% - 3px);
}
.flow__drop--b {
  left: calc(37.5% - 9px);
}
.flow__through {
  left: calc(75% + 6px);
}
.flow__bypass-line {
  left: 50%;
}
.flow__drop::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -2px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 11px solid var(--muted);
}
.flow__ramp-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
}
.flow__bypass {
  position: relative;
}

/* Small caps beside a connector, in the .flow__label voice. */
.flow__conn-label {
  position: absolute;
  margin: 0;
  font-size: 0.7rem;
  font-weight: 800;
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  white-space: nowrap;
}
.flow__conn-label--a {
  left: calc(12.5% + 9px);
}
.flow__conn-label--b {
  left: calc(37.5% + 3px);
}
.flow__feed .flow__conn-label {
  top: 50%;
  transform: translateY(-50%);
}
.flow__bypass .flow__conn-label {
  left: calc(50% + 14px);
  top: 50%;
  transform: translateY(-50%);
  white-space: normal;
  max-width: 13rem;
}
.flow__conn-narrow {
  display: none;
}

/* Out of the ramp. The Y-merge moves right to join the words stub (3rd
   quarter) to the word-list line (right column centre); its stem sits at the
   midpoint, and the caption centres under that stem. */
.flow__join {
  position: relative;
  display: flow-root;
}
.flow__join .flow__conn-label--a {
  top: 40px;
}
.flow__join .flow__conn-label--b {
  top: 6px;
}
.flow__join .flow__merge-bar {
  left: calc(37.5% - 9px);
  right: calc(25% - 6px);
}
.flow__join .flow__merge::before,
.flow__join .flow__merge::after {
  left: calc(56.25% - 1.5px);
}
.flow__join .flow__merge-label {
  padding-left: calc(12.5% - 3px);
}
/* Dashed: a line waits on the ramp, then goes straight to the engine. */
.flow__lane {
  position: absolute;
  left: calc(12.5% - 3px);
  top: 0;
  bottom: 0;
  width: 0;
  border-left: 2px dashed var(--muted);
  opacity: 0.55;
  transform: translateX(-1px);
}
.flow__lane::after {
  content: "";
  position: absolute;
  left: -8px;
  bottom: -2px;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 11px solid var(--muted);
}

/* The ramp chart: one item over time. 2px lines, 8px end dots with a 2px
   surface ring, hairline grid, text in ink (never the series colour). Blue and
   the app's Learning orange pass the colour-blind separation check; the orange
   is under 3:1 on white, so both lines also carry direct end labels. */
.ramp-chart {
  margin: var(--s-4) 0 0;
}
.ramp-chart__head {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  margin-bottom: var(--s-2);
}
.ramp-chart__title {
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--text);
}
.ramp-chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--muted);
}
.ramp-chart__key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ramp-chart__swatch {
  width: 14px;
  height: 3px;
  border-radius: 2px;
}
.ramp-chart__swatch--words {
  background: var(--blue);
}
.ramp-chart__swatch--lines {
  background: var(--learning);
}
.ramp-chart__plot {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}
.ramp-chart__grid {
  stroke: #e6e9ef;
  stroke-width: 1;
}
.ramp-chart__tick {
  font-size: 12px;
  fill: var(--muted);
}
.ramp-chart__end {
  font-size: 12px;
  font-weight: 700;
  fill: var(--text);
}
.ramp-chart__marker {
  fill: var(--muted);
}
.ramp-chart__line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.ramp-chart__line--words {
  stroke: var(--blue);
}
.ramp-chart__line--lines {
  stroke: var(--learning);
}
.ramp-chart__dot {
  stroke: #fff;
  stroke-width: 2;
}
.ramp-chart__dot--words {
  fill: var(--blue);
}
.ramp-chart__dot--lines {
  fill: var(--learning);
}

/* The screenshots run ~2.2x taller than the copy beside them, which leaves the
   bordered stages half empty. Narrow the frames inside the flow so each stage
   is sized by its content rather than by the phone. */
.flow .phone--mini {
  width: 172px;
}
/* ...except the engine's fan, which budgets 3 x 138px (see the fan rules
   above). Without this the rule above wins on source order, and on phones the
   fan overflows the card and pushes the engine text off-screen. */
.flow .flow__node-media--fan .phone--mini {
  width: 138px;
}
@media (max-width: 520px) {
  .flow .flow__node-media--fan .phone--mini {
    width: 112px;
  }
}

/* Flow, narrow screens. This must live AFTER the flow base rules above —
   same specificity, so source order decides. */
@media (max-width: 840px) {
  .flow {
    padding-left: 0;
  }
  .flow__rail {
    display: none;
  }
  /* No rail to ride, so the caption goes back under the end cap. */
  .flow__return {
    position: static;
    margin: var(--s-4) 0 0;
    writing-mode: horizontal-tb;
    transform: none;
    white-space: normal;
    text-align: center;
  }
  .flow__group-inner,
  .flow__node--engine {
    grid-template-columns: 1fr;
    gap: var(--s-5);
  }
  .flow__sources {
    grid-template-columns: 1fr;
  }
  .flow__group-media {
    order: -1;
  }
  .flow__node--engine .flow__node-media {
    order: -1;
  }
  /* Pools stack, so the Y-merge collapses to a single centred stem. */
  .flow__pools {
    grid-template-columns: 1fr;
  }
  .flow__arrow--left {
    margin-inline: auto;
  }
  .flow__intake .flow__conn-label {
    left: calc(50% + 14px);
    max-width: calc(50% - 14px);
    white-space: normal;
  }
  .flow__merge {
    height: 46px;
  }
  .flow__merge-bar {
    display: none;
  }
  .flow__merge::before {
    top: 8px;
  }
  .flow__merge::after {
    top: 32px;
  }
  /* Stacked cards leave no route for the side lines: one arrow feeds the
     ramp, one stem leaves it, and the captions carry the rest. */
  .flow__ramp-row {
    grid-template-columns: 1fr;
  }
  .flow__bypass,
  .flow__through,
  .flow__drop--b,
  .flow__lane,
  .flow__join .flow__conn-label,
  .flow__feed .flow__conn-label--b {
    display: none;
  }
  .flow__drop--a {
    left: 50%;
  }
  .flow__feed .flow__conn-label--a {
    left: calc(50% + 14px);
    max-width: calc(50% - 14px);
    white-space: normal;
  }
  .flow__join .flow__merge::before,
  .flow__join .flow__merge::after {
    left: 50%;
  }
  .flow__join .flow__merge-label {
    padding-left: 0;
  }
  .flow__conn-wide {
    display: none;
  }
  .flow__conn-narrow {
    display: inline;
  }
}

/* --------------------------- Screenshot zoom ---------------------------
   The inline shots are ~150-210px wide; clicking one opens the full-res
   capture. The trigger is a real <button> so it is keyboard-reachable. */

.phone__zoom {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 30px;
  cursor: zoom-in;
}
.phone__zoom:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: rgba(6, 12, 26, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  cursor: zoom-out;
}
.lightbox[hidden] {
  display: none;
}
.lightbox__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  max-height: 100%;
}
.lightbox__img {
  max-width: min(420px, 100%);
  max-height: calc(100vh - 8rem);
  width: auto;
  height: auto;
  border-radius: 22px;
  box-shadow: var(--shadow-phone);
  cursor: default;
}
/* Landscape captures (the browser companion) — the 420px phone cap would open
   them smaller than they already render inline. */
.lightbox__img--wide {
  max-width: min(1280px, 100%);
  border-radius: 12px;
}
.lightbox__caption {
  max-width: 46ch;
  text-align: center;
  color: var(--on-dark-muted);
  font-size: 0.92rem;
}
.lightbox__close {
  position: absolute;
  top: clamp(0.75rem, 2vw, 1.5rem);
  right: clamp(0.75rem, 2vw, 1.5rem);
  width: 44px;
  height: 44px;
  font-size: 1.9rem;
  line-height: 1;
  color: var(--on-dark);
  background: rgba(242, 245, 250, 0.12);
  border: 1px solid rgba(242, 245, 250, 0.22);
  border-radius: 999px;
  cursor: pointer;
}
.lightbox__close:hover {
  background: rgba(242, 245, 250, 0.2);
}

/* Plus items now lead with a bold benefit and explain it underneath, so give
   them more room and mute the explanatory half — the lead still has to scan
   in a glance. */
.checklist--pro {
  gap: var(--s-4);
}
.checklist--pro li {
  color: var(--muted);
  font-size: 0.98rem;
  line-height: 1.55;
}
.checklist--pro li strong {
  color: var(--text);
}
/* The Cloud card splits its list by where you use it. The wrapper takes over
   the checklist's stretch, so the CTA still lines up with the Free card's. */
.plan__groups {
  flex: 1;
  margin-bottom: var(--s-6);
}
.plan__groups .checklist {
  flex: none;
  margin-bottom: var(--s-5);
}
.plan__groups .checklist:last-child {
  margin-bottom: 0;
}
.plan__group {
  margin: 0 0 var(--s-3);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}
/* Journeys leads the Cloud list with a peek at a real Journey: a top crop of
   the screenshot under its text, captioned so it reads as clickable. It opens
   full-size in the lightbox. */
.plan__shot {
  width: 140px;
  margin-top: var(--s-3);
  text-align: left;
}
.plan__shot img {
  aspect-ratio: 3 / 4;
  object-fit: cover;
  object-position: top;
}
.plan__shot-cap {
  display: block;
  margin-top: var(--s-2);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--blue);
}
.plan__shot:hover .plan__shot-cap {
  text-decoration: underline;
}

/* --------------------------- Battles ---------------------------
   The app's namesake, on a dark band between the ladder and the four strands.
   Reuses the .strands grid but NOT the light .feature-card chrome: that card is
   white, while everything inside a .section--dark inherits near-white text, so
   the titles landed white-on-white. Glass cards sitting on the dark instead —
   the same treatment as .pill-chip and the companion panel. */

.battles__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(
      50% 55% at 20% 0%,
      rgba(255, 140, 66, 0.16),
      transparent 70%
    ),
    radial-gradient(45% 50% at 85% 100%, rgba(65, 104, 247, 0.18), transparent 70%);
  pointer-events: none;
}
/* Lifts the content over the 戰 watermark, which is positioned and would
   otherwise paint above these in-flow cards. */
.battles__inner {
  position: relative;
  z-index: 1;
}

/* Near-opaque on purpose: at low alpha the 戰 watermark behind the section
   showed through the cards as grey blocks that read as a rendering glitch. */
.battles .feature-card {
  position: relative;
  overflow: hidden;
  background: rgba(19, 28, 48, 0.94);
  border: 1px solid rgba(242, 245, 250, 0.14);
  box-shadow: none;
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}
/* Belt stripe across the top of each card, echoing .belt-divider. */
.battles .feature-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: linear-gradient(90deg, var(--blue), var(--teal) 55%, var(--orange));
  opacity: 0.9;
}
.battles .feature-card:hover {
  background: rgba(26, 38, 64, 0.96);
  border-color: rgba(242, 245, 250, 0.26);
  transform: translateY(-2px);
}
/* Battles deal in from the right, like cards hitting the table. The card's
   transition above overrides .reveal's (same specificity, later), which also
   dropped the fade, so the entry is a keyframe animation instead — a stagger
   delay on the transition would lag the hover too. Backwards fill only, so no
   end frame holds the transform and the hover lift works once it's dealt. */
.js .battles .feature-card.reveal.in {
  animation: battle-deal 0.7s cubic-bezier(0.2, 1, 0.3, 1) backwards;
}
@keyframes battle-deal {
  from {
    opacity: 0;
    transform: translateX(64px) rotate(3deg);
  }
}
/* .strands adds a top margin for the phase-tag chip these cards don't carry. */
.battles .feature__title {
  margin-top: 0;
  font-size: 1.3rem;
  color: var(--on-dark);
}
.battles .feature__body {
  color: var(--on-dark-muted);
}
/* Each battle card leads with its screen: centred, and tappable into the
   shared lightbox. The light phone UI needs a light edge and a deeper shadow
   to sit on the dark card; the default .shot__zoom border is dark-on-light. */
.battles .shot__zoom {
  width: min(150px, 60%);
  margin: 0 auto var(--s-4);
}
.battles .shot__zoom img {
  border-color: rgba(242, 245, 250, 0.16);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
}
.battles .how__close {
  color: var(--on-dark-muted);
}
.battles .how__close strong {
  color: var(--on-dark);
}
.battles__cta {
  margin: var(--s-5) 0 0;
  text-align: center;
}
/* Three battle types, so one row of three rather than the 2×2 the four strands
   use (which would leave a hole). Below 841px the shared .strands rule stacks. */
@media (min-width: 841px) {
  .battles .strands {
    grid-template-columns: repeat(3, 1fr);
  }
  /* In one row all three reveal together, so deal them in turn. Stacked, each
     reveals as it's scrolled to and needs no delay. */
  .js .battles .feature-card.reveal.in:nth-child(2) {
    animation-delay: 0.14s;
  }
  .js .battles .feature-card.reveal.in:nth-child(3) {
    animation-delay: 0.28s;
  }
}

@media (prefers-reduced-motion: reduce) {
  .battles .feature-card {
    transition: none;
  }
  .js .battles .feature-card.reveal.in {
    animation: none;
  }
  .battles .feature-card:hover {
    transform: none;
  }
}

/* ------------------------------ Hero reel ------------------------------
   One upright phone, three iPhone screen recordings swiping through it
   (main.js heroReel). The phone takes its height from the clips (720x1560,
   a hair off .phone's aspect-ratio) so they fill the screen uncropped. The
   recordings carry their own status bar; the notch is resized to sit over
   the Dynamic Island they show, which the Simulator draws only while the
   mic is live — covering it keeps it from popping in and out. */
.hero-reel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
}
.phone--hero {
  width: 290px;
  aspect-ratio: auto;
}
/* Percentages of the phone box, so the notch tracks the recorded island at
   every frame width. */
.phone--hero .phone__notch {
  top: 3.1%;
  width: 27%;
  height: 4%;
}
.hero-reel__screen {
  position: relative;
  border-radius: 30px;
  overflow: hidden;
  background: #fff;
  /* Safari drops the rounded clip on transformed children without its own
     compositing layer. */
  transform: translateZ(0);
  /* Horizontal swipes step the reel; vertical ones still scroll the page. */
  touch-action: pan-y;
}
.hero-reel__slide {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 720 / 1560;
  background: #fff;
  transform: translateX(100%);
  transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}
/* The first clip stays in flow and gives the screen its height; the other
   two stack over it. */
.hero-reel__slide:first-child {
  position: relative;
  transform: none;
}
/* Without JS the first clip loops on its own and the controls would do
   nothing, so they only appear once main.js is running. */
.hero-reel__controls {
  display: none;
}
.js .hero-reel__controls {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  width: min(360px, 100%);
}
.hero-reel__tabs {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.hero-reel__tab {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 6px 0;
  border: 0;
  background: none;
  color: var(--on-dark-muted);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  cursor: pointer;
}
.hero-reel__tab.is-active {
  color: var(--on-dark);
}
.hero-reel__tab:focus-visible,
.hero-reel__toggle:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
}
.hero-reel__tab:focus-visible {
  border-radius: 6px;
}
.hero-reel__bar {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: rgba(242, 245, 250, 0.18);
  overflow: hidden;
}
.hero-reel__fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--blue), var(--teal));
  transform: scaleX(0);
  transform-origin: left;
}
.hero-reel__toggle {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  margin-top: 2px;
  padding: 0;
  border: 1px solid rgba(242, 245, 250, 0.25);
  border-radius: 50%;
  background: rgba(242, 245, 250, 0.06);
  color: var(--on-dark);
  cursor: pointer;
}
.hero-reel__toggle .icon-play,
.hero-reel__toggle.is-paused .icon-pause {
  display: none;
}
.hero-reel__toggle.is-paused .icon-play {
  display: block;
}
/* After the shared .phone width steps (same specificity, so source order). */
@media (max-width: 520px) {
  .phone--hero {
    width: 236px;
  }
}
@media (max-width: 380px) {
  .phone--hero {
    width: 210px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-reel__slide {
    transition: none;
  }
}


/* ------------------------------ Video catalog ------------------------------
   Design: "LinguaRamp Catalog Section" Claude Design canvas, artboard A.
   Copy on the left, an illustrative picture of the catalog's list view on the
   right; the two stack on narrow screens. The preview uses the catalog's own
   light surfaces and tier colours (LinguaCatalog site/app/assets/tokens.css). */

.catalog__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(2.5rem, 5vw, 3.5rem);
}
.catalog__copy {
  flex: 1 1 340px;
  min-width: 0;
}
.catalog__lede {
  margin: var(--s-4) 0 0;
  color: var(--on-dark-muted);
  font-size: 1.12rem;
}
.catalog__points {
  list-style: none;
  margin: var(--s-6) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.catalog__points li {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  font-weight: 700;
}
.catalog__icon {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.catalog__icon--blue { background: rgba(65, 104, 247, 0.16); color: #8ea6ff; }
.catalog__icon--orange { background: rgba(255, 140, 66, 0.16); color: #ffb07a; }
.catalog__icon--teal { background: rgba(1, 180, 162, 0.16); color: #4fd6c6; }
.catalog__cta {
  margin-top: var(--s-6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-5);
}
.catalog__note {
  color: var(--on-dark-muted);
  font-size: 0.9rem;
}
.hanzi-watermark--catalog {
  font-size: 26rem;
  top: 2rem;
  right: -2.5rem;
  opacity: 0.035;
}

.catalog-preview {
  flex: 1 1 480px;
  min-width: 0;
  max-width: 580px;
  margin: 0 auto;
  background: #f3f4f6;
  color: #1f2328;
  border-radius: 20px;
  box-shadow: var(--shadow-phone);
  overflow: hidden;
  display: block;
  text-decoration: none;
}
/* `.catalog a.` outranks `.js .reveal` / `.js .reveal.in`, which would
   otherwise reset the transition and cancel the hover lift. The fade-in
   keeps its own 0.6s timing. */
.catalog a.catalog-preview {
  transition: opacity 0.6s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.catalog a.catalog-preview:hover {
  color: #1f2328;
  transform: translateY(-3px);
  box-shadow: 0 30px 70px rgba(6, 12, 26, 0.55);
}
@media (prefers-reduced-motion: reduce) {
  .catalog a.catalog-preview,
  .catalog a.catalog-preview:hover {
    transition: none;
    transform: none;
  }
}
.catalog-preview__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  background: #e6e8ec;
}
.catalog-preview__dots {
  display: flex;
  gap: 6px;
}
.catalog-preview__dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #c7cbd2;
}
.catalog-preview__url {
  flex: 1;
  text-align: center;
  font-size: 0.8rem;
  color: #57606a;
  background: #fff;
  border-radius: 8px;
  padding: 5px 10px;
}
.catalog-preview__body {
  padding: 16px;
}
.catalog-preview__head {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 0 2px 10px;
  font-size: 0.8rem;
}
.catalog-preview__sort {
  font-weight: 700;
  color: #3d444d;
}
.catalog-preview__example {
  color: #57606a;
}
.catalog-preview__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.catalog-tile {
  background: #fff;
  border-radius: 12px;
  padding: 10px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.catalog-tile__thumb {
  position: relative;
  flex: none;
  width: 160px;
  height: 90px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.catalog-tile__thumb--green { background: #2f7d4f; color: #eaf7ee; }
.catalog-tile__thumb--teal { background: #2a6f8f; color: #e6f4fa; }
.catalog-tile__thumb--orange { background: #b5652a; color: #fff3e8; }
.catalog-tile__thumb--purple { background: #6a4bb8; color: #f1ecfb; }
.catalog-tile__glyph {
  font-size: 2.75rem;
  font-weight: 700;
  line-height: 1;
}
.catalog-tile__length {
  position: absolute;
  right: 6px;
  bottom: 6px;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
}
.catalog-tile__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.catalog-tile__title {
  font-size: 0.95rem;
  line-height: 1.3;
}
.catalog-tile__meta {
  font-size: 0.75rem;
  color: #57606a;
}
.catalog-tile__stats {
  font-size: 0.75rem;
  color: #3d444d;
}
.catalog-tile__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.catalog-chip {
  padding: 2px 9px;
  border-radius: 999px;
  background: #f0f1f4;
  color: #3d444d;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}
.catalog-chip--beginner { background: #e8f5ec; color: #11602e; font-weight: 700; }
.catalog-chip--preint { background: #e3f3f6; color: #0b5c72; font-weight: 700; }
.catalog-chip--int { background: #fff1e0; color: #8a4b00; font-weight: 700; }
.catalog-chip--upper { background: #fde9e2; color: #9a330a; font-weight: 700; }

@media (max-width: 520px) {
  .catalog-preview__body {
    padding: 12px;
  }
  .catalog-tile {
    gap: 10px;
  }
  .catalog-tile__thumb {
    width: 104px;
    height: 58px;
  }
  .catalog-tile__glyph {
    font-size: 1.9rem;
  }
  .catalog-tile__meta {
    display: none;
  }
  .hanzi-watermark--catalog {
    font-size: 14rem;
  }
}
