/* ftns.ai — one stylesheet, no framework. Black-based dark system (owner
   direction, supersedes the spec's clay palette). The twin illustration is full
   colour — warm clay tones lit by the accent — per the marketing brief §4.1; it
   used to be a grey sculpt and no longer is anywhere on the site. The accent is
   one token, --pulse: every orange pixel on the site is that value, at full
   strength or at an alpha. Neutral text pairs all clear 4.5:1; the accent used
   AS text is 5.3:1 on the page ground and dips to ~4.4:1 where an aurora blob
   sits behind a card surface — measured, and documented in README → "Design
   decisions". */

:root {
  color-scheme: dark;

  --bg: #0a0a0c;            /* page ground — near-black, slightly cool */
  --bg-veil: rgba(10, 10, 12, 0.72);   /* sticky header glass */
  --surface: rgba(255, 255, 255, 0.03);
  --surface-2: rgba(255, 255, 255, 0.06);
  --border: rgba(255, 255, 255, 0.08);
  --border-2: rgba(255, 255, 255, 0.16);

  --text-hi: #f4f4f5;       /* headings, emphasis — ~17:1 on bg */
  --text: #b7b7c0;          /* body — ~9:1 on bg */
  --text-dim: #9797a1;      /* fine print, datelines — 6.8:1 on bg, 5.2:1 over
                               the brightest point the aurora can reach */

  --pulse: #e2571b;         /* single accent — 5.3:1 on bg, text-safe */
  --pulse-ink: #1c0d06;     /* text on accent surfaces — 5.1:1 on the accent */
  --pulse-soft: rgba(226, 87, 27, 0.12);
  --pulse-line: rgba(226, 87, 27, 0.35);
  --ok: #5bd693;            /* success state only — ~9:1 on bg */

  --radius: 1rem;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* FONT-TODO: self-host a display grotesk (woff2 in /public/fonts,
     font-display: swap). Font files could not be fetched in this build
     environment (egress blocked), so type rides the system stack below. */
  --font-display: -apple-system, "SF Pro Display", "Inter", "Segoe UI Variable Display",
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-body: -apple-system, "SF Pro Text", "Inter", "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

/* ---------- base ---------- */

* { box-sizing: border-box; }

html {
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

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

/* ---------- animated background ----------
   Three slow aurora blobs and a drifting dust layer over the black ground.
   Fixed, behind everything, pointer-events: none, and alpha kept low enough
   that the dimmest neutral text still clears 4.5:1 wherever the blobs land
   (measured — see README → Design decisions). Animation is transform/opacity
   only. */

.bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  contain: strict;
}

.bg span {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(60px);
  will-change: transform;
}

.bg-a {
  top: -18vh;
  right: -10vw;
  width: 60vw;
  height: 60vw;
  max-width: 46rem;
  max-height: 46rem;
  background: radial-gradient(circle, rgba(226, 87, 27, 0.13), transparent 70%);
  animation: drift-a 34s ease-in-out infinite alternate;
}

.bg-b {
  top: 32vh;
  left: -18vw;
  width: 55vw;
  height: 55vw;
  max-width: 40rem;
  max-height: 40rem;
  background: radial-gradient(circle, rgba(92, 116, 255, 0.11), transparent 70%);
  animation: drift-b 46s ease-in-out infinite alternate;
}

.bg-c {
  bottom: -22vh;
  right: 6vw;
  width: 50vw;
  height: 50vw;
  max-width: 36rem;
  max-height: 36rem;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.05), transparent 68%);
  animation: drift-c 58s ease-in-out infinite alternate;
}

/* Fine dust: two dot fields at different scales, panning slowly upward. */
.bg-dust {
  inset: -20% -20% -40%;
  width: auto;
  height: auto;
  border-radius: 0;
  filter: none;
  opacity: 0.5;
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.16) 1px, transparent 1px),
    radial-gradient(rgba(255, 255, 255, 0.09) 1px, transparent 1px);
  background-size: 140px 140px, 90px 90px;
  background-position: 0 0, 40px 60px;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 30%, #000 20%, transparent 75%);
  mask-image: radial-gradient(120% 90% at 50% 30%, #000 20%, transparent 75%);
  animation: dust 90s linear infinite;
}

@keyframes drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-8vw, 10vh, 0) scale(1.15); }
}

@keyframes drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(10vw, -12vh, 0) scale(0.95); }
}

@keyframes drift-c {
  from { transform: translate3d(0, 0, 0) scale(0.95); }
  to   { transform: translate3d(-12vw, -8vh, 0) scale(1.2); }
}

@keyframes dust {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-90px, -140px, 0); }
}

h1, h2, h3 {
  font-family: var(--font-display);
  color: var(--text-hi);
  line-height: 1.08;
  margin: 0 0 0.55em;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.3rem, 5.5vw + 1rem, 4rem);
  font-weight: 700;
  letter-spacing: -0.035em;
}

/* Weights stay on hundred steps: 550/650 resolve upward to 700 on the static
   system faces (Roboto, Arial) this stack falls back to. */
h2 {
  font-size: clamp(1.55rem, 2.4vw + 0.85rem, 2.2rem);
  font-weight: 600;
  margin-top: 0;
}

h3 {
  font-size: 1.08rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.3em;
}

p { margin: 0 0 1em; }

a {
  color: var(--text-hi);
  text-decoration-color: var(--border-2);
  text-underline-offset: 0.2em;
  transition: color 160ms ease, text-decoration-color 160ms ease;
}

a:hover {
  color: var(--pulse);
  text-decoration-color: var(--pulse-line);
}

strong { color: var(--text-hi); }

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

