/* ============================================================
   Civic Lattice — Page sections
   A short conversion page: the problem first, one repeated call to
   action, and screenshots used only as evidence for a claim.
   ============================================================ */

/* ============================================================
   HERO
   ============================================================ */
.hero {
  /* Keyed to viewport height, not width: the job is to sit the headline near
     the middle of the screen, and that is a vertical question. */
  padding-block: clamp(3rem, 23vh, 13rem) clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
  text-align: center;
}
.hero__copy { max-width: 42rem; margin-inline: auto; }
.hero__copy > * + * { margin-top: var(--sp-5); }
.hero__copy h1 { margin-top: var(--sp-4); }
.hero__copy .lead { max-width: 38rem; margin-inline: auto; }
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  align-items: center;
  justify-content: center;
}
/* Far enough down that the capture is a hint of what is below rather than a
   second thing competing with the headline. */
.hero__figure { margin-top: clamp(2.5rem, 1rem + 6vh, 5rem); }

/* Centred sections need the eyebrow rule on both sides. */
.hero .eyebrow,
.cta .eyebrow,
.page-error .eyebrow { justify-content: center; }
.hero .eyebrow::after,
.cta .eyebrow::after,
.page-error .eyebrow::after {
  content: '';
  width: 22px;
  height: 1px;
  background: currentColor;
}

/* ============================================================
   STATS — the research behind the problem
   ============================================================ */
.stats {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-6) var(--sp-7);
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (min-width: 640px) { .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.stat { padding-top: var(--sp-4); border-top: 2px solid var(--accent); }
.stat__figure {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2rem, 1.4rem + 2.2vw, 2.75rem);
  line-height: 1.05;
  letter-spacing: var(--ls-tight);
  color: var(--ink);
}
/* Counting figure. Tabular figures keep the digits on a fixed pitch while
   the number climbs; the script also pins the width for fonts that lack
   them. Nothing here is required for the value to display. */
.tick { display: inline-block; font-variant-numeric: tabular-nums; }

.stat__label {
  display: block;
  margin-top: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--ink-2);
}
.stats-note {
  margin-top: clamp(2rem, 1rem + 2vw, 2.75rem);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-sm);
  color: var(--ink-2);
  max-width: 68ch;
}
.stats-note strong { color: var(--ink); font-weight: 600; }

/* ============================================================
   DUO — claim on one side, the evidence for it on the other
   ============================================================ */
.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3vw, 4rem);
  align-items: center;
}
@media (min-width: 900px) {
  /* The visual column is the wider of the two: these are real interface
     captures, and shrinking them below ~0.8 scale makes the UI text
     unreadable. */
  .duo { grid-template-columns: minmax(0, 0.72fr) minmax(0, 1fr); }
  .duo--flip { grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr); }
  .duo--flip > .duo__copy { order: 2; }
  .duo--flip > .duo__visual { order: 1; }
}
.duo__copy > * + * { margin-top: var(--sp-4); }
.duo__copy p { max-width: 46ch; color: var(--ink-2); }
/* Emphasis inside body copy lifts to full ink so the phrase carries, without
   shouting in the accent colour. */
.duo__copy strong,
.scrolly__panel strong { color: var(--ink); font-weight: 600; }

.band + .band { margin-top: clamp(3rem, 1.5rem + 5vw, 5rem); }

/* ── The loop ────────────────────────────────────────────────
   The cycle a plan actually lives in. The ring draws itself once, clockwise
   from the adopted plan, each stage landing as the line reaches it — and then
   a marker keeps going round, because the point is that the loop never ends.
   The global reduced-motion rule in base.css stops all of it. */
/* Fills its column, so its edges land on the same grid lines as the other
   visuals. Written with both classes to outrank the base .diagram max-width,
   which is defined further down the file. */
.diagram.diagram--loop { max-width: none; }
.loop-ring {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  /* A circle's path starts at three o'clock; turning it a quarter back means
     the line sets off from the adopted plan at the top. */
  transform: rotate(-90deg);
  transform-box: view-box;
  transform-origin: 340px 200px;
}
.loop-node { fill: var(--accent); stroke: var(--paper); stroke-width: 3; }
.loop-label {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  fill: var(--ink);
}
/* The constant at the centre of the cycle. Set apart from the stage labels —
   uppercase, tracked, heavier — so it reads as what the ring is about rather
   than a seventh stop on it. */
.loop-hub {
  font-family: var(--font-sans);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 1.5px;
  fill: var(--ink);
  text-anchor: middle;
}
/* A travelling pulse, not a seventh stage: smaller than the stage dots and
   carrying a halo, so it reads as moving rather than sitting there. */
.loop-marker { fill: var(--accent-ink); }
.loop-halo   { fill: var(--accent); opacity: 0.18; }
/* Three arrowheads on the ring. The orbiting marker shows the cycle running,
   but these carry the direction on their own — so the diagram still reads as
   a loop with motion switched off. */
