/* ============================================================================
   Virtual Closer — motion layer
   ----------------------------------------------------------------------------
   Loaded by index.html only, AFTER styles.css. The base sheet stays static and
   zero-JS on purpose (support/privacy/terms and /leads are still legal-grade
   documents that must render with nothing running); this file plus motion.js is
   the marketing layer on top of it.

   Three rules, same spirit as the base sheet:
     1. The red rim is the instrument. Progress, section, state — all read off
        the frame, never off a floating widget dropped on the content.
     2. Nothing moves that isn't the product. The hero thread is a real SMS
        exchange; the floor canvas is real lead flow. No decorative particles.
     3. Everything degrades. No JS → full content, no blank boxes. Reduced
        motion → the same information, arrived at instantly.
   ========================================================================== */

:root {
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --hud-h: 26px;
}

/* ── The rim as instrument ─────────────────────────────────────────────
   A fixed hairline riding the very top of the viewport, over the red page
   background. Ink on red — the letterpress read of a progress bar. */
/* The HUD owns a strip of red at the very top and the shell starts below it.
   Without this the bar floats over the cream sheet as it scrolls past and the
   white type disappears into the paper. */
body { padding-top: var(--hud-h); }

.hud {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60;
  height: var(--hud-h); background: var(--red);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); padding: 0 14px;
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.18em;
  text-transform: uppercase; color: rgba(255, 255, 255, 0.62);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.hud__plate { display: flex; align-items: center; gap: 8px; min-width: 0; }
.hud__plate b { color: #fff; font-weight: 800; }
/* The section name swaps on scroll; fade it rather than snapping. */
.hud__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: opacity 240ms ease; }
.hud__pct { color: #fff; }
.hud__bar {
  position: fixed; top: 0; left: 0; height: 2px; width: 0%;
  background: var(--ink); z-index: 61;
  transition: width 90ms linear;
}
@media (max-width: 720px) {
  /* The rim is a desktop luxury and so is the HUD that reads off it. The bar
     stays — a progress line is useful on a phone; a plate number isn't. */
  .hud { display: none; }
  body { padding-top: 0; }
  .hud__bar { height: 3px; }
}

/* ── Scroll reveal ─────────────────────────────────────────────────────
   Applied by motion.js, never in the HTML — a page whose JS fails must not be
   a page of invisible divs. */
.js .reveal { opacity: 0; transform: translateY(18px); }
.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

/* ── Mono eyebrow — the machine voice, used sparingly ─────────────────── */
.mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-meta);
}
.mono b { color: var(--red); font-weight: 700; }

/* ── Hero split — copy left, the machine running right ────────────────── */
.hero--split { max-width: none; display: grid; grid-template-columns: 1.05fr 0.95fr; gap: var(--s-12); align-items: center; }
.hero--split .hero-copy { max-width: 620px; }

/* ── The live thread ───────────────────────────────────────────────────
   A real SMS exchange playing out: lead texts in, the AI answers, an
   appointment lands. This is the product, so it gets hero real estate. */
.thread {
  background: var(--paper); border: 1.5px solid var(--ink);
  border-radius: var(--r-accent);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.68), var(--shadow-card-lg);
  overflow: hidden; min-height: 430px;
  display: flex; flex-direction: column;
}
.thread__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 10px 14px; border-bottom: 1px dashed var(--divider);
  font-size: 0.64rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-meta);
}
.thread__who { display: flex; align-items: center; gap: 8px; color: var(--ink); }
.thread__live { display: flex; align-items: center; gap: 6px; color: var(--red); }
.thread__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--red); }
.js .thread__dot { animation: pulse 1.8s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } }

/* Fixed height, pinned to the bottom, older messages clipped under a fade —
   a real thread scrolled to the latest. Without this the card grows as the
   script plays and shoves the whole page down mid-read. */
.thread__body {
  padding: var(--s-5); display: flex; flex-direction: column; gap: 10px;
  height: 400px; overflow: hidden; justify-content: flex-end;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
}

.msg { max-width: 84%; display: flex; flex-direction: column; gap: 4px; }
.msg__b {
  padding: 9px 13px; border-radius: 13px; font-size: 0.88rem; line-height: 1.5;
}
.msg__t { font-size: 0.6rem; letter-spacing: 0.1em; color: var(--text-meta); text-transform: uppercase; }
.msg--in { align-self: flex-start; }
.msg--in .msg__b { background: var(--paper-2); border: 1.5px solid var(--border-soft); border-bottom-left-radius: 4px; }
.msg--out { align-self: flex-end; align-items: flex-end; }
.msg--out .msg__b { background: var(--blush); border: 1.5px solid var(--red); border-bottom-right-radius: 4px; box-shadow: var(--red-shadow-low); }

/* Only the outbound side types — the AI is the one composing. */
.typing { display: inline-flex; gap: 4px; align-items: center; padding: 2px 0; }
.typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--red); opacity: 0.4; }
.js .typing i { animation: bounce 1.1s ease-in-out infinite; }
.js .typing i:nth-child(2) { animation-delay: 0.15s; }
.js .typing i:nth-child(3) { animation-delay: 0.3s; }
@keyframes bounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-4px); opacity: 1; } }

/* The payoff. Lands with weight — it's the only thing on the page that slams. */
.booked {
  margin-top: 4px; background: var(--ink); color: var(--paper);
  border-radius: var(--r-card); padding: 13px 15px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  box-shadow: var(--shadow-slab);
}
.booked__l { font-size: 0.6rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--red); font-weight: 800; }
.booked__v { font-size: 0.98rem; font-weight: 800; letter-spacing: -0.01em; margin-top: 2px; }
.booked__c { font-size: 1.5rem; line-height: 1; color: var(--red); }
.js .booked { animation: slam 420ms var(--ease-out) both; }
@keyframes slam { from { opacity: 0; transform: translateY(14px) scale(0.97); } to { opacity: 1; transform: none; } }

.js .msg { animation: rise 340ms var(--ease-out) both; }
@keyframes rise { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }

/* Replay affordance — the loop is long, and people arrive mid-way. */
.thread__foot {
  border-top: 1px dashed var(--divider); padding: 9px 14px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  font-size: 0.66rem; color: var(--text-meta);
}
.thread__replay {
  font: inherit; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  font-size: 0.6rem; color: var(--red); background: none; border: none;
  cursor: pointer; padding: 0; pointer-events: auto;
}
.thread__replay:hover { text-decoration: underline; }

/* ── The floor — live lead flow on canvas ─────────────────────────────── */
.floor {
  position: relative; border-radius: var(--r-accent);
  border: 1px solid var(--hairline-dark); background: rgba(0, 0, 0, 0.22);
  overflow: hidden;
}
.floor canvas { display: block; width: 100%; height: 340px; }
.floor__legend {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-wrap: wrap; gap: var(--s-5);
  padding: 10px 14px; border-top: 1px solid var(--hairline-dark);
  background: rgba(0, 0, 0, 0.35);
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: #a8a8a8;
}
.floor__legend span { display: flex; align-items: center; gap: 7px; }
.floor__legend i { width: 7px; height: 7px; border-radius: 50%; }
/* No-JS: the canvas is empty, so say what it would have shown instead. */
.floor__fallback { padding: var(--s-6); color: #d9d9d9; font-size: 0.92rem; }
.js .floor__fallback { display: none; }

@media (max-width: 720px) {
  .hero--split { grid-template-columns: 1fr; gap: var(--s-8); }
  .thread { min-height: 0; }
  .floor canvas { height: 260px; }
}

/* ── The one non-negotiable ────────────────────────────────────────────
   Everything above is decoration on top of information. Someone who has asked
   the OS to stop moving things gets the information, immediately, in full. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .js .thread__dot, .js .typing i, .js .msg, .js .booked { animation: none; }
  .hud__bar { transition: none; }
}

/* ============================================================================
   Second pass — the parts that make it look like a product, not a brochure
   ========================================================================== */

/* ── Letterpress stamp ─────────────────────────────────────────────────
   The brand's whole conceit is a printing plate hitting paper, so headlines
   press in word by word with a hairline ink bleed that settles. This is the
   site's signature motion — used on headlines only, never on body copy. */
.js [data-stamp] .w { display: inline-block; }
.js [data-stamp].is-in .w {
  animation: stamp 460ms var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 42ms);
}
@keyframes stamp {
  0%   { opacity: 0; transform: translateY(0.16em) scale(1.045); filter: blur(2.5px); }
  55%  { opacity: 1; filter: blur(0); }
  70%  { transform: translateY(-0.012em) scale(0.998); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

/* ── Cursor light on the dark slabs ────────────────────────────────────
   A slab of ink is flat; a slab of ink with a light on it has a surface.
   Pointer-driven, so it simply never appears on touch. */
.band--dark { position: relative; isolation: isolate; }
.band--dark::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; pointer-events: none;
  opacity: 0; transition: opacity 380ms ease;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
              rgba(255, 40, 0, 0.13), transparent 68%);
}
.band--dark.is-lit::before { opacity: 1; }

/* ── The interface ─────────────────────────────────────────────────────
   A hand-built illustration of the real screens. Not screenshots: every screen
   in the product holds live customer PII, so this is drawn instead — which is
   also why it must stay labelled as an illustration. */
.app {
  background: var(--paper); border: 1.5px solid var(--ink);
  border-radius: var(--r-accent); overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6), var(--shadow-slab);
  color: var(--ink);
}
.app__chrome {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-bottom: 1px solid var(--border-soft); background: var(--paper-2);
}
.app__dots { display: flex; gap: 5px; }
.app__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--border-soft); }
.app__dots i:first-child { background: var(--red); }
.app__url {
  flex: 1; text-align: center; font-size: 0.66rem; letter-spacing: 0.08em;
  color: var(--text-meta); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.app__tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border-soft); overflow-x: auto; }
.app__tab {
  flex: 1; min-width: 116px; padding: 11px 14px; background: none; border: none;
  border-bottom: 2px solid transparent; cursor: pointer;
  font: inherit; font-size: 0.68rem; font-weight: 800;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-meta);
  white-space: nowrap; transition: color 160ms ease, border-color 160ms ease;
}
.app__tab:hover { color: var(--ink); }
.app__tab[aria-selected="true"] { color: var(--red); border-bottom-color: var(--red); }

.app__stage { position: relative; min-height: 330px; }
.app__panel { display: none; padding: var(--s-5); }
.app__panel[data-on] { display: block; animation: panel 320ms var(--ease-out) both; }
@keyframes panel { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
.app__cap {
  border-top: 1px dashed var(--divider); padding: 11px 16px;
  font-size: 0.82rem; color: var(--muted-2);
}

/* Rows — the shared primitive behind the worklist and the inbox. */
.r {
  display: grid; grid-template-columns: 15px 1fr auto; gap: 11px; align-items: center;
  padding: 9px 10px; border: 1px solid var(--border-soft); border-radius: 9px;
  background: var(--paper-2); margin-bottom: 7px;
}
.r--hot { border-color: var(--red); background: var(--blush); }
.r__n { font-weight: 800; font-size: 0.86rem; letter-spacing: -0.01em; }
.r__s { font-size: 0.76rem; color: var(--text-meta); margin-top: 1px; }
.r__h { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.74rem; font-weight: 700; }
.r__i { width: 15px; height: 15px; border-radius: 50%; border: 1.5px solid var(--border-soft); }
.r--hot .r__i { border-color: var(--red); background: var(--red); }

.tag {
  display: inline-block; font-size: 0.58rem; font-weight: 800;
  letter-spacing: 0.13em; text-transform: uppercase;
  padding: 3px 7px; border-radius: 5px; border: 1px solid currentColor;
}
.tag--red { color: var(--red); }
.tag--ok { color: var(--signal-ok); }
.tag--mute { color: var(--text-meta); }

.app__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  margin-bottom: 11px; flex-wrap: wrap;
}
.app__title { font-size: 0.82rem; font-weight: 800; letter-spacing: -0.01em; }
.app__sub { font-size: 0.74rem; color: var(--text-meta); }
.app__btn {
  font: inherit; font-size: 0.64rem; font-weight: 800; letter-spacing: 0.11em;
  text-transform: uppercase; padding: 7px 13px; border-radius: 7px;
  background: var(--red); color: var(--paper); border: none;
}