::selection { background: rgba(226, 87, 27, 0.35); color: #fff; }

img, svg { max-width: 100%; height: auto; }

/* ---------- motion primitives ---------- */

@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

@keyframes glow-breathe {
  from { opacity: 0.6; }
  to   { opacity: 1; }
}

/* Scroll-reveal: the hidden state requires (a) motion to be welcome and (b) the
   `js` class that /js/main.js puts on <html> when it actually executes — so if
   the script is blocked, 404s, or never runs, nothing is ever hidden. All
   .reveal sections are below the fold, so arming after first paint is invisible.
   /js/main.js flips .in via IntersectionObserver. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  html.js .reveal {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 700ms ease, transform 700ms var(--ease-out);
  }
  html.js .reveal.in { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
}

/* Print: ink-friendly, and never print a page whose sections are mid-reveal
   (IntersectionObserver does not run during print rasterization) — which
   includes the per-child stagger /soon layers on top of it. */
@media print {
  html.js .reveal { opacity: 1 !important; transform: none !important; }
  html.js .reveal .stagger > * { opacity: 1 !important; transform: none !important; }
  body { background: #fff; color: #000; }
  .bg, section.hero::before, .hero-art::before,
  .soon-hero::before, .soon-hero::after, .soon-vitrine::before, .soon-vitrine::after,
  .soon-join::before, .soon-ring::after { display: none; }
  h1, h2, h3, strong, a, .wordmark, .wordmark .tld, .coming-soon,
  .soon-figure, .soon-kicker, .soon-tagline, .soon-qa dt, .soon-art-caption { color: #000; }
  .hero h1, .soon-hero h1 { -webkit-text-fill-color: #000; }
  .site-header { position: static; background: none; }
  .steps li, .table-scroll, .faq-teaser details, section.honesty, .draft-banner,
  .feature-grid li, section.final-cta, .signup, .signup-success,
  .post-card, .post-toc, .callout, .post-hero, .post-figure .cover,
  .soon-specs, .soon-cols li, .soon-join-card, .soon-vitrine {
    background: none;
    border-color: #999;
  }
  .post-card h2 a::after, .post-card h3 a::after { display: none; }
  .btn-download { background: none; color: #000; border: 1px solid #000; box-shadow: none; }
  .skip-link, .soon-sticky, .soon-cta-row, .soon-band-cta, .soon-inline-cta,
  .site-header nav .nav-cta { display: none; }
}

/* ---------- skip link ---------- */

.skip-link {
  position: absolute;
  left: 1rem;
  top: -3.5rem;
  z-index: 100;
  background: var(--text-hi);
  color: var(--bg);
  font-weight: 600;
  padding: 0.55rem 1rem;
  border-radius: 999px;
  text-decoration: none;
  transition: top 140ms ease;
}

.skip-link:focus { top: 1rem; }

/* ---------- wordmark ---------- */

.wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: -0.03em;
  color: var(--text-hi);
  text-decoration: none;
  text-transform: none; /* the wordmark is lowercase, always (§2.1) */
}

.wordmark .tld { color: var(--pulse); } /* accent ".ai", ≥4.5:1 on bg */

/* ---------- header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg-veil);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color 300ms ease;
}

.site-header.scrolled { border-bottom-color: var(--border); }

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: 68rem;
  margin: 0 auto;
  padding: 0.9rem 1.25rem;
}

/* Explicit gap rather than relying on the whitespace between anchors, now that
   the nav carries more than one destination. */
.site-header nav {
  display: flex;
  align-items: center;
  gap: 0.15rem;
}

.site-header nav a {
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.95rem;
  padding: 0.45rem 0.9rem;
  border-radius: 999px;
  transition: color 160ms ease, background-color 160ms ease;
}

.site-header nav a:hover,
.site-header nav a[aria-current="page"] {
  color: var(--text-hi);
  background: var(--surface-2);
}

/* The one promotion in the nav. Filled accent so it reads as a button beside
   two plain links rather than a third destination — --pulse-ink is the
   existing text-on-accent token (5.1:1 on the accent), so this needs no new
   colour. The margin buys it separation from Blog without a divider. */
.site-header nav .nav-cta {
  background: var(--pulse);
  color: var(--pulse-ink);
  font-weight: 650;
  margin-right: 0.35rem;
  white-space: nowrap;
}

.site-header nav .nav-cta:hover {
  /* Brightened, not recoloured — the same move .btn-download makes, so there
     is still exactly one accent value in the stylesheet. */
  background: var(--pulse);
  color: var(--pulse-ink);
  filter: brightness(1.08);
}

/* A fourth item in the header row does not fit a 320px phone at the default
   spacing — it overflowed by 27px, and nothing on this site is allowed to
   scroll sideways. The text links give up their horizontal padding first; the
   CTA keeps more of its own, because a pill that hugs its label stops reading
   as a button. Vertical padding is untouched: these tap targets are already
   tight and shrinking them further would make it worse. */
@media (max-width: 26rem) {
  .header-inner {
    gap: 0.4rem;
    padding-left: 0.85rem;
    padding-right: 0.85rem;
  }

  .site-header nav { gap: 0; }

  .site-header nav a {
    font-size: 0.9rem;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }

  .site-header nav .nav-cta {
    padding-left: 0.8rem;
    padding-right: 0.8rem;
    margin-right: 0.15rem;
  }
}

/* ---------- layout ---------- */

main {
  display: block;
  padding: 0 1.25rem 4rem;
}

main > section {
  max-width: 44rem;
  margin: 0 auto;
  padding: 3.5rem 0;
}

.prose {
  max-width: 68ch;
  margin: 0 auto;
  padding: 2.5rem 0;
}

/* ---------- hero ---------- */

section.hero {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: center;
  max-width: 68rem;
  padding-top: 3rem;
  padding-bottom: 4.5rem;
}

/* Faint blueprint grid, fading out downward. Decoration only. */
section.hero::before {
  content: "";
  position: absolute;
  /* Horizontal inset exactly cancels main's 1.25rem padding, so the grid bleeds
     to the viewport edge without ever widening the scrollable area. */
  inset: -2rem -1.25rem 20%;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 44px 44px;
  -webkit-mask-image: radial-gradient(90% 100% at 50% 0%, #000 30%, transparent 78%);
  mask-image: radial-gradient(90% 100% at 50% 0%, #000 30%, transparent 78%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  margin: 0 0 1.4rem;
}

.hero h1 {
  margin-bottom: 0.35em;
  background: linear-gradient(180deg, #ffffff 30%, #b9b9c4 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #e6e6ea; /* fallback + forced-colors safety */
}

.hero .sub {
  font-size: 1.2rem;
  line-height: 1.6;
  max-width: 36rem;
  color: var(--text);
}

/* Staggered entrance — above the fold animates on load, not on scroll. */
@media (prefers-reduced-motion: no-preference) {
  .hero-copy > * { animation: rise 800ms var(--ease-out) both; }
  .hero-copy > *:nth-child(1) { animation-delay: 40ms; }
  .hero-copy > *:nth-child(2) { animation-delay: 120ms; }
  .hero-copy > *:nth-child(3) { animation-delay: 200ms; }
  .hero-copy > *:nth-child(4) { animation-delay: 300ms; }
  .hero-art { animation: rise 900ms 250ms var(--ease-out) both; }
}

.hero-art {
  order: -1;
  position: relative;
  text-align: center;
}

.hero-art::before {
  content: "";
  position: absolute;
  inset: 6% 14% 12%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(226, 87, 27, 0.20), transparent 72%);
  animation: glow-breathe 5s ease-in-out infinite alternate;
}

.twin {
  position: relative;
  z-index: 1;
  width: min(48vw, 250px);
  display: inline-block;
}

.twin-shadow { fill: rgba(255, 255, 255, 0.07); }

.bob { animation: bob 5s ease-in-out infinite; }

@media (min-width: 46rem) {
  section.hero {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    padding-top: 5rem;
  }
  .hero-art { order: 0; }
}

/* ---------- CTA / waitlist ---------- */

.cta-primary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin: 2rem 0 0;
}

.btn-download {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--pulse-ink);
  /* The sheen is a white overlay, not a second orange — the accent is exactly
     one token, so changing --pulse changes every accent pixel on the site. */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0) 70%), var(--pulse);
  border-radius: 999px;
  padding: 0.85rem 1.75rem;
  text-decoration: none;
  box-shadow: 0 6px 22px rgba(226, 87, 27, 0.22);
  transition: transform 200ms var(--ease-out), box-shadow 200ms ease, filter 160ms ease;
}

.btn-download:hover {
  color: var(--pulse-ink);
  transform: translateY(-2px);
  filter: brightness(1.06);
  box-shadow: 0 10px 30px rgba(226, 87, 27, 0.34);
}

.btn-download:active { transform: translateY(0); box-shadow: none; }

.cta { margin-top: 1.5rem; }

.cta-note {
  font-size: 0.92rem;
  color: var(--text-dim);
  margin-top: 0.7rem;
}

.cta-primary .cta-note { margin-top: 0; }

.coming-soon { font-weight: 600; color: var(--text-hi); }

.badge-link {
  display: inline-block;
  border-radius: 0.7rem;
  transition: transform 200ms var(--ease-out), box-shadow 200ms ease;
}

.badge-link:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px rgba(226, 87, 27, 0.18);
}

.waitlist label {
  display: block;
  font-weight: 600;
  color: var(--text-hi);
  margin-bottom: 0.6rem;
}

.waitlist-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  max-width: 27rem;
}

.waitlist input,
.signup input {
  flex: 1 1 12rem;
  font: inherit;
  color: var(--text-hi);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 0.7rem;
  padding: 0.7rem 1rem;
  min-width: 0;
  transition: border-color 160ms ease, background-color 160ms ease, box-shadow 160ms ease;
}

.waitlist input:hover,
.signup input:hover { border-color: rgba(255, 255, 255, 0.28); }

.waitlist input:focus,
.signup input:focus {
  /* Transparent (not none): forced-colors mode drops the box-shadow and forces
     the border to a system color, but re-colors this outline so the focused
     field stays distinguishable. */
  outline: 2px solid transparent;
  outline-offset: 2px;
  border-color: var(--pulse);
  box-shadow: 0 0 0 3px var(--pulse-soft);
}

.waitlist input::placeholder,
.signup input::placeholder {
  color: var(--text-dim);
  opacity: 1; /* Firefox dims placeholders by default */
}

.waitlist button,
.signup button {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--pulse-ink);
  background: var(--pulse);
  border: 0;
  border-radius: 0.7rem;
  padding: 0.7rem 1.4rem;
  cursor: pointer;
  transition: transform 200ms var(--ease-out), box-shadow 200ms ease, filter 160ms ease;
}

/* Brightened, not recoloured — same reason as .btn-download's sheen above. */
.waitlist button:hover,
.signup button:hover {
  filter: brightness(1.08);
  transform: translateY(-1px);
  box-shadow: 0 6px 24px rgba(226, 87, 27, 0.30);
}

.waitlist button:active,
.signup button:active { transform: translateY(0); box-shadow: none; }

.waitlist-success,
.signup-success {
  font-weight: 600;
  color: var(--ok);
}

.waitlist-error,
.signup-error {
  font-weight: 600;
  color: var(--pulse);
  margin-bottom: 0.75rem;
}

/* ---------- how it works ---------- */

section.how { max-width: 58rem; }

.steps {
  list-style: none;
  counter-reset: step;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.9rem;
}

@media (min-width: 46rem) {
  .steps { grid-template-columns: repeat(3, 1fr); gap: 1rem; }
}

.steps li {
  counter-increment: step;
  position: relative;
  padding: 3.6rem 1.3rem 1.3rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: transform 250ms var(--ease-out), border-color 250ms ease;
}

.steps li:hover {
  transform: translateY(-3px);
  border-color: var(--border-2);
}

.steps li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: 1.3rem;
  top: 1.15rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  color: var(--pulse);
  background: var(--pulse-soft);
  border: 1px solid var(--pulse-line);
  border-radius: 999px;
  padding: 0.18rem 0.6rem;
}

