/* WalletNoise — the calculator is the marketing surface, so this page has to earn
   trust from a cold visitor in about two seconds. Design rules that follow from
   docs/: big legible type (median user is 47-55 and anxious about money),
   color-blocked sections so the hierarchy survives a skim, and sources/dates
   promoted to visible UI rather than footnotes — being visibly current IS the
   product.

   Light mode only, deliberately: this is a page where misreading a price has
   consequences, and a single palette is one fewer place for contrast to go
   wrong. Self-hosted everything — the CSP forbids inline styles and remote
   images, so every accent below is a gradient, a border, or inline <svg> in
   the markup, never a data URI.

   One webfont, and it is vendored: Baloo 2 ExtraBold, used for the wordmark and
   nothing else. Body copy stays on the system stack, which is the reason there
   is no font loading to design around — no FOIT, no layout shift on the prices,
   and nothing to wait for on a cold 3G open. The font file is served from our
   own origin (`web/static/vendor/`, hashed in its README); a `<link>` to
   Google's CSS API would leak the visitor's IP and Referer to a third party,
   and on this site the Referer is the health claim. */

/* =====================================================================
   WALLETNOISE DESIGN TOKENS — canonical set, from the brand style guide.
   The --wn-* block is the source of truth; the semantic names beneath it
   are what components use, so a token change lands everywhere at once.
   ===================================================================== */
/* `swap` and not `block`: the wordmark must be readable the instant the page
   paints, even on the open where the font has not arrived yet. The fallback is
   the system stack, so the swap moves the wordmark slightly and nothing else —
   it is 11 characters in the header, not a column of prices. */