/* Inbox — list beside a preview. */
.inbox { display: grid; grid-template-columns: 1fr 1.15fr; gap: 13px; }
.inbox__prev {
  border: 1px solid var(--border-soft); border-radius: 9px; padding: 13px;
  background: var(--paper-2); display: flex; flex-direction: column; gap: 8px;
}
.inbox__prev .msg { max-width: 92%; }
.inbox__prev .msg__b { font-size: 0.8rem; padding: 7px 11px; }

/* Dialer — one live call, a queue under it. */
.dial {
  border: 1.5px solid var(--ink); border-radius: 11px; padding: 15px;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap; margin-bottom: 12px; background: var(--paper-2);
}
.dial__t {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em;
}
.dial__meta { font-size: 0.72rem; color: var(--text-meta); margin-top: 3px; }
.wave { display: flex; align-items: flex-end; gap: 3px; height: 26px; }
.wave i { width: 3px; background: var(--red); border-radius: 2px; height: 30%; }
.js .wave i { animation: wv 900ms ease-in-out infinite; }
.js .wave i:nth-child(2) { animation-delay: 120ms } .js .wave i:nth-child(3) { animation-delay: 240ms }
.js .wave i:nth-child(4) { animation-delay: 90ms }  .js .wave i:nth-child(5) { animation-delay: 300ms }
.js .wave i:nth-child(6) { animation-delay: 180ms } .js .wave i:nth-child(7) { animation-delay: 60ms }
@keyframes wv { 0%, 100% { height: 26%; } 50% { height: 100%; } }

/* Pipeline — a board, drawn small. */
.board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; }
.board__c {
  background: var(--paper-2); border: 1px solid var(--border-soft);
  border-radius: 9px; padding: 9px; min-height: 210px;
}
.board__h {
  font-size: 0.58rem; font-weight: 800; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--text-meta); margin-bottom: 9px; display: flex; justify-content: space-between;
}
.board__k {
  background: var(--paper); border: 1px solid var(--border-soft); border-left: 2.5px solid var(--red);
  border-radius: 7px; padding: 8px 9px; margin-bottom: 7px;
  font-size: 0.75rem; font-weight: 700;
}
.board__k span { display: block; font-weight: 500; font-size: 0.68rem; color: var(--text-meta); margin-top: 2px; }

/* ── Guardrail receipts in the hero thread ─────────────────────────────
   The mechanisms that fired, printed under the message they governed. This is
   the part competitors can't fake, so it gets shown rather than claimed. */
.guard {
  align-self: stretch; display: flex; align-items: baseline; gap: 7px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem; line-height: 1.5; color: var(--text-meta);
  padding: 2px 0 1px 2px; border-left: 2px solid var(--signal-ok); padding-left: 9px;
}
.guard b { color: var(--signal-ok); font-weight: 700; }
.js .guard { animation: rise 340ms var(--ease-out) both; }

/* Scenario switcher over the thread. */
.scenes { display: flex; gap: 6px; flex-wrap: wrap; }
.scenes button {
  font: inherit; font-size: 0.58rem; font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; padding: 4px 9px; border-radius: 20px;
  border: 1.5px solid var(--border-soft); background: var(--paper);
  color: var(--text-meta); cursor: pointer; pointer-events: auto;
}
.scenes button[aria-pressed="true"] { border-color: var(--red); color: var(--red); background: var(--blush); }

@media (max-width: 860px) {
  .inbox { grid-template-columns: 1fr; }
  .inbox__prev { display: none; }
  .board { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .app__stage { min-height: 0; }
  .app__panel { padding: var(--s-4); }
  .app__tab { min-width: 104px; flex: 0 0 auto; }
  .board { grid-template-columns: 1fr; }
  .board__c { min-height: 0; }
  .dial__t { font-size: 1.2rem; }
}

@media (prefers-reduced-motion: reduce) {
  .js [data-stamp].is-in .w { animation: none; }
  .app__panel[data-on] { animation: none; }
  .js .wave i { animation: none; height: 60%; }
  .band--dark::before { display: none; }
}

/* ── Live delivery (leads.html hero) ───────────────────────────────────
   The home page's hero shows the AI closing. A lead buyer wants a different
   thing: proof the lead actually lands and gets touched. So this one fills in a
   lead record field by field, delivers it, and shows the AI opening the thread
   — then stacks it and starts another. Labelled a sample, because it is. */
.deliver {
  background: var(--paper); border: 1.5px solid var(--ink);
  border-radius: var(--r-accent); overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.68), var(--shadow-card-lg);
  display: flex; flex-direction: column; min-height: 430px;
}
.deliver__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 10px 14px; border-bottom: 1px dashed var(--divider);
  font-size: 0.64rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-meta);
}
.deliver__body { padding: var(--s-5); flex: 1; display: flex; flex-direction: column; gap: 9px; }

/* The record being filled in. */
.dl {
  display: grid; grid-template-columns: 86px 1fr; gap: 10px; align-items: baseline;
  font-size: 0.86rem; padding: 6px 0; border-bottom: 1px dashed var(--divider);
}
.dl:last-of-type { border-bottom: none; }
.dl__k {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-meta);
}
.dl__v { font-weight: 700; letter-spacing: -0.01em; }
.dl__v .ok { color: var(--signal-ok); font-weight: 800; }
.js .dl { animation: rise 300ms var(--ease-out) both; }

/* The caret that makes a value look typed rather than pasted. */
.caret { display: inline-block; width: 2px; height: 0.95em; background: var(--red); vertical-align: -2px; margin-left: 2px; }
.js .caret { animation: blink 1s step-end infinite; }
@keyframes blink { 50% { opacity: 0; } }

.deliver__stat {
  margin-top: auto; display: flex; align-items: center; gap: 9px;
  background: var(--blush); border: 1.5px solid var(--red); border-radius: var(--r-card);
  padding: 11px 13px; font-size: 0.82rem; font-weight: 700;
  box-shadow: var(--red-shadow-low);
}
.deliver__stat b { color: var(--red); }
.js .deliver__stat { animation: slam 400ms var(--ease-out) both; }