.steps p { margin-bottom: 0; font-size: 0.98rem; }

/* ---------- what is ftns.ai / feature grid ---------- */

section.seo { max-width: 46rem; }

section.seo p { font-size: 1.05rem; }

section.features { max-width: 58rem; }

.feature-grid {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.9rem;
}

@media (min-width: 40rem) {
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 62rem) {
  .feature-grid { grid-template-columns: repeat(3, 1fr); }
}

.feature-grid li {
  padding: 1.3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: transform 250ms var(--ease-out), border-color 250ms ease;
}

.feature-grid li:hover {
  transform: translateY(-3px);
  border-color: var(--border-2);
}

.feature-grid h3 { margin-bottom: 0.4em; }

.feature-grid p { margin-bottom: 0; font-size: 0.96rem; }

/* ---------- pricing ---------- */

section.pricing { max-width: 54rem; }

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin: 1.5rem 0 1rem;
}

/* The Free/Premium comparison table is gone: the privacy policy says there is
   no free tier to compare against (README blocker 22). What replaced it is a
   plain list of what the one membership includes. */
.included-heading {
  font-size: 1rem;
  color: var(--text-hi);
  margin: 2rem 0 0.9rem;
}

.included-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.75rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.1rem 2rem;
}

.included-list li {
  padding: 0.72rem 0 0.72rem 1.6rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.98rem;
  position: relative;
}

/* The tick is decorative — the list semantics already say "included". */
.included-list li::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 1.12rem;
  width: 0.5rem;
  height: 0.28rem;
  border-left: 2px solid var(--pulse);
  border-bottom: 2px solid var(--pulse);
  transform: rotate(-45deg);
}

.smallprint {
  font-size: 0.9rem;
  color: var(--text-dim);
}

/* The gems note and the billing smallprint are two different disclosures and
   used to be separated by the pricing table. Now they are adjacent, so they
   need the gap the table used to give them. */
.note + .smallprint { margin-top: 1.1rem; }

.smallprint a { color: var(--text); }

/* ---------- shared section furniture ----------
   .lead, .note, .point-list and .chips are used by every feature section added
   from the marketing brief (meet / score / future / coach / share / private),
   so they are defined once here rather than per-section. */

section.meet,
section.coach,
section.private { max-width: 52rem; }

section.score,
section.future,
section.share { max-width: 48rem; }

.lead {
  font-size: 1.05rem;
  color: var(--text);
  max-width: 44rem;
}

.note {
  font-size: 0.94rem;
  color: var(--text-dim);
  max-width: 44rem;
  margin-bottom: 0;
}

.note strong { color: var(--text-hi); }

/* Label-led points. The <strong> lead runs inline with its sentence so the list
   reads as prose at phone width instead of a stack of orphaned headings. */
.point-list {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

@media (min-width: 46rem) {
  .point-list { grid-template-columns: repeat(2, 1fr); gap: 0.9rem; }
}

.point-list li {
  padding: 1.15rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.96rem;
  transition: border-color 250ms ease;
}

.point-list li:hover { border-color: var(--border-2); }

.point-list strong {
  color: var(--text-hi);
  font-family: var(--font-display);
  font-weight: 650;
}

/* Chip rows: the six Life Score categories and the six Future Self horizons.
   Labels only — no invented numbers, so nothing here reads as a screenshot. */
.chips {
  list-style: none;
  margin: 1.4rem 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.chips li {
  padding: 0.4rem 0.85rem;
  border: 1px solid var(--pulse-line);
  border-radius: 999px;
  background: var(--pulse-soft);
  color: var(--text-hi);
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 550;
}

.timelines {
  list-style: none;
  margin: 1.4rem 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

@media (min-width: 46rem) {
  .timelines { grid-template-columns: repeat(3, 1fr); }
}

.timelines li {
  padding: 1.15rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.timelines h3 { margin-bottom: 0.3em; font-size: 1.02rem; }

.timelines p { margin-bottom: 0; font-size: 0.94rem; }

/* The 3D-scan tease. Brief §10 marks the feature "in testing", so it is styled
   as an aside rather than as a shipped feature card. */
.teaser {
  margin-top: 1.4rem;
  padding: 1.1rem 1.25rem;
  border: 1px dashed var(--pulse-line);
  border-radius: var(--radius);
  background: var(--pulse-soft);
  font-size: 0.95rem;
  margin-bottom: 0;
}

.teaser strong { color: var(--pulse); }

/* ---------- photos ---------- */

section.photos {
  max-width: 54rem;
  background:
    radial-gradient(120% 130% at 50% 0%, var(--pulse-soft), transparent 55%),
    var(--surface);
  border: 1px solid var(--border);
  border-radius: 1.25rem;
  padding: 2.5rem 1.75rem;
}

section.photos p { margin-bottom: 0; }

/* ---------- FAQ (accordion + support list) ---------- */

.faq-teaser details {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.85rem;
  margin-bottom: 0.7rem;
  transition: border-color 200ms ease;
}

.faq-teaser details:hover { border-color: var(--border-2); }

.faq-teaser details[open] { border-color: var(--border-2); }

.faq-teaser summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--text-hi);
  padding: 1rem 1.15rem;
  cursor: pointer;
  border-radius: 0.85rem;
}

.faq-teaser summary::-webkit-details-marker { display: none; }

/* Border-drawn chevron, not a "+" glyph: generated *text* lands in the
   accessibility tree and would be read after every question, on top of the
   expanded/collapsed state <details> already announces. Borders also survive
   forced-colors mode, where background/gradient markers do not. */
.faq-teaser summary::after {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  margin-bottom: 0.25rem;
  border-right: 2px solid var(--pulse);
  border-bottom: 2px solid var(--pulse);
  transform: rotate(45deg);
  transition: transform 250ms var(--ease-out);
}

.faq-teaser details[open] summary::after { transform: rotate(225deg); }

.faq-teaser details p { padding: 0 1.15rem 1.05rem; margin: 0; }

.faq-more { margin-top: 1.4rem; font-weight: 600; }

.faq h3 { margin-top: 1.6rem; }

/* ---------- closing CTA ---------- */

section.final-cta {
  max-width: 54rem;
  text-align: center;
  background:
    radial-gradient(120% 140% at 50% 0%, var(--pulse-soft), transparent 60%),
    var(--surface);
  border: 1px solid var(--border);
  border-radius: 1.25rem;
  padding: 3rem 1.5rem;
}

section.final-cta p { margin-bottom: 0; }

section.final-cta .cta-primary { justify-content: center; margin-top: 1.75rem; }

/* ---------- legal pages ---------- */

.draft-banner {
  border: 1px solid var(--pulse-line);
  background: var(--pulse-soft);
  color: var(--text-hi);
  font-weight: 600;
  border-radius: 0.85rem;
  padding: 0.9rem 1.15rem;
}

.dateline { color: var(--text-dim); }

/* The privacy policy is 17 sections of continuous prose, not a landing page, so
   its headings drop to reading size and buy their space above instead — the
   global h2 is display-scale with margin-top:0, which is right for a section
   band and wrong for a document you read top to bottom. Scoped to .policy so
   the blog, which also uses .prose, keeps the article scale. */
.policy h2 {
  font-size: clamp(1.2rem, 0.9vw + 1rem, 1.45rem);
  margin-top: 2.4em;
  scroll-margin-top: 1.5rem;
}

.policy .dateline { margin-bottom: 0; }

/* Where the app's policy ends and the website's own notice begins. The rule is
   load-bearing, not decoration: the text above it is published verbatim and the
   text below it is not, and a reader should be able to see the seam. */
.policy-split {
  border: 0;
  border-top: 1px solid var(--border-2);
  margin: 3.5rem 0 2.5rem;
}

.prose ul { padding-left: 1.4rem; }

.prose li { margin-bottom: 0.6em; }

.prose li::marker { color: var(--pulse); }

/* ---------- 404 ---------- */

.notfound {
  text-align: center;
  padding-top: 6rem;
}

/* ---------- coming-soon page (/soon) ----------" through the
   closing brace of the .hp rule, inclusive). Everything below is that block.
   ============================================================================ */

/* ---------- /send — the bio landing page ----------
   Reuses .eyebrow, .btn-download and .cta-primary; the only new furniture is
   the link card, because a URL meant to be read off a screen and long-pressed
   needs to be bigger and more selectable than body copy. */

section.send {
  max-width: 34rem;
  text-align: center;
}

section.send h1 {
  font-size: clamp(1.7rem, 3.5vw + 1rem, 2.4rem);
  margin-bottom: 0.5em;
}

section.send .sub {
  font-size: 1.05rem;
  color: var(--text);
  margin-bottom: 2rem;
}

/* No card chrome here on purpose. --surface is 3% white and --border is 8%,
   which composite to 1.05:1 and 1.19:1 against the page ground — a boundary
   needs about 3:1 to be seen at all, so these boxes were invisible on a phone
   (reported from TikTok's browser, and it is arithmetic, not a webview quirk).
   Rather than thicken a second box, the wrapper gives up its chrome entirely so
   the page has exactly ONE box and it is the link. */
.send-link { padding: 0; }

/* Points at the one thing on the page worth acting on, so it is set like a
   label and not like fine print: full-strength text at 18:1, bold, and tracked
   out. It was --text-dim 0.8rem, which is the styling of an afterthought. */
.send-link-label {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-hi);
  margin: 0 0 0.7rem;
}

/* The one box on the page, and the only thing the reader has to act on.
   No dark fill can carry it — even #212128 is 1.24:1 against the ground — so
   the ACCENT does, at full opacity: --pulse is 5.29:1 against the page, which
   reads on a phone in daylight. The text stays --text-hi (16.25:1 on this
   fill) because a URL has to be legible enough to retype if copying fails.
   `user-select: all` turns one press-and-hold into the whole selection rather
   than a single word, and the wrap rules stop a long URL forcing a sideways
   scroll at 320px. */
.send-url { margin: 0 0 1rem; }

.send-url code {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: clamp(1.05rem, 3.9vw, 1.3rem);
  font-weight: 700;
  /* The URL itself carries the accent now. --pulse on the page ground is
     5.29:1 — comfortably past AA, and the darkest available fill is what buys
     that: the same orange on the old #17171c panel was 4.77:1. */
  color: var(--pulse);
  /* Opaque, and the same value as the page. It looks like no fill at all, and
     that is the point: the aurora blobs drift behind the content, so without an
     opaque backdrop the interior of this box would shift colour as they pass. */
  background: var(--bg);
  border: 2px solid var(--pulse);
  border-radius: 0.8rem;
  padding: 1.05rem 0.9rem;
  /* The same accent glow .btn-download uses — an alpha of the one accent token,
     never a second orange — so the box reads as raised on a phone screen. */
  box-shadow: 0 6px 22px rgba(226, 87, 27, 0.22);
  user-select: all;
  -webkit-user-select: all;
  overflow-wrap: anywhere;
  word-break: break-all;
}

/* Promoted from an aside to the instruction: with no copy button, this is the
   only thing that works in the browser this page is written for. */
.send-hint {
  margin: 0;
  font-size: 1rem;
  color: var(--text);
}

section.send .send-direct {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
}

section.send .send-direct h2 {
  font-size: 1.1rem;
  margin-bottom: 0.4em;
}

section.send .send-direct .cta-primary {
  justify-content: center;
  margin-top: 1.25rem;
}

/* ---------- coming-soon page (/soon) ----------
   A staged landing page, not a form with a picture above it. Every value below
   comes from the tokens at the top of this file and the keyframes already
   defined for the rest of the site (rise / bob / glow-breathe); the scroll
   reveal is the existing .reveal mechanism and the existing observer. No new
   colour, no new font, no new script, no asset — the CSP is 'self' only.

   Three rules this block exists to hold, all of them easy to break by accident:

   1. NO text-transform ANYWHERE on this page. copycheck reads source and
      test/default-mode.test.js greps rendered HTML for the capitalised
      wordmark; neither can see a CSS transform. `uppercase` on a class that
      ever holds "ftns.ai" — the status chip does — puts the capitalised
      wordmark on screen with both gates green. `capitalize` is the same trap.
      Kickers and labels are letterspaced instead.
   2. Every form restyle is scoped under .soon-join. `.waitlist input, .signup
      input` and the matching button, hover, focus and placeholder rules above
      are ONE shared rule with the home hero's waitlist form, so an unscoped
      `.signup` edit silently restyles the home page.
   3. No accent-coloured small text. README → Design decisions measures --pulse
      as text at ~4.4:1 where an aurora blob sits behind a translucent card,
      and this page is made of translucent cards. Here the accent is a fill, a
      border, a rule, a glow, or display-size type where the 3:1 large-text
      floor applies — which is also why the error line is lifted off --pulse
      onto --text-hi with an accent rule.

   Structure: the measure narrows as the page descends — hero 62rem, beats
   40rem, form 34rem — so the geometry itself funnels toward the single form.
   Every CTA on the page is an anchor to #join. There is exactly one <form>,
   because a second would duplicate the input ids and the accessibility tree
   with them. */

/* ---- shell ----
   These blocks are main > div > section, so `main > section`'s 44rem cap and
   3.5rem rhythm never reach them and each one sets its own measure. */

.soon-page { padding-bottom: 1.5rem; }

.soon-proof,
.soon-beat,
.soon-band,
.soon-join,
.soon-recap,
.soon-closer {
  max-width: 40rem;
  margin: 0 auto;
}

/* One beat, one screen. The air is doing most of the work on this page. */
.soon-beat { padding-block: clamp(3.25rem, 10vh, 5.5rem); }

.soon-beat,
.soon-band { position: relative; }

/* Hairline seam between beats — a rule that fades out at both ends, rather
   than a border that stops dead. */
.soon-beat::before,
.soon-band::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border-2), transparent);
}