.loop-arrow { fill: var(--accent); }
.loop-orbit {
  transform-box: view-box;
  transform-origin: 340px 200px;
  animation: loop-orbit 18s linear infinite;
}
@keyframes loop-orbit { to { transform: rotate(360deg); } }

/* The marker sets off from the adopted plan at the top and travels clockwise.
   Amendments sits 240 degrees round, which is two thirds of the way through
   the cycle — so the pulse swells there, on the same 18s clock. Amendment is
   the step the rest of the product exists to reach, and it is the one a firm
   is still needed for, so it gets the emphasis. */
.loop-marker,
.loop-halo {
  transform-box: fill-box;
  transform-origin: center;
  animation: loop-blip 18s linear infinite;
}
@keyframes loop-blip {
  0%, 62%   { transform: scale(1); }
  65%       { transform: scale(1.6); }
  66.7%     { transform: scale(2.6); }
  69%       { transform: scale(1.5); }
  72%, 100% { transform: scale(1); }
}
/* The halo swells further and brightens, so the beat reads from across the page. */
.loop-halo { animation-name: loop-blip-halo; }
@keyframes loop-blip-halo {
  0%, 62%   { transform: scale(1);   opacity: 0.18; }
  65%       { transform: scale(1.9); opacity: 0.34; }
  66.7%     { transform: scale(3.2); opacity: 0.45; }
  69%       { transform: scale(1.8); opacity: 0.3;  }
  72%, 100% { transform: scale(1);   opacity: 0.18; }
}
/* Amendments answers the beat rather than sitting through it. */
.loop-node--5 {
  transform-box: fill-box;
  transform-origin: center;
  animation: loop-answer 18s linear infinite;
}
@keyframes loop-answer {
  0%, 63%   { transform: scale(1); }
  66.7%     { transform: scale(1.5); }
  71%, 100% { transform: scale(1); }
}

/* Armed by the script, so without it the loop is simply drawn. */
.diagram--loop.is-armed .loop-ring {
  stroke-dasharray: 942;
  stroke-dashoffset: 942;
  transition: stroke-dashoffset 1900ms cubic-bezier(.4, 0, .2, 1);
}
.diagram--loop.is-armed .loop-node,
.diagram--loop.is-armed .loop-label,
.diagram--loop.is-armed .loop-arrow,
.diagram--loop.is-armed .loop-hub {
  opacity: 0;
  transition: opacity 420ms ease;
}
.diagram--loop.is-armed .loop-arrow { transition-delay: 1500ms; }
.diagram--loop.is-armed .loop-orbit { opacity: 0; transition: opacity 600ms ease 1900ms; }

.diagram--loop.is-armed.is-drawn .loop-ring { stroke-dashoffset: 0; }
.diagram--loop.is-armed.is-drawn .loop-node,
.diagram--loop.is-armed.is-drawn .loop-label,
.diagram--loop.is-armed.is-drawn .loop-arrow,
.diagram--loop.is-armed.is-drawn .loop-hub { opacity: 1; }
.diagram--loop.is-armed.is-drawn .loop-orbit { opacity: 1; }

/* Each stage lights as the line reaches it, a sixth of the draw apart. */
.diagram--loop.is-armed.is-drawn .loop-node--1,
.diagram--loop.is-armed.is-drawn .loop-label--1 { transition-delay: 0ms; }
.diagram--loop.is-armed.is-drawn .loop-node--2,
.diagram--loop.is-armed.is-drawn .loop-label--2 { transition-delay: 300ms; }
.diagram--loop.is-armed.is-drawn .loop-node--3,
.diagram--loop.is-armed.is-drawn .loop-label--3 { transition-delay: 600ms; }
.diagram--loop.is-armed.is-drawn .loop-node--4,
.diagram--loop.is-armed.is-drawn .loop-label--4 { transition-delay: 900ms; }
.diagram--loop.is-armed.is-drawn .loop-node--5,
.diagram--loop.is-armed.is-drawn .loop-label--5 { transition-delay: 1200ms; }
.diagram--loop.is-armed.is-drawn .loop-node--6,
.diagram--loop.is-armed.is-drawn .loop-label--6 { transition-delay: 1500ms; }

/* The labels scale with the column, so hold them near 11px on a phone. */
@media (max-width: 699px) {
  .loop-label { font-size: 19px; }
  .loop-hub   { font-size: 21px; letter-spacing: 1.2px; }
}

/* ── Card diagrams ──────────────────────────────────────────
   One vocabulary, two diagrams. The carry-forward diagram shows the claim a
   screen capture cannot: the same record surviving a renumbering, with the old
   code set back so the eye reads left-to-right without being told to. The
   Center diagram borrows the same cards, ties and junction to say the matching
   thing about the nonprofit — one source, three kinds of published work.

   Both fill their column so their edges land on the same grid lines as the
   captures elsewhere on the page. */