/* Previously delivered, stacked and dimmed. */
.deliver__past { display: flex; flex-direction: column; gap: 5px; }
.deliver__past div {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: 0.74rem; color: var(--text-meta);
  padding: 6px 9px; border: 1px solid var(--border-soft); border-radius: 7px;
  background: var(--paper-2);
}
.deliver__past .ok { color: var(--signal-ok); font-weight: 700; }
.js .deliver__past div { animation: rise 300ms var(--ease-out) both; }

.deliver__foot {
  border-top: 1px dashed var(--divider); padding: 9px 14px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  font-size: 0.66rem; color: var(--text-meta);
}

@media (max-width: 720px) { .deliver { min-height: 0; } }
@media (prefers-reduced-motion: reduce) {
  .js .dl, .js .deliver__stat, .js .deliver__past div { animation: none; }
  .js .caret { animation: none; display: none; }
}

/* The status slot is occupied from the first frame, in a muted "receiving"
   state, so the card doesn't sit half-empty while the record fills in. */
.deliver__stat--wait {
  background: var(--paper-2); border-color: var(--border-soft);
  color: var(--text-meta); font-weight: 600; box-shadow: none;
}
.deliver__stat--wait b { color: var(--text-meta); }

/* ── The funnel (leads.html) ───────────────────────────────────────────
   The whole flow being sold, top to bottom: the traffic, the landing page,
   consent, delivery, first touch, nurture, calendar. The point of the section
   is positional — everyone else sells step 04, so the diagram has to make
   steps 01-03 and 05-07 visible rather than asserted.

   Rows narrow as they descend. Subtle: enough to read as a funnel, not so much
   that the last row is a sliver nobody can read. */
.funnel { display: flex; flex-direction: column; gap: 10px; align-items: center; }

.fn {
  position: relative; width: 100%;
  display: grid; grid-template-columns: 54px 1fr; gap: var(--s-4); align-items: center;
  background: var(--paper); border: 1.5px solid var(--border-soft);
  border-radius: var(--r-card); padding: var(--s-4) var(--s-5);
  transition: border-color 320ms ease, background 320ms ease, box-shadow 320ms ease;
}
.fn:nth-child(1) { max-width: 100%; }
.fn:nth-child(2) { max-width: 97%; }
.fn:nth-child(3) { max-width: 94%; }
.fn:nth-child(4) { max-width: 91%; }
.fn:nth-child(5) { max-width: 88%; }
.fn:nth-child(6) { max-width: 85%; }
.fn:nth-child(7) { max-width: 82%; }

.fn__n {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
  color: var(--text-meta); transition: color 320ms ease;
}
.fn__k {
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-meta); transition: color 320ms ease;
}
.fn__t { font-size: 1rem; font-weight: 800; letter-spacing: -0.01em; margin-top: 2px; }
.fn__c { font-size: 0.88rem; color: var(--muted); margin-top: 3px; }

/* The row the lead is passing through. */
.fn.is-on {
  border-color: var(--red); background: var(--blush);
  box-shadow: var(--red-shadow-mid);
}
.fn.is-on .fn__n, .fn.is-on .fn__k { color: var(--red); }

/* Rows it has already cleared stay marked, so the whole path reads at a glance
   instead of only the moment. */
.fn.is-done .fn__n::after { content: " ✓"; color: var(--signal-ok); }
.fn.is-done { border-color: rgba(22, 163, 74, 0.35); }

/* The one thing everyone else sells. Called out permanently, not on a timer. */
.fn--them { border-style: dashed; }
.fn__them {
  position: absolute; right: var(--s-5); top: 50%; transform: translateY(-50%);
  font-size: 0.56rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-meta); border: 1px dashed currentColor; border-radius: var(--r-pill);
  padding: 3px 9px; background: var(--paper);
}

.funnel__note {
  margin-top: var(--s-8); text-align: center;
  font-size: 1.02rem; font-weight: 700; letter-spacing: -0.01em;
}
.funnel__note b { color: var(--red); }

@media (max-width: 720px) {
  /* No narrowing on a phone — 82% of a small screen is just a cramped card. */
  .fn, .fn:nth-child(n) { max-width: 100%; }
  .fn { grid-template-columns: 40px 1fr; gap: var(--s-3); padding: var(--s-4); }
  .fn__them { position: static; transform: none; display: inline-block; margin-top: 7px; }
}
@media (prefers-reduced-motion: reduce) {
  .fn { transition: none; }
}

/* ── The product rig (index.html · "Line by line") ─────────────────────
   Eight insurance product lines, one frame. The argument the section makes
   visually is that the MACHINE is one machine and the LADDER is not: IUL sends
   an illustration, health never quotes, supplemental is four steps. That
   difference is the whole reason an agency owner believes this was built by
   people who sell insurance, so the ladder is the loudest element here.

   Progressive enhancement, per §5.3 of docs/VC_INSURANCE_POSITIONING_SCOPE.md:
   with no JS every one of the eight blocks renders stacked and readable — a
   product spec sheet, which is a perfectly good section. The rail and the
   one-at-a-time behavior are BOTH scoped to `.js`, so a page whose script
   fails never becomes a page of hidden divs. */
.rig {
  background: var(--paper); border: 1.5px solid var(--ink);
  border-radius: var(--r-accent); overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6), var(--shadow-slab);
  color: var(--ink);
}

/* Rail: no JS, no rail — the chips would be dead controls.

   WRAPS rather than scrolls. Eight product names don't fit one 1440px row, and
   horizontal overflow cut the eighth chip off mid-word — which reads as a
   broken layout, not as "scroll me", because nothing signals the overflow. Two
   tidy rows beat one row with a hidden tab. */
.rig__rail { display: none; }
.js .rig__rail {
  display: flex; flex-wrap: wrap; gap: 0;
  border-bottom: 1px solid var(--border-soft); background: var(--paper-2);
}
.rig__chip {
  flex: 0 0 auto; padding: 11px 15px; background: none; border: none;
  border-bottom: 2px solid transparent; cursor: pointer;
  font: inherit; font-size: 0.66rem; font-weight: 800;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--text-meta);
  white-space: nowrap; transition: color 160ms ease, border-color 160ms ease;
}
.rig__chip:hover { color: var(--ink); }
.rig__chip[aria-selected="true"] { color: var(--red); border-bottom-color: var(--red); }