/* ---- hero ----
   The launch line is a status chip, not the h1: six words discharge "there is
   nothing to download yet" so the largest type on the page carries the claim
   instead of the announcement. */

.soon-hero {
  position: relative;
  display: grid;
  gap: 2rem;
  align-items: center;
  max-width: 62rem;
  margin: 0 auto;
  padding: 2.5rem 0 clamp(3.5rem, 9vh, 5rem);
}

/* The home hero's blueprint grid, same recipe. The horizontal inset exactly
   cancels main's 1.25rem padding, so it bleeds to the viewport edge without
   ever widening the scrollable area. */
.soon-hero::before {
  content: "";
  position: absolute;
  inset: -1.5rem -1.25rem 22%;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 44px 44px;
  -webkit-mask-image: radial-gradient(90% 100% at 50% 0%, #000 30%, transparent 78%);
  mask-image: radial-gradient(90% 100% at 50% 0%, #000 30%, transparent 78%);
}

/* Scroll cue: a light falling down a hairline. No glyph, no word, nothing to
   announce — so it is allowed to disappear entirely under reduced motion. */
.soon-hero::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0.5rem;
  width: 1px;
  height: 2.5rem;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, var(--pulse), transparent);
  background-size: 100% 220%;
  background-repeat: no-repeat;
  animation: cue-fall 2.8s ease-in-out infinite;
}

/* Nothing to cue when the page is short: the status or the form is already
   the next thing on screen. */
.soon-page.is-status .soon-hero::after { display: none; }

/* Status mode compresses the hero. POST /soon re-renders the whole document and
   the browser drops the reader at scroll-top, so on the full-height hero the
   confirmation and the validation error both landed below the fold on every
   phone — measured at 390x664, .signup-success sat at y=929 against a 664px
   viewport, i.e. the reader saw a screen identical to the one they submitted
   from. The chip and the h1 stay (they say where you are); everything that only
   sells stands down, because the reader has either just bought or needs to fix
   one field. */
.soon-page.is-status .soon-hero {
  padding: 1.25rem 0 1.75rem;
  gap: 1.25rem;
}
.soon-page.is-status .soon-art,
.soon-page.is-status .soon-hero-sub,
.soon-page.is-status .soon-meta,
.soon-page.is-status .cta-note { display: none; }
.soon-page.is-status .soon-hero h1 {
  font-size: clamp(1.75rem, 6vw, 2.4rem);
  margin-bottom: 0;
}

@keyframes cue-fall {
  0%   { background-position: 50% -110%; opacity: 0; }
  30%  { opacity: 0.7; }
  100% { background-position: 50% 210%; opacity: 0; }
}

/* Rides the existing .eyebrow pill (inline-flex with a gap), which is exactly
   why nothing here may ever be transformed to upper case. */
.soon-status { color: var(--text-hi); }

.soon-dot {
  flex: none;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--pulse);
  box-shadow: 0 0 0 3px var(--pulse-soft);
  animation: pulse-dot 2.4s ease-in-out infinite alternate;
}

@keyframes pulse-dot {
  from { opacity: 0.45; }
  to   { opacity: 1; }
}