.diagram--carry,
.diagram--center { max-width: none; }
.cf-era {
  font-family: var(--font-sans); font-size: 11px; font-weight: 700;
  letter-spacing: 0.14em; fill: var(--ink-3);
}
.cf-card { fill: var(--paper); stroke: var(--accent); stroke-width: 1.5; }
.cf-card--old { fill: var(--paper-2); stroke: var(--line); stroke-dasharray: 5 4; }
.cf-code {
  font-family: var(--font-sans); font-size: 19px; font-weight: 700;
  letter-spacing: 0.01em; fill: var(--accent-ink);
}
.cf-code--old { fill: var(--ink-3); }
.cf-text { font-family: var(--font-sans); font-size: 12.5px; fill: var(--ink-2); }
.cf-text--old { fill: var(--ink-3); }
.cf-note {
  font-family: var(--font-sans); font-size: 11px; font-weight: 700;
  letter-spacing: 0.16em; fill: var(--ink-3);
}
.cf-arrow { stroke: var(--ink-3); stroke-width: 1.25; fill: none; }
.cf-arrowhead { fill: var(--ink-3); }
/* Both eras tie into one junction, so the record reads as a single thing that
   changed hands rather than two copies. */
.cf-tie { stroke: var(--accent); stroke-width: 1.5; fill: none; }
.cf-tie--old { stroke: var(--line); stroke-dasharray: 4 4; }
.cf-join { fill: var(--accent); }
.cf-record { fill: var(--accent-wash); stroke: var(--accent-line); stroke-width: 1.5; }
.cf-record-label {
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.13em; fill: var(--accent-ink);
}
.cf-item { font-family: var(--font-sans); font-size: 13px; fill: var(--ink); }
@media (max-width: 699px) {
  .cf-era { font-size: 13px; } .cf-text { font-size: 14px; }
  .cf-item { font-size: 15px; } .cf-record-label { font-size: 12.5px; }
  .cf-note { font-size: 12px; } .cf-code { font-size: 22px; }
}

/* ============================================================
   SCROLLY — one pinned diagram, two panels of copy
   The diagram spans both panels on desktop and stays put while the
   copy scrolls past it. Below 900px there is not enough height for
   pinning to read as anything but a stuck image, so the diagram
   simply sits between the two panels and connects on its own.
   ============================================================ */
.scrolly__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3vw, 4rem);
  align-items: start;
}
.scrolly__panel > * + * { margin-top: var(--sp-4); }
.scrolly__panel p { max-width: 46ch; color: var(--ink-2); }

/* Mobile order: first panel, diagram, second panel. */
.scrolly__panel--a { order: 1; }
.scrolly__visual   { order: 2; }
.scrolly__panel--b { order: 3; }

@media (min-width: 900px) {
  .scrolly__grid { grid-template-columns: minmax(0, 0.72fr) minmax(0, 1fr); }
  /* The last panel's track is taller than a screen, and the panel itself pins
     inside it — so once the copy lines up with the diagram, BOTH are stationary
     and scrolling does nothing for half a screen. They then release at the same
     moment and leave together. Pinning only the diagram would strand it on
     screen after its text had gone. */
  .scrolly__grid:not(.scrolly--flip) { grid-template-rows: auto 120vh; }
  .scrolly__panel--a { grid-column: 1; grid-row: 1; }
  .scrolly__panel--b { grid-column: 1; grid-row: 2; }
  .scrolly__visual   { grid-column: 2; grid-row: 1 / span 2; }

  .scrolly__panel--b {
    position: sticky;
    top: 0;
    height: 100vh;
    align-self: start;
  }

  /* Each panel holds a screenful so there is room to scroll one past
     the other, and centres its copy against the pinned diagram. */
  /* A full viewport each. Anything shorter and the panel's copy reaches the
     middle of the screen before the sticky column has engaged, so the two
     sit out of line for the first panel of every section. */
  .scrolly__panel {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  /* A full-height sticky box with its contents centred pins the visual to the
     middle of the viewport whatever size it renders at — a fixed `top` offset
     only lines up at one particular viewport height. The padding keeps it
     clear of the header. */
  .scrolly__visual {
    position: sticky;
    top: 0;
    height: 100vh;
    box-sizing: border-box;
    padding-block: 4rem;
    display: grid;
    align-content: center;
  }

  /* Copy that has not reached the pinned visual yet sits back, and comes to
     full strength as it arrives. The panel nearest the middle is always at
     full strength, so there is never dimmed text without a lit counterpart. */
  .is-driven .scrolly__panel {
    opacity: 0.3;
    transition: opacity 620ms ease;
  }
  .is-driven .scrolly__panel.is-focus { opacity: 1; }

  /* The script sets these claims' opacity directly, frame by frame, so no
     transition here — a transition would lag behind the scroll. Without the
     script they simply stay at full strength. */
  .is-driven.scrolly--flip .scrolly__panel { transition: none; }
}

/* ── Mirror: heading + captures pinned left, claims scrolling right ──
   The heading rides with the captures rather than sitting above the copy,
   so it never scrolls over the text it governs and the three claims stay
   visibly attached to one question. */
.proof__head { margin-bottom: var(--sp-5); }
.proof__more {
  margin-top: var(--sp-3);
  max-width: 40ch;
  font-size: var(--fs-sm);
  color: var(--ink-2);
}
/* Claim headings carry the same display face and weight as the two-column rows
   elsewhere on the page, so a claim reads as the same kind of statement. */
.proof h3 {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 1.85rem);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: var(--ls-tight);
}
/* Written with the parent class so it outranks `.scrolly__panel p`, which
   otherwise wins on specificity and paints the numeral body-grey. */
