/* =============================================================
   POLARITY — marketing site
   Pure CSS design system. Dark theme only.
   Palette lifted from the app. No external fonts or CDNs.
   ============================================================= */

/* ---------- Design tokens ---------- */
:root {
  /* Surfaces — base is pure black; cards keep a faint neutral lift */
  --bg:        #000000;
  --bg-lift:   #070709;
  --card:      #0B0B0E;
  --card-hi:   #101014;

  /* Brand */
  --violet:      #7A5CDB;
  --violet-deep: #57367A;
  --violet-light:#C9B8FF; /* light violet — inline links, accents */
  --grad-primary: linear-gradient(135deg, var(--violet) 0%, var(--violet-deep) 100%);
  --grad-cta:     linear-gradient(100deg, #7E64E6 0%, #7A5CDB 50%, #9463C9 100%);

  /* Category accents */
  --teal:   #45BCB3;
  --pink:   #E05C94;
  --orange: #EB732E;
  --green:  #5CAD57;
  --blue:   #578FF0;
  --yellow: #D1C238;

  /* Text */
  --text:      #F2F3F7;
  --text-soft: #C4C8D4;
  --text-dim:  #9BA0AE;
  --text-faint:#7C8394; /* AA-compliant (~5.3:1) for small print on --bg */

  /* Lines & glass */
  --hairline:   rgba(255, 255, 255, .06);
  --hairline-2: rgba(255, 255, 255, .10);
  --glass:      rgba(255, 255, 255, .025);

  /* Radii */
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 34px;

  /* Rhythm */
  --pad: clamp(20px, 5vw, 40px);
  --section-y: clamp(88px, 13vw, 160px);
  --maxw: 1180px;
  --nav-h: 61px;   /* sticky nav-inner (60px) + 1px border */
  /* The one reading measure. A length, not ch, deliberately: ch resolves
     against each element's own font-size, so 62ch on a .92rem caption is a
     different pixel width than 62ch on 1rem prose — and the two centre onto
     different axes. 39rem is the prose's old 62ch, now shared exactly. */
  --measure: 39rem;
  /* The one interior inset. Every bordered box on the essay opens its content
     at exactly this distance, so card interiors share a single edge instead of
     landing 2-3px apart and reading as a wobble. */
  --inset: clamp(26px, 3.2vw, 38px);

  /* Motion */
  --ease: cubic-bezier(.22, .61, .36, 1);
  --dur:  .28s;

  --font: -apple-system, "SF Pro Display", "SF Pro Text", system-ui,
          "Inter", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------- Reset / base ---------- */
* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h); /* anchors land clear of the sticky nav */
  -webkit-text-size-adjust: 100%;
}

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

body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
}

/* Ambient background field — soft violet/blue/pink glows over the black base.
   Drifts on scroll (parallax, --aurora-y set in JS). Generously oversized and
   broadly spread so the drift never reveals an edge and never opens a black gap
   between the glows. The bright centres sit BELOW the nav strip so the sticky
   header stays neutral. */
body::before {
  content: "";
  position: fixed;
  inset: -24%;
  z-index: -2;
  background:
    /* PURPLE is the whole field — one broad violet wash + a violet base so the
       hero melts into purple (never a jump to purple-red). RED is only a small,
       dim secondary accent off to the lower right. Kept dim overall. */
    radial-gradient(96% 78% at 48% 50%,   rgba(122, 92, 219, .16), transparent 74%),
    radial-gradient(120% 72% at 51% 84%,   rgba(108, 80, 205, .15), transparent 78%),
    radial-gradient(46% 40% at 76% 68%,    rgba(198, 74, 118, .09), transparent 72%);
  transform: translate3d(0, var(--aurora-y, 0px), 0);
  pointer-events: none;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  body::before { transform: none; }
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  /* subtle film grain via layered gradient — keeps flat dark from banding */
  background-image: radial-gradient(rgba(255,255,255,.015) 1px, transparent 1px);
  background-size: 3px 3px;
  opacity: .5;
  pointer-events: none;
}

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

a { color: inherit; text-decoration: none; }

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

::selection { background: rgba(122, 92, 219, .35); color: #fff; }

/* ---------- Layout ---------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.section { padding-block: var(--section-y); position: relative; }

.skip-link {
  position: absolute;
  left: 12px; top: -60px;
  background: var(--violet);
  color: #fff;
  padding: 10px 16px;
  border-radius: 10px;
  z-index: 200;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: 12px; }

/* ---------- Typography ---------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 20px;
}
.eyebrow::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 12px 2px rgba(122, 92, 219, .8);
}

h1, h2, h3 {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.04;
  color: var(--text);
}

.h-section {
  font-size: clamp(2.1rem, 5.2vw, 3.4rem);
  letter-spacing: -0.035em;
}

.lead {
  font-size: clamp(1.05rem, 2.4vw, 1.3rem);
  color: var(--text-soft);
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.gradient-text {
  background: linear-gradient(100deg, var(--violet-light) 0%, #7A5CDB 30%, #578FF0 55%, var(--violet-light) 80%, #7A5CDB 100%);
  background-size: 220% auto;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: shimmer 8s linear infinite;
}
@keyframes shimmer { to { background-position: 220% center; } }
@media (prefers-reduced-motion: reduce) {
  .gradient-text { animation: none; background-position: 0 center; }
}

/* ---------- Nav ---------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
  border-bottom: 1px solid transparent;
  /* Subtle top scrim — keeps the header neutral over the ambient glow at the very
     top of the page (before it gains its scrolled glass), fading to nothing so
     there's no hard band. */
  background: linear-gradient(180deg, rgba(2, 3, 7, .9) 0%, rgba(2, 3, 7, .5) 48%, transparent 100%);
}
.nav.scrolled {
  /* Nearly opaque near-black so the ambient background glow can't tint the
     header — it reads the same neutral dark it always has, over any section. */
  background: rgba(4, 5, 10, .88);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--hairline);
}
.nav-inner {
  display: flex;
  align-items: center;
  /* flex-start (not space-between): the language switcher owns the corner,
     the wordmark sits just right of it, and .nav-links pushes itself to the
     far edge with margin-left:auto. */
  justify-content: flex-start;
  height: 60px;
  /* Full-bleed: the bar spans the whole page width (no --maxw cap), with just
     the page gutter keeping the wordmark and links off the edges. */
  padding-inline: var(--pad);
}
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: .26em;
  color: var(--text);
  padding-right: .1em;
}
.wordmark .moon {
  width: 20px; height: 20px;
  flex: none;
  filter: drop-shadow(0 0 8px rgba(122, 92, 219, .6));
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.nav-links a {
  font-size: .92rem;
  color: var(--text-dim);
  padding: 8px 14px;
  border-radius: 10px;
  white-space: nowrap;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.nav-links a:hover { color: var(--text); background: var(--glass); }
.nav-links a.active { color: var(--text); }
.nav-links a[data-spy].active::after {
  content: "";
  display: block;
  height: 2px;
  margin: 3px 4px -5px;
  border-radius: 2px;
  background: var(--violet);
  box-shadow: 0 0 10px rgba(122, 92, 219, .8);
  animation: spyIn var(--dur) var(--ease);
}
@keyframes spyIn { from { transform: scaleX(0); opacity: 0; } to { transform: none; opacity: 1; } }
.nav-links a.nav-cta {
  color: #fff;
  background: rgba(122, 92, 219, .16);
  border: 1px solid rgba(122, 92, 219, .4);
  margin-left: 8px;
}
.nav-links a.nav-cta.active::after { display: none; }
.nav-links a.nav-cta:hover {
  background: rgba(122, 92, 219, .28);
  box-shadow: 0 0 24px rgba(122, 92, 219, .3);
}

/* Hamburger — hidden on desktop */
.nav-burger {
  display: none;
  position: relative;
  /* On phones .nav-links is position:absolute, so the burger (not the links)
     is what needs to claim the right edge. */
  margin-left: auto;
  /* 44x44 is Apple's minimum touch target (HIG); this stood 2px under it */
  width: 44px; height: 44px;
  border: 1px solid var(--hairline-2);
  border-radius: var(--r-sm);
  background: var(--glass);
  cursor: pointer;
  padding: 0;
}
.nav-burger span {
  position: absolute;
  left: 11px;
  width: 20px; height: 1.6px;
  border-radius: 2px;
  background: var(--text);
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease), top var(--dur) var(--ease);
}
.nav-burger span:nth-child(1) { top: 14px; }
.nav-burger span:nth-child(2) { top: 20px; }
.nav-burger span:nth-child(3) { top: 26px; }
.nav.open .nav-burger span:nth-child(1) { top: 20px; transform: rotate(45deg); }
.nav.open .nav-burger span:nth-child(2) { opacity: 0; }
.nav.open .nav-burger span:nth-child(3) { top: 20px; transform: rotate(-45deg); }

/* Language switcher — a small globe pill in the corner, with a dropdown of
   the five site languages. Selecting one records the choice in a cookie so
   the edge redirect on "/" can honor it (see vercel.json + main.js). */
.lang-switch {
  position: relative;
  flex: none;
  margin-right: 16px; /* the gap that nudges the wordmark rightward */
}
.lang-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 10px 0 12px;
  border: 1px solid var(--hairline-2);
  border-radius: 999px;
  background: var(--glass);
  color: var(--text-dim);
  font: inherit;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .09em;
  cursor: pointer;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.lang-btn:hover { color: var(--text); background: rgba(122, 92, 219, .16); }
.lang-btn svg { width: 15px; height: 15px; flex: none; }
.lang-btn .lang-chevron {
  width: 12px; height: 12px;
  transition: transform var(--dur) var(--ease);
}
.lang-switch.open .lang-chevron { transform: rotate(180deg); }
.lang-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 172px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: rgba(6, 8, 15, .92);
  backdrop-filter: saturate(180%) blur(22px);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility 0s var(--dur);
  z-index: 120;
}
.lang-switch.open .lang-menu {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility 0s;
}
.lang-menu a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 10px;
  color: var(--text-dim);
  font-size: .92rem;
  white-space: nowrap;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.lang-menu a:hover { color: var(--text); background: var(--glass); }