.soon-hero h1 {
  font-size: clamp(2.6rem, 10.5vw, 5rem);
  line-height: 0.96;
  letter-spacing: -0.045em;
  margin-bottom: 0.3em;
  background: linear-gradient(180deg, #ffffff 30%, #b9b9c4 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #e6e6ea; /* fallback + forced-colors safety */
}

.soon-hero-sub {
  font-size: clamp(1.08rem, 0.55vw + 1rem, 1.3rem);
  line-height: 1.55;
  color: var(--text);
  max-width: 32rem;
  text-wrap: pretty;
  margin-bottom: 1.6rem;
}

.soon-cta-row { margin: 0 0 0.7rem; }

.soon-cta { font-size: 1.1rem; padding: 0.95rem 1.9rem; }

.soon-hero-copy .cta-note { margin-top: 0; }

.soon-meta {
  font-size: 0.85rem;
  color: var(--text-dim);
  margin: 0.9rem 0 0;
}

/* A lit vitrine rather than a floating sticker: a hairline case, an inset top
   highlight, the accent bloom this page already used, and a plinth under the
   figure's feet. All CSS, because there are no assets to add. */
/* DOM order governs: copy first, art second. The art led on phones until it was
   measured at 390x664 — it took 49% of the first screen and pushed the h1 and
   the CTA below the fold, so a bio-link visitor's first view was a placeholder
   figure and a caption. The two-column rule below restores the side-by-side
   arrangement once there is width for it. */
.soon-art {
  position: relative;
  text-align: center;
}

.soon-vitrine {
  position: relative;
  display: inline-flex;
  justify-content: center;
  padding: 1.75rem 2.25rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 1.75rem;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.015));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 40px 90px -50px #000;
}

.soon-vitrine::before {
  content: "";
  position: absolute;
  inset: 12% 18% 16%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(226, 87, 27, 0.22), transparent 72%);
  animation: glow-breathe 5s ease-in-out infinite alternate;
}

/* The plinth is light on the floor, never a shadow cast on the figure — the
   twin is never rendered dimmer, darker or less than it is anywhere. */
.soon-vitrine::after {
  content: "";
  position: absolute;
  left: 22%;
  right: 22%;
  bottom: 1rem;
  height: 0.6rem;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(226, 87, 27, 0.30), transparent 75%);
}

.soon-art .twin { width: min(34vw, 155px); }

.soon-art-caption {
  margin: 0.9rem 0 0;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-hi);
}

@media (min-width: 52rem) {
  .soon-hero {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    gap: 3rem;
    padding-top: 4rem;
  }
  .soon-art .twin { width: min(24vw, 235px); }
}

/* Above the fold animates on load, not on scroll, and it is an `animation …
   both` rather than a hidden state — so a blocked script can never leave the
   hero blank. */
@media (prefers-reduced-motion: no-preference) {
  .soon-hero-copy > * { animation: rise 800ms var(--ease-out) both; }
  .soon-hero-copy > *:nth-child(1) { animation-delay: 40ms; }
  .soon-hero-copy > *:nth-child(2) { animation-delay: 120ms; }
  .soon-hero-copy > *:nth-child(3) { animation-delay: 200ms; }
  .soon-hero-copy > *:nth-child(4) { animation-delay: 300ms; }
  .soon-hero-copy > *:nth-child(5) { animation-delay: 380ms; }
  .soon-hero-copy > *:nth-child(6) { animation-delay: 440ms; }
  .soon-art { animation: rise 900ms 250ms var(--ease-out) both; }
}

/* ---- proof strip ----
   Four countable facts, and the only numerals on the page. Accent as text is
   allowed here and in nowhere else: these are display-size, where the 3:1
   large-text floor applies and --pulse clears it comfortably. */

.soon-proof { padding-block: clamp(1.25rem, 4vh, 2.25rem); }

.soon-specs {
  list-style: none;
  margin: 0;
  padding: 1.25rem 0.5rem;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  text-align: center;
}

.soon-specs li { padding: 0 0.75rem; }

.soon-figure {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 5.5vw, 2.2rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--pulse);
  font-variant-numeric: tabular-nums;
}

.soon-figure-label {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--text);
}

@media (min-width: 46rem) {
  .soon-specs { grid-template-columns: repeat(4, 1fr); }
  .soon-specs li + li { border-left: 1px solid var(--border); }
}

/* ---- beats ---- */

.soon-kicker {
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.12em; /* letterspaced, never uppercased — rule 1 above */
  color: var(--text-dim);
  margin: 0 0 0.6rem;
}

.soon-beat h2 {
  font-size: clamp(1.8rem, 5.4vw, 2.6rem);
  line-height: 1.06;
  letter-spacing: -0.03em;
}

.soon-lead {
  font-size: clamp(1.15rem, 1vw + 1rem, 1.4rem);
  line-height: 1.45;
  color: var(--text-hi);
  max-width: 32rem;
  text-wrap: pretty;
}

.soon-body {
  color: var(--text);
  max-width: 34rem;
}

.soon-affiliation { margin-top: 1.4rem; }

.soon-cols {
  list-style: none;
  margin: 1.8rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.soon-cols li {
  padding: 1.2rem 1.3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: transform 250ms var(--ease-out), border-color 250ms ease;
}

.soon-cols li:hover {
  transform: translateY(-3px);
  border-color: var(--border-2);
}

.soon-cols h3 { margin-bottom: 0.3em; }

.soon-cols p { margin: 0; font-size: 0.95rem; }

@media (min-width: 46rem) {
  .soon-cols { grid-template-columns: repeat(3, 1fr); gap: 0.9rem; }
}

/* ---- mid-page band ----
   Explicit permission to stop reading and act, which is the moment most long
   pages lose people. */

.soon-band {
  text-align: center;
  padding-block: clamp(2.5rem, 7vh, 4rem);
}

.soon-band-lead {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 3vw + 0.5rem, 1.6rem);
  line-height: 1.3;
  color: var(--text-hi);
  max-width: 30rem;
  margin: 0 auto 1.5rem;
  text-wrap: balance;
}

.soon-band-cta { margin: 0; }

/* ---- the Life Score ring ----
   Six equal ticks, one per category, around an empty centre carrying the
   RANGE.

   This is the part to protect in review: a partial arc IS a number. A ring
   drawn 62% round reads as a score of 62 whatever the label in the middle
   says, and this page may never render a result — there is no user, no data
   and no score behind it. Every segment is the same length at the same alpha.
   Do not "finish" the ring by filling it, and never put a numeral inside. */

.soon-ring {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(9rem, 32vw, 11.5rem);
  aspect-ratio: 1;
  margin: 2rem auto 1.5rem;
}

.soon-ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: repeating-conic-gradient(from -90deg,
    var(--pulse-line) 0deg 54deg,
    rgba(255, 255, 255, 0.05) 54deg 60deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 71%, #000 72%);
  mask-image: radial-gradient(closest-side, transparent 71%, #000 72%);
}

.soon-ring::after {
  content: "";
  position: absolute;
  inset: 14%;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--pulse-soft), transparent 72%);
  animation: glow-breathe 5s ease-in-out infinite alternate;
}

.soon-ring span {
  position: relative;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--text-hi);
}

/* The six categories reuse the site's .chips recipe untouched — accent fill,
   accent border, neutral text. */
.soon-beat .chips { justify-content: center; }

/* ---- the horizon rail ----
   A time axis. The dots are decoration; the labels are real list text, so the
   six horizons still read with the CSS gone. */

.soon-rail {
  list-style: none;
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0.35rem;
  margin: 2rem 0 1.6rem;
  padding: 0;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text);
}

.soon-rail::before {
  content: "";
  position: absolute;
  left: 8%;
  right: 8%;
  top: 5.5px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--pulse-line) 12%, var(--pulse-line) 88%, transparent);
}

.soon-rail li {
  position: relative;
  padding-top: 1.35rem;
}

.soon-rail-dot {
  position: absolute;
  top: 0;
  left: 50%;
  width: 11px;
  height: 11px;
  margin-left: -5.5px;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--pulse-line);
  transition: transform 200ms var(--ease-out);
}

/* The furthest horizon, not a better one. */
.soon-rail li:last-child .soon-rail-dot {
  background: var(--pulse);
  border-color: var(--pulse);
  box-shadow: 0 0 0 6px var(--pulse-soft);
}

.soon-rail li:hover .soon-rail-dot { transform: scale(1.35); }

.soon-free {
  color: var(--text-hi);
  font-weight: 600;
  font-size: 0.98rem;
  margin: 0 0 0.5rem;
}

/* /terms §2, word for word, and it sits in the same viewport as the beat. */
.soon-projection {
  max-width: 34rem;
  margin-top: 0;
}

/* A text CTA, not a third button — the page should never read as a funnel. */
.soon-inline-cta { margin: 1.8rem 0 0.5rem; }

.soon-inline-cta a {
  font-family: var(--font-display);
  font-size: 1.12rem;
  font-weight: 600;
  color: var(--text-hi);
  text-decoration: none;
  padding-bottom: 0.15rem;
  border-bottom: 1px solid var(--pulse-line);
  transition: border-color 200ms ease;
}

/* The arrow is drawn, not typed: no English glyph enters the template. */
.soon-inline-cta a::after {
  content: "";
  display: inline-block;
  width: 0.45rem;
  height: 0.45rem;
  margin-left: 0.55rem;
  border-right: 2px solid var(--pulse);
  border-bottom: 2px solid var(--pulse);
  transform: rotate(-45deg) translateY(-0.1rem);
  transition: transform 200ms var(--ease-out);
}