.proof .proof__num {
  font-family: var(--font-sans);
  font-size: clamp(2.1rem, 1.5rem + 1.5vw, 2.9rem);
  font-weight: 700;
  line-height: 1;
  /* Tight rather than tracked out: at this size the default spacing of a
     two-digit numeral already reads as wide. */
  letter-spacing: -0.02em;
  color: var(--accent-ink);
  margin-bottom: 0;
}
/* The numeral labels the claim under it, so it sits close to it. */
.proof .proof__num + h3 { margin-top: var(--sp-2); }
.proof__shot { transition: opacity 620ms ease, transform 620ms cubic-bezier(.22, .61, .36, 1); }

@media (min-width: 900px) {
  .scrolly--flip {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr);
    /* Six claim rows plus the dwell. Seven claims at a full screen each would be
       an endurance test, so this section's panels are shorter than the two-panel
       section's — see .scrolly--flip .scrolly__panel below. */
    grid-template-rows: auto auto auto auto auto auto auto;
  }

  .proof__aside {
    grid-column: 1;
    grid-row: 1 / span 7;
    position: sticky;
    top: 0;
    height: 100vh;
    box-sizing: border-box;
    /* A 9rem band at the top belongs to the heading — the site header is sticky
       and 69px tall, so anything higher than that hides underneath it. The
       capture centres in what remains, and the claims are offset to match. */
    padding-top: 13rem;
    padding-bottom: 4rem;
    position: sticky;
    /* Centred as a block against the claim beside it, and the three captures
       share one cell so they cross-fade in place instead of pushing the
       heading around. */
    display: grid;
    align-content: center;
  }
  /* Parked at the top so the capture below it can centre on the viewport and
     line up with the claim beside it. Kept high and tight enough to clear the
     capture's top edge — at this size the two would otherwise collide. */
  .proof__head {
    position: absolute;
    top: 5.5rem;
    left: 0;
    right: 0;
    margin-bottom: 0;
  }
  .proof__more { margin-top: var(--sp-3); max-width: 34ch; }
  .proof__shot { grid-row: 1; grid-column: 1; }

  /* Each claim pins at the same centre line as the capture beside it: an 88vh
     box offset 6vh from the top centres its copy on 50vh, exactly where the
     capture centres. The row is taller than the box, and that difference is
     how long the claim holds there before the next one takes over. */
  /* Built exactly like the pinned column beside it — same height, same bands —
     so the copy and the capture centre on the same line by construction.

     The row is taller than the claim, and that surplus is the pause: the claim
     travels up with the scroll, holds still at full strength long enough to be
     read, then travels on. Claim and capture are both 100vh boxes, so a claim
     that is merely passing overlaps the one being read — the script keeps the
     far ones at zero, so only one is ever drawn. */
  /* The PANEL stays in normal flow, so two claims can never occupy the same
     space — that has to be true without any script running. The copy inside it
     is what pins: it travels up with the scroll, holds on the capture's centre
     line long enough to be read, then travels on. The surplus row height is
     the length of that hold.

     `top` is the settled centre of the pinned column (its 13rem head band plus
     half of what remains), and the translate lifts the block by half its own
     height so its middle lands there whatever its length. */
  .scrolly--flip .scrolly__panel {
    display: block;
    min-height: 120vh;
    padding: 0;
  }
  .scrolly--flip .proof--7 { min-height: 168vh; }
  /* Same construction as the pinned column — a full-height box with the same
     head and foot bands — so the copy centres on exactly the line the capture
     does. No transform: a translate on a sticky element stops it sticking in
     Chrome, which is what made the hold slide straight past. */
  .proof__inner {
    position: sticky;
    top: 0;
    height: 100vh;
    box-sizing: border-box;
    padding-top: 13rem;
    padding-bottom: 4rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .proof--1 { grid-column: 2; grid-row: 1; }
  .proof--2 { grid-column: 2; grid-row: 2; }
  .proof--3 { grid-column: 2; grid-row: 3; }
  .proof--4 { grid-column: 2; grid-row: 4; }
  .proof--5 { grid-column: 2; grid-row: 5; }
  .proof--6 { grid-column: 2; grid-row: 6; }
  .proof--7 { grid-column: 2; grid-row: 7; }

  /* Hidden until its claim arrives; the first stands in until the script
     takes over, so the column is never empty. */
  .proof__shot { opacity: 0; transform: translateY(18px) scale(0.985); }
  .proof__shot--1 { opacity: 1; transform: none; }
  .is-driven .proof__shot { opacity: 0; transform: translateY(18px) scale(0.985); }
  .is-driven .proof__shot.is-active { opacity: 1; transform: none; }
}