.rig__stage { position: relative; padding: var(--s-6); }

/* Every line is visible until JS says otherwise (Rule 2). */
.line + .line { margin-top: var(--s-10); padding-top: var(--s-10); border-top: 1px dashed var(--divider); }
.js .line { display: none; }
.js .line[data-on] { display: block; animation: panel 320ms var(--ease-out) both; }
.js .line + .line { margin-top: 0; padding-top: 0; border-top: none; }

.line__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-6);
}
.line__name { font-size: 1.15rem; font-weight: 900; letter-spacing: -0.02em; }
.line__what { font-size: 0.86rem; color: var(--text-meta); margin-top: 3px; max-width: 54ch; }

/* The four beats. Same four for every product — that IS the point. */
.beats {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-4);
}
.beat {
  border: 1px solid var(--border-soft); border-radius: 9px;
  background: var(--paper-2); padding: var(--s-4);
}
.beat__n {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.14em; color: var(--red);
}
.beat__t { font-size: 0.86rem; font-weight: 800; letter-spacing: -0.01em; margin-top: 5px; }
.beat__b { font-size: 0.78rem; color: var(--muted-2); line-height: 1.6; margin-top: 5px; }

/* Staggered entrance, scoped to .js so it can never hide content permanently. */
.js .rig__stage .beat { opacity: 0; transform: translateY(10px); }
.js .line[data-on] .beat {
  animation: beatIn 420ms var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 90ms);
}
@keyframes beatIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ── The shared board ────────────────────────────────────────────────
   This ladder used to sit INSIDE each product panel, one per line, and it was
   the site's loudest claim: every product supposedly had its own stages. That
   was false (see scripts/build_rig.py for the full account). The rungs are
   identical for all eight, so the ladder is now shown ONCE, above the rail,
   and says so. Keep it there. */
.rig__shared {
  border: 1px solid var(--border-soft); border-radius: var(--r-lg);
  background: var(--paper); padding: var(--s-5) var(--s-6);
  margin-bottom: var(--s-5);
}
.rig__shared-lab {
  margin: 0; font-size: 0.62rem; font-weight: 900;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--red);
}
.rig__shared-note {
  margin: var(--s-4) 0 0; font-size: 0.78rem; color: var(--text-meta);
}

.ladder {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin: var(--s-4) 0 0; padding: 0; list-style: none;
}
.rung {
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.06em;
  padding: 6px 11px; border-radius: var(--r-pill);
  border: 1.5px solid var(--border-soft); background: var(--paper);
  color: var(--text-meta); white-space: nowrap;
  transition: color 220ms ease, border-color 220ms ease, background-color 220ms ease;
}
.rung::after { content: ""; }
.ladder .sep { color: var(--border-soft); font-size: 0.7rem; }
.rung.is-lit { color: var(--ink); border-color: var(--ink); background: var(--paper); }
/* The rungs a rep actually steers by. Permanently red — an identity, not a
   step in the animation, so it must survive the sweep. `.rung.is-lit` is two
   classes and was out-specifying a bare `.rung--key`, turning every key rung
   plain ink the moment the ladder lit. Both selectors are needed. */
.rung--key,
.rung--key.is-lit { color: var(--red); border-color: var(--red); background: var(--blush); }
.rung--end.is-lit { color: var(--signal-ok); border-color: var(--signal-ok); }

.rig__cap {
  border-top: 1px dashed var(--divider); padding: 11px 16px; margin: 0;
  font-size: 0.8rem; color: var(--muted-2);
}

@media (max-width: 860px) {
  .beats { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .beats { grid-template-columns: 1fr; }
  .rig__stage { padding: var(--s-4); }
  .rig__chip { padding: 10px 12px; font-size: 0.6rem; }
  .rig__shared { padding: var(--s-4); }
  .rung { font-size: 0.62rem; padding: 5px 9px; }
}
@media (prefers-reduced-motion: reduce) {
  /* Rule 3: keep the information, drop the movement. Everything arrives lit. */
  .js .rig__stage .beat { opacity: 1; transform: none; }
  .js .line[data-on] { animation: none; }
  .js .line[data-on] .beat { animation: none; }
  .rung { transition: none; }
}

/* ── Business Tracker primitives (index.html · #money) ─────────────────
   The second .app illustration. It reuses the desk's chrome/tabs/panels
   wholesale; these are only the pieces the money screens need that the
   worklist screens didn't: a ladder of comp levels, a grid table, KPI tiles,
   and proportion bars. Same rule as the desk — every number inside is sample
   data in something labeled "Interface illustration", never a claim about our
   own performance. */

/* Comp ladder — rows of levels, the agency's own marked. */
.lad { display: grid; gap: 6px; }
.lad__r {
  display: grid; grid-template-columns: 1fr auto auto; gap: 11px; align-items: center;
  padding: 9px 12px; border: 1px solid var(--border-soft); border-radius: 9px;
  background: var(--paper-2);
}
.lad__r--house { border-color: var(--red); background: var(--blush); }
.lad__n { font-weight: 800; font-size: 0.86rem; letter-spacing: -0.01em; }
.lad__s { font-size: 0.74rem; color: var(--text-meta); margin-top: 1px; }
.lad__v {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem; font-weight: 700;
}
/* Indent the ladder so the hierarchy reads as a hierarchy, not a list. */
.lad__r[data-depth="1"] { margin-left: 14px; }
.lad__r[data-depth="2"] { margin-left: 28px; }
.lad__r[data-depth="3"] { margin-left: 42px; }

/* Grid table — carrier × product × age band × level. */
.ledg { width: 100%; border-collapse: collapse; font-size: 0.78rem; }
.ledg th, .ledg td { padding: 7px 9px; text-align: left; border-bottom: 1px solid var(--border-soft); }
.ledg th {
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-meta);
}
.ledg td.num, .ledg th.num {
  text-align: right; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700;
}
.ledg tr:last-child td { border-bottom: none; }
.ledg .hot { color: var(--red); }
.ledg .up { color: var(--signal-ok); }
.ledg__wrap { overflow-x: auto; }