.soon-inline-cta a:hover {
  color: var(--text-hi);
  border-bottom-color: var(--pulse);
}

.soon-inline-cta a:hover::after { transform: rotate(-45deg) translate(0.15rem, -0.15rem); }

/* ---- the doubts ----
   Hairlines rather than cards, so the page changes texture here instead of
   reading as one more stack of boxes. */

.soon-qa { margin: 1.8rem 0 0; }

.soon-qa-item {
  padding: 1.15rem 0;
  border-top: 1px solid var(--border);
  transition: border-color 250ms ease;
}

.soon-qa-item:hover { border-top-color: var(--border-2); }

.soon-qa dt {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 650;
  color: var(--text-hi);
  margin-bottom: 0.3rem;
}

.soon-qa dd {
  margin: 0;
  font-size: 0.96rem;
  color: var(--text);
}

@media (min-width: 46rem) {
  .soon-qa { display: grid; grid-template-columns: repeat(2, 1fr); column-gap: 2rem; }
}

/* ---- the sticky chip (phones) ----
   A repeated signup opportunity without a second form. It is sticky inside
   .soon-story, so its containing block bounds it: it appears when the hero
   scrolls off and leaves on its own before the form arrives, and it is
   structurally incapable of covering the form or sitting on the footer's
   not-a-medical-device line. No script, no timer, no observer. */

/* The bottom padding is what the sticky pill floats over. Without it the pill
   sat on top of the last rows of live content — measured on a 390px phone, it
   clipped the Life Score chips ("Sleep" rendering as "Sle…"). */
.soon-story { position: relative; padding-bottom: 4.5rem; }

.soon-sticky {
  position: sticky;
  bottom: max(0.9rem, env(safe-area-inset-bottom));
  z-index: 5;
  display: none;
  justify-content: center;
  padding-top: 1.25rem;
  pointer-events: none; /* the bar is air; only the pill is clickable */
}

.soon-sticky-btn {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--pulse-ink);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0) 70%), var(--pulse);
  border-radius: 999px;
  padding: 0.7rem 1.3rem;
  text-decoration: none;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.55), 0 4px 18px rgba(226, 87, 27, 0.30);
  transition: transform 200ms var(--ease-out), filter 160ms ease;
}

.soon-sticky-btn:hover {
  color: var(--pulse-ink);
  filter: brightness(1.08);
  transform: translateY(-1px);
}

.soon-sticky-btn .soon-dot { background: var(--pulse-ink); box-shadow: none; }

@media (max-width: 45.99rem) {
  .soon-sticky { display: flex; }
}

/* ---- the payoff ---- */

.soon-join {
  position: relative;
  max-width: 34rem;
  scroll-margin-top: 5rem; /* the sticky header must not eat the heading */
  padding-block: clamp(2.5rem, 8vh, 4rem) clamp(2rem, 6vh, 3rem);
}

/* The page visibly brightens toward the form: light falls onto the card from
   above rather than the card being outlined. Insets stay non-negative so the
   bloom can never widen the scroll box on a phone. */
.soon-join::before {
  content: "";
  position: absolute;
  inset: 0 0 22%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(95% 70% at 50% 8%, rgba(226, 87, 27, 0.16), transparent 70%);
  animation: glow-breathe 6s ease-in-out infinite alternate;
}

.soon-join-objection {
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 2.6vw + 0.4rem, 1.3rem);
  font-weight: 600;
  color: var(--text-hi);
  margin-bottom: 1.5rem;
  text-wrap: balance;
}

.soon-join-card {
  position: relative;
  border: 1px solid var(--border-2);
  border-radius: 1.5rem;
  padding: clamp(1.5rem, 4vw, 2.25rem);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.025));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 40px 90px -45px rgba(0, 0, 0, 0.9);
  transition: border-color 240ms ease, box-shadow 240ms ease;
}

/* A lit seam along the top edge — light, not a second border colour. */
.soon-join-card::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 12%;
  right: 12%;
  height: 1px;
  opacity: 0.7;
  background: linear-gradient(90deg, transparent, var(--pulse), transparent);
}

/* The payoff lights up the moment you touch it: the product's own loop, in
   miniature. */
.soon-join-card:focus-within {
  border-color: var(--pulse-line);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 0 0 4px var(--pulse-soft),
    0 40px 90px -45px rgba(0, 0, 0, 0.9);
}

.soon-join-card h2 {
  font-size: clamp(1.7rem, 5vw, 2.2rem);
  line-height: 1.06;
  letter-spacing: -0.03em;
  margin-bottom: 0.35em;
}

.soon-join-lead {
  color: var(--text-hi);
  font-weight: 600;
  margin-bottom: 0.6rem;
}

.soon-join-offer {
  font-size: 0.95rem;
  color: var(--text);
  margin-bottom: 1.5rem;
}

/* Scoped to .soon-join for the reason in rule 2 at the top of this block. */
.soon-join .signup { text-align: left; }

.soon-join .signup .field { margin: 0 0 1.15rem; }

.soon-join .signup label {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-hi);
  margin-bottom: 0.45rem;
}

.soon-join .signup input {
  width: 100%;
  font-size: 1.02rem; /* ≥1rem, so iOS Safari never zooms the page on focus */
  padding: 0.9rem 1.05rem;
  border-radius: 0.85rem;
  background: var(--surface);
}

/* The active field lifts out of the card; the shared rule's accent border and
   --pulse-soft ring, and its transparent outline for forced-colors mode, are
   inherited untouched. */
.soon-join .signup input:focus { background: var(--surface-2); }

.soon-join .signup button {
  width: 100%;
  font-size: 1.08rem;
  padding: 1rem 1.5rem;
  border-radius: 0.9rem;
  /* The white sheen the site uses instead of a second orange. */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0) 70%), var(--pulse);
  box-shadow: 0 10px 34px -10px rgba(226, 87, 27, 0.55);
}

/* Three stacked rows become two above 40rem: same fields, same order, same
   ids, names, types, maxlengths and autocomplete — a third less perceived
   length for no change to the markup. */
@media (min-width: 40rem) {
  .soon-join .signup {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 1rem;
  }
  .soon-join .signup > * { grid-column: 1 / -1; }
  .soon-join .signup > .field:nth-of-type(1) { grid-column: 1; }
  .soon-join .signup > .field:nth-of-type(2) { grid-column: 2; }
}

/* The whole offer in three fragments, at the pixel where hesitation happens.
   Divided by hairlines rather than a middot glyph, so there is nothing extra
   for a screen reader to announce. */
.soon-deal {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem 0;
  margin: 1rem 0 0;
  padding: 0;
  font-size: 0.85rem;
  color: var(--text);
}

.soon-deal li { padding: 0 0.7rem; }

.soon-deal li + li { border-left: 1px solid var(--border-2); }

.soon-join .signup-note {
  font-size: 0.85rem;
  color: var(--text-dim);
  margin: 1rem 0 0;
  text-align: center;
}

.soon-join .signup-note a { color: var(--text); }

/* The one place --pulse used to sit under AA on this page: the error line was
   accent-coloured small text. It is --text-hi on an accent-tinted strip with
   an accent rule instead, so the colour still says "something went wrong"
   without the contrast dip README → Design decisions measures. role="alert"
   is unchanged. */
.soon-join .signup-error {
  color: var(--text-hi);
  font-weight: 600;
  font-size: 0.95rem;
  background: var(--pulse-soft);
  border: 1px solid var(--pulse-line);
  border-left: 2px solid var(--pulse);
  border-radius: 0.6rem;
  padding: 0.75rem 0.9rem;
  margin: 0 0 1.25rem;
}

/* The confirmation inherits the card, so completing the form reads as arriving
   rather than as the page emptying out. --ok is reserved for this line. */
.soon-join .signup-success {
  background: none;
  border: 0;
  padding: 0;
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 4vw, 1.7rem);
  line-height: 1.2;
}

.soon-success-note { margin: 0; color: var(--text); }

.soon-fine { text-align: center; margin-top: 1.25rem; }

.soon-recap {
  max-width: 34rem;
  padding-block: 0 clamp(2rem, 6vh, 3rem);
}

.soon-recap h2 { font-size: 1.25rem; }

.soon-recap .included-list {
  grid-template-columns: 1fr;
  margin-bottom: 1rem;
}

.soon-closer {
  text-align: center;
  padding-block: clamp(2.5rem, 8vh, 4rem) clamp(1.5rem, 4vh, 2.5rem);
  border-top: 1px solid var(--border);
}

.soon-tagline {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 3vw + 0.5rem, 1.6rem);
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-hi);
  margin: 0;
  text-wrap: balance;
}