/* Below 900px there is no room for a pinned column, so the wrapper stops
   being a box and its children take their place in the single-column flow:
   the heading, then each claim followed by its own capture. Interleaved, not
   seven claims and then seven images — a claim read a screen away from the
   thing it is describing proves nothing. */
@media (max-width: 899px) {
  .proof__aside { display: contents; }
  .proof__head    { order: 0; }
  .proof--1       { order: 1; }
  .proof__shot--1 { order: 2; }
  .proof--2       { order: 3; }
  .proof__shot--2 { order: 4; }
  .proof--3       { order: 5; }
  .proof__shot--3 { order: 6; }
  .proof--4       { order: 7; }
  .proof__shot--4 { order: 8; }
  .proof--5       { order: 9; }
  .proof__shot--5 { order: 10; }
  .proof--6       { order: 11; }
  .proof__shot--6 { order: 12; }
  .proof--7       { order: 13; }
  .proof__shot--7 { order: 14; }

  /* A claim and its capture are one unit, so the capture sits close under the
     words and the air goes between one pair and the next. `order` reorders the
     boxes but not the sibling axis, so the separation is set on the claims
     themselves rather than with an adjacent-sibling rule. */
  .scrolly--flip { row-gap: var(--sp-4); }
  .scrolly--flip .scrolly__panel { margin-top: var(--sp-7); }
  .scrolly--flip .proof--1 { margin-top: 0; }

  /* The claim's own parts need separating too. `.scrolly__panel > * + *` only
     reaches the pinned block, not the heading and paragraphs inside it, so on
     a phone the two paragraphs of a claim ran together. The numeral keeps its
     tight bond with the heading it labels. */
  .proof__inner > * + * { margin-top: var(--sp-4); }
}

/* ============================================================
   SCREENSHOTS
   Graded rather than framed: the capture fades out along the edge
   that faces the page, so it reads as part of the row instead of a
   card dropped into it.
   ============================================================ */
.shot img { width: 100%; height: auto; display: block; }