/* KPI tiles. */
.kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; }
.kpi { border: 1px solid var(--border-soft); border-radius: 9px; background: var(--paper-2); padding: 10px 12px; }
.kpi__l { font-size: 0.6rem; font-weight: 800; letter-spacing: 0.11em; text-transform: uppercase; color: var(--text-meta); }
.kpi__v {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.02rem; font-weight: 700; margin-top: 4px; letter-spacing: -0.02em;
}
.kpi--warn { border-color: var(--red); background: var(--blush); }
.kpi--warn .kpi__v { color: var(--red); }

/* Proportion bars — persistency by line of business. */
.bars { display: grid; gap: 9px; }
.bar__t { display: flex; justify-content: space-between; font-size: 0.76rem; margin-bottom: 4px; }
.bar__t b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.bar__track { height: 7px; border-radius: var(--r-pill); background: var(--border-soft); overflow: hidden; }
.bar__f { height: 100%; border-radius: var(--r-pill); background: var(--signal-ok); width: var(--w, 0%); }
.js .bar__f { width: 0; transition: width 900ms var(--ease-out); }
.js .app__panel[data-on] .bar__f { width: var(--w, 0%); }

@media (max-width: 720px) {
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .lad__r[data-depth] { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .js .bar__f { width: var(--w, 0%); transition: none; }
}

/* ── Screenshot decks ──────────────────────────────────────────────────
   One deck per FEATURE, not a gallery of everything. A visitor should be able
   to say "that's the pipeline board" after three slides; forty loose
   screenshots say "look how much we built" and land as noise.

   Progressive enhancement, same contract as the product rig: the markup carries
   only slides. Every control — frame, arrows, dots, counter — is BUILT BY
   motion.js, because a control that does nothing without JS is worse than no
   control. With JS off, all slides render stacked and captioned, which is a
   perfectly good feature walkthrough.

   CLS: every <img> MUST carry width and height attributes. They're what let the
   browser reserve the box before the file arrives. Without them a deck shifts
   the whole page as it loads, and Speed Insights is now watching. */
.deck { margin: var(--s-6) 0 0; }

/* No JS: stacked, readable, no controls. */
.deck__slide { margin: 0 0 var(--s-6); }
.deck__slide img {
  display: block; width: 100%; height: auto;
  /* Padding on the image itself is the matte — the screenshot stops short of
     the frame instead of running into it, which is what made this read as a
     box with something crammed in it. */
  padding: clamp(10px, 1.6vw, 22px);
  background: var(--paper);
  border: 1px solid var(--border-soft); border-radius: var(--r-accent);
  box-shadow: var(--shadow-card);
}
.deck__slide figcaption {
  margin-top: var(--s-4); font-size: 0.85rem; color: var(--muted-2); line-height: 1.6;
  max-width: 60ch;
}
.deck__slide figcaption b { color: var(--ink); font-weight: 700; }

/* With JS: one at a time inside a frame. */
/* ONE FIXED STAGE. The screenshots range from 0.80:1 (a portrait report) to
   2.87:1 (a wide strip), so sizing the frame to each image made it swing from
   ~380px to ~1370px tall between slides — nothing lined up, and the arrows
   landed somewhere different on every picture.

   The stage is now locked to 16/9, which is where 11 of the 18 screenshots
   already sit. Every image is `contain`-fitted and centred inside it: wide
   strips letterbox top and bottom, taller ones letterbox at the sides, and
   NOTHING is ever cropped or stretched. The frame height never changes, so the
   arrows are always in the same place and the page never jumps. */
.js .deck__frame {
  position: relative;
  display: grid;
  aspect-ratio: 16 / 9;
  background: var(--paper);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-accent);
  box-shadow: var(--shadow-card);
  padding: clamp(10px, 1.4vw, 20px);
  overflow: hidden;
}
.js .deck__slide { grid-area: 1 / 1; display: none; margin: 0; min-width: 0; }
.js .deck__slide[data-on] { display: grid; place-items: center; animation: panel 300ms var(--ease-out) both; }

/* Inside the stage the image carries no frame of its own — the stage IS the
   frame. Two nested borders is what made this read as boxes inside boxes. */
.js .deck__slide img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  padding: 0; border: none; box-shadow: none; background: none;
  border-radius: 4px;
}

/* Caption sits below the stage, at a fixed minimum so a long one doesn't
   shove the next section down when you page through. */
.deck__cap {
  margin: var(--s-4) 0 0; font-size: 0.86rem; line-height: 1.6;
  color: var(--muted-2); max-width: 68ch; min-height: 2.9em;
}
.deck__cap b { color: var(--ink); font-weight: 700; }
.band--dark .deck__cap { color: #d9d9d9; }
.band--dark .deck__cap b { color: var(--paper); }

.deck__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--border-soft); background: var(--paper); color: var(--muted-2);
  /* padding:0 matters — a <button> has UA padding, which shifts the glyph off
     centre even inside a flex container. */
  padding: 0; line-height: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-card); transition: background 140ms ease, color 140ms ease;
}
.deck__nav svg { display: block; }
.deck__nav:hover { background: var(--red); border-color: var(--red); color: var(--paper); }
.deck__nav:disabled { opacity: 0.35; cursor: default; }
.deck__nav:disabled:hover { background: var(--paper); color: var(--ink); border-color: var(--ink); }
.deck__nav--prev { left: 10px; }
.deck__nav--next { right: 10px; }

.deck__bar {
  display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap;
  margin-top: var(--s-4);
}
/* The dots already say where you are. A counter saying it again is clutter. */
.deck__count { display: none; }
.deck__dots { display: flex; gap: 7px; flex-wrap: wrap; }
.deck__dot {
  width: 9px; height: 9px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1.5px solid var(--border-soft); background: var(--paper-2);
  transition: background 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.deck__dot:hover { border-color: var(--red); }
.deck__dot[aria-selected="true"] { background: var(--red); border-color: var(--red); transform: scale(1.25); }

.deck__count {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem; font-weight: 700; color: var(--text-meta);
}
/* Honesty label. Every deck carries one — these are a seeded demo account, and
   saying so is what keeps the screenshots trustworthy rather than staged. */
.deck__tag {
  margin: 0 0 0 auto; font-size: 0.66rem; font-weight: 800;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-meta);
  border: 1px dashed var(--divider); border-radius: var(--r-pill); padding: 4px 11px;
}