.lang-menu a[aria-current="true"] { color: var(--text); }
.lang-menu a[aria-current="true"]::after {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 8px rgba(122, 92, 219, .8);
}
/* The tightest phones: drop the chevron so the pill never crowds the wordmark */
@media (max-width: 400px) {
  .lang-btn { gap: 6px; padding: 0 11px; }
  .lang-btn .lang-chevron { display: none; }
  .lang-switch { margin-right: 12px; }
}

/* 980px (not 860): six links + the CTA need the extra room before collapsing */
@media (max-width: 980px) {
  .nav-burger { display: block; }
  .nav-links {
    position: absolute;
    top: 60px; left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding: 12px var(--pad) 20px;
    background: rgba(6, 8, 15, .86);
    backdrop-filter: saturate(180%) blur(22px);
    -webkit-backdrop-filter: saturate(180%) blur(22px);
    border-bottom: 1px solid var(--hairline);
    transform: translateY(-12px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    /* visibility flips instantly on open (so the revealed links are immediately
       focusable) but is delayed on close until the opacity fade finishes. */
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility 0s var(--dur);
  }
  .nav.open .nav-links {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
    transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility 0s;
  }
  .nav-links a {
    padding: 13px 14px;
    font-size: 1.02rem;
    border-radius: var(--r-sm);
  }
  .nav-links a[data-spy].active::after { display: none; }
  .nav-links a.active { background: var(--glass); }
  .nav-links a.nav-cta {
    margin: 6px 0 0;
    text-align: center;
    justify-content: center;
    padding: 14px;
  }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  padding: 15px 26px;
  border-radius: 100px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              background var(--dur) var(--ease), border-color var(--dur) var(--ease);
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  -webkit-user-select: none;
}
.btn:active { transform: translateY(1px) scale(.99); }

.btn-primary {
  color: #fff;
  background: var(--grad-cta);
  background-size: 200% 200%;
  background-position: 0% 50%;
  box-shadow: 0 10px 40px -12px rgba(122, 92, 219, .7),
              inset 0 1px 0 rgba(255, 255, 255, .18);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              background-position .7s var(--ease);
}
.btn-primary:hover {
  transform: translateY(-2px);
  background-position: 100% 50%;
  box-shadow: 0 18px 50px -12px rgba(122, 92, 219, .85),
              inset 0 1px 0 rgba(255, 255, 255, .25);
}

.btn-ghost {
  color: var(--text);
  background: var(--glass);
  border-color: var(--hairline-2);
  backdrop-filter: blur(8px);
}
.btn-ghost:hover {
  background: rgba(255, 255, 255, .06);
  border-color: rgba(255, 255, 255, .18);
  transform: translateY(-2px);
}
.btn svg { width: 18px; height: 18px; }

/* ---------- Hero ---------- */
/* ---------- Hero: layered cinematic phone reveal ----------
   Stack (back→front): #000 stage · bg canvas (reveal animation) · violet glow ·
   headline (outlined, BEHIND the phone) · fg canvas (alpha-matted phone) ·
   UI (eyebrow + CTAs, in front). Frames are solid black, so the whole stage
   reads as one seamless black field. */
.hero {
  position: relative;
  /* svh, deliberately, and it is a trade rather than an oversight. svh is the
     screen with Safari's URL bar OPEN, and it never grows — so the hero fills
     the screen on load and keeps its box when the bar retracts, which is the
     ~90px the section appears to "lose" on the first swipe. The alternatives
     are both worse: lvh sizes to the retracted screen and pushes the Download
     button 53px under the URL bar on load, and dvh re-resolves as the bar
     animates, resizing the stage mid-scroll and reviving the headline flutter
     this was moved off dvh to cure. A visible CTA outranks a settled edge. */
  height: calc(100svh - var(--nav-h));
  min-height: 560px;
  background: var(--bg);
  overflow: hidden;
  /* Soften the hard bottom edge into transparency (below the CTAs + corner
     labels) so the black hero melts into the ambient glow of the section
     beneath — no hard seam on scroll. The fade band is capped at 40px so it
     can't grow past the corner labels' 42px offset on very tall viewports. */
  -webkit-mask: linear-gradient(#000 0%, #000 calc(100% - min(3%, 40px)), transparent 100%);
          mask: linear-gradient(#000 0%, #000 calc(100% - min(3%, 40px)), transparent 100%);
}
.hero-stage { position: absolute; inset: 0; }

/* Pinned exit — the hero sticks in place while the PHONE glides up and away,
   uncovering the solid white headline behind it (the front outline layer
   fills in to white as it goes — see the exit handler in main.js). The tall
   .hero-track supplies the scroll distance the pin consumes (--hero-pin) —
   long, so the flight runs near 1:1 with the scroll and the phone can be
   watched on its way out, with a readable all-white beat at the end. Only
   when motion is welcome; reduced-motion users keep the plain scroll-away
   (and the JS exit handler is skipped for them too). */
.hero-track { position: relative; }
@media (prefers-reduced-motion: no-preference) {
  :root { --hero-pin: 110vh; }
  .hero-track {
    height: calc(100svh - var(--nav-h) + var(--hero-pin));
    min-height: calc(560px + var(--hero-pin));
  }
  .hero { position: sticky; top: var(--nav-h); }
}
/* The phone's flight is ~(hero + nav) * 0.95 px of travel, and it is spent by
   q = 0.85 of the pin. The pin therefore has to be about that long for the
   flight to run near 1:1 with the finger and stay watchable — shorter and the
   phone simply leaves faster than the gesture, which reads as a skip rather
   than a departure. Desktop's 110vh is that 1:1; a phone's hero is shorter, so
   it needs a touch less to buy the same watchable flight plus the white beat. */
/* A thumb is not a wheel. The flight is scroll-linked, so what makes it
   watchable is the DISTANCE it is spread over, not time: a wheel advances ~100px
   a notch, so desktop's 1:1 flight reads fine, but a flick carries ~800px with
   momentum — at 1:1 the phone is gone inside a single swipe. Spreading the same
   flight over 200vh drops it to half the finger's speed: the phone lifts away
   under the thumb instead of vanishing with it, and the headline has the room to
   land white before the page moves on. */
@media (prefers-reduced-motion: no-preference) and (max-width: 680px) {
  :root { --hero-pin: 200vh; }
}

/* The bottom fade costs the phone its frame budget: a mask on a sticky,
   full-screen box makes Safari composite everything inside it — both canvases,
   the animated glow, three text layers carrying drop-shadows — into one layer
   and re-mask it every frame the phone is in flight, at 1170x2532 device px.
   Measured on the device: without it the flight is smooth, with it it stutters.
   40px of softened edge is not worth that. The desktop keeps the fade — it has
   the frames to spend, and the seam is more visible on a large screen. */
@media (max-width: 680px) {
  .hero {
    -webkit-mask: none;
    mask: none;
  }
}

/* 1 · background reveal animation. The frame's rectangular edges are melted to
   pure black directly on the canvas (see fadeEdges() in main.js), so no seam /
   "square" ever shows — including the bottom edge where the phone rises in. */
.hero-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 2;
  display: block;
  will-change: transform;   /* scroll-driven flight — keep it composited */
}

/* 2 · violet bloom — a large, saturated purple wash behind the device
   (screen-blended so it lifts the phone + white headline off pure black) */
.hero-glow {
  position: absolute;
  z-index: 3;
  left: 50%; top: 47%;
  width: min(112vw, 1240px);
  aspect-ratio: 1.18;
  transform: translate(-50%, -50%);
  background:
    radial-gradient(closest-side,
      rgba(128, 96, 232, .58) 0%,
      rgba(96, 66, 196, .30) 42%,
      rgba(64, 44, 140, .12) 62%,
      transparent 78%);
  filter: blur(46px);
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.hero.is-revealed .hero-glow,
.hero.is-static .hero-glow {
  opacity: 1;
  /* slow "breathing" pulse at a calm ~4.6s rhythm (starts after the fade-in) */
  animation: heroBreathe 4.6s ease-in-out 1.2s infinite;
}
@keyframes heroBreathe {
  0%, 100% { opacity: .78; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.07); }
}
@media (prefers-reduced-motion: reduce) {
  .hero.is-revealed .hero-glow, .hero.is-static .hero-glow { animation: none; }
}

/* 3 · headline — drawn twice: SOLID white behind the phone (.hh-solid) and an
   OUTLINE copy in front (.hh-outline). Words read solid off the device and as an
   outline where they cross it. Letters slot-machine in one-by-one on reveal. */
.hero-headline {
  position: absolute;
  left: 50%; top: 46%;
  transform: translate(-50%, -50%);
  margin: 0;
  width: min(1320px, 96vw);
  text-align: center;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.035em;
  font-size: clamp(2.4rem, 7vw, 6.2rem);
  pointer-events: none;
  opacity: 0;
}
.hero.is-revealed .hero-headline,
.hero.is-static .hero-headline { opacity: 1; }
.hh-solid   { z-index: 4; color: #F5F6FB; }
.hh-outline {
  z-index: 6;
  /* Frosted fill (not fully transparent): where the words cross the phone they
     read as ghosted-white glass. The dark drop-shadows cut the letters out from
     the screen behind them. NO -webkit-text-stroke here: SF Pro's heavy weights
     have self-overlapping contours, and WebKit strokes their internal seams —
     dark slits through the glyphs (the layer's shadow shows through). A denser
     fill stands in for the rim. */
  color: rgba(242, 243, 247, .38);
  filter: drop-shadow(0 3px 14px rgba(0, 0, 0, .65)) drop-shadow(0 1px 3px rgba(0, 0, 0, .5));
}
/* Third copy, solid white, above the outline. Invisible at rest; the pinned
   exit fades it in as the phone lifts away — opacity only, so the scroll never
   repaints text (the ghost layer + its shadows stay static underneath). */
.hh-white {
  z-index: 6;
  color: #F5F6FB;
  will-change: opacity;
}
.hero.is-revealed .hh-white,
.hero.is-static .hh-white { opacity: 0; }

.hh-visual { display: block; }
.hh-line { display: block; white-space: nowrap; }
.hh-line + .hh-line { margin-top: -0.34em; }
/* Each cell is sized by its FINAL glyph (.hh-final, in-flow but hidden); the reel
   is absolutely positioned so wide random glyphs never widen the cell. Cells run
   1.5em tall (glyph centred) so deep descenders like g/y/p are never clipped by
   the reel window's overflow:hidden; the negative line margin above pulls the two
   lines back to a tight visual leading. */
.hh-ch {
  position: relative;
  display: inline-block;
  overflow: hidden;
  height: 1.5em;
  vertical-align: top;
  /* The cell is sized to the glyph's advance MINUS the -0.035em tracking, which
     leaves it narrower than the ink of glyphs like y/w — overflow:hidden then
     clips their sides. Pad the cell horizontally to give the ink room inside the
     clip, and pull it back with an equal negative margin so tracking + line
     width stay exactly the same. */
  padding-inline: 0.07em;
  margin-inline: -0.07em;
}
.hh-final { display: block; height: 1.5em; line-height: 1.5em; visibility: hidden; }
.hh-sp { display: inline-block; width: .26em; }
.hh-reel {
  position: absolute;
  top: 0; left: 0; right: 0;
  text-align: center;
  will-change: transform, filter;
}
.hh-reel > span { display: block; height: 1.5em; line-height: 1.5em; }

/* 4 · foreground alpha phone — occludes the solid headline, casts a soft shadow */
.hero-fg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 5;
  display: block;
  pointer-events: none;
  filter: drop-shadow(0 40px 60px rgba(0, 0, 0, .6));
  opacity: 0;
  will-change: transform;   /* scroll-driven flight — keep the filtered layer cached */
}
.hero.is-revealed .hero-fg,
.hero.is-static .hero-fg { opacity: 1; }

/* 6 · outlined corner labels */
.hero-corners { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
.hc {
  position: absolute;
  margin: 0;
  font-weight: 800;
  font-size: clamp(12.5px, 1.45vw, 20px);
  line-height: 1.18;
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, .96);
  text-shadow: 0 1px 12px rgba(0, 0, 0, .55), 0 0 2px rgba(0, 0, 0, .4);
  opacity: 0;
  transition: opacity .6s var(--ease);
}
.hero.is-revealed .hc,
.hero.is-static .hc { opacity: 1; }
/* staggered entrance — corners fade in one-by-one after the headline lands */
.hero.is-revealed .hc-tl { transition-delay: 1.25s; }
.hero.is-revealed .hc-tr { transition-delay: 1.35s; }
.hero.is-revealed .hc-bl { transition-delay: 1.45s; }
.hero.is-revealed .hc-br { transition-delay: 1.55s; }
.hc-tl { top: clamp(18px, 3.6vh, 42px); left: clamp(20px, 4vw, 58px); }
.hc-tr { top: clamp(18px, 3.6vh, 42px); right: clamp(20px, 4vw, 58px); text-align: right; }
.hc-bl { bottom: clamp(18px, 3.6vh, 42px); left: clamp(20px, 4vw, 58px); }
.hc-br { bottom: clamp(18px, 3.6vh, 42px); right: clamp(20px, 4vw, 58px); text-align: right; }

/* 7 · UI — CTA + note cluster at the bottom, in front of the phone */
.hero-ui {
  position: absolute;
  inset: 0;
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding: 0 20px clamp(38px, 6vh, 64px);
  text-align: center;
  pointer-events: none;
}
.hero-ui > *,
.hero-ui a { pointer-events: auto; }
.hero-foot { display: flex; flex-direction: column; align-items: center; gap: 15px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
.hero-note {
  margin: 0;
  font-size: .85rem;
  color: var(--text-faint);
  display: flex; align-items: center; gap: 8px;
}
.hero-note svg { width: 15px; height: 15px; flex: none; opacity: .8; }

/* entrance for the CTA cluster (fires with .is-revealed). Gated on body.ready
   (added by JS at boot) so the CTAs stay visible for no-JS visitors — otherwise
   is-revealed never fires and the buttons would be stuck transparent. */
body.ready .hero-foot {
  opacity: 0; transform: translateY(14px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.hero.is-revealed .hero-foot,
.hero.is-static .hero-foot { opacity: 1; transform: none; transition-delay: .12s; }

.hero-still {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  z-index: 5;
}

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

/* Static / reduced-motion: no entrance transitions, everything shown */
@media (prefers-reduced-motion: reduce) {
  .eyebrow, .hero-foot { opacity: 1; transform: none; transition: none; }
  .hero-glow, .hero-headline { transition: none; }
}

/* Tighter on phones — smaller stroke + padding, roomier gradient word */
@media (max-width: 680px) {
  /* Phone is centred (set in JS); layer the headline over its upper body so the
     words read solid off the device and as an outline where they cross it —
     the same behind-object composition as desktop. */
  .hero-headline { top: 40%; letter-spacing: -0.03em; }
  /* The phone screen is busier at mobile sizes — a stronger frosted fill keeps
     the words legible where they cross it. */
  .hh-outline { color: rgba(242, 243, 247, .5); }
  .hero-corners { display: none; }        /* keep the mobile hero uncluttered */
  .hero-cta { width: 100%; }
  .hero-cta .btn { flex: 1 1 auto; justify-content: center; }
}

/* ---------- iPhone frame (drawn in CSS, no PNG) ---------- */
.device {
  --dw: 300px;
  position: relative;
  width: var(--dw);
  aspect-ratio: 300 / 620;
  border-radius: 52px;
  padding: 11px;
  background:
    linear-gradient(160deg, #23262F 0%, #0d0f16 42%, #050609 100%);
  box-shadow:
    0 2px 3px rgba(255, 255, 255, .10) inset,
    0 0 0 2px rgba(0, 0, 0, .6),
    0 1px 0 rgba(255, 255, 255, .08),
    0 60px 120px -30px rgba(0, 0, 0, .85);
}
.device::before {
  /* side-button & edge sheen */
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 47px;
  border: 1px solid rgba(255, 255, 255, .05);
  pointer-events: none;
}
.device-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 42px;
  overflow: hidden;
  background: #03050B;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .8) inset;
}
.device-screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}
.device-island {
  position: absolute;
  top: 12px; left: 50%;
  transform: translateX(-50%);
  width: 82px; height: 24px;
  background: var(--bg);
  border-radius: 14px;
  z-index: 3;
  box-shadow: 0 0 0 1px rgba(255,255,255,.04);
}

/* Frameless device render — a full-phone image shown without the CSS iPhone
   frame, floating with a soft shadow. Use a transparent-background PNG so it
   sits cleanly on the ambient field (a solid-bg image would show a rectangle). */
.device-shot {
  width: min(340px, 74vw);
  height: auto;
  display: block;
  filter: drop-shadow(0 44px 70px rgba(0, 0, 0, .55));
}

/* Live demo embeds — the check-in dial + mood slider animations, shown in a
   style-isolated iframe. The frame is sized to the animation card's native
   dimensions (--w/--h) and the iframe is scaled down with a JS-set --scale
   (transform), so it stays crisp and never clips at any width. No card / box —
   the animation pages are transparent, so they float straight on the section
   (no background, no border-radius, no shadow). */
.demo-frame {
  width: 100%;
  max-width: 440px;
  margin-inline: auto;
  aspect-ratio: var(--w) / var(--h);
  position: relative;
}
.demo-frame iframe {
  position: absolute;
  top: 0; left: 0;
  width: calc(var(--w) * 1px);
  height: calc(var(--h) * 1px);
  border: 0;
  transform-origin: top left;
  transform: scale(var(--scale, 0.76));
  background: transparent;
  /* The page declares color-scheme:dark, which is INHERITED into this iframe and
     forces its canvas opaque (an opaque white box appears even though the embedded
     body is transparent). Overriding to `light` here restores the see-through
     backdrop so the animation floats on the section. The embedded pages set every
     colour explicitly, so this doesn't change how they look. */
  color-scheme: light;
}
/* ---------- Feature showcase rows ---------- */
.showcase {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(32px, 6vw, 88px);
}
.showcase.reverse .showcase-media { order: -1; }
.showcase-copy { max-width: 500px; }
.showcase-copy h2 { margin-bottom: 20px; }
.showcase-copy .lead { margin: 0; }
.showcase-media { display: flex; justify-content: center; }

.mini-list {
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  display: grid;
  gap: 14px;
}
.mini-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  color: var(--text-soft);
  font-size: .98rem;
}
.mini-list .tick {
  flex: none;
  width: 22px; height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(122, 92, 219, .16);
  margin-top: 1px;
}
.mini-list .tick svg { width: 12px; height: 12px; color: var(--violet); }

/* ---------- Unlock highlight (two reward cards) ---------- */
.unlock-head { text-align: center; max-width: 640px; margin: 0 auto clamp(40px, 6vw, 64px); }
.unlock-head h2 { margin-bottom: 18px; }
.unlock-head .lead { margin: 0 auto; }

.reward {
  position: relative;
  border-radius: var(--r-lg);
  padding: 34px 32px 36px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0) 40%),
    var(--card);
  border: 1px solid var(--hairline);
  overflow: hidden;
}
.reward::before {
  content: "";
  position: absolute;
  top: -40%; right: -30%;
  width: 70%; aspect-ratio: 1;
  background: radial-gradient(circle, rgba(122,92,219,.25), transparent 70%);
  pointer-events: none;
}
.reward .badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 6px 12px;
  border-radius: 100px;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--hairline);
  margin-bottom: 22px;
}
.reward .badge .dot { width: 7px; height: 7px; border-radius: 50%; }
.reward-icon {
  width: 52px; height: 52px;
  border-radius: 15px;
  display: grid; place-items: center;
  background: var(--grad-primary);
  box-shadow: 0 10px 30px -8px rgba(122,92,219,.6);
  margin-bottom: 20px;
}
.reward-icon svg { width: 26px; height: 26px; color: #fff; }
.reward h3 { font-size: 1.4rem; margin-bottom: 12px; }
.reward p { color: var(--text-dim); margin: 0; font-size: .98rem; }

.unlock-note {
  max-width: 54ch;
  margin: 16px auto 0;
  font-size: .92rem;
  color: var(--text-faint);
}
.unlock-showcase {
  display: grid;
  grid-template-columns: minmax(0, .92fr) 1.08fr;
  align-items: center;
  gap: clamp(28px, 5vw, 64px);
  margin-top: clamp(36px, 5vw, 56px);
}
.unlock-showcase .showcase-media { display: flex; justify-content: center; }
.unlock-rewards { display: grid; gap: 18px; }
.unlock-rewards .reward { padding: 28px 30px 30px; }
@media (max-width: 860px) {
  .unlock-showcase { grid-template-columns: 1fr; gap: 36px; }
  .unlock-showcase .showcase-media { order: -1; }
}

/* ---------- Achievements: trophy room ---------- */
.trophy-room {
  width: 100%;
  max-width: 380px;
  border-radius: var(--r-lg);
  padding: 26px 24px 28px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.045), rgba(255,255,255,0) 55%),
    var(--card);
  border: 1px solid var(--hairline);
  box-shadow: 0 50px 100px -45px rgba(0,0,0,.85);
}
.trophy-room-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 22px;
}
.trophy-room-title { font-weight: 700; font-size: 1.12rem; letter-spacing: -0.02em; }
.trophy-room-count {
  font-size: .74rem;
  font-weight: 600;
  padding: 5px 11px;
  border-radius: 100px;
  color: var(--violet-light);
  background: rgba(122,92,219,.14);
  border: 1px solid rgba(122,92,219,.28);
}
.trophy-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px 8px;
}
.trophy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}
.trophy-medal {
  width: 60px; height: 60px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  background: var(--c); /* fallback if color-mix is unsupported */
  background:
    radial-gradient(120% 120% at 30% 22%, rgba(255,255,255,.4), transparent 55%),
    linear-gradient(160deg,
      color-mix(in srgb, var(--c) 92%, #fff),
      color-mix(in srgb, var(--c) 55%, #000));
  border: 1px solid color-mix(in srgb, var(--c) 60%, transparent);
  box-shadow:
    0 12px 26px -8px color-mix(in srgb, var(--c) 55%, transparent),
    inset 0 1px 0 rgba(255,255,255,.4);
  transition: transform var(--dur) var(--ease);
}
.trophy-medal svg { width: 28px; height: 28px; }
.trophy:not(.locked):hover .trophy-medal { transform: translateY(-3px) scale(1.05); }
.trophy-label { font-size: .8rem; color: var(--text-soft); letter-spacing: -0.01em; }
.trophy.locked .trophy-medal {
  background: rgba(255,255,255,.03);
  border: 1px solid var(--hairline);
  color: var(--text-faint);
  box-shadow: none;
}
.trophy.locked .trophy-label { color: var(--text-faint); }

/* ---------- Features grid (7) ---------- */
.features-head { text-align: center; max-width: 620px; margin: 0 auto clamp(44px, 6vw, 68px); }
.features-head h2 { margin-bottom: 18px; }

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
.feature {
  position: relative;
  padding: 28px 26px;
  border-radius: var(--r-md);
  background: var(--glass);
  border: 1px solid var(--hairline);
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.feature:hover {
  transform: translateY(-4px);
  background: rgba(255,255,255,.045);
  border-color: var(--hairline-2);
}

/* Cursor-follow glow (applied to .feature / .reward by JS; pointer devices only) */
.has-glow::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
              rgba(122, 92, 219, .18), transparent 62%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}
.has-glow:hover::after { opacity: 1; }
.feature-icon {
  width: 46px; height: 46px;
  border-radius: 13px;
  display: grid; place-items: center;
  margin-bottom: 18px;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--hairline);
}
.feature-icon svg { width: 24px; height: 24px; }
.feature h3 { font-size: 1.14rem; margin-bottom: 8px; letter-spacing: -0.02em; }
.feature p { margin: 0; color: var(--text-dim); font-size: .94rem; line-height: 1.5; }

/* ---------- Privacy teaser ---------- */
.privacy-teaser {
  position: relative;
  border-radius: var(--r-xl);
  padding: clamp(40px, 6vw, 72px);
  text-align: center;
  background:
    radial-gradient(80% 120% at 50% 0%, rgba(122,92,219,.16), transparent 60%),
    var(--card);
  border: 1px solid var(--hairline);
  overflow: hidden;
}
.privacy-teaser .shield {
  width: 60px; height: 60px;
  margin: 0 auto 26px;
  border-radius: 18px;
  display: grid; place-items: center;
  background: rgba(122,92,219,.14);
  border: 1px solid rgba(122,92,219,.3);
}
.privacy-teaser .shield svg { width: 30px; height: 30px; color: var(--violet-light); }
.privacy-teaser h2 { margin-bottom: 16px; }
.privacy-teaser .lead { max-width: 46ch; margin: 0 auto 30px; }

.chip-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-bottom: 32px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: .86rem;
  color: var(--text-soft);
  padding: 8px 15px;
  border-radius: 100px;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--hairline);
}
.chip svg { width: 14px; height: 14px; color: var(--violet); }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--violet-light);
  font-weight: 500;
  transition: gap var(--dur) var(--ease), color var(--dur) var(--ease);
}
.text-link:hover { gap: 11px; color: #fff; }
.text-link svg { width: 16px; height: 16px; }

/* ---------- FAQ ---------- */
.faq-wrap {
  display: grid;
  grid-template-columns: .82fr 1.18fr;
  gap: clamp(32px, 6vw, 80px);
  align-items: start;
}
.faq-head { position: sticky; top: 100px; }
.faq-head h2 { margin-bottom: 18px; }
.faq-head .lead { margin: 0; font-size: 1.02rem; }
.faq-head .text-link { display: inline; }

.faq-list { display: grid; gap: 12px; }
.faq {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--glass);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.faq[open] { border-color: rgba(122,92,219,.28); background: rgba(122,92,219,.04); }
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 22px;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur) var(--ease);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: #fff; }
.faq-chevron {
  flex: none;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--hairline);
  color: var(--text-dim);
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.faq-chevron svg { width: 16px; height: 16px; }
.faq[open] .faq-chevron { transform: rotate(180deg); background: rgba(122,92,219,.16); color: var(--violet-light); }
.faq-body { padding: 0 22px 22px; }
.faq-body p { margin: 0; color: var(--text-dim); line-height: 1.65; font-size: .98rem; }
.faq[open] .faq-body { animation: faqReveal .4s var(--ease); }
@keyframes faqReveal {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .faq[open] .faq-body { animation: none; }
}
@media (max-width: 860px) {
  .faq-wrap { grid-template-columns: 1fr; gap: 32px; }
  .faq-head { position: static; text-align: center; }
  .faq-head .lead { margin-inline: auto; max-width: 44ch; }
}