.field-optional {
  font-weight: 400;
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-left: 0.35rem;
}

/* Honeypot: out of view, out of the tab order, out of the accessibility tree.
   Not display:none — the bots worth catching skip fields hidden that
   obviously. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---- motion ----
   Child stagger driven by the observer that already exists: main.js only ever
   toggles .in on the .reveal parent and CSS does the rest, inside the same
   double gate as the .reveal hidden state — scripting enabled, motion welcome,
   and the `js` class main.js sets when it actually runs. A blocked or failed
   script therefore hides nothing here, exactly as everywhere else. */

@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  html.js .soon-page .reveal .stagger > * {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 600ms ease, transform 600ms var(--ease-out);
  }
  html.js .soon-page .reveal.in .stagger > * { opacity: 1; transform: none; }
  html.js .soon-page .reveal.in .stagger > *:nth-child(2) { transition-delay: 90ms; }
  html.js .soon-page .reveal.in .stagger > *:nth-child(3) { transition-delay: 180ms; }
  html.js .soon-page .reveal.in .stagger > *:nth-child(4) { transition-delay: 260ms; }
  html.js .soon-page .reveal.in .stagger > *:nth-child(5) { transition-delay: 320ms; }
  html.js .soon-page .reveal.in .stagger > *:nth-child(n + 6) { transition-delay: 380ms; }
}

/* The global reduce block zeroes every duration, which leaves an infinite
   animation frozen at whatever its final keyframe happens to be. These are the
   loops on this page, stopped outright so each settles at a chosen resting
   state instead: the status dot and the glows sit at full strength, and the
   scroll cue — which carries nothing — goes away. */
@media (prefers-reduced-motion: reduce) {
  .soon-page .bob,
  .soon-dot,
  .soon-vitrine::before,
  .soon-vitrine::after,
  .soon-ring::after,
  .soon-join::before { animation: none !important; }
  .soon-hero::after { display: none; }
}

@media (max-width: 29.99rem) {
  .soon-beat { padding-block: 2.75rem; }
  .soon-specs { padding: 1rem 0.25rem; gap: 1rem 0.25rem; }
  .soon-specs li { padding: 0 0.4rem; }
  .soon-rail { grid-template-columns: repeat(3, 1fr); row-gap: 1rem; }
  .soon-rail::before { display: none; }
  .soon-join-card { border-radius: 1.25rem; }
}

/* ---------- blog (/blog, /blog/page/N, /blog/:slug) ----------
   No new tokens, no new visual language. The cards are the feature-grid card
   recipe (surface fill, hairline border, --radius, 3px lift on hover); the
   article body reuses .prose, the same 68ch measure and accent list markers as
   /privacy and /terms; the accent is the same single --pulse. If a value below
   looks arbitrary it is because it was copied from the section above it. */

section.blog-head {
  max-width: 68rem;
  padding-top: 3rem;
  padding-bottom: 1rem;
}

/* One step down from the home hero's h1: this is a section front door, not the
   product's opening statement. */
section.blog-head h1 {
  font-size: clamp(2.1rem, 3.8vw + 1rem, 3.4rem);
}

section.blog-head .sub {
  font-size: 1.15rem;
  line-height: 1.6;
  max-width: 40rem;
  color: var(--text);
  margin-bottom: 0;
}

/* Same staggered entrance the hero and /soon use. */
@media (prefers-reduced-motion: no-preference) {
  .blog-head > * { animation: rise 800ms var(--ease-out) both; }
  .blog-head > *:nth-child(1) { animation-delay: 40ms; }
  .blog-head > *:nth-child(2) { animation-delay: 120ms; }
  .blog-head > *:nth-child(3) { animation-delay: 200ms; }
}

section.blog-list {
  max-width: 68rem;
  padding-top: 1rem;
}

/* ---------- article cards ---------- */

.post-grid {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 1rem;
}

/* Same two breakpoints as .feature-grid, so a row of articles and a row of
   features break at exactly the same widths. */
@media (min-width: 40rem) {
  .post-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 62rem) {
  .post-grid { grid-template-columns: repeat(3, 1fr); }
}

.post-grid > li { display: flex; }

.post-card {
  position: relative; /* anchor for the stretched title link below */
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 250ms var(--ease-out), border-color 250ms ease;
}

.post-card:hover {
  transform: translateY(-3px);
  border-color: var(--border-2);
}

.post-cover {
  display: block;
  line-height: 0; /* no inline-SVG descender gap under the art */
  border-bottom: 1px solid var(--border);
  background: #0e0e12; /* the cover art's own ground, so the seam is invisible */
}

.cover { display: block; width: 100%; height: auto; }

/* Lead card only: the art column is as tall as the copy beside it, so the SVG
   fills and crops (preserveAspectRatio="slice") instead of letter-boxing. */
.cover--fit { width: 100%; height: 100%; }

.post-card-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  padding: 1.25rem;
}

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.7rem;
  font-size: 0.85rem;
  color: var(--text-dim);
}

/* The separator travels with the reading time as one flex item, so a wrapping
   meta row can never start a line with a lone "·". */
.meta-read {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  white-space: nowrap;
}

.post-cat {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-hi);
  background: var(--pulse-soft);
  border: 1px solid var(--pulse-line);
  border-radius: 999px;
  padding: 0.18rem 0.62rem;
}

.post-cat--now {
  color: var(--pulse-ink);
  background: var(--pulse);
  border-color: var(--pulse);
}

.post-card h2,
.post-card h3 {
  font-size: 1.2rem;
  line-height: 1.24;
  margin: 0 0 0.5rem;
}

.post-card h3 { font-size: 1.06rem; }

.post-card h2 a,
.post-card h3 a {
  color: var(--text-hi);
  text-decoration: none;
  transition: color 160ms ease;
}

.post-card:hover h2 a,
.post-card:hover h3 a { color: var(--pulse); }

/* Stretched link: the whole card is clickable, but the only thing in the
   accessibility tree is the title — a screen reader hears one link name per
   card instead of the heading, the excerpt and a "read more" all over again. */
.post-card h2 a::after,
.post-card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
}

/* The stretched link makes the focus ring land on the title text alone, which
   is nearly invisible on a large card — so the card takes the ring instead. */
.post-card:has(a:focus-visible) {
  outline: 2px solid var(--pulse);
  outline-offset: 3px;
}

.post-excerpt {
  font-size: 0.95rem;
  color: var(--text);
  margin: 0 0 1rem;
}

.post-more {
  margin: auto 0 0; /* pinned to the bottom, so cards in a row line up */
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--pulse);
}

/* Lead card — page 1 only. Same card, wider and two-up on desktop. */
.post-card--lead { margin-top: 1.5rem; }

@media (min-width: 52rem) {
  .post-card--lead {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    align-items: stretch;
  }
  .post-card--lead .post-cover {
    border-bottom: 0;
    border-right: 1px solid var(--border);
  }
  .post-card--lead .post-card-body {
    justify-content: center;
    padding: 2.25rem 2rem;
  }
  .post-card--lead h2 { font-size: 1.8rem; letter-spacing: -0.025em; }
  .post-card--lead .post-excerpt { font-size: 1.02rem; }
}

.post-card--compact .post-card-body { padding: 1.15rem 1.25rem; }

/* ---------- pagination ---------- */

/* Three columns rather than space-between, so the page indicator stays exactly
   centred whether or not both arrows are present. */
.pagination {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  margin-top: 2.5rem;
}

.pagination > :last-child { justify-self: end; }

.page-link {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-hi);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 999px;
  padding: 0.6rem 1.2rem;
  transition: color 160ms ease, background-color 160ms ease,
    border-color 160ms ease, transform 200ms var(--ease-out);
}

.page-link:hover {
  color: var(--pulse);
  background: var(--pulse-soft);
  border-color: var(--pulse-line);
  transform: translateY(-2px);
}

.page-of {
  font-size: 0.9rem;
  color: var(--text-dim);
  text-align: center;
}

/* Below ~416px the indicator and two pills cannot share a row without the
   labels breaking, so the indicator takes its own line above them. */
@media (max-width: 26rem) {
  .pagination {
    grid-template-columns: 1fr 1fr;
    row-gap: 0.85rem;
  }
  .page-of {
    grid-column: 1 / -1;
    order: -1;
  }
  .pagination > :last-child { justify-self: stretch; }
  .page-link { text-align: center; }
}

/* ---------- article ---------- */

/* One column for the whole article — breadcrumb, header, hero, contents, body
   and footer all share a single left edge, at a ~72-character measure. The body
   overrides .prose's own max-width for that reason; it still inherits .prose's
   list markers, li rhythm and link treatment, which is why it keeps the class. */
.post {
  max-width: 40rem;
  margin: 0 auto;
  padding: 2rem 0 0;
}

.post-body,
.post-toc,
.post-footer { max-width: none; }