@media (max-width: 860px) {
  .deck__nav--prev { left: 6px; }
  .deck__nav--next { right: 6px; }
  .deck__tag { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .js .deck__slide[data-on] { animation: none; }
  .deck__dot { transition: none; }
}

/* Decks sitting on a dark slab. The component was built and verified on cream;
   dropped into .band--dark the caption, counter and tag inherited ink colours
   and went nearly invisible. Inverted here rather than in the base rules so a
   deck on a cream page is untouched. */
.band--dark .deck__slide figcaption { color: #d9d9d9; }
.band--dark .deck__slide figcaption b { color: var(--paper); }
.band--dark .deck__bar { border-top-color: var(--hairline-dark); }
.band--dark .deck__count { color: #a8a8a8; }
.band--dark .deck__tag { color: #a8a8a8; border-color: var(--hairline-dark); }
.band--dark .deck__dot { background: rgba(255, 255, 255, 0.08); border-color: var(--hairline-dark); }
.band--dark .deck__dot[aria-selected="true"] { background: var(--red); border-color: var(--red); }
/* The arrows keep their cream body on purpose — they sit ON the screenshot,
   which is itself light, so inverting them would lose them against the image. */

/* ── The override cascade ──────────────────────────────────────────────
   The one idea that's hard in prose and obvious in motion: a producer writes
   the business at their rate, the agency is contracted at a higher one, and the
   difference travels up the ladder. A paragraph takes four sentences to say
   that. This says it while you're still reading the headline. */
.cas { margin: var(--s-8) 0 0; }
.cas__grid { display: grid; grid-template-columns: minmax(0, 1fr) 190px; gap: var(--s-6); align-items: stretch; }

.cas__lad { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.cas__row {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: var(--s-4);
  padding: 13px 16px; border-radius: 9px;
  border: 1px solid var(--border-soft); background: var(--paper);
  opacity: 0.45; transition: opacity 380ms var(--ease-out), border-color 380ms var(--ease-out),
                             box-shadow 380ms var(--ease-out), transform 380ms var(--ease-out);
}
.cas__row b { font-size: 0.92rem; font-weight: 700; letter-spacing: -0.01em; }
.cas__row small { display: block; font-size: 0.72rem; color: var(--text-meta); font-weight: 400; margin-top: 1px; }
.cas__row span {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.95rem; font-weight: 700;
}
/* Lit as the sale travels up. */
.cas.is-on .cas__row { opacity: 1; }
.cas.is-on .cas__row[data-writer] { border-color: var(--ink); box-shadow: var(--shadow-card); }
.cas.is-on .cas__row[data-house] { border-color: var(--red); background: var(--blush); box-shadow: var(--red-shadow-low); }
.cas.is-on .cas__row.is-passed { transform: translateX(3px); }

/* The spread — one bar from where it was written to where the agency sits. */
.cas__spread {
  position: relative; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  border-left: 1px dashed var(--divider); padding-left: var(--s-5);
}
.cas__bar {
  position: absolute; left: -1px; bottom: 0; width: 3px; background: var(--red);
  height: 0; border-radius: 2px;
  transition: height 1100ms var(--ease-out);
}
.cas.is-on .cas__bar { height: 100%; }
.cas__amt {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: clamp(1.6rem, 3.4vw, 2.3rem); font-weight: 700; color: var(--red); letter-spacing: -0.03em;
}
.cas__lab { font-size: 0.74rem; font-weight: 800; letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-meta); margin-top: 4px; }
.cas__sub { font-size: 0.76rem; color: var(--text-meta); margin-top: 7px; line-height: 1.5; max-width: 22ch; }

.cas figcaption { margin-top: var(--s-5); font-size: 0.84rem; color: var(--muted-2); line-height: 1.6; max-width: 68ch; }
.cas figcaption b { color: var(--ink); }

.band--dark .cas__row { background: rgba(255,255,255,0.04); border-color: var(--hairline-dark); color: var(--paper); }
.band--dark .cas__row small { color: #a8a8a8; }
.band--dark .cas.is-on .cas__row[data-writer] { border-color: var(--paper); }
.band--dark .cas figcaption { color: #d9d9d9; }
.band--dark .cas figcaption b { color: var(--paper); }
.band--dark .cas__spread { border-left-color: var(--hairline-dark); }

@media (max-width: 720px) {
  .cas__grid { grid-template-columns: 1fr; }
  .cas__spread { border-left: none; border-top: 1px dashed var(--divider); padding: var(--s-5) 0 0; }
  .cas__bar { left: 0; right: 0; top: 0; bottom: auto; width: 0; height: 3px; transition: width 1100ms var(--ease-out); }
  .cas.is-on .cas__bar { width: 100%; height: 3px; }
}
@media (prefers-reduced-motion: reduce) {
  .cas__row, .cas__bar { transition: none; }
}

/* The house row is blush-on-cream by default; on a dark slab that put white
   text on a pale pink box. Tint instead of fill. */
.band--dark .cas.is-on .cas__row[data-house] {
  background: rgba(255, 40, 0, 0.16);
  border-color: var(--red);
  color: var(--paper);
}
.band--dark .cas.is-on .cas__row[data-house] small { color: #f3c6bd; }

/* ── The lead's journey ────────────────────────────────────────────────
   One lead, left to right, with the clock beside it. The point is the gap
   between "lands" and "first text" — that's the whole product in one measure,
   and a number in a paragraph doesn't land the way a moving token does. */
.jny { margin: var(--s-8) 0 0; }
.jny__track { list-style: none; margin: 0; padding: 0; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0; }
.jny__step { position: relative; padding: 0 var(--s-3); text-align: center; }
.jny__step::before {
  content: ""; position: absolute; top: 13px; left: 0; right: 50%; height: 2px;
  background: var(--border-soft);
}
.jny__step::after {
  content: ""; position: absolute; top: 13px; left: 50%; right: 0; height: 2px;
  background: var(--border-soft);
}
.jny__step:first-child::before, .jny__step:last-child::after { display: none; }
.jny__dot {
  position: relative; z-index: 1; width: 28px; height: 28px; margin: 0 auto var(--s-3);
  border-radius: 50%; border: 2px solid var(--border-soft); background: var(--paper);
  display: grid; place-items: center; font-size: 0.7rem; font-weight: 800; color: var(--text-meta);
  transition: background 300ms var(--ease-out), border-color 300ms var(--ease-out),
              color 300ms var(--ease-out), transform 300ms var(--ease-out);
}
.jny__step b { display: block; font-size: 0.86rem; font-weight: 700; letter-spacing: -0.01em; opacity: 0.5; transition: opacity 300ms ease; }
.jny__step span {
  display: block; margin-top: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem; color: var(--text-meta); opacity: 0; transition: opacity 300ms ease;
}
.jny__step.is-on .jny__dot { background: var(--red); border-color: var(--red); color: var(--paper); transform: scale(1.12); }
.jny__step.is-on b { opacity: 1; }
.jny__step.is-on span { opacity: 1; }
.jny__step.is-on::before { background: var(--red); }
.jny__note { margin-top: var(--s-6); font-size: 0.84rem; color: var(--muted-2); line-height: 1.6; max-width: 62ch; }
.jny__note b { color: var(--ink); }
.band--dark .jny__dot { background: rgba(255,255,255,0.06); border-color: var(--hairline-dark); }
.band--dark .jny__step::before, .band--dark .jny__step::after { background: var(--hairline-dark); }
.band--dark .jny__step b { color: var(--paper); }
.band--dark .jny__note { color: #d9d9d9; }
.band--dark .jny__note b { color: var(--paper); }
@media (max-width: 780px) {
  .jny__track { grid-auto-flow: row; grid-auto-columns: auto; gap: var(--s-4); }
  .jny__step { display: grid; grid-template-columns: 28px 1fr; gap: var(--s-4); text-align: left; align-items: center; padding: 0; }
  .jny__step::before, .jny__step::after { display: none; }
  .jny__dot { margin: 0; }
}

/* ── The team floor ────────────────────────────────────────────────────
   Producers working in parallel. The bars fill at different rates on purpose —
   "who is actually working" is the question this answers, and a chart where
   everyone matches would be answering a different one. */
.team { margin: var(--s-8) 0 0; display: grid; gap: 11px; }
.team__row { display: grid; grid-template-columns: minmax(120px, 190px) 1fr auto; gap: var(--s-4); align-items: center; }
.team__who { font-size: 0.88rem; font-weight: 700; }
.team__who small { display: block; font-size: 0.72rem; color: var(--text-meta); font-weight: 400; }
.team__track { height: 12px; border-radius: var(--r-pill); background: var(--paper-2); border: 1px solid var(--border-soft); overflow: hidden; }
.team__fill { display: block; height: 100%; width: 0; background: var(--red); border-radius: var(--r-pill); transition: width 1500ms var(--ease-out); }
.team__fill--low { background: var(--text-meta); }
.team.is-on .team__fill { width: var(--w, 0%); }
.team__n { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.86rem; font-weight: 700; min-width: 3.4em; text-align: right; }
.band--dark .team__track { background: rgba(255,255,255,0.06); border-color: var(--hairline-dark); }
.band--dark .team__who { color: var(--paper); }
@media (max-width: 620px) { .team__row { grid-template-columns: 1fr auto; } .team__track { grid-column: 1 / -1; } }

/* ── The book over time ────────────────────────────────────────────────
   A policy issues, the advance lands, then it lapses in month four and the
   clawback comes out. The negative column is the point — it's the number
   agencies discover late. */
.book { margin: var(--s-8) 0 0; }
/* A real zero line. Without it the clawback column grew UPWARD like the
   positives and a chargeback read as a gain — the exact opposite of the point.
   Positives sit above the line, negatives hang below it. */
.book__cols { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: var(--s-3); }
.book__col { display: grid; grid-template-rows: 150px 1px 116px auto auto; text-align: center; }
.book__up { display: flex; align-items: flex-end; }
.book__down { display: flex; align-items: flex-start; }
.book__base { background: var(--border-soft); }
.band--dark .book__base { background: rgba(255, 255, 255, 0.22); }

.book__bar {
  width: 100%; height: 0; border-radius: 5px 5px 0 0; background: var(--signal-ok);
  transition: height 900ms var(--ease-out);
}
.book__bar--neg { border-radius: 0 0 5px 5px; background: var(--red); }
.book.is-on .book__bar { height: var(--h, 0px); }

.book__m { font-size: 0.72rem; font-weight: 700; color: var(--text-meta); margin-top: 4px; }
.book__v {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.74rem; font-weight: 700;
  opacity: 0; transition: opacity 400ms ease; margin-top: 8px;
}
.book.is-on .book__v { opacity: 1; }
.book__v--neg { color: var(--red); }
.book__note { margin-top: var(--s-5); font-size: 0.84rem; color: var(--muted-2); line-height: 1.6; max-width: 64ch; }
.book__note b { color: var(--ink); }
.band--dark .book__note { color: #d9d9d9; }
.band--dark .book__note b { color: var(--paper); }
.band--dark .book__v { color: var(--paper); }
.band--dark .book__v--neg { color: #ff7a5e; }

/* ── The product thread ──────────────────────────────────────────────────
   The tab row sits between the header and the transcript. The transcript's top
   46px are masked to fade (it reads as "scrolled up"), and with the tabs flush
   against it the faded first message looked like it was sliding UNDER the
   tabs rather than out of view. Give the row its own band. */
[data-thread="products"] .scenes {
  padding: 11px var(--s-5);
  border-bottom: 1px solid var(--border-soft);
  background: var(--surface-soft);
}
[data-thread="products"] .thread__body { padding-top: var(--s-6); height: 430px; }

@media (max-width: 720px) {
  /* Eight product tabs wrap to three rows on a phone; keep them compact and
     let the transcript keep most of the height. */
  [data-thread="products"] .scenes { padding: 9px 12px; gap: 5px; }
  [data-thread="products"] .scenes button { font-size: 0.52rem; padding: 4px 7px; }
  [data-thread="products"] .thread__body { height: 360px; padding: var(--s-4); }
  .msg { max-width: 92%; }
  .msg__b { font-size: 0.82rem; }
}