.shot--graded { margin: 0; }
.shot--graded img {
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
/* Fades the right and bottom edges. Used wherever the crop is truncated on
   its right — beside copy on the left, and for the pinned captures whose
   cut-off right edge faces the claims. */
.shot--right img {
  -webkit-mask-image:
    linear-gradient(to right,  #000 86%, transparent 100%),
    linear-gradient(to bottom, #000 88%, transparent 100%);
  mask-image:
    linear-gradient(to right,  #000 86%, transparent 100%),
    linear-gradient(to bottom, #000 88%, transparent 100%);
}
/* Fades the left and bottom edges. Gentler than the right-hand variant
   because these crops lead with a headline figure at their left edge. */
.shot--left img {
  -webkit-mask-image:
    linear-gradient(to left,   #000 93%, transparent 100%),
    linear-gradient(to bottom, #000 88%, transparent 100%);
  mask-image:
    linear-gradient(to left,   #000 93%, transparent 100%),
    linear-gradient(to bottom, #000 88%, transparent 100%);
}
/* Stacked on a phone there is no side for the image to face, so only the
   bottom fades. */
@media (max-width: 899px) {
  .shot--right img,
  .shot--left img {
    -webkit-mask-image: linear-gradient(to bottom, #000 90%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 90%, transparent 100%);
  }
}

/* The hero capture is the one that stays whole — it is the proof the
   product exists. Only its bottom dissolves into the page. */
.shot--hero img {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  -webkit-mask-image: linear-gradient(to bottom, #000 92%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 92%, transparent 100%);
}

/* ============================================================
   DIAGRAMS
   ============================================================ */
.diagram { width: 100%; max-width: 560px; height: auto; margin-inline: auto; display: block; }
/* In a pinned column the diagram fills the column, so its edges land on the
   same grid lines as every other visual on the page. */
.scrolly__visual .diagram { max-width: none; }

.dg-box { fill: var(--paper); stroke: var(--line); stroke-width: 1.25; }
.dg-label {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  fill: var(--ink);
  text-anchor: middle;
}

.dg-plan-off {
  fill: var(--paper-2);
  stroke: var(--ink-3);
  stroke-width: 1.25;
  stroke-dasharray: 5 4;
  transform-box: fill-box;
  transform-origin: center;
}
/* The plan waits to be connected, and stops the moment it is. Scaling an SVG
   rect needs its own box as the reference; the global reduced-motion rule in
   base.css already stops the animation for anyone who asks. */
.diagram--morph:not(.is-connected) .dg-plan-off {
  animation: dg-waiting 2.2s ease-in-out infinite;
}
@keyframes dg-waiting {
  0%, 100% { stroke-opacity: 0.28; stroke-width: 1.25; transform: scale(1);     }
  50%      { stroke-opacity: 1;    stroke-width: 2.1;  transform: scale(1.045); }
}
.dg-plan-on  { fill: var(--accent-wash); stroke: var(--accent); stroke-width: 1.5; }

.dg-plan-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-anchor: middle;
}
.dg-plan-title--off { fill: var(--ink-3); }
.dg-plan-title--on  { fill: var(--accent-ink); }

.dg-plan-sub {
  font-family: var(--font-sans);
  font-size: 11.5px;
  fill: var(--ink-3);
  text-anchor: middle;
}
.dg-plan-sub--on { fill: var(--accent-ink); }

.dg-link { fill: none; stroke: var(--accent); stroke-width: 1.5; }

/* ── The two states of one diagram ──────────────────────────
   The connectors are authored from the plan outward. Their lengths are pure
   geometry (161 for the curves, 62 for the straight pair), so a dash a little
   longer than each hides it completely and draws it completely — no measuring
   pass, and nothing to flash before the script runs. */
.diagram--morph .dg-link path {
  stroke-dasharray: 170;
  stroke-dashoffset: 170;
  transition: stroke-dashoffset 1150ms cubic-bezier(.22, .61, .36, 1);
}
.diagram--morph .dg-link path:nth-child(2),
.diagram--morph .dg-link path:nth-child(5) {
  stroke-dasharray: 70;
  stroke-dashoffset: 70;
}
/* Middle pair first, then the top curves, then the bottom: the plan reaches
   out rather than six lines arriving together. */
.diagram--morph .dg-link path:nth-child(1),
.diagram--morph .dg-link path:nth-child(4) { transition-delay: 280ms; }
.diagram--morph .dg-link path:nth-child(3),
.diagram--morph .dg-link path:nth-child(6) { transition-delay: 450ms; }

.dg-state { transition: opacity 520ms ease; }
.diagram--morph .dg-state--on  { opacity: 0; }
.diagram--morph .dg-state--off { opacity: 1; }

/* Connected. Listed last so it wins over the per-path offsets above. */
.diagram--morph.is-connected .dg-link path { stroke-dashoffset: 0; }
.diagram--morph.is-connected .dg-state--on  { opacity: 1; transition-delay: 300ms; }
.diagram--morph.is-connected .dg-state--off { opacity: 0; }

/* The SVG scales with its column, so on a phone these labels would land
   near 8px. Enlarge them in viewBox units to hold ~10px on screen. */
@media (max-width: 699px) {
  .dg-label      { font-size: 18px; }
  .dg-plan-title { font-size: 16px; }
  .dg-plan-sub   { font-size: 13px; }
}

/* ============================================================
   NONPROFIT
   ============================================================ */
.equity > * + * { margin-top: var(--sp-4); }
.equity h2 a { color: inherit; text-decoration-color: var(--accent); }
.equity h2 a:hover { color: var(--accent-ink); }

/* ============================================================
   CLOSING CTA
   ============================================================ */
.cta { text-align: center; }
.cta > * + * { margin-top: var(--sp-5); }
.cta h2 { max-width: 20ch; margin-inline: auto; }
.cta .lead { max-width: 46ch; margin-inline: auto; }
.cta__note { font-size: var(--fs-sm); color: var(--on-ink-2); }

/* ============================================================
   FOOTNOTES — sources collected at the foot of the page, so the
   figures themselves stay uncluttered
   ============================================================ */
.fnref {
  font-size: 0.5em;
  vertical-align: super;
  line-height: 0;
  margin-left: 0.15em;
  text-decoration: none;
  color: var(--accent-ink);
}
.fnref:hover { text-decoration: underline; }

.footnotes { padding-block: var(--sp-7); border-top: 1px solid var(--line); }
.footnotes__title {
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--ink-3);
}
.footnotes__list {
  list-style: none;
  padding: 0;
  margin: var(--sp-4) 0 0;
  display: grid;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--ink-3);
}
.footnotes__list li { display: flex; gap: var(--sp-3); }
.fnmark { flex: 0 0 1rem; color: var(--accent-ink); }
/* Highlight the entry a marker jumped to. */
.footnotes__list li:target { color: var(--ink); }

/* ============================================================
   404
   ============================================================ */
.page-error { text-align: center; padding-block: clamp(4rem, 10vw, 8rem); }
.page-error > div { max-width: 40rem; margin-inline: auto; }
.page-error > div > * + * { margin-top: var(--sp-4); }
.page-error .lead { margin-inline: auto; }
.page-error .btn-row { justify-content: center; margin-top: var(--sp-6); }

/* ============================================================
   UTILITIES
   ============================================================ */
.site-footer--minimal { padding-block: var(--sp-6); }
.site-footer--minimal .site-footer__bottom { padding-top: 0; }

/* ============================================================
   CREDIBILITY STRIP
   ============================================================ */
.section--tight { padding-block: clamp(2rem, 1rem + 2vw, 3rem); }

.cred {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-6) var(--sp-6);
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (min-width: 620px)  { .cred { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1020px) { .cred { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.cred__item { padding-top: var(--sp-4); border-top: 2px solid var(--accent); }
.cred__key {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.008em;
}
.cred__item p {
  margin-top: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--ink-2);
}

/* ============================================================
   TWO-UP TEXT BLOCK (for firms)
   ============================================================ */
.split-2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-5) var(--sp-8);
  align-items: start;
}
@media (min-width: 860px) { .split-2 { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr); } }
.split-2 h2 { margin-top: var(--sp-4); }
.split-2 p { color: var(--ink-2); max-width: 54ch; }

/* ============================================================
   SUB-CLAIMS inside the evidence section
   ============================================================ */
.duo__copy h3 {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 1.85rem);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: var(--ls-tight);
  color: var(--ink);
}

/* ============================================================
   HERO META
   ============================================================ */
.hero__meta {
  margin-top: var(--sp-4);
  font-size: var(--fs-xs);
  color: var(--ink-3);
}

/* ============================================================
   PRICING
   Cards of equal height in a row, each with a fixed-height badge strip above
   it so every card body starts on the same line whether or not it carries a
   badge. Feature lists sit above a button pinned to the bottom edge.
   ============================================================ */
.page-head--tight { padding-bottom: 0; }
.section--plans { padding-top: clamp(2.5rem, 1.5rem + 3vw, 4rem); }

/* ── Segmented switches ─────────────────────────────────── */
.switches {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-7);
}
.seg {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.seg__btn {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-2);
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  transition: background 160ms ease, color 160ms ease;
}
.seg__btn:hover { color: var(--ink); }
.seg__btn.is-on { background: var(--ink); color: var(--paper); }
.seg--interval[hidden] { display: none; }

/* ── The cards ──────────────────────────────────────────── */
.plans {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-5);
  list-style: none;
  padding: 0;
  margin: 0;
  align-items: stretch;
}
@media (min-width: 720px)  { .plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .plans { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.plans[hidden] { display: none; }

.plan { display: flex; flex-direction: column; }
.plan__badge {
  /* Reserved whether or not there is a badge, so every card body lines up. */
  min-height: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent-ink);
}
.plan__badge--empty { visibility: hidden; }

.plan__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.plan--lead > .plan__body {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.plan__name {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  font-weight: 400;
  letter-spacing: var(--ls-tight);
  margin: 0;
}
.plan__tag {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--ink-3);
  min-height: 2.4em;
}

.plan__mods { display: flex; flex-wrap: wrap; gap: 5px; list-style: none; padding: 0; margin: var(--sp-4) 0 0; }
.plan__mod {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 3px 7px;
  border-radius: 4px;
  color: var(--accent-ink);
  border: 1px solid var(--accent-line);
  background: var(--accent-wash);
}
.plan__mod--off { color: var(--ink-3); border-color: var(--line); background: transparent; }

.plan__price {
  margin-top: var(--sp-5);
  font-family: var(--font-display);
  line-height: 1.05;
  letter-spacing: var(--ls-tight);
  color: var(--ink);
}
.plan__label {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-3);
  margin-bottom: 2px;
}
.plan__amount { font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.2rem); font-variant-numeric: tabular-nums; }
.plan__unit { font-family: var(--font-sans); font-size: var(--fs-sm); color: var(--ink-2); margin-left: 4px; }
.plan__sub { margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--ink-3); min-height: 2.4em; }
.plan__promo {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent-ink);
}

.plan__stats {
  list-style: none;
  padding: var(--sp-4) 0;
  margin: var(--sp-4) 0 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
  color: var(--ink-2);
}
.plan__stats li + li { margin-top: 2px; }

.plan__head {
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink);
}
.plan__feats { list-style: none; padding: 0; margin: var(--sp-3) 0 var(--sp-5); font-size: var(--fs-sm); }
.plan__feats li {
  position: relative;
  padding-left: 1.35rem;
  margin-bottom: 6px;
  line-height: 1.45;
  color: var(--ink-2);
}
.plan__feats li::before {
  position: absolute;
  left: 0;
  font-weight: 700;
}
/* A tick and a dash rather than colour alone — the distinction has to survive
   being read in greyscale or by someone who cannot separate the two hues. */