/* ---------- Download / beta ---------- */
.early {
  position: relative;
  border-radius: var(--r-xl);
  padding: clamp(44px, 7vw, 84px) clamp(24px, 5vw, 64px);
  text-align: center;
  background:
    radial-gradient(90% 130% at 50% -10%, rgba(122,92,219,.22), transparent 55%),
    linear-gradient(180deg, var(--card-hi), var(--card));
  border: 1px solid var(--hairline-2);
  overflow: hidden;
}
.early h2 { margin-bottom: 16px; }
.early > .wrap-narrow { max-width: 560px; margin-inline: auto; }
.early .lead { margin: 0 auto 34px; max-width: 42ch; }

.download-note {
  margin: 22px auto 0;
  font-size: .82rem;
  color: var(--text-faint);
  max-width: 44ch;
  line-height: 1.5;
}

/* ---------- Footer ---------- */
.footer {
  border-top: 1px solid var(--hairline);
  padding-block: clamp(48px, 7vw, 72px) 40px;
  margin-top: clamp(40px, 6vw, 80px);
}
.footer-top {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 40px;
  margin-bottom: 44px;
}
.footer-brand { max-width: 300px; }
.footer-brand .wordmark { margin-bottom: 16px; padding: 0; }
.footer-brand p { color: var(--text-dim); font-size: .92rem; margin: 0; }