.crumbs { margin: 0 0 1.5rem; font-size: 0.85rem; }

.crumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  color: var(--text-dim);
}

.crumbs li + li::before {
  content: "/";
  margin-right: 0.4rem;
  color: var(--border-2);
}

.crumbs a { color: var(--text-dim); text-decoration: none; }

.crumbs a:hover { color: var(--pulse); }

.post-header { margin-bottom: 1.9rem; }

.post-header h1 {
  font-size: clamp(2rem, 3.4vw + 1rem, 3rem);
  margin-bottom: 0.28em;
}

.post-dek {
  font-size: 1.16rem;
  line-height: 1.5;
  color: var(--text);
  max-width: 34rem;
  margin-bottom: 1.1rem;
}

.post-byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-dim);
}

.post-author { color: var(--text-hi); font-weight: 600; }

@media (prefers-reduced-motion: no-preference) {
  .post-header > *,
  .post-hero { animation: rise 800ms var(--ease-out) both; }
  .post-header > *:nth-child(1) { animation-delay: 40ms; }
  .post-header > *:nth-child(2) { animation-delay: 110ms; }
  .post-header > *:nth-child(3) { animation-delay: 180ms; }
  .post-header > *:nth-child(4) { animation-delay: 250ms; }
  .post-hero { animation-delay: 300ms; }
}

.post-hero {
  line-height: 0;
  border: 1px solid var(--border);
  border-radius: 1.25rem;
  overflow: hidden;
  margin-bottom: 2.25rem;
}

/* The featured image is the one thing allowed out of the text column, and only
   once there is room for it either side of the measure. */
@media (min-width: 56rem) {
  .post-hero { margin-left: -2.5rem; margin-right: -2.5rem; }
}

/* In-article contents. Static, not sticky — no scroll listener, no JS. */
.post-toc {
  max-width: 68ch;
  margin: 0 auto 2.25rem;
  padding: 1.15rem 1.35rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.post-toc ol {
  margin: 0;
  padding-left: 1.2rem;
  display: grid;
  gap: 0.35rem;
}

.post-toc li::marker { color: var(--pulse); }

.post-toc a {
  font-size: 0.95rem;
  color: var(--text);
  text-decoration: none;
}

.post-toc a:hover { color: var(--pulse); }

/* Key takeaways. Answer-first: the summary sits above the article, and it is
   the block most likely to be lifted whole into a search answer. */
.takeaways {
  margin: 0 0 2.25rem;
  padding: 1.35rem 1.5rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.takeaways-title,
.post-toc-title {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 0.7rem;
}

.takeaways ul {
  margin: 0;
  padding-left: 1.2rem;
  display: grid;
  gap: 0.55rem;
}

.takeaways li { font-size: 0.97rem; }

.takeaways li::marker { color: var(--pulse); }

/* Comparison tables. Same visual language as the pricing table on the home
   page, and inside the same .table-scroll container — a six-column grid scrolls
   within its own box instead of widening the page on a phone. */
.post-body .table-scroll { margin: 1.8rem 0 1rem; }

/* Same rule as the featured image: media may leave the text column, text may
   not. Without it the six-column category grid scrolls inside its box even on
   a desktop, and that grid is the most useful thing on the page. */
@media (min-width: 56rem) {
  .post-body .table-scroll {
    margin-left: -2.5rem;
    margin-right: -2.5rem;
  }
}

@media (min-width: 64rem) {
  .post-body .table-scroll {
    margin-left: -4rem;
    margin-right: -4rem;
  }
}

.post-table {
  width: 100%;
  border-collapse: collapse;
}

.post-table th,
.post-table td {
  padding: 0.7rem 0.95rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  text-align: left;
  font-size: 0.92rem;
  /* Keeps cells legible rather than letting a wide table crush its columns;
     the container scrolls instead. */
  min-width: 7.5rem;
}

.post-table thead th {
  font-family: var(--font-display);
  font-size: 0.95rem;
  color: var(--text-hi);
  border-bottom: 1px solid var(--border-2);
}

.post-table tbody th {
  color: var(--text-hi);
  font-weight: 600;
  min-width: 8.5rem;
}

.post-table tbody tr { transition: background-color 160ms ease; }

.post-table tbody tr:hover { background: var(--surface); }

.post-table tbody tr:last-child th,
.post-table tbody tr:last-child td { border-bottom: 0; }

/* The "this is us" column, tinted so a reader can follow it across a wide
   table. Tint only — the numbers in it are not styled to look better. */
.post-table .col-hi { background: var(--pulse-soft); }

.post-table thead .col-hi { color: var(--pulse); }

.table-note,
.post-disclosure {
  font-size: 0.85rem;
  color: var(--text-dim);
}

.table-note { margin: 0 0 1.6rem; }

.post-body { padding-top: 0; }

.post-body > :first-child { margin-top: 0; }

/* The opening paragraph is the lede — one step up, like the .lead class the
   marketing sections use. */
.post-body > p:first-of-type {
  font-size: 1.12rem;
  color: var(--text-hi);
}

.post-body h2 {
  font-size: 1.5rem;
  margin-top: 2.4rem;
}

.post-body h3 {
  font-size: 1.12rem;
  margin-top: 1.8rem;
}

/* Headings are link targets (they carry ids), and the header is sticky — so
   an anchor jump has to stop below it rather than under it. */
.post-body h2,
.post-body h3 { scroll-margin-top: 5rem; }

.post-body p { margin-bottom: 1.15em; }

.post-body ul,
.post-body ol { margin: 0 0 1.3em; }

.post-body blockquote {
  margin: 1.9rem 0;
  padding: 0.15rem 0 0.15rem 1.4rem;
  border-left: 3px solid var(--pulse);
}

.post-body blockquote p {
  font-family: var(--font-display);
  font-size: 1.18rem;
  line-height: 1.45;
  letter-spacing: -0.015em;
  color: var(--text-hi);
  margin-bottom: 0.4rem;
}

.post-body blockquote footer {
  font-size: 0.88rem;
  color: var(--text-dim);
}

.callout {
  margin: 1.9rem 0;
  padding: 1.15rem 1.35rem;
  background: var(--pulse-soft);
  border: 1px solid var(--pulse-line);
  border-radius: var(--radius);
}

.callout p { font-size: 0.98rem; margin-bottom: 0; }

.callout .callout-label {
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--pulse);
  margin: 0 0 0.35rem;
}

.post-figure { margin: 2.1rem 0; }

.post-figure .cover {
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.post-figure figcaption {
  margin-top: 0.7rem;
  font-size: 0.88rem;
  color: var(--text-dim);
  text-align: center;
}

/* Per-article FAQ. A real section of the page, in the contents list and in the
   FAQPage markup — not an SEO block bolted on under the fold. */
.post-faq {
  margin-top: 2.6rem;
  padding-top: 1.7rem;
  border-top: 1px solid var(--border);
}

.post-faq h2 {
  margin-top: 0;
  font-size: 1.5rem;
  scroll-margin-top: 5rem;
}

.post-faq h3 {
  font-size: 1.05rem;
  margin: 1.6rem 0 0.4em;
  scroll-margin-top: 5rem;
}

.post-faq p { margin-bottom: 0; }

/* Non-affiliation notice and correction invitation on comparison posts. */
.post-disclosure {
  margin: 0 0 1.1rem;
  padding: 0.9rem 1.15rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.85rem;
}

.post-footer {
  max-width: 68ch;
  margin: 2.5rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.post-note {
  font-size: 0.88rem;
  color: var(--text-dim);
  margin-bottom: 1rem;
}

.post-back a {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--text-hi);
  text-decoration: none;
}

.post-back a:hover { color: var(--pulse); }

/* Wider than the article it follows, so its heading is centred rather than
   left-aligned — a third left edge under the article would read as a mistake. */
section.related {
  max-width: 68rem;
  text-align: center;
}

section.related .lead {
  margin: 0 auto;
  max-width: 32rem;
}

section.related .post-grid { text-align: left; }

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--border);
  padding: 3rem 1.25rem 3.5rem;
  text-align: center;
}

.footer-wordmark { margin-bottom: 0.2rem; }

.footer-tagline {
  color: var(--text-dim);
  margin-bottom: 1.4rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  margin-bottom: 1.4rem;
}

.footer-links a {
  color: var(--text);
  text-decoration: none;
}

.footer-links a:hover { color: var(--pulse); }

.footer-links span { color: var(--text-dim); }

.fineprint {
  font-size: 0.85rem;
  color: var(--text-dim);
  margin: 0.25rem 0;
}

/* The not-medical-advice line (brief §9). Fine print by size, but it is the
   one sentence in the footer that must stay readable, so it gets a measure and
   a little air rather than being buried between the other two. */
.disclaimer {
  max-width: 40rem;
  margin: 1.1rem auto 0.9rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border);
}