@font-face {
  font-family: "Baloo 2";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/static/vendor/baloo2-800-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  /* Surfaces — warm, calm, DTC-health cream */
  --wn-cream:      #FBF9F5;
  --wn-surface:    #FFFFFF;
  --wn-surface-2:  #F3F0EA;
  --wn-ink-deep:   #0F1F38;
  --wn-ink-deep-2: #1A2E4F;

  /* Ink */
  --wn-ink:        #14243E;
  --wn-ink-soft:   #4C5A70;
  --wn-ink-mute:   #8A93A3;
  --wn-on-dark:    #FFFFFF;
  --wn-on-dark-m:  #A7B6CC;

  /* Brand */
  --wn-blue:       #2A78D6;
  --wn-blue-deep:  #1C5CAB;
  --wn-blue-tint:  #E3EEFB;
  --wn-gold:       #E89B2D;
  --wn-gold-deep:  #A96A15;
  --wn-gold-tint:  #FBF1DC;
  --wn-green:      #177A3E;
  --wn-green-tint: #E4F3E9;
  /* Brand accent, and only the mark uses it: the clasp on the wallet. Kept
     apart from --wn-green on purpose — that green means "verified" in the UI
     and a second job would blunt it. Never use teal to signal state. */
  --wn-teal:       #2FAE96;
  --wn-red:        #C1352F;
  --wn-red-tint:   #FBEAE9;

  /* Hairlines & depth */
  --wn-line:       rgba(20, 36, 62, 0.10);
  --wn-line-2:     rgba(20, 36, 62, 0.18);
  --wn-line-dark:  rgba(255, 255, 255, 0.12);
  --wn-shadow-1:   0 1px 2px rgba(15, 31, 56, 0.04), 0 8px 24px -12px rgba(15, 31, 56, 0.10);
  --wn-shadow-2:   0 2px 4px rgba(15, 31, 56, 0.05), 0 20px 44px -16px rgba(15, 31, 56, 0.16);
  --wn-shadow-3:   0 4px 10px rgba(15, 31, 56, 0.07), 0 32px 60px -20px rgba(15, 31, 56, 0.26);

  /* Shape */
  --wn-r-card:     18px;
  --wn-r-input:    12px;
  --wn-r-pill:     999px;

  /* Type — self-hosted or system ONLY. Never a fonts CDN. */
  --wn-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* The wordmark only. Rounded terminals to match the mark; deliberately not
     the UI face, because a friendly display cut is not what you want carrying
     a four-figure price. */
  --wn-display: "Baloo 2", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Motion — calm is the brand */
  --wn-ease:     cubic-bezier(.22, .61, .28, 1);
  --wn-t-fast:   160ms;
  --wn-t-base:   280ms;
  --wn-t-reveal: 520ms;
  --wn-rise:     14px;

  /* ---- semantic aliases used by components ---- */
  --plane: var(--wn-cream);
  --plane-2: var(--wn-surface-2);
  --surface: var(--wn-surface);
  --surface-2: var(--wn-surface-2);
  --cream: var(--wn-cream);
  --cream-2: var(--wn-surface-2);

  --ink: var(--wn-ink);
  --ink2: var(--wn-ink-soft);
  --muted: var(--wn-ink-mute);

  --hair: var(--wn-line);
  --hair-2: var(--wn-line-2);

  --blue: var(--wn-blue);
  --blue-deep: var(--wn-blue-deep);
  --blue-pale: var(--wn-blue-tint);
  --amber: var(--wn-gold);
  --amber-ink: var(--wn-gold-deep);
  --amber-pale: var(--wn-gold-tint);
  --green: var(--wn-green);
  --green-pale: var(--wn-green-tint);
  --teal: var(--wn-teal);
  --red: var(--wn-red);
  --red-pale: var(--wn-red-tint);

  --navy: var(--wn-ink-deep);
  --grad-slab: linear-gradient(138deg, #0B1729 0%, #14263F 46%, #1F3F6B 100%);
  --grad-btn: linear-gradient(135deg, #2A78D6 0%, #1C5CAB 100%);
  --grad-amber: linear-gradient(96deg, #C97A16 0%, #E89B2D 100%);

  --shadow-1: var(--wn-shadow-1);
  --shadow-2: var(--wn-shadow-2);
  --shadow-3: var(--wn-shadow-3);

  --r-sm: 9px;
  --r-md: var(--wn-r-input);
  --r-lg: var(--wn-r-card);
  --r-xl: 26px;
  --r-pill: var(--wn-r-pill);

  --fs-hero: clamp(2.2rem, 1.45rem + 3.2vw, 3.75rem);
  --fs-price: clamp(2.8rem, 1.9rem + 3.8vw, 4.4rem);
  --fs-h2: clamp(1.15rem, 1.05rem + 0.4vw, 1.32rem);

  --ease: var(--wn-ease);
  --wrap: 1060px;
  /* Section rhythm, per the guide: 64-96px between ideas. */
  --wn-rhythm: clamp(52px, 7vw, 88px);
}

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--ink);
  font: 17px/1.6 var(--wn-sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Several elements carry .wrap AND a layout class (header .bar, main). A
   padding *shorthand* on either one silently wipes the other's axis, which is
   what put the header 22px out of line with the page and left main with no
   vertical padding at all. Everything that touches padding here uses longhands. */
.wrap { max-width: var(--wrap); margin: 0 auto; padding-left: 22px; padding-right: 22px; }
a { color: var(--blue); text-underline-offset: 2px; }
a:hover { color: var(--blue-deep); }

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

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: -9999px; top: 8px;
  background: var(--surface); color: var(--ink);
  padding: 10px 18px; border-radius: var(--r-sm); z-index: 30;
  box-shadow: var(--shadow-2); text-decoration: none; font-weight: 650;
}
.skip:focus { left: 16px; }

/* ---------- the dated-change strip ----------
   Structurally this is the promo bar every DTC health site runs. Theirs says
   SUMMER SALE; ours can only say something with a real date behind it, pulled
   from the corpus and one click from its source. If nothing is scheduled the
   strip does not render — which is the whole difference. */
.alertbar {
  display: flex; align-items: center; justify-content: center; gap: 8px 16px; flex-wrap: wrap;
  padding: 9px 22px; text-decoration: none;
  background: var(--wn-ink-deep); color: var(--wn-on-dark-m);
  font-size: 13px; line-height: 1.4;
}
.alertbar:hover { color: var(--wn-on-dark); }
.ab-when {
  flex: none; font-size: 11px; font-weight: 750; letter-spacing: 0.07em; text-transform: uppercase;
  color: #2b1400; background: var(--wn-gold); padding: 3px 10px; border-radius: var(--wn-r-pill);
}
.ab-what strong { color: var(--wn-on-dark); font-weight: 650; }
.ab-go { flex: none; display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: #CFE0F8; }
.ab-go svg { width: 14px; height: 14px; transition: transform var(--wn-t-fast) var(--wn-ease); }
.alertbar:hover .ab-go svg { transform: translateX(3px); }
@media (max-width: 620px) { .ab-go { display: none; } .alertbar { font-size: 12.5px; padding: 8px 16px; } }

/* ---------- header ---------- */
header.site {
  position: sticky; top: 0; z-index: 20;
  border-bottom: 1px solid var(--hair);
  background: color-mix(in srgb, var(--surface) 85%, transparent);
  backdrop-filter: saturate(1.7) blur(14px);
  -webkit-backdrop-filter: saturate(1.7) blur(14px);
}
.bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 13px; padding-bottom: 13px; position: relative; }
.brand {
  display: inline-flex; align-items: center; gap: .32em;
  font-weight: 750; font-size: 27px; letter-spacing: -0.03em;
  color: var(--ink); text-decoration: none;
}
/* The mark is a wallet silhouette, not a tile — so no border-radius and no box
   shadow on its box, both of which would frame empty space around the shape.
   Colours are set once, here: both planes the mark sits on (the header bar and
   the footer) are light, so there is no inverted lockup to keep in step. A
   dark placement would need its own rule — the navy body would vanish. */
.mark .wl-body { fill: var(--navy); }
.mark .wl-card { fill: var(--amber); }
.mark .wl-rx { fill: var(--cream); }
.mark .wl-clasp { fill: var(--teal); }
.mark .wl-stud { fill: var(--navy); }
/* Sized by HEIGHT, with width left to the intrinsic aspect. The mark's viewBox
   is cropped tight to the artwork, so its box is 0.82:1, not square — forcing a
   square box would letterbox it and reintroduce ~3.6px of dead space on the
   right, which is where most of the old gap actually came from. The favicon
   keeps a square viewBox; a favicon has to be square, a lockup does not.

   With the box tight, `gap` finally means the gap you see, and it is set in em
   so it tracks the wordmark rather than the mark. Deriving it from the mark's
   width (as the reference's own proportions suggest) reads too tight here: our
   mark is far smaller relative to the type than in the reference art, so the
   type is the sounder thing to scale against. */
.brand .mark { height: 31px; width: auto; flex: none; }

/* The wordmark, and only the wordmark. Weight is pinned to 800 because that is
   the single cut vendored — leaving the inherited 750 would resolve to 800 in a
   browser that has the file and to a synthesised bold in one that does not, so
   naming it keeps the two paths identical. Baloo 2 sits on a taller
   x-height than the fallback, so the swap on a cold load shifts the wordmark's
   optical size a little — acceptable for 11 characters, and the reason the face
   is nowhere near the prices. */
.brand .word,
.footbrand span {
  font-family: var(--wn-display);
  font-weight: 800;
  letter-spacing: -0.03em;
  /* Flex centres the line box, but "WalletNoise" has no descender, so its ink
     sits ~1.3px above the box centre and the wordmark reads high against the
     mark. Measured, not guessed: without this the ink centre lands 1.96px high
     in the header (27px type) and 1.64px high in the footer (18px), so one
     integer nudge of 2px leaves both inside half a pixel. Integer on purpose —
     a fractional offset softens the text. Re-measure if either size changes;
     the offset scales with the type, not with this rule. */
  position: relative;
  top: 2px;
}

/* The nav pills align by their box, not their text: the active pill is a filled
   shape, so its edge is what reads as the alignment, matching the brand mark
   opposite it and the page gutter below. */
nav.mainnav { display: flex; flex-wrap: wrap; gap: 3px; }
nav.mainnav a {
  color: var(--ink2); text-decoration: none; font-size: 15px; font-weight: 600;
  padding: 8px 14px; border-radius: var(--r-pill);
  transition: background-color 0.16s var(--ease), color 0.16s var(--ease);
}
nav.mainnav a:hover { color: var(--ink); background: var(--plane-2); }
nav.mainnav a.is-active { color: #fff; background: var(--navy); }
/* The toggle only exists once JS is running. Without it the nav stays open and
   wraps, which is worse-looking but never traps a reader — content and
   navigation must not depend on a script. */
.navtoggle { display: none; }
.js .navtoggle {
  border: 1px solid var(--wn-line-2); background: var(--wn-surface); color: var(--wn-ink);
  border-radius: var(--wn-r-input); padding: 8px; cursor: pointer; line-height: 0;
}
.js .navtoggle:hover { border-color: var(--wn-ink-soft); }
.navtoggle svg { width: 22px; height: 22px; }
.navtoggle .bar-t, .navtoggle .bar-m, .navtoggle .bar-b {
  transition: transform var(--wn-t-base) var(--wn-ease), opacity var(--wn-t-fast) var(--wn-ease);
  transform-origin: center;
}
.navtoggle[aria-expanded="true"] .bar-t { transform: translateY(5px) rotate(45deg); }
.navtoggle[aria-expanded="true"] .bar-m { opacity: 0; }
.navtoggle[aria-expanded="true"] .bar-b { transform: translateY(-5px) rotate(-45deg); }

/* 760px, and the number is measured rather than chosen: the wordmark is 153px,
   the four links need 522px on one line, and the bar carries 44px of padding —
   743px before anything can breathe. Below that the links wrap to a second row
   and the header grows to ~135px, which is a quarter of a phone screen spent on
   navigation. The old 620px left a dead zone from 621-743px where the nav
   wrapped with no toggle to collapse it. If a nav item is ever renamed,
   re-measure and move this number. */
@media (max-width: 760px) {
  .bar { padding-bottom: 13px; }
  .js .navtoggle { display: block; }
  /* Collapsed by default only when the toggle exists to reopen it. */
  .js nav.mainnav {
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; gap: 2px; padding: 8px 22px 16px;
    background: var(--wn-surface); border-bottom: 1px solid var(--wn-line);
    box-shadow: var(--wn-shadow-2);
    display: none;
  }
  .js header.site.open nav.mainnav { display: flex; }
  nav.mainnav a { font-size: 15.5px; padding: 11px 12px; }
}

@media (max-width: 620px) {
  header.site { position: sticky; }
}

main { padding-top: 32px; padding-bottom: clamp(64px, 8vw, 104px); }

/* ---------- the slab: one bold color block per page ---------- */
.slab {
  position: relative; isolation: isolate; overflow: hidden;
  border-radius: var(--r-xl);
  background: var(--grad-slab);
  color: #fff;
  padding: clamp(24px, 2.6vw, 38px);
  margin: 0 0 26px;
  box-shadow: var(--shadow-3);
}
/* dot field + warm glow, both drawn in CSS so nothing is fetched */
.slab::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: radial-gradient(rgba(255, 255, 255, 0.20) 1.2px, transparent 1.2px);
  background-size: 24px 24px;
  -webkit-mask-image: linear-gradient(115deg, #000 5%, transparent 55%);
  mask-image: linear-gradient(115deg, #000 5%, transparent 55%);
}
.slab::after {
  content: ""; position: absolute; z-index: -1;
  right: -140px; top: -180px; width: 480px; height: 480px; border-radius: 50%;
  background: radial-gradient(circle, rgba(240, 169, 46, 0.42), transparent 62%);
}
.slabgrid { display: grid; grid-template-columns: 1.02fr 0.98fr; gap: clamp(26px, 3vw, 44px); align-items: center; }
@media (max-width: 900px) { .slabgrid { grid-template-columns: 1fr; } }

.slab .eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: #ffd79a; background: rgba(240, 169, 46, 0.18);
  border: 1px solid rgba(240, 169, 46, 0.35);
  padding: 6px 14px; border-radius: var(--r-pill); margin: 0 0 18px;
}
.slab .eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: #f0a92e; flex: none; }
.slab h1 {
  font-size: var(--fs-hero); line-height: 1.08; letter-spacing: -0.022em;
  margin: 0 0 16px; font-weight: 640; color: #fff;
}
.slab .grad { color: #f6b850; }
.nb { white-space: nowrap; }
.slab .sub { color: rgba(233, 240, 252, 0.86); font-size: clamp(1.02rem, 0.97rem + 0.25vw, 1.18rem); margin: 0; max-width: 46ch; }
.slab .crumbs, .slab .crumbs a { color: rgba(220, 232, 250, 0.72); }
.slab .crumbs a:hover { color: #fff; }

.slab.compact { padding: clamp(24px, 2.6vw, 36px) clamp(24px, 3vw, 40px); }
.slab.compact h1 { font-size: clamp(1.85rem, 1.3rem + 1.9vw, 2.6rem); margin-bottom: 10px; }
.slab.compact .sub { max-width: 68ch; }

.trust { display: flex; flex-wrap: wrap; gap: 9px; list-style: none; margin: 24px 0 0; padding: 0; }
.trust li {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 600; color: #eaf1fd;
  background: rgba(255, 255, 255, 0.10); border: 1px solid rgba(255, 255, 255, 0.18);
  padding: 7px 15px 7px 12px; border-radius: var(--r-pill);
}
.trust svg { width: 15px; height: 15px; flex: none; stroke: #7fe0a1; }

/* ---------- cards ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  padding: 22px 24px;
  margin: 16px 0;
  box-shadow: var(--shadow-1);
}
.card.banded { padding: 0; overflow: hidden; }
.cardhead {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 22px; background: var(--navy); color: #fff;
}
/* flex-basis 0, not auto: with wrap enabled an item whose basis exceeds the
   space left on the line moves to the next line before it ever shrinks, which
   is what stranded the icon on a line of its own on a phone. */
.cardhead h2 { flex: 1 1 0; min-width: 0; }
.cardhead h2 {
  font-size: 13.5px; margin: 0; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 700; color: #fff;
}
.cardhead svg { width: 17px; height: 17px; flex: none; stroke: #f0a92e; }
.cardhead.blue { background: var(--blue-deep); }
.cardhead.blue svg { stroke: #9fc4ff; }
.cardhead.amber { background: linear-gradient(100deg, #8a4a10, #b8721f); }
.cardbody { padding: 20px 24px 24px; }
.cardbody > :first-child { margin-top: 0; }

.crumbs { font-size: 14px; margin-bottom: 12px; }

/* Light page header, used where a bold block already owns the page (results). */
.pagehead { margin: 2px 0 20px; }
.pagehead .crumbs { color: var(--muted); }
.pagehead .crumbs a { color: var(--muted); }
.pagehead .crumbs a:hover { color: var(--ink2); }
.pagehead h1 {
  font-size: clamp(1.8rem, 1.3rem + 1.9vw, 2.55rem); line-height: 1.1;
  letter-spacing: -0.032em; margin: 0 0 8px; font-weight: 760;
}
/* Context as chips: the sentence version read as leftover metadata on the one
   page most people land on cold. */
.ctxchips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 14px 0 0; padding: 0; }
.ctxchips li {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600; color: var(--ink2);
  background: var(--surface); border: 1px solid var(--hair);
  border-radius: var(--r-pill); padding: 6px 14px 6px 11px; box-shadow: var(--shadow-1);
}
.ctxchips svg { width: 14px; height: 14px; flex: none; stroke: var(--muted); }

/* ---------- calculator ---------- */
/* The calculator sits inside the slab, so it has to re-assert its own colours. */
.calc { margin: 0; color: var(--ink); box-shadow: var(--shadow-3); border-radius: var(--r-xl); padding: 22px; border-color: transparent; }
.calc .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
.calc label.wide { grid-column: 1 / -1; }
@media (max-width: 520px) { .calc .grid { grid-template-columns: 1fr; } }
.calc .formtitle { margin: 0 0 14px; font-size: 18px; font-weight: 700; letter-spacing: -0.02em; }
.calc label { display: flex; flex-direction: column; gap: 7px; font-size: 13.5px; font-weight: 700; color: var(--ink2); }
.calc .opt { font-weight: 400; color: var(--muted); }
/* Field-label glyphs. Thin, muted and 15px — they should register as texture
   when you scan and disappear when you read. */
/* Inline flow, not flex: as a flex row the trailing "(optional)" became its own
   item and broke the label across two lines instead of wrapping as text. */
.calc .flabel { display: block; }
.calc .flabel svg { width: 15px; height: 15px; stroke: var(--muted); vertical-align: -2px; margin-right: 7px; }
.calc .fieldhint { font-size: 12.5px; font-weight: 400; line-height: 1.45; color: var(--muted); margin-top: -1px; }
.calc .fieldhint em { font-style: normal; font-weight: 650; color: var(--ink2); }
.calc optgroup { font-style: normal; font-weight: 700; color: var(--ink2); }
.calc optgroup option { font-weight: 400; color: var(--ink); }
/* The ramp tool reuses the calculator's controls verbatim — one control
   style, so a field looks the same wherever it appears. */
.calc select, .calc input, .rampform select {
  font: inherit; font-size: 16px; padding: 12px 14px; border: 1.5px solid var(--hair-2);
  border-radius: var(--r-md); background: var(--surface-2); color: var(--ink);
  width: 100%; transition: border-color 0.16s var(--ease), box-shadow 0.16s var(--ease);
}
.calc select, .rampform select { appearance: none; -webkit-appearance: none; padding-right: 40px; cursor: pointer; }
.calc select:focus, .calc input:focus, .rampform select:focus {
  outline: none; border-color: var(--blue);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--blue) 20%, transparent);
}
/* CSS-only chevron: no data URI, so nothing to trip img-src */
.selectwrap { position: relative; display: block; }
.selectwrap::after {
  content: ""; position: absolute; right: 16px; top: 50%; width: 8px; height: 8px;
  border-right: 2.2px solid var(--ink2); border-bottom: 2.2px solid var(--ink2);
  transform: translateY(-70%) rotate(45deg); pointer-events: none; border-radius: 1px;
}

.btn {
  margin-top: 18px; width: 100%; padding: 8px 10px 8px 26px; font: inherit; font-weight: 700; font-size: 17px;
  color: #fff; background: var(--grad-btn); border: 0; border-radius: var(--r-pill); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 14px;
  box-shadow: var(--shadow-2);
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease), filter 0.16s var(--ease);
}
.btn .arrow {
  display: grid; place-items: center; width: 38px; height: 38px; flex: none;
  border-radius: 50%; background: rgba(255, 255, 255, 0.18);
  transition: transform 0.2s var(--ease);
}
.btn .arrow svg { width: 19px; height: 19px; }
.btn:hover { filter: brightness(1.08); box-shadow: var(--shadow-3); transform: translateY(-1px); }
.btn:hover .arrow { transform: translateX(3px); }
.btn:active { transform: translateY(0); box-shadow: var(--shadow-1); }

.hint { font-size: 12.5px; color: var(--muted); margin: 11px 0 0; text-align: center; }

/* ---------- the wire: what's already scheduled ----------
   The corpus knows the future, so the homepage leads with it. Dates get a
   tear-off calendar block rather than a text column, and each row says how long
   you have — the number people actually want from a dated change. Renders
   nothing when there is none: no "no changes!" filler. */
.wire {
  position: relative; overflow: hidden;
  margin-top: var(--wn-rhythm); border-radius: var(--r-xl);
  background: var(--surface); border: 1px solid var(--hair);
  box-shadow: var(--shadow-1);
}
.wire::before { content: ""; position: absolute; inset: 0 0 auto; height: 4px; background: var(--grad-amber); }
.wirehead {
  display: flex; align-items: center; gap: 8px 16px; flex-wrap: wrap;
  padding: 20px 26px 16px; border-bottom: 1px solid var(--hair);
}
.wirelbl {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 750; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--amber-ink); background: var(--amber-pale);
  border-radius: var(--r-pill); padding: 5px 13px;
}
.wirelbl svg { width: 13px; height: 13px; stroke: currentColor; }
.wirehead h2 { font-size: 17.5px; margin: 0; flex: 1 1 auto; letter-spacing: -0.022em; }
.wiremore {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 14px; font-weight: 650; text-decoration: none; white-space: nowrap;
}
.wiremore svg { width: 15px; height: 15px; transition: transform 0.18s var(--ease); }
.wiremore:hover svg { transform: translateX(3px); }

.wirelist { list-style: none; margin: 0; padding: 0; }
.wireitem {
  display: flex; align-items: center; gap: 16px;
  padding: 14px 26px; border-top: 1px solid var(--hair);
  transition: background-color 0.16s var(--ease);
}
.wireitem:first-child { border-top: 0; }
.wireitem:hover { background: var(--surface-2); }

/* Tear-off calendar block: month plate over a tabular day numeral. */
.wcal {
  flex: none; width: 46px; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--hair); text-align: center; background: var(--surface);
}
.wmon {
  display: block; background: var(--navy); color: #fff;
  font-size: 10px; font-weight: 750; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 0 2px;
}
.wday { display: block; font-size: 19px; font-weight: 750; line-height: 1.35; font-variant-numeric: tabular-nums; }
.wcal.withyear .wday { line-height: 1.2; padding-top: 2px; }
.wyr {
  display: block; font-size: 9.5px; font-weight: 700; letter-spacing: 0.08em;
  color: var(--muted); padding-bottom: 4px; font-variant-numeric: tabular-nums;
}

.wbody { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; flex: 1 1 auto; min-width: 0; }
.wwhat { font-size: 15px; color: var(--ink2); }
.wwhat strong { color: var(--ink); font-weight: 650; }
/* Source and countdown ride together on the right of a strip row: the row
   states a price, so it shows where the price came from. */
.wmeta { display: inline-flex; align-items: baseline; gap: 14px; flex: none; }
.wmeta .srcref { font-size: 12px; }
/* Callouts state figures too — the attribution sits under the sentence. */
.callout .srcline { display: block; margin-top: 8px; font-size: 12px; opacity: 0.9; }
.wcount {
  flex: none; font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--amber-ink); background: var(--amber-pale);
  border-radius: var(--r-pill); padding: 4px 11px; white-space: nowrap;
}
@media (max-width: 700px) {
  .wirehead { padding: 16px 18px 14px; }
  .wireitem { padding: 14px 18px; gap: 13px; align-items: flex-start; }
  .wbody { flex-direction: column; align-items: flex-start; gap: 8px; }
  .wmeta { gap: 12px; }
}

/* ---------- changelog ---------- */
.changelog { list-style: none; margin: 0; padding: 0; }
/* Grid, not a wrapping flexbox. As a flex row the body's flex-basis was its
   content width, so any entry whose prose was wider than the space left over
   was pushed onto a second line — leaving the date block stranded alone on the
   first. Long entries then started at the card edge and short ones at the 62px
   rail, so no two rows in the list lined up. A grid track cannot do that: the
   date rail is 46px at every width and the body always sits beside it. */
.chrow {
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto;
  align-items: start; gap: 16px; padding: 16px 0; border-top: 1px solid var(--hair);
}
.chrow:first-child { border-top: 0; padding-top: 0; }
.chbody { min-width: 0; }
/* Narrow: the countdown drops under the prose it qualifies — into the body
   track, so it inherits that alignment rather than guessing at it in pixels. */
@media (max-width: 660px) {
  .chrow { grid-template-columns: 46px minmax(0, 1fr); row-gap: 10px; }
  .chrow .wcount { grid-column: 2; justify-self: start; }
}
.chscope {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 4px; font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
/* A payer's full legal name plus its PBM is long, and at 12px with this much
   tracking it pushed the kind pill onto a line of its own. Tighten the tracking
   before letting the pill break away — the label is set in caps, which already
   carries the separation the tracking was there for. */
@media (max-width: 520px) { .chscope { letter-spacing: 0.03em; gap: 8px; } }
.chwhat { margin: 0; font-size: 15.5px; color: var(--ink2); }
.chwhat strong { color: var(--ink); }
/* A scheduled row wears the amber of the band it sits under, on the date plate
   itself — so the block says "not yet" on its own, without reading the header.
   This rule spent its life as `.changelog.ahead .chdate`, which never matched:
   .chdate is the plan page's date, these rows render a .wcal. */
.changelog.ahead .wmon { background: var(--grad-amber); color: #2b1400; }
.empty { margin: 0; color: var(--muted); font-size: 15px; }

/* ---------- pillars ----------
   Not three cards in a row: that pattern is the most template-looking thing on
   the web, and it said nothing about this product. This is one warm band, split
   by hairlines rather than boxed, opened by the brand's own waveform going flat
   — the mark is a noise-cancellation curve, so the page may as well use it at a
   size where it reads. */
.pillars {
  position: relative; overflow: hidden;
  margin-top: var(--wn-rhythm); border-radius: var(--r-xl);
  background: linear-gradient(168deg, var(--cream) 0%, var(--cream-2) 100%);
  border: 1px solid var(--hair);
  padding: clamp(30px, 3.6vw, 46px) clamp(24px, 3vw, 40px) clamp(34px, 3.6vw, 48px);
}
.pillars::after {
  content: ""; position: absolute; right: -90px; bottom: -140px;
  width: 320px; height: 320px; border-radius: 50%;
  background: radial-gradient(circle, rgba(240, 169, 46, 0.16), transparent 65%);
  pointer-events: none;
}
.wavedivide { display: flex; align-items: center; margin-bottom: clamp(24px, 2.6vw, 34px); color: var(--amber); }
/* Rendered 1:1 with its viewBox. Scaled, the 2.4 stroke came out at 2.69px
   while the rule continuing it stayed 2.4px, and the thickness step read as a
   break in the line exactly at the SVG's right edge. */
.wavedivide svg { width: 150px; height: 32px; flex: none; }
.wavedivide::after {
  content: ""; flex: 1 1 auto; height: 2.4px; margin-left: -22px; border-radius: 0 2px 2px 0;
  background: linear-gradient(90deg, var(--amber) 0 24px, color-mix(in srgb, var(--amber) 8%, transparent));
}

.pillargrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; position: relative; z-index: 1; }
.pillar { padding: 0 clamp(14px, 2vw, 26px); border-left: 1px solid var(--hair-2); }
.pillar:first-child { border-left: 0; padding-left: 0; }
.pillar:last-child { padding-right: 0; }
@media (max-width: 800px) {
  .pillargrid { grid-template-columns: 1fr; }
  /* Padding on both sides of the rule: with padding-top only, the divider sat
     hard against the paragraph above it and floated well clear of the numeral
     below, which read as belonging to the wrong item. */
  .pillar { padding: 26px 0; border-left: 0; border-top: 1px solid var(--hair-2); }
  .pillar:first-child { padding-top: 0; border-top: 0; }
  .pillar:last-child { padding-bottom: 0; }
}
.pnum {
  display: block; font-size: 48px; line-height: 1; font-weight: 800;
  letter-spacing: -0.04em; font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--amber) 34%, transparent);
  margin-bottom: 12px;
}
.pillar h3 { margin: 0 0 10px; font-size: 19px; letter-spacing: -0.02em; }
.pillar h3 svg { width: 16px; height: 16px; stroke: var(--amber-ink); vertical-align: -2px; margin-right: 9px; }
.pillar p { margin: 0; font-size: 14.5px; color: var(--ink2); }

/* ---------- results: the headline number ---------- */
.best {
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--grad-slab); color: #fff;
  border: 0; border-radius: var(--r-xl);
  padding: clamp(24px, 2.6vw, 34px);
  margin: 0 0 18px;
  box-shadow: var(--shadow-3);
}
.best::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: radial-gradient(rgba(255, 255, 255, 0.18) 1.2px, transparent 1.2px);
  background-size: 24px 24px;
  -webkit-mask-image: linear-gradient(115deg, #000 0%, transparent 50%);
  mask-image: linear-gradient(115deg, #000 0%, transparent 50%);
}
.besttop { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.lbl {
  font-size: 12px; letter-spacing: 0.11em; text-transform: uppercase;
  color: rgba(216, 230, 250, 0.85); margin: 0; font-weight: 700;
}
.sticker {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--grad-amber); color: #2b1400;
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 5px 12px; border-radius: var(--r-pill);
}
/* Two columns so the block reads the same whether there is one stat (no budget
   entered) or three. A single stat stretched edge-to-edge looked broken. */
.bestgrid {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 20px 32px; align-items: center;
}
@media (max-width: 760px) { .bestgrid { grid-template-columns: 1fr; gap: 18px; } }
.bestmain { min-width: 0; }
.bigprice {
  margin: 0; font-size: var(--fs-price); font-weight: 760; letter-spacing: -0.045em; line-height: 1;
  font-variant-numeric: tabular-nums; color: #fff;
}
.per { font-size: 0.36em; font-weight: 600; color: rgba(214, 228, 250, 0.85); margin-left: 4px; letter-spacing: -0.01em; }
.bestname { margin: 10px 0 0; font-size: 19px; font-weight: 700; color: #cfe0f8; }
/* Holds a dark-ground verified pill, which carries its own link colours. */
.bestsrc { margin: 14px 0 0; font-size: 12.5px; color: rgba(203, 219, 243, 0.8); }

.beststats {
  margin: 0; padding: 4px 18px;
  background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--r-md);
}
.stat {
  display: flex; justify-content: space-between; align-items: baseline; gap: 18px;
  padding: 12px 0; border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.stat:first-child { border-top: 0; }
.stat dt { font-size: 11.5px; letter-spacing: 0.07em; text-transform: uppercase; color: rgba(206, 222, 246, 0.82); font-weight: 700; }
.stat dd { margin: 0; font-size: 19px; font-weight: 720; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: #fff; white-space: nowrap; }
.stat dd.good { color: #86e6a8; }
.stat dd.bad { color: #ffb0a8; }

/* Budget meter. CSP blocks inline styles, so the fill width arrives as a
   bucketed class the template picks (5% steps), not style="width:…". */
.meter { margin: 20px 0 0; }
.meter .track {
  height: 12px; border-radius: var(--r-pill); overflow: hidden;
  background: rgba(255, 255, 255, 0.14); border: 1px solid rgba(255, 255, 255, 0.16);
}
.meter .fill { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, #6fd995, #34b96d); }
.meter.over .fill { background: var(--grad-amber); }
.meter .cap { display: flex; justify-content: space-between; gap: 14px; margin: 9px 0 0; font-size: 13px; color: rgba(210, 226, 248, 0.82); }
.p0{width:0} .p5{width:5%} .p10{width:10%} .p15{width:15%} .p20{width:20%} .p25{width:25%}
.p30{width:30%} .p35{width:35%} .p40{width:40%} .p45{width:45%} .p50{width:50%} .p55{width:55%}
.p60{width:60%} .p65{width:65%} .p70{width:70%} .p75{width:75%} .p80{width:80%} .p85{width:85%}
.p90{width:90%} .p95{width:95%} .p100{width:100%}

.cardnote { margin: 18px 0 0; font-size: 14.5px; color: rgba(219, 231, 249, 0.9); border-top: 1px solid rgba(255, 255, 255, 0.16); padding-top: 16px; }
.cardnote strong { color: #ffd79a; }

/* ---------- callouts ---------- */
.callout {
  display: flex; gap: 14px; align-items: flex-start;
  border-radius: var(--r-md); padding: 15px 18px; margin: 14px 0;
  font-size: 15px; line-height: 1.55; border: 1px solid transparent; border-left-width: 5px;
}
.callout svg { width: 19px; height: 19px; flex: none; margin-top: 3px; }
.callout.info {
  background: var(--blue-pale); color: var(--blue-deep);
  border-color: color-mix(in srgb, var(--blue) 30%, transparent); border-left-color: var(--blue);
}
.callout.info svg { stroke: var(--blue-deep); }
.callout.warn {
  background: var(--amber-pale); color: var(--amber-ink);
  border-color: color-mix(in srgb, var(--amber) 40%, transparent); border-left-color: var(--amber);
}
.callout.warn svg { stroke: var(--amber-ink); }
.callout a { color: inherit; }

/* ---------- results: the route list ---------- */
.sechead {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  margin: 30px 0 0; padding: 14px 22px;
  background: var(--navy); color: #fff;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.sechead h2 { font-size: 13.5px; margin: 0; min-width: 0; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 700; }
.sechead .note { font-size: 13px; color: rgba(206, 222, 246, 0.82); margin: 0; }

.quotes {
  list-style: none; margin: 0 0 18px; padding: 0;
  background: var(--surface); border: 1px solid var(--hair); border-top: 0;
  border-radius: 0 0 var(--r-lg) var(--r-lg); overflow: hidden; box-shadow: var(--shadow-1);
}
.quote {
  position: relative; padding: 20px 24px 20px 26px; border-bottom: 1px solid var(--hair);
  transition: background-color 0.16s var(--ease);
}
.quote:last-child { border-bottom: 0; }
.quote::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; background: var(--hair-2); }
.quote:hover { background: var(--surface-2); }
.quote.k-manufacturer_dtc::before { background: linear-gradient(180deg, #1f63c8, #123e86); }
.quote.k-commercial_card::before { background: var(--grad-amber); }
.quote.k-government_program::before { background: linear-gradient(180deg, #0a7130, #34ab5e); }
.quote.k-discount_card::before { background: linear-gradient(180deg, #4b8ae6, #8fbcf7); }
.quote.ref { opacity: 0.84; }
.quote.ref::before, .quote.exhausted::before { background: var(--hair-2); }
.quote.exhausted { opacity: 0.74; }

.qtop { display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; }
.qhead { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; min-width: 0; }
.rank {
  flex: none; display: grid; place-items: center;
  width: 27px; height: 27px; border-radius: 9px;
  background: var(--plane-2); color: var(--ink2);
  font-size: 13px; font-weight: 750; font-variant-numeric: tabular-nums;
}
.quote:first-child .rank { background: var(--navy); color: #fff; }
.rank.off { background: none; border: 1.5px dashed var(--hair-2); color: var(--muted); font-weight: 500; }
/* Route-kind glyph: says whether a row is a manufacturer channel, a card, a
   government programme or a discount before the name is read. Muted, and it
   inherits the row's keyline colour so the two agree. */
.kindico { display: inline-grid; place-items: center; flex: none; }
.kindico svg { width: 17px; height: 17px; stroke: var(--muted); }
.quote.k-manufacturer_dtc .kindico svg { stroke: var(--blue-deep); }
.quote.k-commercial_card .kindico svg { stroke: var(--amber-ink); }
.quote.k-government_program .kindico svg { stroke: var(--green); }
.quote.k-discount_card .kindico svg { stroke: var(--blue); }
.quote h3 { font-size: 18px; margin: 0; letter-spacing: -0.02em; font-weight: 700; }
.qprice { font-size: 27px; font-weight: 750; letter-spacing: -0.03em; white-space: nowrap; font-variant-numeric: tabular-nums; }
.qprice .per { font-size: 13.5px; color: var(--muted); }
.lapsed {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 12px 0 0; font-size: 13.5px; color: var(--amber-ink); font-weight: 600;
  background: var(--amber-pale); border-radius: var(--r-sm); padding: 8px 12px;
}
.lapsed svg { width: 15px; height: 15px; flex: none; margin-top: 1px; stroke: currentColor; }
.conds { margin: 12px 0 0; padding-left: 0; list-style: none; color: var(--ink2); font-size: 14.5px; }
.conds li { margin: 5px 0; padding-left: 20px; position: relative; }
.conds li::before {
  content: ""; position: absolute; left: 5px; top: 0.62em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--hair-2);
}
/* Sources are the differentiator, so they read as a badge, not a footnote. */
/* Sources are the differentiator, so they stay visible — but as a link, not a
   chip. A background behind an underlined word reads as a dead button. The
   label is the domain, which says who published the claim before you click. */
.srcline { margin: 14px 0 0; font-size: 12.5px; color: var(--muted); }
.srcref {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--blue); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--blue) 35%, transparent);
}
.srcref svg { width: 12px; height: 12px; flex: none; opacity: 0.8; }
.srcref:hover { color: var(--blue-deep); border-bottom-color: currentColor; }

/* ---------- titration ramp projection ---------- */
.planner { border: 1px solid var(--hair); border-radius: var(--r-md); padding: 14px 16px; margin: 0 0 20px; background: var(--surface-2); }
.plannerhead { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
.plannerlbl { margin: 0; font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); }
.plannerreset { font-size: 12.5px; font-weight: 600; }
.steplist { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; margin: 0; padding: 0; }
.step {
  display: inline-flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--hair); border-radius: var(--r-pill);
  padding: 5px 6px 5px 14px;
}
.step.target { background: var(--amber-pale); border-color: color-mix(in srgb, var(--amber) 34%, transparent); padding-right: 14px; }
.stepdose { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stephold { font-size: 12.5px; font-weight: 650; color: var(--amber-ink); }
.stepctl { display: inline-flex; align-items: center; gap: 2px; }
.stepbtn {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  font-size: 16px; font-weight: 700; line-height: 1; text-decoration: none;
  color: var(--blue); background: var(--plane-2);
}
.stepbtn:hover { background: var(--navy); color: #fff; }
.stepbtn.off { color: var(--hair-2); background: none; }
.stepn { min-width: 22px; text-align: center; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
.plannernote { margin: 12px 0 0; font-size: 12.5px; color: var(--muted); max-width: 74ch; }
.nopr { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.rampsame { margin: 0; align-self: center; font-size: 14.5px; color: var(--ink2); max-width: 46ch; }
.rampfills { list-style: none; margin: 0 0 18px; padding: 0; border: 1px solid var(--hair); border-radius: var(--r-md); overflow: hidden; }
.rampfill {
  display: grid; grid-template-columns: 32px 62px 74px 1fr auto; gap: 12px; align-items: baseline;
  padding: 9px 14px; font-size: 14.5px; border-top: 1px solid var(--hair);
}
.rampfill:first-child { border-top: 0; }
.rampfill.up { background: var(--amber-pale); }
.rfn {
  display: grid; place-items: center; width: 22px; height: 22px; border-radius: 7px;
  background: var(--plane-2); color: var(--ink2); font-size: 11.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; align-self: center;
}
.rampfill.up .rfn { background: var(--amber); color: #2b1400; }
.rfdate { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.rfdose { font-weight: 700; font-variant-numeric: tabular-nums; }
.rfroute { color: var(--ink2); font-size: 13.5px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rfprice { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
@media (max-width: 620px) {
  .rampfill { grid-template-columns: 26px 1fr auto; gap: 8px; }
  .rfdate, .rfroute { grid-column: 2 / -1; }
}

.labelquote { margin-top: 16px; border-top: 1px solid var(--hair); padding-top: 14px; }
.labelquote summary { cursor: pointer; font-size: 14px; font-weight: 650; color: var(--ink2); }
.labelquote summary:hover { color: var(--ink); }
.labelquote p { font-size: 14.5px; color: var(--ink2); margin: 12px 0 0; max-width: 76ch; }

/* ---------- pills ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 700; padding: 4px 11px;
  border-radius: var(--r-pill); vertical-align: middle; letter-spacing: 0.02em;
  white-space: nowrap; text-transform: uppercase;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
/* A pill carrying its own glyph drops the dot — one marker is enough. */
.pill:has(svg)::before { display: none; }
.pill svg { width: 12px; height: 12px; flex: none; stroke: currentColor; }
.pill.green { background: var(--green-pale); color: var(--green); }
.pill.amber { background: var(--amber-pale); color: var(--amber-ink); }
.pill.red { background: var(--red-pale); color: var(--red); }
.pill.blue { background: var(--blue-pale); color: var(--blue-deep); }
.pill.gray { background: var(--plane-2); color: var(--ink2); }

/* ---------- related / plan list ---------- */
.related h3 { margin: 0 0 6px; font-size: 18px; }
.related p { margin: 6px 0; color: var(--ink2); font-size: 15px; }
.planlinks { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 14px; }
.planlinks a {
  text-decoration: none; font-size: 14px; font-weight: 600;
  padding: 8px 16px; border-radius: var(--r-pill);
  background: var(--plane-2); color: var(--ink2); white-space: nowrap;
  transition: background-color 0.16s var(--ease), color 0.16s var(--ease);
}
.planlinks a:hover { background: var(--navy); color: #fff; }

.planlist { display: grid; gap: 14px; margin-top: 4px; }
.planrow {
  display: flex; align-items: flex-start; gap: 18px;
  text-decoration: none; color: inherit; margin: 0; position: relative; overflow: hidden;
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease), border-color 0.18s var(--ease);
}
/* Initials in our own type and palette, not the payer's logo — see the
   monogram comment in server.go. Gives the list a visual anchor per row
   without implying anyone endorses us. */
.pmono {
  flex: none; display: grid; place-items: center; align-self: flex-start;
  width: 48px; height: 48px; border-radius: 15px;
  font-size: 17px; font-weight: 750; letter-spacing: -0.02em; color: #fff;
  background: var(--grad-btn);
}
.planrow:nth-child(3n+2) .pmono { background: var(--grad-amber); }
.planrow:nth-child(3n+3) .pmono { background: linear-gradient(140deg, #0a7130, #34ab5e); }
.planrow:hover { border-color: var(--hair-2); box-shadow: var(--shadow-2); transform: translateY(-2px); }
.planmain { min-width: 0; flex: 1 1 auto; }
.planrow h2 { margin: 0 0 4px; font-size: 20px; letter-spacing: -0.022em; }

/* The status row is the whole point of the list: it turns a paragraph into
   coverage you can scan, and it carries the drug names people actually search
   next to a payer's name. */
.statusrow { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 12px; padding: 0; }
.statuschip {
  display: inline-flex; align-items: baseline; gap: 8px;
  background: var(--surface-2); border: 1px solid var(--hair);
  border-radius: var(--r-pill); padding: 5px 13px; font-size: 13px;
}
.statuschip .scdrug { font-weight: 650; color: var(--ink); }
.statuschip .status { font-size: 12.5px; }

/* The .big variant is the plan page's "Coverage right now", and it is a
   different job from the strip on the index: it is the first thing on the page
   and the question it answers is "which of these am I covered for". As pills it
   could not answer that — drug names run 8 to 34 characters, so each status sat
   at whatever x its own name ended at, and five drugs put five statuses in five
   places across three ragged rows. Reading it meant zig-zagging.
   So: a list. Name left, status right, hairline between, two columns of it on a
   wide card. Statuses now land on two edges instead of five. */
.statusrow.big {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 44px; margin: 0 0 4px;
}
@media (max-width: 780px) { .statusrow.big { grid-template-columns: minmax(0, 1fr); } }
.statusrow.big .statuschip {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  padding: 11px 0; font-size: 15px;
  background: none; border: 0; border-top: 1px solid var(--hair); border-radius: 0;
}
/* No rule above the first row — one column of it when stacked, two when not. */
.statusrow.big .statuschip:nth-child(-n+2) { border-top: 0; }
@media (max-width: 780px) { .statusrow.big .statuschip:nth-child(2) { border-top: 1px solid var(--hair); } }
.statusrow.big .statuschip .scdrug { min-width: 0; }
.statusrow.big .statuschip .status { flex: none; font-size: 14px; }

/* Summary drops to a two-line lede — still fully in the DOM for indexing. */
.planrow .sum {
  margin: 0; font-size: 14.5px; color: var(--ink2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.planmeta { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.metabit { display: inline-flex; align-items: center; gap: 6px; }
.metabit svg { width: 14px; height: 14px; flex: none; stroke: var(--muted); opacity: 0.9; }
.metabit strong { color: var(--ink2); font-weight: 700; }

.glanceh { font-size: var(--fs-h2); margin: 0 0 14px; }
.lede { margin: 16px 0 0; font-size: 16px; color: var(--ink2); max-width: 76ch; }
.planrow .kind { margin: 0 0 14px; font-size: 12.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 650; }
.planrow .go {
  flex: none; align-self: center; display: grid; place-items: center; width: 40px; height: 40px;
  border-radius: 50%; background: var(--plane-2); color: var(--blue);
  transition: background-color 0.18s var(--ease), color 0.18s var(--ease), transform 0.18s var(--ease);
}
.planrow .go svg { width: 18px; height: 18px; }
.planrow:hover .go { background: var(--navy); color: #fff; transform: translateX(3px); }

/* ---------- coverage vocabulary legend ---------- */
.legendintro { margin: 0 0 18px; font-size: 15px; color: var(--ink2); max-width: 76ch; }
.legenddl { margin: 0; display: grid; gap: 0; }
.legendrow { display: grid; grid-template-columns: 168px 1fr; gap: 4px 20px; padding: 12px 0; border-top: 1px solid var(--hair); }
.legendrow:first-child { border-top: 0; padding-top: 0; }
@media (max-width: 660px) { .legendrow { grid-template-columns: 1fr; } }
.legenddl dt { margin: 0; }
.legenddl dd { margin: 0; font-size: 14.5px; color: var(--ink2); }
.legend { margin-top: 18px; border-top: 1px solid var(--hair); padding-top: 14px; }
.legend summary { cursor: pointer; font-size: 14px; font-weight: 650; color: var(--ink2); }
.legend summary:hover { color: var(--ink); }
.legend .legenddl { margin-top: 14px; }

/* ---------- tables ---------- */
.polwrap { overflow-x: auto; }
table.pol { width: 100%; border-collapse: collapse; font-size: 15px; min-width: 720px; }
table.pol th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); font-weight: 700; padding: 0 16px 9px 0; border-bottom: 2px solid var(--hair-2);
}
table.pol th:nth-child(1) { width: 25%; }
table.pol th:nth-child(2) { width: 15%; }
table.pol th:nth-child(4) { width: 11%; }
table.pol td { padding: 14px 16px 14px 0; border-bottom: 1px solid var(--hair); vertical-align: top; color: var(--ink2); }
table.pol td:last-child, table.pol th:last-child { padding-right: 0; }
table.pol tbody tr { transition: background-color 0.14s var(--ease); }
table.pol tbody tr:hover { background: var(--surface-2); }
table.pol tr:last-child td { border-bottom: 0; }
.nowrap { white-space: nowrap; color: var(--ink); font-weight: 650; }
.status { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; font-size: 14px; }
.status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.status.preferred { color: var(--green); }
.status.covered { color: var(--blue-deep); }
.status.non_preferred { color: var(--amber-ink); }
.status.excluded, .status.na, .status.exception_only { color: var(--red); }
.status.varies, .status.unknown { color: var(--muted); }
.eff { font-size: 12px; color: var(--muted); font-weight: 400; margin-top: 4px; }
.srclink { font-size: 13px; white-space: nowrap; }

/* Label and value live in a shared row so the rule between rows belongs to the
   row, which frees the two cells to align on their BASELINES. They are 11.5px
   and 15px, so matching their box tops — what a flat grid does — left the label
   sitting 4.7px high. */
dl.pa { display: grid; gap: 0; margin: 0; }
.parow {
  display: grid; grid-template-columns: 210px 1fr; gap: 4px 20px;
  align-items: baseline; padding: 13px 0; border-top: 1px solid var(--hair);
}
.parow:first-child { border-top: 0; }
@media (max-width: 660px) { .parow { grid-template-columns: 1fr; } }
dl.pa dt {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted);
  font-weight: 700; margin: 0;
}
/* Inline flow so a long label still wraps as text under its own glyph. */
dl.pa dt svg { width: 14px; height: 14px; stroke: currentColor; vertical-align: -2px; margin-right: 8px; opacity: 0.85; }
dl.pa dd { margin: 0; font-size: 15px; color: var(--ink2); }
/* First row of an appeal card is the number people came for. */
dl.pa.lead .parow:first-child dd { font-size: 21px; font-weight: 720; color: var(--ink); letter-spacing: -0.02em; }

/* ---------- change timeline ---------- */
.changes { list-style: none; margin: 0; padding: 0 0 0 26px; position: relative; }
.changes::before { content: ""; position: absolute; left: 5px; top: 10px; bottom: 14px; width: 2px; background: var(--hair-2); border-radius: 2px; }
.changes li { position: relative; padding: 0 0 20px; font-size: 15px; color: var(--ink2); }
.changes li::before {
  content: ""; position: absolute; left: -26px; top: 8px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--surface); border: 3px solid #e79a2c;
}
.changes li:last-child { padding-bottom: 0; }
.chdate {
  display: block; font-size: 12px; font-weight: 750; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 3px;
}

/* ---------- footer ---------- */
footer.site { border-top: 1px solid var(--hair); background: var(--surface); padding: 30px 0 46px; }
/* Same curve as the pillars band. Carrying it here is what makes it read as a
   signature rather than a decoration used once. */
.footwave { display: flex; align-items: center; color: var(--amber); margin-bottom: 26px; opacity: 0.75; }
.footwave svg { width: 150px; height: 32px; flex: none; }
.footwave::after {
  content: ""; flex: 1 1 auto; height: 2.4px; margin-left: -22px;
  background: linear-gradient(90deg, var(--amber) 0 24px, color-mix(in srgb, var(--amber) 6%, transparent));
}
.footbrand { display: inline-flex; align-items: center; gap: .32em; font-weight: 750; font-size: 18px; letter-spacing: -0.025em; margin: 0 0 10px; }
.footbrand .mark { height: 27px; width: auto; }
.foottag { margin: 0 0 20px; font-size: 16px; font-weight: 600; color: var(--ink2); letter-spacing: -0.015em; max-width: 46ch; }
.badges { display: flex; flex-wrap: wrap; gap: 9px; margin: 0 0 22px; padding: 0; list-style: none; }
.badges li {
  font-size: 12.5px; font-weight: 650; color: var(--ink2);
  border: 1px solid var(--hair-2); border-radius: var(--r-pill); padding: 6px 14px;
}
/* A URL quoted inside prose. The privacy page has to show a results address
   verbatim to make its point about what one discloses, and a proportional face
   makes a path read as a sentence rather than as a thing you could paste. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em; background: var(--surface-2); color: var(--ink2);
  padding: 2px 6px; border-radius: 6px; word-break: break-all;
}
.fineprint { font-size: 13px; color: var(--muted); line-height: 1.6; max-width: 86ch; margin: 0 0 13px; }
.fineprint:last-child { margin-bottom: 0; }

h2 { letter-spacing: -0.02em; }

/* On a phone the calculator is the point of the page, so the slab above it
   gets tighter rather than letting the form fall below two screens. */
@media (max-width: 620px) {
  main { padding-top: 18px; }
  .slab { padding: 22px 20px; border-radius: 20px; }
  .slab h1 { font-size: 1.95rem; margin-bottom: 10px; }
  .slab .sub { font-size: 15.5px; line-height: 1.5; }
  /* Keep the kicker to one line. Wrapped, it stops reading as a small label
     above the headline and becomes a slab of its own. */
  .slab .eyebrow { font-size: 10.5px; margin-bottom: 14px; letter-spacing: 0.045em; padding: 5px 11px; gap: 7px; }
  .slab .eyebrow .dot { width: 6px; height: 6px; }
  .trust { margin-top: 16px; gap: 7px; }
  .trust li { font-size: 12.5px; padding: 5px 12px 5px 10px; }
  .calc { padding: 20px; }
  .card { padding: 18px 18px; }
  .cardhead, .sechead { padding: 12px 18px; }
  .cardbody { padding: 16px 18px 20px; }
  .quote { padding: 16px 18px 16px 20px; }
  .best { padding: 22px 20px; }
  .beststats { padding: 2px 15px; }
}

/* ---------- coverage rows on a phone ----------
   The index strip is still pills, and at this width a drug name wrapped inside
   its own rounded shape gave every chip a different lumpy height. Same answer
   as the plan page, one breakpoint later: name left, status right, hairline
   between. (.big is already a list at every width — see .statusrow.big.) */
@media (max-width: 620px) {
  .statusrow { display: block; margin-bottom: 14px; }
  .statuschip {
    display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
    width: 100%; padding: 9px 0; font-size: 14px;
    background: none; border: 0; border-top: 1px solid var(--hair); border-radius: 0;
  }
  .statuschip:first-child { border-top: 0; padding-top: 0; }
  .statuschip .scdrug { min-width: 0; }
  .statuschip .status { flex: none; font-size: 13px; }

  /* Monogram and the go affordance share the top line; the body drops below,
     so the arrow no longer floats in the middle of the status list. */
  .planrow { flex-wrap: wrap; align-items: center; }
  .pmono { order: 1; }
  .planrow .go { order: 2; margin-left: auto; align-self: center; }
  .planmain { order: 3; flex: 1 1 100%; margin-top: 16px; }
  .planrow h2 { font-size: 19px; margin-bottom: 4px; }
  .planrow .kind { font-size: 11px; letter-spacing: 0.03em; margin-bottom: 12px; }
  .planrow .sum { -webkit-line-clamp: 3; }
  .planmeta { gap: 6px 14px; }
}

/* ---------- reveal system ----------
   Gated behind .js so nothing is hidden when scripts are off. One direction,
   once, then done moving — the brand rule is that motion settles. */
.js .rv {
  opacity: 0; transform: translateY(var(--wn-rise));
  transition: opacity var(--wn-t-reveal) var(--wn-ease), transform var(--wn-t-reveal) var(--wn-ease);
}
.js .rv.in { opacity: 1; transform: none; }
/* Named rvd, not d, because .d0-.d5 are the chart's dose colours. The two sets
   never met, but a stagger class one element away from repainting a bar is not
   a distinction worth keeping in one stylesheet. */
.js .rv.rvd1 { transition-delay: 70ms; }
.js .rv.rvd2 { transition-delay: 140ms; }

/* ---------- verified pill: the signature component ----------
   The brand promise rendered at 12px. Every figure wears one. */
.vpill {
  display: inline-flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 12px; font-weight: 650; letter-spacing: 0.01em;
  background: var(--wn-green-tint); color: var(--wn-green);
  padding: 5px 13px; border-radius: var(--wn-r-pill);
}
.vpill .dot { width: 7px; height: 7px; border-radius: var(--wn-r-pill); background: currentColor; flex: none; }
.vpill a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.vpill a:hover { color: var(--wn-green); }
.vpill.on-dark { background: rgba(255, 255, 255, 0.14); color: #CFE6D6; }
.vpill.on-dark a { color: #fff; }

/* ---------- trust bar ---------- */
.trustbar {
  display: flex; flex-wrap: wrap; gap: 12px 34px; align-items: center; justify-content: center;
  padding: 18px 24px; margin: var(--wn-rhythm) 0 0;
  background: var(--wn-surface); border: 1px solid var(--wn-line);
  border-radius: var(--wn-r-card); box-shadow: var(--wn-shadow-1);
}
.trustbar .t { display: flex; gap: 9px; align-items: center; font-size: 13.5px; font-weight: 600; color: var(--wn-ink-soft); }
.trustbar .tick {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  background: var(--wn-green-tint); color: var(--wn-green);
  display: grid; place-items: center;
}
.trustbar .tick svg { width: 12px; height: 12px; stroke: currentColor; }

/* ---------- tinted section bands ----------
   MEDVi alternates soft washes down the page rather than stacking white cards.
   Ours is the same idea kept to one wash, so a band still means something —
   green and blue variants were drawn and never used, and a variant nothing
   selects is not a system, it is a swatch. Add one back when a page needs it. */
.band {
  border-radius: var(--r-xl); padding: clamp(28px, 3.4vw, 44px);
  margin: var(--wn-rhythm) 0 0; border: 1px solid var(--wn-line);
}
.band.warm { background: var(--wn-surface-2); }

.bandgrid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: clamp(20px, 3vw, 34px); align-items: center; }
@media (max-width: 760px) { .bandgrid { grid-template-columns: 1fr; } }
.bandgrid .fig { margin: 0; }
.bandcopy h2 { margin: 0 0 14px; font-size: clamp(1.55rem, 1.2rem + 1.5vw, 2.3rem); letter-spacing: -0.025em; }
.bandcopy p { margin: 0 0 10px; font-size: 15.5px; color: var(--wn-ink-soft); max-width: 56ch; }
.bandcta { margin-top: 16px !important; }
.bandcta a { display: inline-flex; align-items: center; gap: 8px; font-weight: 650; text-decoration: none; }
.bandcta svg { width: 16px; height: 16px; transition: transform var(--wn-t-fast) var(--wn-ease); }
.bandcta a:hover svg { transform: translateX(3px); }

/* ---------- imagery ----------
   One image frame, used inside .bandgrid. The empty-slot placeholder that used
   to live here — a gradient with the photo brief printed on it — went out with
   the real files, and its rules went with it. */
.fig { position: relative; overflow: hidden; border-radius: var(--wn-r-card); border: 1px solid var(--wn-line); }
.fig img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ---------- motion & print ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* People print plan and appeal pages to take to an appointment. */
@media print {
  header.site, .btn, .trust, .badges, .slab::before, .slab::after { display: none; }
  body { background: #fff; color: #000; font-size: 11.5pt; }
  .slab, .best, .cardhead, .sechead { background: #fff !important; color: #000 !important; box-shadow: none; border: 1px solid #999; }
  .slab h1, .slab .sub, .cardhead h2, .sechead h2, .bigprice, .stat dd, .bestname, .lbl { color: #000 !important; }
  .card, .quotes { box-shadow: none; border: 1px solid #bbb; break-inside: avoid; page-break-inside: avoid; }
  a { color: #000; }
  /* A printed page is taken to an appointment, where the link is unclickable —
     so the URL has to be on the paper. Targets the source links we actually
     render; the old `.src a` matched nothing after those were renamed. */
  a.srcref::after, a.srclink::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all; }
}

/* ---- htmx ----------------------------------------------------------------
   There are no in-flight styles here, and that is two decisions, not an
   omission.

   htmx normally injects .htmx-indicator rules as an inline <style> at load,
   which style-src 'self' blocks; the injection is disabled via the htmx-config
   meta in the document head. We shipped a first-party copy of those rules to
   replace it — but nothing on the site sets hx-indicator or wears the class, so
   the copy sat here matching nothing. If you ever add hx-indicator, the styles
   for it are yours to write: htmx's own will not arrive.

   The whole-block dim was the other one, and it was wrong twice over: htmx puts
   htmx-request on the element that made the request, not on the target, so
   #ramp never matched it — and dimming the calculator while someone is reading
   it is the pulse this tool must not have. The readout animating in on arrival
   is the feedback. */

/* ============================================================================
   RAMP COST CALCULATOR
   A tool that opens in place on the results page. Two states, one element:
   the opener (.rampcta) and the open tool (.rampcard). htmx swaps between them,
   so both have to look finished on their own.
   ========================================================================== */

.ramptool { margin: 26px 0; }

/* ---- closed: the opener ---- */
/* Deliberately the heaviest element on the page after the price itself. It is
   competing with a visitor's instinct to stop reading once they have this
   month's number, and the whole point of the product is the months after it. */
.rampcta {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 20px;
  padding: 24px 26px;
  border-radius: var(--r-lg);
  background: var(--grad-slab);
  color: var(--wn-on-dark);
  text-decoration: none;
  box-shadow: var(--shadow-2);
  transition: transform var(--wn-t-base) var(--ease), box-shadow var(--wn-t-base) var(--ease);
}
.rampcta:hover { transform: translateY(-2px); box-shadow: var(--shadow-3); }
.rampcta:focus-visible { outline: 3px solid var(--amber); outline-offset: 3px; }

.rampcta-ico {
  display: grid; place-items: center;
  width: 52px; height: 52px; flex: none;
  border-radius: 14px;
  background: rgba(232, 155, 45, 0.16);
  border: 1px solid rgba(232, 155, 45, 0.34);
}
.rampcta-ico svg { width: 26px; height: 26px; color: var(--amber); }

.rampcta-body { display: block; min-width: 0; }
.rampcta-h {
  display: block;
  font-size: clamp(1.05rem, 0.98rem + 0.4vw, 1.3rem);
  font-weight: 720; letter-spacing: -0.02em;
}
.rampcta-p {
  display: block; margin-top: 6px;
  font-size: 14.5px; line-height: 1.5; color: var(--wn-on-dark-m);
  max-width: 62ch;
}
.rampcta-go { flex: none; }
.rampcta-btn {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 13px 20px; border-radius: var(--r-pill);
  background: var(--grad-amber); color: #24140A;
  font-size: 15px; font-weight: 720; white-space: nowrap;
}
.rampcta-btn svg { width: 17px; height: 17px; transition: transform var(--wn-t-fast) var(--ease); }
.rampcta:hover .rampcta-btn svg { transform: translateX(4px); }

@media (max-width: 760px) {
  .rampcta { grid-template-columns: auto 1fr; gap: 14px 16px; padding: 20px; }
  .rampcta-go { grid-column: 1 / -1; }
  .rampcta-btn { width: 100%; justify-content: center; }
}

/* ---- open: the tool ---- */
.rampcard {
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.ramphead {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 20px 24px;
  background: var(--grad-slab);
  color: var(--wn-on-dark);
}
.ramphead h2 { margin: 6px 0 0; font-size: var(--fs-h2); font-weight: 720; letter-spacing: -0.02em; color: #fff; }
.rampeyebrow {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--amber);
}
.rampeyebrow svg { width: 15px; height: 15px; }
.rampclose {
  flex: none; font-size: 13px; font-weight: 650; text-decoration: none;
  color: var(--wn-on-dark-m);
  padding: 7px 13px; border-radius: var(--r-pill);
  border: 1px solid var(--wn-line-dark);
  transition: color var(--wn-t-fast) var(--ease), border-color var(--wn-t-fast) var(--ease);
}
.rampclose:hover { color: #fff; border-color: rgba(255, 255, 255, 0.4); }

/* ---- the controls ---- */
.rampform { padding: 20px 24px; border-bottom: 1px solid var(--hair); background: var(--surface-2); }
.rampfields { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 14px; }
.rampfield { display: flex; flex-direction: column; gap: 7px; min-width: 0; flex: 1 1 170px; }
.rampflabel {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--muted);
}
.rampfield .selectwrap { width: 100%; }
/* The arrow reads the two dose fields as one journey rather than two settings.
   It sits on the baseline of the inputs, not the labels. */
.ramparrow { flex: none; padding-bottom: 13px; color: var(--muted); }
.ramparrow svg { width: 18px; height: 18px; }

/* Without JS the form needs a submit; with it, change fires the swap. */
.rampgo { margin-top: 14px; }
.js .rampgo { display: none; }
.rampnote { margin: 14px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); max-width: 78ch; }

@media (max-width: 620px) {
  .rampfields { gap: 12px; }
  .rampfield { flex: 1 1 100%; }
  .ramparrow { display: none; }
}

.rampbody { padding: 22px 24px 24px; }

/* ---- stat tiles ---- */
.rampstats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 12px;
  margin-bottom: 22px;
}
.rampstat {
  display: flex; flex-direction: column; gap: 4px;
  padding: 15px 16px;
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.rampstat.lead { background: var(--navy); border-color: var(--navy); color: #fff; }
.rampstat.lead .rslbl { color: var(--amber); }
.rampstat.lead .rsfoot { color: var(--wn-on-dark-m); }
.rslbl { font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); }
/* Proportional figures, deliberately. tabular-nums gives every digit the width
   of a zero, which makes a large standalone value look loose and gappy; it is
   for columns that must align vertically (the fills list, the axis ticks), not
   for a headline number. */
.rsbig { font-size: 30px; font-weight: 760; letter-spacing: -0.035em; }
.rsnum { font-size: 21px; font-weight: 730; letter-spacing: -0.025em; }
.rsfoot { font-size: 12px; color: var(--muted); line-height: 1.4; }
.rampstat.good { background: var(--green-pale); border-color: color-mix(in srgb, var(--green) 24%, transparent); }
.rampstat.good .rsnum { color: var(--green); }
.rampstat.bad { background: var(--amber-pale); border-color: color-mix(in srgb, var(--amber) 30%, transparent); }
.rampstat.bad .rsnum { color: var(--amber-ink); }

/* ---- the chart ----------------------------------------------------------
   Dose is ORDINAL, so it gets an ordinal ramp: one hue, light to dark, higher
   dose = darker. Not six unrelated hues — that would throw away the ordering
   the data has. The ramp is validated (monotone lightness, adjacent gaps, light
   end clears the surface, single hue) rather than picked by eye.

   Colour is keyed to the dose's slot in the drug's full ladder, never to its
   position in the current plan, so narrowing the range cannot repaint a dose.
   Identity is never colour-alone: the ramp above the chart names every dose. */
.rampchart { margin: 0 0 24px; }
.rampchart-cap {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px;
}

/* The dosage ramp — legend and story in one. Present at every step the plan
   passes through, including the ones whose price is identical to the last. */
.doseramp {
  display: flex; flex-wrap: wrap; gap: 6px;
  list-style: none; margin: 0 0 12px; padding: 0;
}
.dosekey {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px 5px 8px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  font-size: 12.5px; color: var(--ink2);
}
.dk-sw { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.dk-mg { font-weight: 700; color: var(--ink); }
.dk-n { color: var(--muted); font-variant-numeric: tabular-nums; }

/* The validated ordinal ramp. Do not hand-edit a step without re-running
   scripts/validate_palette.js --ordinal against the chart surface. */
.d0 .dk-sw, .rbar.d0 { background: #7FA9DE; fill: #7FA9DE; }
.d1 .dk-sw, .rbar.d1 { background: #5A8FCF; fill: #5A8FCF; }
.d2 .dk-sw, .rbar.d2 { background: #3B73BB; fill: #3B73BB; }
.d3 .dk-sw, .rbar.d3 { background: #2A5A9B; fill: #2A5A9B; }
.d4 .dk-sw, .rbar.d4 { background: #1D447A; fill: #1D447A; }
.d5 .dk-sw, .rbar.d5 { background: #122F55; fill: #122F55; }

/* height:auto against a viewBox keeps the aspect ratio, so the bars and the
   labels scale together and the type never squashes. */
.rampsvg {
  width: 100%; height: auto;
  border-radius: var(--r-lg);
  background: var(--surface-2);
}
.rgline { stroke: var(--hair); stroke-width: 1; }
.rglabel {
  font-size: 10px; font-weight: 600; letter-spacing: 0.03em;
  fill: var(--muted); font-variant-numeric: tabular-nums;
}
.rblabel { font-size: 10px; font-weight: 600; letter-spacing: 0.02em; fill: var(--muted); }
.rbpeak { font-size: 12.5px; font-weight: 750; fill: var(--ink); }

.rbar { transition: fill var(--wn-t-fast) var(--ease); }
.rbar.none { fill: var(--hair-2); }
.rbar:hover { fill: var(--amber-ink); }

/* Dated coverage and price changes landing inside the plan window. A rule, not
   a gridline: gridlines are recessive structure, this is an event, so it wears
   the status colour and carries a dot to catch the eye. Solid — a dashed line
   would read as "projected", and these dates are documented.

   Price marks only. A red variant for policy moves was written here, but the
   timeline never carries one: a policy row names its drug in prose rather than
   in a field, so scoping a mark to this drug would mean parsing text. The
   changelog carries those in full. The .k- class on each mark is the hook if
   that ever changes. */
.rmark line { stroke: var(--amber); stroke-width: 1.5; }
.rmark circle { fill: var(--amber); }

/* Colour never carries this alone: every mark is also listed underneath. */
.markkey { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 7px; }
.markkey li { display: flex; align-items: baseline; gap: 9px; font-size: 13px; color: var(--ink2); }
.mk-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--amber); transform: translateY(1px); }
.mk-when {
  flex: none; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums;
  min-width: 52px;
}
.mk-what { min-width: 0; }
.mk-note { font-size: 12.5px; color: var(--muted); padding-left: 17px; }

/* ---- the fills table, now folded away by default ---- */
.rampfilldetail { margin-bottom: 18px; }
.rampfilldetail summary {
  cursor: pointer; font-size: 13.5px; font-weight: 650; color: var(--ink2);
  padding: 10px 0;
}
.rampfilldetail summary:hover { color: var(--ink); }
.rampfilldetail .rampfills { margin-top: 12px; }

@media (prefers-reduced-motion: reduce) {
  .rampcta, .rampcta-btn svg { transition: none; }
  .rampcta:hover { transform: none; }
}

/* The form band is --surface-2, so its controls take the white surface instead
   or the field disappears into its own container. */
.rampform select { background: var(--surface); }

/* The chart holds its aspect ratio and scrolls sideways instead of squashing.
   At 375px a 760-unit viewBox would put the axis labels at about 4px, which is
   decoration rather than information. The page body never scrolls with it. */
.rampchart-scroll { overflow-x: auto; overscroll-behavior-x: contain; }
.rampsvg { min-width: 600px; }
@media (min-width: 761px) { .rampsvg { min-width: 0; } }
/* SVG font-size is in user units, so it scales with the viewBox. At the 600px
   mobile floor, 10 units renders at ~8px; 12 brings it back to ~9.5px. */
@media (max-width: 760px) { .rglabel, .rblabel { font-size: 12px; } }

/* ============================================================================
   RAMP CALCULATOR — MOTION

   No JavaScript of ours, and no animation state on the server. Two htmx
   mechanisms plus one structural decision do the whole job:

   1. `transition:true` on hx-swap runs each swap through the View Transitions
      API, so the opener morphs into the card instead of blinking. Free, and it
      degrades to an instant swap where the API is missing.
   2. Swapping the RIGHT element is what stops the entrance replaying. Opening
      and closing replace #ramp; a recalculation replaces only #rampout. The
      card is therefore inserted exactly once per opening, so a plain CSS
      animation on it fires exactly when it should — including on a direct load
      of a shared ?ramp=1 link, where no swap happens at all and htmx's own
      htmx-added / htmx-settling classes never exist.

   This is why there is no `entering` flag on the view model: an earlier version
   had one, and TestRampFragmentIsASubstringOfThePage rejected it for making the
   same URL render different bytes depending on how it was asked for.

   Brand rule throughout: one easing, one direction, once. Nothing bounces.
   Every keyframe starts from the hidden state and ends at the natural one, so
   if animations never run — reduced motion, an old engine — the content is
   simply there.
   ========================================================================== */

/* Scope the view transition to this block. Unnamed, the browser cross-fades a
   snapshot of the whole page, which reads as a flicker on everything the swap
   did not touch.

   The name is why ONLY the open/close swaps carry `transition:true`. A named
   element is captured whole: run a view transition while a recalculation is
   swapping #rampout, and the browser cross-fades the entire calculator —
   header, controls and all — because #ramp is the named ancestor. That reads as
   the whole box pulsing every time a select changes. Recalculations therefore
   swap plainly and let the readout's own arrival animation do the talking. */
#ramp { view-transition-name: ramp; }
::view-transition-old(ramp) { animation: rampVtOut 180ms var(--ease) both; }
::view-transition-new(ramp) { animation: rampVtIn 300ms var(--ease) 60ms both; }
@keyframes rampVtOut { to { opacity: 0; } }
@keyframes rampVtIn { from { opacity: 0; } }

@keyframes rampIn { from { opacity: 0; transform: translateY(var(--wn-rise)); } }
@keyframes rampPart { from { opacity: 0; transform: translateY(10px); } }
@keyframes rampBar { from { transform: scaleY(0); } }

/* Inserted once per opening. */
.rampcard { animation: rampIn var(--wn-t-reveal) var(--ease) both; }
.rampcard .rampform { animation: rampPart 420ms var(--ease) 90ms both; }

/* Inserted on opening AND on every recalculation — and in both cases what it
   means is "these numbers are new", which is exactly the feedback wanted. */
#rampout .rampstat { animation: rampPart 380ms var(--ease) both; }
#rampout .rampstat:nth-child(1) { animation-delay: 40ms; }
#rampout .rampstat:nth-child(2) { animation-delay: 80ms; }
#rampout .rampstat:nth-child(3) { animation-delay: 120ms; }
#rampout .rampstat:nth-child(4) { animation-delay: 160ms; }
#rampout .rampchart { animation: rampPart 400ms var(--ease) 120ms both; }
#rampout .planner { animation: rampPart 380ms var(--ease) 200ms both; }
#rampout .rampfilldetail { animation: rampPart 380ms var(--ease) 240ms both; }

/* The bars grow out of the axis rather than fading — the one moment in the tool
   that should read as a measurement being taken. transform-box is what makes
   transform-origin mean "the bottom of this rect" inside an SVG. */
#rampout .rbar {
  transform-box: fill-box;
  transform-origin: bottom;
  animation: rampBar 360ms var(--ease) both;
}
#rampout .rampsvg path:nth-of-type(1) { animation-delay: 170ms; }
#rampout .rampsvg path:nth-of-type(2) { animation-delay: 186ms; }
#rampout .rampsvg path:nth-of-type(3) { animation-delay: 202ms; }
#rampout .rampsvg path:nth-of-type(4) { animation-delay: 218ms; }
#rampout .rampsvg path:nth-of-type(5) { animation-delay: 234ms; }
#rampout .rampsvg path:nth-of-type(6) { animation-delay: 250ms; }
#rampout .rampsvg path:nth-of-type(7) { animation-delay: 266ms; }
#rampout .rampsvg path:nth-of-type(8) { animation-delay: 282ms; }
#rampout .rampsvg path:nth-of-type(9) { animation-delay: 298ms; }
#rampout .rampsvg path:nth-of-type(10) { animation-delay: 314ms; }
#rampout .rampsvg path:nth-of-type(11) { animation-delay: 330ms; }
#rampout .rampsvg path:nth-of-type(12) { animation-delay: 346ms; }
#rampout .rampsvg path:nth-of-type(13) { animation-delay: 362ms; }
#rampout .rampsvg path:nth-of-type(14) { animation-delay: 378ms; }
#rampout .rampsvg path:nth-of-type(15) { animation-delay: 394ms; }
#rampout .rampsvg path:nth-of-type(16) { animation-delay: 410ms; }
#rampout .rampsvg path:nth-of-type(17) { animation-delay: 426ms; }
#rampout .rampsvg path:nth-of-type(18) { animation-delay: 442ms; }
#rampout .rampsvg path:nth-of-type(19) { animation-delay: 458ms; }
#rampout .rampsvg path:nth-of-type(20) { animation-delay: 474ms; }
#rampout .rampsvg path:nth-of-type(21) { animation-delay: 490ms; }
#rampout .rampsvg path:nth-of-type(22) { animation-delay: 506ms; }
#rampout .rampsvg path:nth-of-type(23) { animation-delay: 522ms; }
#rampout .rampsvg path:nth-of-type(24) { animation-delay: 538ms; }
#rampout .rampsvg path:nth-of-type(25) { animation-delay: 554ms; }
#rampout .rampsvg path:nth-of-type(26) { animation-delay: 570ms; }

@media (prefers-reduced-motion: reduce) {
  .rampcard,
  .rampcard .rampform,
  #rampout .rampstat,
  #rampout .rampchart,
  #rampout .planner,
  #rampout .rampfilldetail,
  #rampout .rbar { animation: none; }
  ::view-transition-old(ramp),
  ::view-transition-new(ramp) { animation: none; }
}

/* The reset link appears the moment a hold goes above the label minimum. Left
   to itself it wraps onto a second line and pushes the +/- controls down mid
   press, so the slot is always occupied — by the link, or by a placeholder of
   identical size that is invisible to both eye and screen reader. */
.plannerreset.ghost { visibility: hidden; }
/* A gap in the corpus, rendered as words. Never a zero — a figure implies we
   looked it up and it was nothing, when in fact we could not source it. */
.rsnum.nofig { font-size: 15px; font-weight: 650; color: var(--muted); letter-spacing: 0; }