.footer-cols {
  display: flex;
  gap: clamp(40px, 8vw, 88px);
  flex-wrap: wrap;
}
.footer-col h3 {
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0 0 16px;
  font-weight: 600;
}
/* 44px min-height is Apple's minimum touch target (HIG). These stood 32px tall
   — the text sits where it did; the box around it is now thumb-sized. */
.footer-col a:not(.appstore-badge) {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-dim);
  font-size: .92rem;
  transition: color var(--dur) var(--ease);
}
.footer-col a:not(.appstore-badge):hover { color: var(--text); }

/* App Store badge — drawn after Apple's official badge, TestFlight beta variant:
   solid black plate, thin white keyline, white mark + two-line lockup. */
.appstore-badge {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  padding: 9px 20px 10px 16px;
  border-radius: 11px;
  background: #000;
  border: 1px solid rgba(255,255,255,.42);
  color: #fff;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.appstore-badge:hover {
  border-color: rgba(255,255,255,.7);
  box-shadow: 0 8px 28px -12px rgba(255,255,255,.28);
}
.appstore-badge svg { width: 27px; height: 27px; flex: none; margin-top: -2px; }
.appstore-badge .ab-text { display: flex; flex-direction: column; }
.appstore-badge .ab-label {
  font-size: .67rem;
  color: rgba(255,255,255,.88);
  line-height: 1.15;
  letter-spacing: .012em;
}
.appstore-badge .ab-store {
  font-size: 1.24rem;
  font-weight: 600;
  color: #fff;
  line-height: 1.12;
  letter-spacing: -0.018em;
  margin-top: 1px;
}