.plan__yes::before { content: '\2713'; color: var(--accent-ink); }
.plan__no::before  { content: '\2013'; color: var(--ink-3); }
.plan__no { color: var(--ink-3); }

.plan__cta { margin-top: auto; }
.btn--block { display: flex; width: 100%; }

/* Plan buttons carry more weight than a body link — they are the one thing
   on the card you are meant to press. */
.plan__cta .btn {
  min-height: 50px;
  font-weight: 600;
  font-size: var(--fs-sm);
}
.plan__cta .btn--ghost {
  border-color: var(--ink-3);
  color: var(--ink);
}
.plan__cta .btn--ghost:hover {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

/* Not released yet. Rendered as a button but inert: no href, not focusable,
   and announced as disabled rather than merely looking it. */
.plan__cta .btn[aria-disabled="true"] {
  cursor: not-allowed;
  background: transparent;
  border-color: var(--line);
  color: var(--ink-3);
}
.plan__cta .btn[aria-disabled="true"]:hover {
  background: transparent;
  border-color: var(--line);
  color: var(--ink-3);
}

.plans__note { margin-top: var(--sp-7); }
.plans__note[hidden] { display: none; }
.plans__note p { max-width: 62ch; font-size: var(--fs-sm); color: var(--ink-2); }
.plans__note p + p, .plans__note .tablewrap + p { margin-top: var(--sp-4); }

.bands__title {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.1rem + 0.9vw, 1.6rem);
  font-weight: 400;
  letter-spacing: var(--ls-tight);
  margin: 0;
}
.tablewrap { overflow-x: auto; margin-top: var(--sp-5); }
.bands { border-collapse: collapse; width: 100%; min-width: 34rem; font-size: var(--fs-sm); }
.bands th,
.bands td { text-align: left; padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); }
.bands thead th {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  border-bottom-color: var(--ink-3);
}
.bands tbody th { font-weight: 500; color: var(--ink); white-space: nowrap; }
.bands td { color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bands tbody tr:last-child th,
.bands tbody tr:last-child td { border-bottom: none; }

/* ============================================================
   INNER PAGES
   ============================================================ */
.page-head { padding-block: clamp(3rem, 1.5rem + 5vw, 5rem) clamp(1.5rem, 1rem + 2vw, 2.5rem); }
.page-head > .container > * + * { margin-top: var(--sp-4); }
.page-head h1 { max-width: 20ch; }
.page-head .lead { max-width: 56ch; }
.page-head__meta { font-size: var(--fs-xs); color: var(--ink-3); }

.prose { max-width: 68ch; }  /* a child of .container, so it stays left-aligned */
.prose > * + * { margin-top: var(--sp-4); }
.prose h2 {
  margin-top: var(--sp-8);
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.75rem);
}
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--ink-2); }
.prose strong { color: var(--ink); font-weight: 600; }
.prose abbr { text-decoration: underline dotted; cursor: help; }
.prose__cta { margin-top: var(--sp-8); }