.disclaimer {
  font-size: .82rem;
  color: var(--text-faint);
  line-height: 1.55;
  max-width: 68ch;
  padding: 18px 20px;
  border-radius: var(--r-md);
  background: rgba(255,255,255,.02);
  border: 1px solid var(--hairline);
  margin-bottom: 28px;
}
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
  font-size: .85rem;
  color: var(--text-faint);
}
/* Same 44px floor: these were the smallest targets on the site at 16px tall. */
.footer-legal a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.footer-legal a:hover { color: var(--text); }

/* ---------- Privacy policy page ---------- */
.doc {
  max-width: 760px;
  margin-inline: auto;
  padding-top: clamp(48px, 8vw, 96px);
}
.doc-head { margin-bottom: 48px; }
.doc-head h1 {
  font-size: clamp(2.4rem, 6vw, 3.6rem);
  margin-bottom: 14px;
}
.doc-updated { color: var(--text-dim); font-size: .95rem; }
.doc-section { margin-bottom: 40px; }
.doc-section h2 {
  font-size: 1.5rem;
  margin-bottom: 12px;
  letter-spacing: -0.02em;
}
.doc-section p {
  color: var(--text-soft);
  margin: 0 0 14px;
  line-height: 1.7;
}
.doc-section a { color: var(--violet-light); text-decoration: underline; text-underline-offset: 3px; }
.doc-disclaimer {
  margin-top: 8px;
  padding: 22px 24px;
  border-radius: var(--r-md);
  background: rgba(122,92,219,.06);
  border: 1px solid rgba(122,92,219,.16);
  color: var(--text-soft);
  font-size: .95rem;
}
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-dim);
  font-size: .92rem;
  margin-bottom: 32px;
  transition: color var(--dur) var(--ease), gap var(--dur) var(--ease);
}
.back-link:hover { color: var(--text); gap: 12px; }
.back-link svg { width: 16px; height: 16px; }

/* ---------- Scroll-reveal ---------- */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  will-change: opacity, transform;
}
.reveal.in { opacity: 1; transform: none; will-change: auto; }
.reveal.d1 { transition-delay: .08s; }
.reveal.d2 { transition-delay: .16s; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .btn, .feature, .nav-links a { transition: none; }
  .nav-links a[data-spy].active::after { animation: none; }
  * { scroll-behavior: auto !important; }
}

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .showcase { grid-template-columns: 1fr; text-align: center; gap: 40px; }
  .showcase.reverse .showcase-media { order: 0; }
  .showcase-copy { max-width: 100%; margin-inline: auto; }
  .showcase-copy .lead { margin-inline: auto; max-width: 46ch; }
  .mini-list { max-width: 400px; margin-inline: auto; text-align: left; }

  .feature-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .feature-grid { grid-template-columns: 1fr; }
  .footer-top { flex-direction: column; gap: 32px; }
}

/* =============================================================
   Vision — the philosophy / manifesto page.
   A text-only editorial page: no screenshots, no cards to chase.
   Wide, quiet vertical rhythm; a single centred reading column
   that a few moments (hero statement, pull quotes) break wider.
   Built entirely from the site's existing tokens.
   ============================================================= */
.vision {
  max-width: 940px;
  margin-inline: auto;
  padding-top: clamp(40px, 8vw, 104px);
  padding-bottom: clamp(56px, 9vw, 120px);
}
.vision .back-link { margin-bottom: clamp(40px, 7vw, 72px); }

/* ---- Opening statement ---- */
.vision-hero { margin-bottom: clamp(72px, 12vw, 148px); }
.vision-hero .eyebrow { margin-bottom: 26px; }
.v-statement {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.034em;
  line-height: 1.1;
  font-size: clamp(2.05rem, 4.7vw, 3.3rem);
  color: var(--text);
  max-width: 22ch;
  text-wrap: balance;
}
/* One warm accent moment on the page — a soft, static (non-shimmering)
   violet wash on the emphasised phrase of the opening statement. */
.v-statement em {
  font-style: normal;
  background: linear-gradient(100deg, var(--violet-light) 0%, #9E86F0 42%, #7A5CDB 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Solid fallback where clip-to-text is unsupported, so the word never vanishes. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .v-statement em { color: var(--violet-light); }
}
.v-hero-lead {
  margin: clamp(28px, 4vw, 40px) 0 0;
  max-width: 56ch;
  font-size: clamp(1.12rem, 2.2vw, 1.42rem);
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: -0.012em;
  color: var(--text-soft);
}

/* ---- Movements: the essay's building blocks ---- */
.v-block { margin-block: clamp(46px, 8vw, 96px); }
.v-block:first-child { margin-top: 0; }

/* prose block — the narrow reading measure */
.v-prose { max-width: 62ch; }
.v-prose .v-eyebrow {
  display: block;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 20px;
}
.v-prose h2 {
  font-size: clamp(1.7rem, 3.6vw, 2.6rem);
  letter-spacing: -0.032em;
  line-height: 1.1;
  margin-bottom: clamp(18px, 2.6vw, 26px);
}
.v-prose p {
  margin: 0 0 20px;
  font-size: clamp(1.06rem, 1.7vw, 1.22rem);
  line-height: 1.72;
  color: var(--text-soft);
}
.v-prose p:last-child { margin-bottom: 0; }

/* principle pairing — a belief bound to a decision, marked by a soft violet rule */
.v-principle {
  position: relative;
  max-width: 66ch;
  padding-left: clamp(22px, 3.4vw, 38px);
}
.v-principle::before {
  content: "";
  position: absolute;
  left: 0; top: .28em; bottom: .28em;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--violet) 0%, rgba(122, 92, 219, .12) 100%);
}
.v-principle .v-principle-line {
  margin: 0;
  font-size: clamp(1.5rem, 3.1vw, 2.25rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.12;
  color: var(--text);
}
.v-principle .v-because {
  margin: clamp(16px, 2vw, 22px) 0 0;
  font-size: clamp(1.05rem, 1.7vw, 1.2rem);
  line-height: 1.66;
  color: var(--text-soft);
}

/* Triad — three gentle statements set as one row of icon cards (two quotes
   flanking the belief they support). Shares the glass-card language; each card
   is centred and leads with a soft violet icon tile. */
.v-triad {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
}
.v-triad-card {
  --tg: clamp(14px, 1.6vw, 18px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--tg);
  padding: clamp(26px, 2.8vw, 36px) clamp(20px, 2.2vw, 28px);
  border: 1px solid var(--hairline);
  border-radius: 18px;
  background: var(--glass);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.v-triad-card:hover { border-color: var(--hairline-2); background: rgba(255, 255, 255, .045); }
.v-triad-icon {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  border-radius: 13px;
  color: var(--violet-light);
  background: rgba(122, 92, 219, .14);
  border: 1px solid rgba(122, 92, 219, .22);
}
.v-triad-icon svg { width: 23px; height: 23px; }
.v-triad-line {
  margin: 0;
  font-size: clamp(1.08rem, 1.5vw, 1.26rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.28;
  color: var(--text);
  text-wrap: balance;
}
.v-triad-because {
  margin: 0;
  font-size: clamp(.98rem, 1.3vw, 1.06rem);
  line-height: 1.62;
  color: var(--text-soft);
  text-wrap: pretty;
}
@media (max-width: 860px) {
  .v-triad { grid-template-columns: 1fr; gap: clamp(14px, 3vw, 18px); }
}

/* ---- "More" disclosure — fold a card's reasoning so it matches the
   headline-only cards beside it, and open it on demand. Progressive
   enhancement: with no JS the text is shown and the button stays hidden
   (the .js-more gate is added by initRevealMore only when it wires up). ---- */
.v-more { display: none; }
.js-more .v-more {
  display: inline-flex;
  align-items: center;
  gap: .36em;
  padding: .46em .55em .46em .95em;
  border: 1px solid var(--hairline-2);
  border-radius: 999px;
  background: rgba(255, 255, 255, .03);
  color: var(--text-soft);
  font: inherit;
  font-size: .92rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.js-more .v-more:hover { border-color: var(--violet); background: rgba(122, 92, 219, .12); color: var(--text); }
.js-more .v-more:focus-visible { outline: 2px solid var(--violet-light); outline-offset: 2px; }
/* pull the button up by one card-gap while collapsed so the empty (0-height)
   region doesn't leave a double gap under the headline */
.js-more [data-reveal-more]:not(.is-open) .v-more { margin-top: calc(-1 * var(--tg)); }
.v-more-chevron { width: 1.05em; height: 1.05em; transition: transform var(--dur) var(--ease); }
.v-more[aria-expanded="true"] .v-more-chevron { transform: rotate(180deg); }

/* the collapsible region — grid rows 0fr → 1fr gives a smooth height reveal
   with no pixel measuring; only collapsed once JS has opted the card in */
.js-more [data-reveal-more] .v-triad-because-wrap {
  display: grid;
  grid-template-rows: 0fr;
  width: 100%;
  opacity: 0;
  transition: grid-template-rows .5s var(--ease), opacity .42s var(--ease);
}
.js-more [data-reveal-more].is-open .v-triad-because-wrap { grid-template-rows: 1fr; opacity: 1; }
.v-triad-because-wrap > .v-triad-because { min-height: 0; overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
  .js-more [data-reveal-more] .v-triad-because-wrap { transition: none; }
}

/* pull quote — the emotional peaks, a centred column of a few calm lines.
   The measure is em-relative so the line count stays ~constant as the type
   scales (roughly 28 characters per line at any size). */
.v-quote {
  max-width: 14em;
  margin-inline: auto;
  text-align: center;
  padding-block: clamp(8px, 2vw, 20px);
  /* the large type lives here so the em-based measure above resolves against it */
  font-size: clamp(1.7rem, 4.4vw, 3rem);
}
.v-quote::before {
  content: "";
  display: block;
  width: 30px; height: 2px;
  margin: 0 auto clamp(26px, 3.4vw, 38px);
  border-radius: 2px;
  background: var(--violet);
  box-shadow: 0 0 14px 1px rgba(122, 92, 219, .7);
}
.v-quote p {
  margin: 0;
  font-size: 1em; /* inherits the sizing set on .v-quote */
  font-weight: 600;
  letter-spacing: -0.032em;
  line-height: 1.2;
  color: var(--text);
  text-wrap: balance;
}

/* ---- Principles index — a quiet, numbered table of beliefs ---- */
.vision-principles {
  margin-top: clamp(72px, 11vw, 132px);
  padding-top: clamp(40px, 6vw, 64px);
  border-top: 1px solid var(--hairline);
}
.vision-principles .v-index-head {
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 clamp(24px, 3vw, 34px);
}
.v-index { counter-reset: principle; list-style: none; margin: 0; padding: 0; }
/* .v-index-item layout + the number glyph are defined by the card-grid rules
   further down (the "restyled from a list into a card grid" block); only this
   one unique tracking value isn't repeated there. */
.v-index-item::before { letter-spacing: .02em; }
.v-index-name {
  font-size: clamp(1.06rem, 1.7vw, 1.2rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}
.v-index-gloss {
  font-size: clamp(1rem, 1.5vw, 1.08rem);
  line-height: 1.55;
  color: var(--text-dim);
}

/* ---- Closing ---- */
.vision-closing {
  margin-top: clamp(72px, 11vw, 132px);
  max-width: 62ch;
}
.vision-closing h2 {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  letter-spacing: -0.032em;
  line-height: 1.1;
  margin-bottom: clamp(20px, 2.6vw, 28px);
}
.vision-closing p {
  margin: 0 0 20px;
  font-size: clamp(1.08rem, 1.8vw, 1.28rem);
  line-height: 1.68;
  color: var(--text-soft);
}
.vision-closing p:last-of-type { margin-bottom: 0; }
.vision-closing .v-closing-note {
  margin-top: clamp(30px, 4vw, 42px);
  font-size: .95rem;
  color: var(--text-faint);
  line-height: 1.6;
}
.vision-closing .v-closing-note a {
  color: var(--violet-light);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color var(--dur) var(--ease);
}
.vision-closing .v-closing-note a:hover { color: #fff; }

/* ============================================================
   Vision — centred editorial column.
   The full-width reading blocks (hero, prose, principle statements,
   closing) are centre-aligned. The side-by-side moments keep their own
   alignment: the pairs beside a phone (.v-pair-text) stay left, the
   privacy trio stays in its boxes, and the pull-quotes were already
   centred. Scoped to .vision so the shared essay primitives remain
   left-aligned on the education page.
   ============================================================ */
.vision-hero { text-align: center; }
.vision-hero .v-statement,
.vision-hero .v-hero-lead { margin-inline: auto; }

/* prose reading blocks — but not the text set beside a phone in a pair */
.vision .v-prose:not(.v-pair-text .v-prose) {
  text-align: center;
  margin-inline: auto;
}

/* principle statements — centred, with the left rule restyled as a centred
   accent above the line (echoing the pull-quote's centred mark). Pair
   principles beside a phone keep their original left rule untouched. */
.vision .v-principle:not(.v-pair-text .v-principle) {
  text-align: center;
  margin-inline: auto;
  padding-left: 0;
}
.vision .v-principle:not(.v-pair-text .v-principle)::before {
  position: static;
  display: block;
  width: 32px;
  height: 2px;
  margin: 0 auto clamp(18px, 2.4vw, 24px);
  background: linear-gradient(90deg, var(--violet-light) 0%, var(--violet) 100%);
  box-shadow: 0 0 12px rgba(122, 92, 219, .45);
}

/* closing movement */
.vision-closing {
  text-align: center;
  margin-inline: auto;
}

/* quiet essay-foot links (kindred pages + a gentle return), centred */
.vision-foot {
  margin-top: clamp(48px, 7vw, 76px);
  padding-top: clamp(30px, 4vw, 40px);
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  align-items: center;
  justify-content: center;
}
.vision-foot a {
  color: var(--text-dim);
  font-size: .95rem;
  transition: color var(--dur) var(--ease);
}
.vision-foot a:hover { color: var(--text); }
.vision-foot .v-foot-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--violet-light);
  font-weight: 500;
}
.vision-foot .v-foot-primary:hover { color: #fff; }
.vision-foot .v-foot-primary svg { width: 16px; height: 16px; transition: transform var(--dur) var(--ease); }
.vision-foot .v-foot-primary:hover svg { transform: translateX(3px); }

/* ============================================================
   Vision — imagery: essay figures, the two-axis diagram, and the
   principle recap restyled from a list into a card grid.
   ============================================================ */

/* Screenshot figures set between essay blocks */
.v-figure { margin: clamp(52px, 9vw, 108px) auto; text-align: center; }
.v-figure .v-shot {
  display: block;
  width: min(320px, 72vw);
  height: auto;
  margin: 0 auto;
  border-radius: clamp(30px, 5vw, 40px);
  border: 1px solid var(--hairline-2);
  box-shadow: 0 44px 84px rgba(0, 0, 0, .6);
}
.v-figcaption {
  margin: 24px auto 0;
  max-width: 44ch;
  font-size: .96rem;
  line-height: 1.55;
  color: var(--text-faint);
}
.v-figcaption em { font-style: normal; color: var(--text-soft); }
/* Angled phone RENDERS (already device-shaped) float, rather than sit in a card */
.v-figure .v-shot.v-render {
  width: min(360px, 78vw);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  filter: drop-shadow(0 40px 72px rgba(0, 0, 0, .55));
}

/* Flat screenshots wear the shared iPhone .device frame (the same one the
   Reports shot uses on the home page), scaled down to sit beside the essay.
   Every frame metric is authored in em against a font-size of --dw/300, so the
   whole phone — bezel, corner radius, Dynamic Island — scales as one unit when
   --dw changes; at the native 300px it reproduces the home-page frame exactly. */
.v-figure .device {
  --dw: clamp(216px, 42vw, 248px);
  /* --dw is a length, so dividing by 300 yields a length: 1em now equals
     (device width / 300)px, i.e. exactly the per-unit scale of the native
     300px frame. Do NOT multiply by 1px here — px × px is invalid. */
  font-size: calc(var(--dw) / 300);
  margin-inline: auto;
  padding: 11em;
  border-radius: 52em;
}
.v-figure .device::before { inset: 4em; border-radius: 47em; }
.v-figure .device-screen { border-radius: 42em; }
.v-figure .device-island { top: 12em; width: 82em; height: 24em; border-radius: 14em; }

/* Paired movement — a screenshot set beside the text that explains it, rather
   than stacked between blocks. Add .is-flip to put the screenshot on the left,
   so successive pairs alternate side. Collapses to text-above-image on narrow
   screens. The outer margin stays small so it doesn't double with the adjacent
   .v-block margins (a grid box forms its own BFC and won't margin-collapse). */
.v-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(48px, 9vw, 112px);
  margin-block: clamp(12px, 2vw, 24px);
}
.v-pair-text {
  order: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(24px, 3.4vw, 40px);
}
.v-pair-text > .v-block { margin-block: 0; }
/* the figure column is sized to the phone; the cap keeps the caption from
   widening the column past the device */
.v-pair > .v-figure { order: 2; margin: 0 auto; max-width: 260px; }
/* flip — screenshot on the left, text on the right. Mirror the columns too, so
   the figure sits in the content-sized (auto) track and the text takes the
   flexible one — otherwise the text's auto column starves the figure's track,
   the phone overflows, and the gap between them collapses. */
.v-pair.is-flip { grid-template-columns: auto minmax(0, 1fr); }
.v-pair.is-flip .v-pair-text { order: 2; }
.v-pair.is-flip > .v-figure { order: 1; }
/* the angled render matches the framed phones' width */
.v-pair .v-shot.v-render { width: 100%; max-width: 260px; }

@media (max-width: 760px) {
  .v-pair,
  .v-pair.is-flip { grid-template-columns: 1fr; }
  .v-pair { gap: clamp(28px, 6vw, 40px); }
  /* once stacked, the explaining text always leads its screenshot */
  .v-pair .v-pair-text,
  .v-pair.is-flip .v-pair-text { order: 1; }
  .v-pair > .v-figure,
  .v-pair.is-flip > .v-figure { order: 2; }
}

/* The two-axis diagram — the page's core idea, made visual */
.v-figure-axes { max-width: 640px; }
.v-axes {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  align-items: center;
  gap: clamp(18px, 4vw, 44px);
  padding: clamp(28px, 4vw, 42px) clamp(20px, 3vw, 34px);
  background: var(--glass);
  border: 1px solid var(--hairline);
  border-radius: 22px;
  text-align: center;
}
.v-axes-panel { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.v-axes-rule { align-self: stretch; background: var(--hairline); }
.v-axes-tag {
  font-size: .7rem; font-weight: 700; letter-spacing: .15em;
  text-transform: uppercase; color: var(--text-faint);
}
.v-axes-tag.is-on { color: var(--violet-light); }
.v-axes-art { width: 100%; max-width: 210px; height: auto; overflow: visible; }
.v-axes-art text { fill: var(--text-faint); font-size: 12px; font-weight: 500; }
/* the POLARITY bars glow gently in their own colour — the richer, "alive" side */
.v-axes-art .v-bar-dep { filter: drop-shadow(0 3px 14px rgba(93, 140, 239, .45)); }
.v-axes-art .v-bar-ele { filter: drop-shadow(0 3px 14px rgba(233, 162, 59, .40)); }
.v-axes-cap { font-size: .88rem; line-height: 1.4; color: var(--text-dim); max-width: 20ch; }
@media (max-width: 560px) {
  .v-axes { grid-template-columns: 1fr; }
  .v-axes-rule { height: 1px; }
}

/* Principles — restyled from a list into a two-up card grid (visual recap) */
.v-index { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(12px, 1.5vw, 18px); }
.v-index-item {
  display: block;
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  background: var(--glass);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.v-index-item:hover { border-color: var(--hairline-2); background: rgba(255, 255, 255, .045); }
.v-index-item::before {
  counter-increment: principle;
  content: counter(principle, decimal-leading-zero);
  display: block;
  margin-bottom: 14px;
  color: var(--violet-light);
  font-size: .82rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.v-index-name { display: block; margin-bottom: 8px; }
.v-index-gloss { display: block; }
@media (max-width: 620px) {
  .v-index { grid-template-columns: 1fr; }
}

/* =============================================================
   Understanding Bipolar Disorder — long-form educational essay.
   Reuses the shared essay primitives (.v-block, .v-prose, .v-quote,
   .v-principle, .v-statement, .v-hero-lead, .back-link) and adds the
   education-only components below: figures/diagrams, the four state
   blocks, the "where trackers fall short" list, and a gentle care note.
   ============================================================= */
.edu {
  max-width: 1040px;
  margin-inline: auto;
  padding-top: clamp(40px, 8vw, 104px);
  padding-bottom: clamp(56px, 9vw, 120px);
}
.edu .back-link { margin-bottom: clamp(40px, 7vw, 72px); }

/* Centered title block over the left-aligned essay — a settled editorial opening
   (the long reading body stays left-aligned for legibility). */
.edu-hero { margin-bottom: clamp(60px, 9vw, 108px); text-align: center; }
.edu-hero .v-statement,
.edu-hero .v-hero-lead,
.edu-hero .edu-hero-note { margin-inline: auto; }
.edu-hero .eyebrow { margin-bottom: 26px; }

/* ---- Reading layout: a sticky section rail beside the essay ----
   Desktop: [rail] + [reading column] centered as one unit, so the essay is
   balanced instead of hugging the left. Collapses to a single column (rail
   hidden) on tablet/mobile, preserving the original one-column read. */
.edu-layout {
  display: grid;
  grid-template-columns: 186px minmax(0, 760px);
  gap: clamp(36px, 5vw, 72px);
  justify-content: center;
  align-items: start;
}
.edu-body { min-width: 0; }

.edu-rail {
  position: sticky;
  top: calc(var(--nav-h) + 40px);
  align-self: start;
}
.edu-rail-title {
  margin: 0 0 14px;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.edu-rail-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 1px solid var(--hairline);
}
.edu-rail-list li { margin: 0; }
.edu-rail-list a {
  display: grid;
  grid-template-columns: 1.7rem 1fr;
  align-items: baseline;
  gap: 4px;
  margin-left: -1px;
  padding: 9px 0 9px 16px;
  border-left: 2px solid transparent;
  color: var(--text-dim);
  font-size: .95rem;
  line-height: 1.28;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.edu-rail-num {
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  color: var(--text-faint);
  transition: color var(--dur) var(--ease);
}
.edu-rail-list a:hover { color: var(--text-soft); }
.edu-rail-list a:hover .edu-rail-num { color: var(--text-dim); }
.edu-rail-list a.is-active { color: var(--text); border-left-color: var(--violet); }
.edu-rail-list a.is-active .edu-rail-num { color: var(--violet-light); }

/* Thin reading-progress line pinned just under the sticky nav */
.edu-progress {
  position: fixed;
  left: 0; right: 0;
  top: var(--nav-h);
  height: 2px;
  z-index: 90;
  pointer-events: none;
}
.edu-progress-bar {
  display: block;
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
  background: linear-gradient(90deg, var(--violet) 0%, var(--violet-light) 100%);
  box-shadow: 0 0 8px rgba(122, 92, 219, .55);
}
@media (prefers-reduced-motion: no-preference) {
  .edu-progress-bar { transition: transform .1s linear; }
}
/* No-JS: the bar never fills, so keep it invisible until JS drives it. */
.no-js .edu-progress { display: none; }

/* Wide screens: the essay takes the page's centre line and the rail steps out
   into the left gutter, level with the POLARITY wordmark that the full-bleed
   nav pins at var(--pad). Inside .wrap there is no room for both a centred
   760px column and a rail beside it (that needs ~1276px against a 1180px cap),
   so the rail has to leave the flow for the essay to sit on centre with the
   hero and the coda. Below this width the grid above still applies. */
@media (min-width: 1180px) {
  .edu-layout {
    position: relative;
    display: block;
    max-width: 760px;
    margin-inline: auto;
  }
  .edu-rail {
    position: absolute;
    top: 0;
    bottom: 0;
    /* Undo the grid-item alignment above. On an absolutely positioned box a
       non-normal align-self suppresses stretching — the box takes its content
       height and parks at the start — so top/bottom would be ignored, the rail
       would stand 259px tall in a 12980px essay, and the sticky child inside
       would have no room to travel. */
    align-self: stretch;
    width: 150px;
    /* 100% is the reading column, so this lands the rail var(--pad) from the
       viewport's left edge — the wordmark's own left edge. */
    left: calc(var(--pad) - (100vw - 100%) / 2);
  }
  /* The rail box spans the essay; this is what actually sticks inside it. */
  .edu-rail-inner {
    position: sticky;
    top: calc(var(--nav-h) + 40px);
  }
}

@media (max-width: 920px) {
  .edu-layout { grid-template-columns: 1fr; }
  .edu-rail { display: none; }
  .edu-progress { display: none; }
}
/* a quiet responsibility line under the hero lead */
.edu-hero .edu-hero-note {
  margin: clamp(24px, 3vw, 32px) 0 0;
  font-size: .92rem;
  color: var(--text-faint);
  line-height: 1.6;
}
.edu-hero .edu-hero-note a { color: var(--violet-light); text-decoration: underline; text-underline-offset: 3px; }
.edu-hero .edu-hero-note a:hover { color: #fff; }

/* ---- Figures / diagrams ---- */
.edu-figure {
  margin: clamp(44px, 7vw, 80px) 0;
  padding: clamp(24px, 4vw, 38px) var(--inset) clamp(18px, 2.6vw, 26px);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .022), rgba(255, 255, 255, 0) 60%),
    var(--card);
}
.edu-figure svg { display: block; width: 100%; height: auto; overflow: visible; }
.edu-figcaption {
  margin: clamp(20px, 2.6vw, 28px) 0 0;
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 64ch;
}
.edu-figcaption .edu-illus {
  color: var(--text-faint);
  font-style: italic;
}
/* SVG typography wears text tokens (identity is carried by labels, not colour alone) */
.edu-svg text { font-family: var(--font); }
.edu-svg .lbl { fill: var(--text-soft); font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.edu-svg .sub { fill: var(--text-dim); font-size: 13.5px; }
.edu-svg .tiny { fill: var(--text-faint); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; }
.edu-svg .axis { stroke: var(--hairline-2); stroke-width: 1; }
.edu-svg .grid { stroke: var(--hairline); stroke-width: 1; }

/* ---- The four states ---- */
/* Colour-keyed cards, each carrying a two-axis "signature" mini-shape so the
   four states read as one comparable set in the page's blue/pink line language. */
.edu-states {
  display: grid;
  gap: clamp(18px, 2.6vw, 24px);
  margin: clamp(32px, 5vw, 52px) 0 clamp(18px, 2.4vw, 24px);
}
.edu-state {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 148px;
  gap: clamp(20px, 3vw, 34px);
  align-items: start;
  position: relative;
  max-width: none;
  padding: clamp(24px, 3vw, 32px) var(--inset);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .022), rgba(255, 255, 255, 0) 55%),
    var(--card);
  overflow: hidden;
}
/* colour keyline down the left edge (works with the mixed-state gradient too) */
.edu-state::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--edu-c, var(--violet));
}
.edu-state-main { min-width: 0; }
.edu-state-heading {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 5px 12px;
  margin-bottom: clamp(10px, 1.4vw, 14px);
}
.edu-state h3 {
  font-size: clamp(1.35rem, 2.6vw, 1.8rem);
  letter-spacing: -0.028em;
  line-height: 1.12;
  margin: 0;
}
.edu-state-tag {
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.edu-state .edu-essence {
  margin: 0 0 clamp(12px, 1.6vw, 16px);
  font-size: clamp(1.06rem, 1.7vw, 1.2rem);
  font-weight: 500;
  color: var(--text-soft);
  letter-spacing: -0.01em;
}
.edu-state p {
  margin: 0 0 14px;
  font-size: clamp(1.02rem, 1.55vw, 1.14rem);
  line-height: 1.68;
  color: var(--text-soft);
}
.edu-state p:last-child { margin-bottom: 0; }
.edu-state a {
  color: var(--violet-light);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color var(--dur) var(--ease);
}
.edu-state a:hover { color: #fff; }
/* the signature shape — top-aligned so all four line up into one column */
.edu-state-sig { margin: 0; align-self: start; padding-top: 6px; }
.edu-state-sig svg { display: block; width: 100%; height: auto; overflow: visible; }
/* one legend teaches the colour language before the first full diagram */
.edu-states-legend {
  margin: 0;
  font-size: .86rem;
  line-height: 1.5;
  color: var(--text-faint);
}
.edu-states-legend b { font-weight: 600; }
.edu-sig-dep { color: #578FF0; }
.edu-sig-ele { color: #E05C94; }
@media (max-width: 620px) {
  .edu-state { grid-template-columns: 1fr; gap: 16px; }
  .edu-state-sig { max-width: 220px; }
}

/* ---- Where existing trackers fall short — a calm list, not an attack ---- */
.edu-shortfalls {
  display: grid;
  gap: clamp(16px, 2.2vw, 22px);
  margin: clamp(34px, 5vw, 52px) 0;
  padding: 0;
  list-style: none;
}
.edu-shortfall {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: clamp(12px, 2vw, 18px);
  align-items: start;
  padding-bottom: clamp(16px, 2.2vw, 22px);
  border-bottom: 1px solid var(--hairline);
}
.edu-shortfall:last-child { border-bottom: 0; padding-bottom: 0; }
.edu-shortfall .edu-mark {
  margin-top: 3px;
  width: 22px; height: 22px;
  flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, .035);
  border: 1px solid var(--hairline-2);
  color: var(--text-faint);
}
.edu-shortfall .edu-mark svg { width: 12px; height: 12px; }
.edu-shortfall b {
  color: var(--text);
  font-weight: 600;
  font-size: clamp(1.05rem, 1.6vw, 1.18rem);
  letter-spacing: -0.015em;
}
.edu-shortfall span:not(.edu-mark) {
  display: block;
  margin-top: 3px;
  color: var(--text-dim);
  font-size: clamp(.98rem, 1.4vw, 1.06rem);
  line-height: 1.6;
}

/* ---- How POLARITY responds — the six design decisions as a numbered card grid.
   Same card language as the state-signature cards; each led by a small purpose-built
   diagram (card 01 carries the blue/pink two-axis signature, tying the payoff to the
   page's thesis). ---- */
.edu-responds {
  list-style: none;
  margin: clamp(30px, 4vw, 44px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 20px);
}
.edu-respond {
  display: flex;
  flex-direction: column;
  /* the same interior as .edu-state, so both card families share one inner
     edge instead of missing each other by 10px */
  padding: clamp(24px, 3vw, 32px) var(--inset);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .022), rgba(255, 255, 255, 0) 58%),
    var(--card);
}
.edu-respond-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: clamp(15px, 2vw, 19px);
}
.edu-respond-fig {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px; height: 36px;
  flex: none;
  border-radius: 11px;
  border: 1px solid var(--hairline-2);
  background: var(--glass);
  color: var(--violet-light);
}
.edu-respond-fig svg { width: 30px; height: auto; }
.edu-respond-num {
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}
.edu-respond-line {
  margin: 0;
  font-size: clamp(1.12rem, 1.5vw, 1.3rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.28;
  color: var(--text);
}
.edu-respond-because {
  margin: 12px 0 0;
  font-size: clamp(.98rem, 1.2vw, 1.05rem);
  line-height: 1.62;
  color: var(--text-soft);
}
@media (max-width: 560px) {
  .edu-responds { grid-template-columns: 1fr; }
}

/* ---- Gentle care note (responsibility, never alarming) ---- */
.edu-note {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  margin: clamp(40px, 6vw, 64px) 0;
  padding: clamp(22px, 3vw, 28px) var(--inset);
  border-radius: var(--r-md);
  background: rgba(122, 92, 219, .06);
  border: 1px solid rgba(122, 92, 219, .16);
}
.edu-note .edu-note-icon {
  flex: none;
  width: 26px; height: 26px;
  color: var(--violet-light);
}
.edu-note .edu-note-icon svg { width: 26px; height: 26px; }
.edu-note p {
  margin: 0;
  color: var(--text-soft);
  font-size: 1rem;
  line-height: 1.62;
}
.edu-note a { color: var(--violet-light); text-decoration: underline; text-underline-offset: 3px; }
.edu-note a:hover { color: #fff; }

/* ---- Section intro (an eyebrow + heading + lead that opens a movement group) ---- */
.edu-section-head { max-width: var(--measure); margin: clamp(56px, 9vw, 104px) 0 clamp(30px, 4vw, 44px); }
.edu-section-head .eyebrow { margin-bottom: 18px; }

/* ---- One reading axis ----
   Every text block on the page resolves to a single measure, centred on the
   page's centre line; figures, cards and quotes are the only things allowed to
   be wider, and they bleed symmetrically either side of it. Before this the
   essay ran three measures (62ch prose, 64ch heads and captions) hard against
   the column's left edge while the hero and the coda were centred — so the
   axis moved as the page scrolled. */
.edu-body .v-prose,
.edu-section-head,
.edu-figcaption,
.edu-states-legend {
  max-width: var(--measure);
}
/* The principle carries a rule where a card carries a border, so it needs the
   inset plus that 1px for its text to meet the same interior edge the cards
   open on, rather than missing it by a pixel. */
.edu-body .v-principle {
  padding-left: calc(var(--inset) + 1px);
}

/* Centring the measure only pays once the column clears it by more than a
   card's own padding — the same 1180px where the rail leaves the flow and the
   column settles at a true 760. Below that the inset shrinks to ~30px, lands
   within a few pixels of the cards' interior edge, and two edges that nearly
   but don't quite agree read worse than none: there, every line stays flush to
   the column, which is what the essay did before. */
@media (min-width: 1180px) {
  .edu-body .v-prose,
  .edu-section-head,
  .edu-figcaption,
  .edu-states-legend {
    margin-inline: auto;
  }
  /* The principle is running text, not a card: its violet rule hangs in the
     margin so the words keep the same axis as every other line instead of
     starting 38px to their left. */
  .edu-body .v-principle {
    max-width: calc(var(--measure) + var(--inset) + 1px);
    margin-left: max(0px, calc(50% - var(--measure) / 2 - var(--inset) - 1px));
    margin-right: 0;
  }
  /* The note's icon hangs in its padding for the same reason: what is left is
     the icon (26) plus the flex gap (16) plus the card's 1px border, so the
     paragraph itself lands on the axis instead of 5px off it. */
  .edu-note {
    padding-left: max(22px, calc((100% - var(--measure)) / 2 - 26px - 16px - 1px));
  }
}
.edu-section-head h2 {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  letter-spacing: -0.032em;
  line-height: 1.08;
  margin-bottom: clamp(16px, 2vw, 22px);
}
.edu-section-head p {
  margin: 0;
  font-size: clamp(1.08rem, 1.8vw, 1.28rem);
  line-height: 1.62;
  color: var(--text-soft);
}

/* ---- Closing + essay foot (twins of the Vision essay's) ---- */
.edu-closing { margin: clamp(64px, 10vw, 120px) auto 0; max-width: var(--measure); text-align: center; }
.edu-closing h2 {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  letter-spacing: -0.032em;
  line-height: 1.1;
  margin-bottom: clamp(20px, 2.6vw, 28px);
}
.edu-closing p {
  margin: 0 0 20px;
  font-size: clamp(1.08rem, 1.8vw, 1.28rem);
  line-height: 1.68;
  color: var(--text-soft);
}
.edu-closing p:last-of-type { margin-bottom: 0; }
.edu-closing .edu-closing-note {
  margin-top: clamp(30px, 4vw, 42px);
  font-size: .95rem;
  color: var(--text-faint);
  line-height: 1.6;
}
.edu-closing .edu-closing-note a { color: var(--violet-light); text-decoration: underline; text-underline-offset: 3px; }
.edu-closing .edu-closing-note a:hover { color: #fff; }

/* The essay's pull-quotes are quieter than /vision's — a plain hairline instead
   of the glowing violet pip (which read faintly notification-like), a touch
   lighter, and a slightly wider measure so short lines don't orphan. Scoped to
   .edu so /vision keeps its own mark. */
.edu .v-quote { max-width: 16em; }
.edu .v-quote p { font-weight: 500; line-height: 1.24; }
.edu .v-quote::before {
  width: 56px; height: 1px;
  background: var(--hairline-2);
  box-shadow: none;
  margin: 0 auto clamp(22px, 3vw, 32px);
}
/* Closing coda — the sign-off set as a centred italic line, with a small
   terminal dot that echoes the eyebrow mark opening every section. */
.edu-closing .edu-closing-note { text-align: center; font-style: italic; }
.edu-closing .edu-closing-note::after {
  content: "";
  display: block;
  width: 6px; height: 6px;
  margin: clamp(28px, 4vw, 40px) auto 0;
  border-radius: 50%;
  background: var(--violet);
  box-shadow: 0 0 12px 2px rgba(122, 92, 219, .8);
}
/* Crisis action lifted so it's findable at a glance within the care note. */
.edu-note b { color: var(--text); font-weight: 600; }

.edu-foot {
  margin-top: clamp(48px, 7vw, 76px);
  padding-top: clamp(30px, 4vw, 40px);
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  align-items: center;
  justify-content: center;
}
.edu-foot a { color: var(--text-dim); font-size: .95rem; transition: color var(--dur) var(--ease); }
.edu-foot a:hover { color: var(--text); }
.edu-foot .edu-foot-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--violet-light);
  font-weight: 500;
}
.edu-foot .edu-foot-primary:hover { color: #fff; }
.edu-foot .edu-foot-primary svg { width: 16px; height: 16px; transition: transform var(--dur) var(--ease); }
.edu-foot .edu-foot-primary:hover svg { transform: translateX(3px); }

@media (max-width: 620px) {
  .edu-note { flex-direction: column; gap: 12px; }
}