/* ============================================================
   FORM
   ============================================================ */
.form {
  max-width: 34rem;
  margin: var(--sp-6) auto 0;
  text-align: left;
}
.form__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-4);
}
@media (min-width: 560px) { .form__row { grid-template-columns: 1fr 1fr; } }
.form__row + .form__row { margin-top: var(--sp-4); }

.field label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--on-ink-2);
}
.field input {
  width: 100%;
  min-height: 46px;
  padding: 0 var(--sp-4);
  background: var(--ink-surface-2);
  border: 1px solid var(--on-ink-line);
  border-radius: var(--r);
  color: var(--on-ink);
  font-size: var(--fs-sm);
}
.field input:focus-visible {
  outline: 3px solid var(--accent-on-ink);
  outline-offset: 2px;
  border-color: transparent;
}
/* :user-invalid only matches after the visitor has interacted, so empty
   required fields do not look like errors on first paint. */
.field input:user-invalid { border-color: var(--accent); }

/* Honeypot: off-screen for people, still reachable for the bots that fill it. */
.field--hp {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.form button[type='submit'] { margin-top: var(--sp-5); width: 100%; }
.form__status {
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--on-ink-2);
  min-height: 1.5em;
}
.form__status--error { color: var(--accent-on-ink); }
.form__status--ok { color: var(--on-ink); }
.form.is-sent .form__row,
.form.is-sent button[type='submit'] { display: none; }

/* Once the form is gone the confirmation is the only thing left in the column,
   so it has to carry the section rather than read like a validation note. The
   form body is left-aligned inside a centred section — the confirmation is not
   a form field, so it returns to the centre with the rest of the copy. */
.form.is-sent { text-align: center; }
.form.is-sent .form__status {
  margin: 0 auto;
  max-width: 32ch;
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 1rem + 0.7vw, 1.45rem);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: var(--ls-tight);
  color: var(--on-ink);
}
/* The same short rule the centred eyebrows use, so the confirmation is
   recognisably part of the same system. */
.form.is-sent .form__status::before {
  content: '';
  display: block;
  width: 22px;
  height: 2px;
  background: var(--accent-on-ink);
  margin: 0 auto var(--sp-5);
}

/* ============================================================
   FOOTER ADDITIONS
   ============================================================ */
.site-footer__addr { margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--on-ink-2); }
