/* Generated by `manage.py build_css_bundle` — do not edit directly.
   Source of truth is the individual files under css/tokens/ and css/components/,
   listed in apps/core/management/commands/build_css_bundle.py. Rebuild after any
   change to one of them, before deploying. */

/* --- css/tokens/fonts.css --- */
/* ==========================================================================
   Fonts — self-hosted, open-licensed, no CDN request on any page.

   **Replaced 2026-10-01, on the owner's instruction to adopt `docs/ui_ref_v5/`.** The pair this
   replaces was Instrument Serif for display and Host Grotesk for everything else. Both were
   correct choices for the palette they shipped with and neither survives the new reference pack,
   which sets the whole interface in one grotesk and keeps a serif for a single accent word.

   - **Manrope** (SIL OFL), weights 400/500/600/700/800. Body, navigation, data, controls, and —
     the change that carries the new look — headings at weight 800 rather than a serif. The pack
     asks for a 44-86px display line at weight 800 and 1.01-1.13 line-height, which no variable
     axis on the old grotesk could reach.
   - **Fraunces italic 500** (SIL OFL), for one accented word inside a heading and nothing else.
     Never body copy, never a table cell, never a second string on the same line.

   The pack ships these as TrueType: 768KB across six faces, on the render-blocking path of a
   money page whose LCP is the price table. They are subset to the Latin range the site actually
   renders and converted to WOFF2 here — 104KB for all six, against 50KB for the three faces they
   replace. The rise is the cost of five static weights where there used to be one variable face,
   and it is spent deliberately: the weight contrast between 400 body and 800 headings is most of
   what makes the new direction read.

   Only the two faces above the fold are preloaded (400 and 800). Fraunces is not preloaded at
   all: a page that sets no accent word never fetches 22KB for one it does not use.

   `font-display: swap` throughout, with a metric-adjacent fallback, so a font that fails to
   arrive never takes the price with it.

   Licences ship beside the files in `static/fonts/` — see `manrope-OFL.txt` and
   `fraunces-OFL.txt` in the reference pack for the full text.
   ========================================================================== */

/* The one range the site renders: Latin plus the punctuation, currency and arrows the tables and
   the change feed use. Subsetting to it is what makes five weights affordable. */
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../../fonts/manrope-400-latin.2fb860cc7421.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;
}

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../../fonts/manrope-500-latin.efa22ac9f935.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;
}

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../../fonts/manrope-600-latin.6d495f185417.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;
}

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../../fonts/manrope-700-latin.560f819a8f33.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;
}

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../../fonts/manrope-800-latin.a4099f4b59d3.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;
}

/* Italic only, because the accent word is the only thing it sets. There is no roman cut on the
   site and asking for one gets a synthesised slant rather than a missing glyph. */
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("../../fonts/fraunces-italic-latin.54f23fb9a2fc.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 {
  /* Arial rather than a system stack: it is the metric-nearest widely installed face to Manrope,
     so a swap moves the price table least. */
  --font-sans: "Manrope", Arial, "Helvetica Neue", Helvetica, sans-serif;
  --font-body: var(--font-sans);

  /* The accent face. Named for its job, not its family, because exactly one string per page may
     use it and a token called `--font-serif` invites a second.

     **Nothing uses it today** (2026-10-01). Two components asked for `--font-serif` when that
     meant Instrument Serif and the alias quietly pointed them at Fraunces, so the unpriced fold
     set its money figures in an italic display serif at a weight the file does not contain. Both
     were moved to the grotesk, which left this declared and unreferenced.

     Kept rather than deleted. It costs nothing until something asks for it — `font-display: swap`
     fetches nothing for a face no rule uses — and the reference pack has a real job for it: one
     accented word inside a page's headline, the way its own hero reads "A *clearer* price". Our
     headlines are editorial copy from the database, so using it means giving an editor a way to
     mark that word, which is a content decision and not a CSS one. Wire it the day that exists;
     do not reintroduce it by slanting a word chosen by code. */
  --font-editorial: "Fraunces", Georgia, "Times New Roman", serif;

  /* Retained so no component breaks while the migration runs. The old pair is gone; these point
     at what replaced them. */
  --font-display: var(--font-sans);
  --font-serif: var(--font-editorial);
}

/* --- css/tokens/colors.css --- */
/* ==========================================================================
   Colour — the v5 palette, and the migration layer that lets 39 component files keep working.

   **Replaced 2026-10-01**, on the owner's instruction: `docs/ui_ref_v5/` is the reference and the
   previous packs are not to be consulted. The green interface this replaces is gone outright, not
   adjusted. Every value below is the pack's own, and every contrast figure in a comment was
   measured against the named ground rather than estimated — the pack's `docs/CONTRAST.md` carries
   the same numbers for the pairs it specifies, and the dark-band ladder further down was derived
   here because the pack does not specify one.

   **Why there are two sets of names.** The real tokens come first and are named for what they
   are. Underneath them sits an alias block mapping the names the existing components already use.
   That block is a migration shim, not a design decision: it exists so the whole site adopts the
   new palette in one step instead of waiting for 39 component files to be rewritten, and it is
   meant to shrink to nothing as each component moves to the real names.

   The alternative was to keep the old names and change their values, which this project has
   already been burned by: a token called `--blue` held green for months while a design document
   described a blue site that no longer existed. A name that lies is worse than a name that is
   merely old, so `--blue` below is written down as an alias of `--plum` and says so.
   ========================================================================== */

:root {
  color-scheme: light;

  /* --- Brand -------------------------------------------------------------
     Five hues, not one accent plus neutrals. Plum carries action and emphasis; peach, sky and
     yellow are the brand's rhythm across surfaces and carry no judgement — a peach card is not a
     better provider than a sky one. The pack is explicit about that and it matters here, because
     this site is monetized and a colour that reads as a verdict is a colour that reads as an ad. */
  --ink: #362139;          /* primary text; also the one dark full-bleed band  14.42:1 on paper */
  --muted: #65566a;        /* supporting text, dates, captions                  6.68:1 on paper */
  --paper: #fffdf9;        /* the page ground */
  --white: #ffffff;        /* cards, table surfaces, the price receipt */
  --plum: #4b254d;         /* the single action colour: links, CTA, price result 12.37:1 */
  --plum-hover: #321835;   /* hover and press */
  --lavender: #e8e1f4;     /* table headers, quiet panels */
  --peach: #ffd9c4;        /* second brand surface */
  --sky: #dfedf5;          /* third brand surface, explanatory blocks */
  --yellow: #f4f0a3;       /* small accent, used sparingly */

  /* --- Edges -------------------------------------------------------------
     Two, and the difference is load-bearing. `--control` is the edge of something a reader can
     operate and clears 3:1 against every brand surface, so it may be the only signal that an
     input or a button is there. `--line` does not clear it and is decorative only — a divider
     drawn in it must never be the sole thing distinguishing a control from text. */
  --control: #87758a;      /* 4.18:1 on paper, 3.23:1 on peach — the lowest of its grounds */
  --line: #d8cdd9;         /* decorative rules only */
  --focus: #672e91;        /* keyboard focus, 2px at a 2px offset  8.70:1 on paper */

  /* --- Semantic ----------------------------------------------------------
     Three states with their own grounds, each pair measured by the pack. These say what a fact
     is, never how good a company is: a filed lawsuit renders in `--bad` because it is a serious
     record, not because the site has reached a verdict. */
  --good: #23543f;
  --good-bg: #e9f1e6;      /* 7.54:1 */
  --caution: #764814;
  --caution-bg: #fff0d5;   /* 6.90:1 */
  --bad: #8d283c;
  --bad-bg: #fce8ec;       /* 7.15:1 */

  /* --- On the dark band --------------------------------------------------
     The pack specifies one value here (`#dfd3e0`, 10.14:1) and the site needs a full ladder, so
     the rest were derived against `--ink` and measured rather than picked. Held in the brand's
     own hue family so the band reads as one surface instead of grey text dropped onto plum.
     The two border values are deliberately below 3:1: they are rules, not controls, and nothing
     on that band is operated by edge alone. */
  --text-on-ink: #e2d3e3;        /* 10.23:1 */
  --text-on-ink-2: #d9c8da;      /*  9.23:1 */
  --link-on-ink: #dcc4ee;        /*  9.19:1 */
  --text-on-ink-muted: #c0aec2;  /*  7.03:1 */
  --text-on-ink-dim: #a18aa3;    /*  4.66:1 — the floor for text, nothing smaller goes here */
  --border-on-ink: #554257;      /*  1.61:1 — decorative rule */
  --border-on-ink-2: #755a77;    /*  2.43:1 — decorative rule */
  /* **The focus ring needs a ground too** (2026-10-02, Codex review of the receipt phase).
     `--focus` is #672e91, which is 8.70:1 on paper and **1.66:1 on `--ink`** — a WCAG 1.4.11
     failure (3:1 for a focus indicator) on every keyboard stop inside the two dark surfaces this
     site has: the home page's key-numbers strip and the footer. Both scope `--focus-color` to
     this instead. It is the same value the links on those grounds already take on hover, so focus
     and hover agree rather than introducing a third colour. 9.19:1 on ink, 6.77:1 on the strip's
     own hover lift. */
  --focus-on-ink: #dcc4ee;       /*  9.19:1 */

  /* **The brand mark's own two colours** (2026-10-04, with the logo pack).
   *
   * Not `--ink` and `--lavender`, though the first is close: the pack's plum #2E1B35 differs
   * from `--ink` by 1.08:1, which is invisible, but it is the designer's value for a drawn mark
   * and is used as supplied rather than approximated. The second is genuinely a different
   * colour — the pack's #B3A6DC is a mid-tone ink, while `--lavender` #E8E1F4 is a pale card
   * surface. They are different roles, so this is a new pair rather than a reuse.
   *
   * Declared as tokens because the mark is inline SVG and has to invert on the ink footer:
   * plum-on-ink is 1.08:1 and would vanish. See `.dl-footer` in chrome.css. */
  --brand-mark-1: #2e1b35;
  --brand-mark-2: #b3a6dc;

  /* The light cut of `--good`, for the one place a verified fact sits on the dark band: the
     byline's "Prices hand-checked". Derived from `--good`'s own hue (154.3deg) rather than picked,
     because the value it replaces was a hardcoded `#9fe3b9` that predates this palette and
     belonged to no part of it.

     Worth saying what the swap did *not* find here: that hardcoded mint clears 9.88:1 on the new
     ink, so this was never a contrast fault. What it was is an off-palette colour surviving a
     change of palette, which is the thing a token file exists to prevent. */
  --good-on-ink: #a9dbc5;  /* 9.51:1 on ink */

  /* Status marks. Never the only signal — every one of these sits beside its own word. */
  --dot-active: #3e986b;         /* 4.12:1 on ink */
  --dot-degraded: #c0841b;       /* 4.57:1 on ink */
  --dot-dead: #e05265;           /* 3.88:1 on ink */
  /* #966c09, not #b8860b (2026-10-02). A star is a graphical object, so 1.4.11 holds it at 3:1
     against its own ground — and the card headers took brand surfaces in phase 5, where the old
     value measured 2.48:1 on lavender and 2.72:1 on peach. This clears 3.7 and 3.6 on those two,
     4.65 on paper, 3.95 on sky. Found by sweeping every rendered element against its computed
     background rather than against the pairs a token list happened to name. */
  --rating-star: #966c09;        /* a sourced user rating, not our verdict */

  /* --- Table rows --------------------------------------------------------
     The two tints a 39-row table needs to stay readable, both from the pack. They are the one
     place a near-invisible fill is the correct answer: a stripe a reader can *notice* is a stripe
     that competes with the data, and the row under the cursor needs to be found, not announced.

     The hover this replaces was `--gray-bg`, which the migration layer had just pointed at
     `--lavender` — a 1.25:1 fill where the pack asks for 1.10:1. Lavender is the header row's
     colour; using it for hover as well made the hovered row read as a second header.

     Body ink clears comfortably on both: ink 14.14:1 / 13.29:1, muted 6.55:1 / 6.16:1. */
  --row-stripe: #fdfafd;  /* 1.04:1 against white — rhythm, not a band */
  --row-hover: #f7f2fb;   /* 1.10:1 — findable under the cursor, silent otherwise */

  /* --- Surfaces, by role -------------------------------------------------
     Components ask for a job rather than a hue, so a surface can be retuned in one place. */
  --surface-page: var(--paper);
  --surface-elevated: var(--white);
  --surface-band: var(--lavender);
  --surface-tip: var(--sky);
  --surface-gray: var(--lavender);
  --surface-ink: var(--ink);
  --surface-brand: var(--plum);

  /* --- Text, by role ----------------------------------------------------- */
  --text-body: var(--ink);
  --text-muted: var(--muted);
  --text-inverse: var(--white);
  --text-brand: var(--plum);
  --verified-ink: var(--muted);

  /* --- Borders, by role -------------------------------------------------- */
  --border-hairline: var(--line);
  --border-input: var(--control);
  --border-control: var(--control);
  --border-focus: var(--focus);

  /* --- Status, by role --------------------------------------------------- */
  --status-active: var(--good);
  --status-degraded: var(--caution);
  --status-dead: var(--bad);

  /* ======================================================================
     MIGRATION LAYER — the names the current components use.

     Each line is a component file waiting to be rewritten against the real tokens above. Delete
     a line when nothing references it; the file is finished when this block is empty. Nothing
     new may be written against these names.
     ====================================================================== */

  --text: var(--ink);
  --text-2: var(--muted);
  --page-bg: var(--paper);

  /* `--blue` has not been blue since 2026-08. It is the action colour and it is now plum. */
  --blue: var(--plum);
  --blue-dark: var(--plum-hover);
  --green: var(--good);
  --navy: var(--ink);            /* the dark structural band */
  --accent2: var(--ink);
  --accent2-dark: var(--plum-hover);

  --band: var(--lavender);
  --tip: var(--sky);
  --gray-bg: var(--lavender);
  --border: var(--line);
  --border-2: var(--control);

  --amber: var(--caution);
  --amber-bg: var(--caution-bg);
  --red: var(--bad);
  --red-bg: var(--bad-bg);
  --red-border: #f0c2cc;         /* 1.26:1 on --bad-bg — a rule, not a control */
  --green-bg: var(--good-bg);
  --green-border: #c3d9bb;       /* 1.22:1 on --good-bg — a rule, not a control */

  --status-dot-active: var(--good);
  --status-dot-degraded: var(--caution);
  --status-dot-dead: var(--bad);
  --status-dot-active-on-navy: var(--dot-active);
  --status-dot-degraded-on-navy: var(--dot-degraded);
  --status-dot-dead-on-navy: var(--dot-dead);

  --text-on-navy: var(--text-on-ink);
  --text-on-navy-2: var(--text-on-ink-2);
  --text-on-navy-muted: var(--text-on-ink-muted);
  --text-on-navy-dim: var(--text-on-ink-dim);
  --link-on-navy: var(--link-on-ink);
  --border-on-navy: var(--border-on-ink);
  --border-on-navy-2: var(--border-on-ink-2);
  --priceband-ink-1: var(--text-on-ink);
  --priceband-ink-2: var(--text-on-ink-2);

  --surface-navy: var(--ink);

  /* The four tint pairs the stat icons use, one per brand ground.

     **They must stay four.** `key-numbers.css` gives each data category its own tint so a reader
     can tell a price fact from a coverage fact at a glance, and the first pass of this file sent
     both purple and neutral to `--lavender` — two categories rendering as one surface, which is
     information loss dressed as a palette tidy-up. Caught by the Codex review of this phase.
     Peach takes neutral; the palette has exactly four surfaces and there are exactly four tints.

     Each ink measured against its own ground: these are 16-20px glyphs, so 1.4.11 holds them at
     3:1 and all four clear it with room. */
  --tint-green-bg: var(--good-bg);
  --tint-green-ink: var(--good);        /*  7.54:1 */
  --tint-blue-bg: var(--sky);
  --tint-blue-ink: #2b5068;             /*  7.17:1 */
  --tint-purple-bg: var(--lavender);
  --tint-purple-ink: var(--plum);       /*  9.88:1 */
  --tint-neutral-bg: var(--peach);
  --tint-neutral-ink: var(--ink);       /* 11.14:1 */

  --blue-a10: rgb(75 37 77 / 10%);

  /* Two names `price-history.css` has always asked for and nothing has ever defined. An undefined
     `var()` makes the declaration invalid, and on an SVG that means `stroke` falls back to `none`
     — so the price-history chart has been drawing its line and its points in no colour at all.
     Found while auditing this file, fixed here because it is a missing token, not a missing
     chart. The component's own comment says the line stays neutral whichever way the price
     moved; these point at the neutral ink it meant. */
  --dl-ink: var(--ink);
  --border-muted: var(--control);
}

/* --- css/tokens/typography.css --- */
/* ==========================================================================
   Type tokens — one grotesk for the whole interface, one serif for one word.

   **Replaced 2026-10-01** for `docs/ui_ref_v5/`. The scale this replaces carried headings in
   Instrument Serif at weight 400 and leaned on size alone for hierarchy. The new reference does
   the opposite: Manrope at weight 800 carries every heading, and the contrast between 400 body
   and 800 heading is most of what makes the direction read. Fraunces italic survives for a single
   accented word inside a headline and has no other job on the site.

   The families themselves are declared in `fonts.css`. This file must not redefine `--font-sans`
   or `--font-display`: the previous version did, which meant the family a heading actually got
   depended on which of two token files loaded last.

   Scale, from the pack:
     display  44 -> 86 / 1.01-1.13 / 800   the one headline per page, ~43px at 360px
     H2       29 -> 43 / 1.13     / 800
     H3       22               / 800
     price    48 -> 75          / 800      the provider-page anchor figure
     card     38               / 800      the same figure inside a card
     lead     18 / 1.5
     body     16 / 1.6
     caption  14                           labels, and "Price checked ..." — a load-bearing line
     meta     13                           secondary metadata

   **The bottom of the scale moved up and that is the point.** Micro went 12 -> 13 and fine went
   11 -> 12, because the smallest text on this site is the verification date, the source link and
   the affiliate disclosure. The pack states the rule plainly: secondary metadata is 13px and is
   not to be used for hiding limitations. A site whose product is dated evidence cannot set the
   date in the smallest type it owns.

   Tabular figures stay narrowly applied — prices and dates only — so a column of numbers lines
   up. Manrope ships `tnum` as a real OpenType feature.
   ========================================================================== */

:root {
  /* --- Weight -----------------------------------------------------------
     The whole ladder shifted up. The old values topped out at 600 because a 400-weight serif set
     the headings and anything heavier beside it looked shouted. With one grotesk doing both jobs,
     800 is the heading weight and 400 is the reading weight. */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-black: 800;

  /* --- Steps ------------------------------------------------------------
     Named for the job. Components reference these rather than the raw sizes below them. */
  --step-hero: clamp(2.75rem, 5.5vw, 5.4rem);      /* 44 -> 86 */
  --step-price: clamp(3rem, 4.5vw, 4.6875rem);     /* 48 -> 75 — the provider-page figure */
  --step-section: clamp(1.8125rem, 2.6vw, 2.6875rem); /* 29 -> 43 — H2 */
  --step-metric: 2.375rem;  /* 38 — the same price inside a card, and other large numbers */
  --step-title: 1.375rem;   /* 22 — H3, provider names, panel titles */
  --step-lede: 1.125rem;    /* 18 — lead copy */
  --step-body: 1rem;        /* 16 */
  --step-small: 0.875rem;   /* 14 — labels, captions, the verification line */
  --step-micro: 0.8125rem;  /* 13 — secondary metadata. Never a limitation. */
  --step-fine: 0.75rem;     /* 12 — chips inside a table cell, and nothing else */

  /* --- Roles ------------------------------------------------------------ */
  --type-hero-size: var(--step-hero);
  --type-hero-lh: 1.01;
  --type-hero-ls: -0.03em;

  --type-h1-size: var(--step-hero);
  --type-h1-lh: 1.05;
  --type-h1-ls: -0.03em;

  /* A product H1 sits above dense data rather than opening a page, so it takes the H2 size. */
  --type-h1-product-size: var(--step-section);
  --type-h1-product-lh: 1.13;
  --type-h1-product-ls: -0.02em;

  --type-h2-size: var(--step-section);
  --type-h2-lh: 1.13;
  --type-h2-ls: -0.02em;

  /* 22px, up from 16px. An H3 used to be body copy in a heavier weight, which made a section head
     inside a data block indistinguishable from the block. */
  --type-h3-size: var(--step-title);
  --type-h3-lh: 1.25;
  --type-h3-ls: -0.01em;

  --type-body-size: var(--step-body);
  --type-body-lh: 1.6;
  --type-lede-size: var(--step-lede);
  --type-lede-lh: 1.5;

  --type-small-size: var(--step-small);
  --type-small-lh: 1.5;
  --type-micro-size: var(--step-micro);
  --type-micro-lh: 1.4;

  --type-cta-size: var(--step-body);
  --type-cta-ls: 0.01em;

  --type-money-size: var(--step-lede);
  --type-money-lg-size: var(--step-metric);

  --tabular: "tnum" 1;

  /* Three names components have always asked for and nothing ever defined, so the declarations
     using them were invalid and silently dropped. Resolved here rather than left to fall back:
     an undefined `var()` is not a default, it is a deleted rule.

     `--font-stretch-condensed` is a leftover from an Archivo pass that was deleted in 2026. No
     family on the site has a width axis now, and asking for a stretch a font does not have gets a
     synthesised one. It resolves to normal so the request is a no-op rather than a distortion. */
  --fs-small: var(--step-small);
  --type-source-sm-size: var(--step-micro);
  --font-stretch-condensed: 100%;
}

/* --- css/tokens/spacing.css --- */
/* ==========================================================================
   Spacing tokens — v2.

   `--page-max`/`--page-pad` are the mockups' own `.wrap{max-width:1128px;padding:0 24px}`,
   verbatim. `--section-gap` (46px) matches the homepage's `.sect{margin:46px 0}` — the
   gap between "Our pick", "Latest changes" and "Head-to-head comparisons". The master
   table still has to land in the first 1280x800 viewport (tools/cell_geometry.py checks
   it); that budget is met by the hero band's own height, not by starving section gaps
   further down the page, so this value is taken directly from the source file rather
   than cut to hit the budget the way the previous pass's 24px was.

   Table rows are not a fixed height in the mockups — `.sumtable td{padding:22px 14px}`
   with content-driven height (~120px in practice with a description line, rating and
   best-for line, price + sub + verified date, and a CTA + microcopy + review link all
   stacked in one cell). `--row-pad-v` captures that padding; there is no `--row-h`.
   ========================================================================== */

:root {
  --space-0: 0;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-14: 56px;
  --space-16: 64px;
  --space-20: 80px;

  --gutter: 24px;
  --section-gap: 46px;
  /* 1360, up from the mockups' 1128 (2026-08-18). The owner's approved reference is laid out
     at ~1440 with a 1360 column; at 1128 the provider table had no room for its verification
     column and the three ranked cards ran narrow. Every grid below is fluid, so nothing else
     needed to change with it. */
  /* **The pack's own `.wrap`**: `width: min(1280px, calc(100% - 80px))` (2026-10-02). Ours was
     1360 wide with 24px gutters — 80px wider with a third of the air, which is why our pages read
     as edge-to-edge against the reference's. The gutter steps down with the viewport exactly as
     the pack's does: 80 -> 48 -> 32 -> 28. */
  --page-max: 1280px;
  --page-pad: 40px;

  --row-pad-v: 22px;
  --row-pad-h: 14px;

  --control-h: 36px;
  --control-h-sm: 28px;
  --control-h-lg: 44px;

  /* Review page: ~250px sticky TOC + 44px gap + fluid content column. */
  --review-aside-w: 250px;
  --review-gap: 44px;
}

/* --- css/tokens/shape.css --- */
/* ==========================================================================
   Shape tokens — v4.

   Three passes fought over radius (a soft 6px everywhere, then a 2px cap on every container
   including badges, then v3's 4px controls) and the first two read wrong in opposite
   directions: generic-slop soft, then unfinished tax form. v4 settles it with a three-step
   scale of its own (`docs/ui_ref_v4/tokens/spacing.css`), and the values below are that scale
   verbatim rather than a rule re-derived from screenshots:

     controls (buttons, inputs, selects,
       segmented control, flag chips)         radius 6px  (--radius-sm in v4)
     mid surfaces                             radius 8px  (--radius-md)
     panels, the comparison frame             radius 10px (--radius-lg)
     the table itself, section rules          radius 0 — a table is not a card

   v4 caps the scale at 10px, so the 12px `--radius-card` from the depth pass is folded back
   into it: two "panel" radii one pixel apart is a distinction no reader perceives and every
   future component has to guess at.

   Explicitly not adopted: pill-shaped CTAs. They were on the shortlist for this pass and v4's
   own button contradicts them — 40px tall, 6px radius, solid green. A pill on a money page
   reads as a consumer app's sign-up, and this site's action is "read our price check", not
   "start now".

   Separation is mostly done with 1px/2px rules, exactly as before, but the mockups do
   use two soft, small drop shadows (the stat chip, the calculator card) — both call
   them out explicitly in their own <style> blocks, so "no shadows anywhere" is revised:
   shadows are allowed in exactly those two spots, at the mockup's own low opacity, and
   nowhere else (never on a button, a table row or a hover state).
   ========================================================================== */

:root {
  --radius: 6px; /* controls: buttons, inputs, selects, flag chips */
  --radius-md: 8px; /* stat chips */

  /* The site header's own height, measured at 62px on 2026-10-01. It is content-driven, so this
     is a record of a measurement rather than a declaration — nothing sets the header to it. One
     thing depends on it: the master table's sticky column headers offset by this much, so they
     come to rest below the site header instead of underneath it. If the header's contents change
     height, re-measure and update here; the failure mode is a row of column headers hidden behind
     the nav, which is visible immediately. */
  /* **Measured, not remembered** (2026-10-06). The note above says "if the header's contents
     change height, re-measure and update here" and names the failure mode exactly: a row of
     column headers hidden behind the nav. It changed and nobody re-measured. The header renders
     89px on desktop against this 62px, so the master table's sticky `thead` came to rest 27px
     under the nav and its labels were cut in half at every width above 899px.

     Two values because the header has two shapes: `.dl-header__inner` wraps at
     `max-width: 899px` (chrome.css) and the bar grows to 135px. Measured at 1440, 1280, 1024,
     900, 880, 760 and 390. */
  --header-h: 89px;
  --radius-lg: 10px; /* tipbox, cards, TOC, quick-facts, updates list */
  --radius-xl: 10px; /* calculator card — v4's scale stops at 10px */

  --border-width: 1px;

  --rule-heavy: 2px solid var(--text); /* table header bottom rule; verdict box border */
  --rule-row: 1px solid var(--border); /* between table rows, list items */
  --rule-hairline: 1px solid var(--border); /* non-structural dividers */

  --icon-stroke: 1.5px;
  --icon-size-sm: 14px;
  --icon-size: 16px;
  --icon-size-lg: 20px;

  /* The two named exceptions to "no shadows" — both from the mockups' own CSS. */
  --shadow-chip: 0 1px 2px rgba(23, 43, 77, 0.08);
  --shadow-card: 0 1px 3px rgba(23, 43, 77, 0.1);
  --shadow-flat: none;

  /* 2026-08: the owner-directed depth pass (docs/ai/depth_and_second_accent_2026-08-04.md)
     lifts "no shadows anywhere" for card-like surfaces sitting on the new --page-bg — a
     two-layer shadow, soft and low-opacity like the two the mockups already carried, not the
     lift/scale/glow treatment the anti-list forbids (nothing here moves on hover). Per the
     owner's own measurement of the reference build's actual component code, elevation there is
     used "sparingly, not everywhere" (two panels out of many carry it) — so `--shadow-elevated`
     is reserved for the score panel and the metric-tile grid, the two most number-forward new
     surfaces, and every other new/updated card in this pass (quick-facts strip, provider-pick
     card, the comparison "at a glance" cards, the record panel) takes the plain bordered-card
     treatment below instead, with no shadow. */
  --shadow-elevated: 0 8px 24px rgba(23, 43, 77, 0.1), 0 2px 6px rgba(23, 43, 77, 0.06);

  /* `--radius-card` was 12px, measured off the earlier reference build's `rounded-xl`. v4 tops
     its scale out at 10px, so this now aliases `--radius-lg` rather than sitting two pixels
     above it. The token stays because ~20 call sites read it and "the panel radius" is a useful
     name; what it must not be any more is a fourth step nobody can see. */
  --radius-card: var(--radius-lg);

  --focus-width: 2px;
  --focus-offset: 2px;
  --focus-color: var(--blue);
}

/* The header's wrapped shape, same breakpoint as `.dl-header__inner`'s own. Scoped to the token
   so the one rule that consumes it needs no media query of its own. */
@media (max-width: 899px) {
  :root {
    --header-h: 135px;
  }
}

/* --- css/tokens/motion.css --- */
/* ==========================================================================
   Motion tokens.

   120ms colour/border transitions on focus, hover and sort-state only, per the spec —
   tightened from the discarded draft's 150ms. No entrance animation, no counters, no
   carousels, no ticker: there is no continuous motion anywhere on this site any more.
   ========================================================================== */

:root {
  --ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --dur-fast: 80ms;
  --dur: 120ms;
  --dur-slow: 120ms;
  --transition-control: background-color var(--dur) var(--ease-standard),
    border-color var(--dur) var(--ease-standard), color var(--dur) var(--ease-standard);
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0ms;
    --dur: 0ms;
    --dur-slow: 0ms;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- css/tokens/base.css --- */
/* ==========================================================================
   Element defaults — v4, Instrument Serif (display) + Host Grotesk (text).

   White is the only page surface. `color-scheme: light` so browsers don't auto-invert
   native controls (selects, checkboxes) into a dark treatment on a white page.

   `.dl-label` and `.dl-value` are the two type primitives most components lean on:
   `.dl-label` is the small semibold treatment (section eyebrows, table headers,
   filter labels), `.dl-value` is a plain numeral with tabular figures switched on for
   any number that appears outside a table cell or price component of its own (a count,
   an inline date). Neither is monospace — this system has no monospace family.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  color-scheme: light;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Anchor targets — the review page's sidebar table of contents jumps to these, and a
   sticky header must not cover the target heading. */
:target,
[id] {
  scroll-margin-top: var(--space-6);
}

/* No layout may scroll the page sideways. Wide content (the master table) scrolls
   inside its own container instead; this is the backstop.

   `clip`, not `hidden`: `overflow-x: hidden` makes the body a scroll container, which
   breaks `position: sticky` on the review page's sidebar TOC. `clip` cuts the overflow
   without creating a scrollport, so the sidebar keeps sticking. */
body {
  overflow-x: clip;
}

body {
  margin: 0;
  /* 2026-08: warm page surface, not --surface-page (plain white) — see
     tokens/colors.css's header comment. The header (chrome.css) and footer (chrome.css) both
     set their own explicit white/navy background regardless, so this only shows through the
     gaps between them and the page's own card-like surfaces. */
  background: var(--page-bg);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  font-weight: var(--weight-regular);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-underline-offset: 0.18em;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-sans);
  font-weight: var(--weight-black);
  margin: 0;
  color: var(--text-body);
  text-wrap: balance;
}

/* Every heading is the grotesk at 800 (2026-10-01, v5).
 *
 * The rule this replaces split H1/H2 into a serif at weight 400 and left H3 in the text face,
 * drawing the line at size rather than level: a serif earned its place at 34px and added nothing
 * but a second family at 16px. That reasoning was sound for the face it described and does not
 * survive it. There is one family now, so there is no line to draw, and the hierarchy is carried
 * by weight instead — 800 against 400 body, which is the contrast the new reference is built on.
 *
 * `font-synthesis-weight: none` stays, for the opposite reason it was added. It used to stop a
 * browser faking a bold out of a single-weight serif; now it guarantees that if the 800 cut fails
 * to load, the heading falls back to a real weight rather than a smeared one. */
h1,
h2,
h3 {
  font-family: var(--font-sans);
  font-weight: var(--weight-black);
  font-synthesis-weight: none;
}

/* ...and the exceptions, kept here in one list rather than scattered across the components
 * that cause them.
 *
 * The rule above keys off the heading *level*, but the reason for it is the heading *size* —
 * and four components legitimately set an h2 far below the display face's floor: a section
 * eyebrow at 13px, the marks-panel label at 14px, the tipbox's heading at 16px. Rendered in a
 * high-contrast serif those read as a different site's furniture dropped into ours, which is
 * exactly what a screenshot of /providers/ro/ showed ("What stands out", 14px, serif).
 *
 * CSS cannot ask "is this heading small?", so the list is manual — and `tools/type_scale.py`
 * exists to catch the next component that forgets. Its old test was "no Instrument Serif under
 * 20px", which no longer means anything: one family sets everything now. The surviving reason for
 * this list is weight, not family — a 13px eyebrow at 800 is a shout, so these opt back down.
 *
 * And until the Codex review of this phase they did not. The block kept `--weight-black` while
 * the comment above it claimed the opposite, which was harmless when black meant 600 and became a
 * defect the moment it meant 800: three small headings were set two steps heavier than anything
 * around them. A comment that describes the opposite of its own rule is worse than no comment,
 * because it is what the next reader checks instead of the value. */
.dl-section-head__label,
.dl-marks-panel__label,
.dl-tipbox__text h2,
/* The three method-strip headings at the foot of every page ("How we rate providers", "No
 * clinical review", "How we make money"). 16px, and inheriting 800 made each one shout from the
 * quietest part of the page — found by `tools/type_scale.py` on the day that tool was rewritten to
 * measure weight instead of family, which is the first thing it caught. */
.dl-method__grid h3 {
  font-family: var(--font-sans);
  font-weight: var(--weight-semibold);
}

h1 {
  font-size: var(--type-h1-size);
  line-height: var(--type-h1-lh);
  letter-spacing: var(--type-h1-ls);
}

h2 {
  font-size: var(--type-h2-size);
  line-height: var(--type-h2-lh);
  letter-spacing: var(--type-h2-ls);
}

h3 {
  font-size: var(--type-h3-size);
  line-height: var(--type-h3-lh);
  letter-spacing: var(--type-h3-ls);
}

/* `<b>` and `<strong>` default to 700 in every browser, and 700 is not a weight this system
   has — tokens/typography.css defines 400/500/600 and says bold is not used. Thirteen strings
   on the home page were rendering at 700 for this reason alone, which `tools/type_inventory.py`
   found and no stylesheet grep could: the weight was never written down anywhere.
   500 is the system's emphasis weight; the semantics of the element are untouched. */
b,
strong {
  font-weight: var(--weight-medium);
}

p {
  margin: 0 0 var(--space-4);
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--blue);
  text-decoration: none;
  /* 4px, the pack's own. Ours was the browser default, which on Manrope's descenders put the rule
     through the tails of g, p and y in every link in running prose. */
  text-underline-offset: 4px;
}

a:hover {
  text-decoration: underline;
}

/* Visible focus on every interactive element. Never `outline: none`. */
:focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

table {
  border-collapse: collapse;
}

ul,
ol {
  margin: 0;
  padding-left: 1.25em;
}

hr {
  border: 0;
  border-top: var(--rule-hairline);
  margin: var(--space-6) 0;
}

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

sup {
  font-size: 0.62em;
  line-height: 1;
}

::selection {
  background: var(--band);
}

/* --- the two type primitives most components lean on ---------------------- */

/* Section eyebrows, table headers, filter labels: 13px, semibold, sentence case.
 *
 * Uppercase until 2026-08-14, and it was the last of it on the site — v4 bans caps, the pass
 * that removed them from fourteen components missed this primitive and `.dl-section-head__label`
 * (layout.css), and both were still shouting under a system that had otherwise stopped. */
.dl-label {
  font-family: var(--font-sans);
  font-size: var(--type-small-size);
  line-height: 1.3;
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-label--muted {
  color: var(--text-muted);
}

/* Source / date microcopy, 12-13.5px. Values only — not a monospace face. */
.dl-meta {
  font-family: var(--font-sans);
  font-size: var(--type-small-size);
  line-height: 1.5;
  color: var(--text-muted);
}

.dl-meta--sm {
  font-size: var(--type-micro-size);
}

/* Inline numeric values at reading size: counts, inline dates. */
.dl-value {
  font-family: var(--font-sans);
  font-variant-numeric: var(--tabular);
  color: var(--text-body);
}

.dl-value--muted {
  color: var(--text-muted);
}

.dl-rule {
  border-top: var(--rule-heavy);
}

/* Screen-reader-only, but focusable when it needs to be (the skip link). */
.dl-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.dl-skip {
  position: absolute;
  left: var(--space-4);
  top: var(--space-2);
  z-index: 60;
  transform: translateY(-200%);
  padding: var(--space-2) var(--space-4);
  background: var(--navy);
  color: var(--text-inverse);
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  text-decoration: none;
  transition: transform var(--dur) var(--ease-standard);
}

.dl-skip:focus {
  transform: translateY(0);
  color: var(--text-inverse);
}

/* The inlined icon sprite must not occupy layout. */
.dl-sprite {
  display: none;
}

/* --- css/layout.css --- */
/* ==========================================================================
   The page wrap, the navy hero band, and the responsive behaviour the source brief
   left for this port to decide (reasoning in docs/ai/frontend_responsive_decisions.md).

   Structure per docs/ui_refs_v3/glp1-index-v2.html: a single centred `.dl-page` wrap
   (max 1128px, 24px padding) — not the previous pass's 12-column ledger grid with an
   annotation strip. That grid is kept only as a plain block-flow compatibility shim
   (`.dl-grid`/`.dl-grid__wide`/`.dl-grid__main`/`.dl-grid__aside` below) for the page
   types this pass did not rebuild (comparison, coverage, state, info pages) so they
   keep rendering on the new tokens without a markup change; it no longer draws an
   asymmetric margin or an annotation column anywhere.

   The homepage alone gets a full-bleed navy band behind its masthead and H1, reversed
   out in white — scoped to the homepage the same way the mockups only band a "best X"
   category page, never a single provider's own review.

   Responsive tiers:
     >= 1140px   full two-column layouts (review: 250px TOC + fluid content)
     900-1139px  review's TOC narrows slightly; footer/verdict grids go 2-up
     < 900px     one column everywhere; sticky TOC becomes a static block that moves
                 above the content (`order: -1`); the master table remains a real
                 `<table>` that scrolls horizontally inside its own wrapper — see
                 data-table.css's mobile rules. Nothing here turns the table into a
                 stack of cards.
   ========================================================================== */

/* --- page container ------------------------------------------------------- */

/* **The gutter comes off the viewport, not out of the content** (2026-10-02).
 *
 * This was `max-width: var(--page-max)` plus `padding-inline: var(--page-pad)`, written in the
 * same pass that recorded the pack's rule in `tokens/spacing.css` as `width: min(1280px,
 * calc(100% - 80px))`. With `box-sizing: border-box` those are not the same rule: the max-width
 * caps the *border* box, so the 40px gutters were taken out of the 1280 and every page on this
 * site rendered 1200px of content against the reference's 1280.
 *
 * The 80px landed on the three leading cards, which is where the owner saw it: 387px each against
 * the pack's 416, so a card of the same content stood 1:1.87 where the pack's stands 1:1.58, and
 * the word for that is stretched. The table, the hero and every grid on the site were narrowed by
 * the same amount.
 *
 * Below 1360px viewport the two forms are identical — the gutter wins in both — so this changes
 * nothing at the widths the geometry tools budget against, and everything above them. */
.dl-page {
  width: min(var(--page-max), 100% - var(--page-pad) * 2);
  margin-inline: auto;
}

/* The gutter steps the pack declares for `.wrap`, as its own breakpoints. Written here rather than
 * on the token so every container moves together. */
@media (max-width: 1023px) {
  :root {
    --page-pad: 24px;
  }
}

@media (max-width: 599px) {
  :root {
    --page-pad: 16px;
  }
}

@media (max-width: 400px) {
  :root {
    --page-pad: 14px;
  }
}

.dl-page--bleed {
  width: auto;
  max-width: none;
  padding-inline: 0;
}

/* --- compatibility shim for the 12-column grid on not-yet-rebuilt pages ----- */

.dl-grid {
  display: block;
}

.dl-grid__main,
.dl-grid__aside,
.dl-grid__wide {
  width: 100%;
  min-width: 0;
}

.dl-grid__aside {
  margin-top: var(--space-6);
}

.dl-annotation {
  border-left: var(--rule-hairline);
  padding-left: var(--space-3);
  margin-top: var(--space-4);
}

/* --- the homepage hero band -------------------------------------------------- */

/* Full-bleed navy band behind the masthead and the H1, reversed out in white.
   Homepage only: the provider review, provider index, state pages and info pages keep
   the plain titleblock below — the same split the mockups themselves draw between a
   "best X" category page (banded) and a single provider's own page (not).

   A single provider's *page* still gets no title band — that split holds. Its *price*
   does, since 2026-08-25: `.dl-priceband` (editorial.css) is a second, narrower `--navy`
   surface, not a reuse of this one, built for the fold's price band rather than the
   masthead — see docs/ai/review_price_composition_codex_2026-08-25.md. */
/* **Light, not dark** (2026-10-02). The reference pack's hero is a lavender-to-peach gradient
 * with ink text on it; ours was a dark plum band with white text, which is the previous pack's
 * composition wearing the new palette. The owner put the two screenshots side by side, and the
 * difference is the first thing on every page.
 *
 * `115deg` from `--lavender` to the pack's own `#f9e3da`, which is its provider-hero value. The
 * home hero takes a richer version of the same idea in home-hero.css. */
.dl-hero {
  background: linear-gradient(115deg, var(--lavender), #f9e3da);
  /* Bottom padding trimmed from the mockup's own 30px to 22px — pure band chrome, not
     the H1, byline or lede it wraps — as part of closing the home page's "table in the
     first 1280x800 viewport" gap alongside the merged trust strip (see
     docs/ai/mobile_table_cards_and_trust_strip_2026-08-02.md). Nothing inside the band
     shrinks or loses contrast. */
  padding: 20px 0 22px;
}

/* **Everything written for white-on-dark, resolved for light-on-light** (2026-10-02).
 *
 * Fifty-two rules across six files set a colour for text sitting on this band, and they were all
 * correct for a band that was dark. Rather than edit fifty-two rules, the band redefines the
 * tokens they ask for: a custom property is scoped to its subtree, so every nested rule resolves
 * to the light-band value without being touched, and the footer and the statistics strip — which
 * are genuinely dark — keep the originals.
 *
 * The names still say "on-navy". They are wrong now and they are the migration layer's problem,
 * not this one's: renaming them means touching the same fifty-two rules this block exists to
 * avoid touching today. */
.dl-hero,
.dl-priceband {
  --text-on-navy: var(--ink);
  --text-on-navy-2: var(--ink);
  --text-on-navy-muted: var(--muted);
  --text-on-navy-dim: var(--muted);
  --link-on-navy: var(--plum);
  --border-on-navy: var(--line);
  --border-on-navy-2: var(--control);
  --priceband-ink-1: var(--ink);
  --priceband-ink-2: var(--muted);
  --status-dot-active-on-navy: var(--good);
  --status-dot-degraded-on-navy: var(--caution);
  --status-dot-dead-on-navy: var(--bad);
  --good-on-ink: var(--good);
}

.dl-hero h1,
.dl-hero__title {
  color: var(--ink);
}

.dl-hero .dl-crumbs {
  margin-bottom: 18px;
}

.dl-hero__title {
  font-family: var(--font-display);
  font-size: var(--type-hero-size);
  line-height: var(--type-hero-lh);
  letter-spacing: var(--type-hero-ls);
  /* 800. The previous value was 400 and the comment explaining it was correct about a face that
     is no longer on the site: a single-weight serif at 64px needed no help and could only be
     faked heavier. Manrope has a real 800 cut, and the headline is where the new direction is
     most visible. See tokens/typography.css. */
  font-weight: var(--weight-black);
  font-synthesis-weight: none;
  max-width: 24ch;
  /* Overrides base.css's global `text-wrap: balance`: on a long seeded heading
     (no fixed length — this is editorial copy, not a template string) balance can
     strand a short trailing fragment like an em dash on its own line. Plain wrapping
     reads better across the range of headings an editor might actually write. */
  text-wrap: wrap;
}

.dl-hero__sub {
  /* 12px, not 16, and a shorter measure. The band is the single largest block above the payload
     on every page that has one (tools/vertical_cost.py), and a lede is the part of it that costs
     most per word — at 52rem it ran to three lines on the ranking page. Nothing is cut; the
     measure just stops the sentence sprawling. */
  margin: var(--space-3) 0 0;
  max-width: 44rem;
  font-size: var(--type-body-size);
  color: var(--text-on-navy);
}

.dl-hero__sub a {
  color: var(--link-on-navy);
  font-weight: var(--weight-semibold);
}

/* State page (and any future non-homepage banded page) shares .dl-hero but sets its H1
   in the general article size (design-system.html's "every other H1"), not the
   homepage-only --type-hero-size — the two tokens are close but distinct, see
   tokens/typography.css. */
.dl-hero--article .dl-hero__title {
  font-size: var(--type-h1-size);
  line-height: var(--type-h1-lh);
  letter-spacing: var(--type-h1-ls);
  max-width: 26ch;
}

/* The status legend under the byline on a banded page, per
   docs/ui_refs_v3/state-template.html: "13 of 15 tracked providers ship to Texas ·
   1 paused · 1 never served TX". Reuses .dl-status (status.css) with its dot colours
   swapped for the on-navy variants — see tokens/colors.css's header comment for why. */
.dl-hero__status-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-4);
}

.dl-hero__status-legend .dl-status {
  color: var(--ink);
}

.dl-hero__status-legend .dl-status--active .dl-status__dot {
  background: var(--status-dot-active-on-navy);
}

.dl-hero__status-legend .dl-status--degraded .dl-status__dot {
  background: var(--status-dot-degraded-on-navy);
}

.dl-hero__status-legend .dl-status--dead .dl-status__dot {
  background: var(--status-dot-dead-on-navy);
}

/* "N never served <state>" is a coverage fact, not a provider status — the provider is
   not necessarily dead, it simply never shipped here. Reusing --status-dot-dead (red)
   for it would be exactly the "status colour for something other than provider status"
   the hard rules forbid, even though the pack's own mockup does reach for its red dot
   here. Plain muted grey (already --text-on-navy-dim, 5.3:1 on navy) instead. */
.dl-hero__status-legend .dl-status--muted .dl-status__dot {
  background: var(--text-on-navy-dim);
}

/* Byline set on the navy band: lighter text, white name, a paler mint fact so it
   still clears AA on the dark surface (see tokens/colors.css's header comment). */
.dl-hero .dl-byline {
  margin-top: 16px;
  color: var(--text-on-navy-2);
}

.dl-hero .dl-byline__name {
  color: var(--ink);
}

.dl-hero .dl-byline__initials,
.dl-hero .dl-byline__mark {
  border-color: var(--border-on-navy-2);
  color: var(--text-on-navy-2);
}

.dl-hero .dl-byline__fact {
  color: var(--good-on-ink);
}

.dl-hero .dl-byline__link {
  color: inherit;
}

/* --- plain titleblock (review, provider index, state, info pages) ----------- */

.dl-titleblock {
  padding: var(--space-6) 0 8px;
  border-bottom: var(--rule-hairline);
  margin-bottom: var(--space-8);
}

/* --- the header row: breadcrumb left, the dated pill right --------------------------------- */

/* New 2026-08-18, on the provider profile and provider review templates. The pill used to sit
   on its own line above the H1 — a second short line of chrome between the breadcrumb and the
   headline that did no work of its own. Sharing one line with the breadcrumb recovers that line
   without dropping either fact; both keep their own component styling (`.dl-crumbs`,
   `.dl-badge-pill`) and lose only the vertical margin that made sense when each stood alone. */
.dl-titlerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.dl-titlerow .dl-crumbs {
  margin-bottom: 0;
}

.dl-titlerow .dl-badge-pill {
  margin-bottom: 0;
}

/* `--flush` drops the block's closing hairline.
   For pages whose next block is a section head, which draws its own heavier rule: the reader
   otherwise gets two horizontal lines ~48px apart with nothing between them, because an index page
   has no byline to separate them. Applied per template rather than with `:has(+ …)`, which cannot
   work here — the title block and the section head sit in different `.dl-page` wrappers, so they
   are not siblings. A first attempt used that selector and matched nothing at all. */
.dl-titleblock--flush {
  border-bottom: 0;
}

/* Provider index only: one step tighter on the same two chrome values, toward the
   "table in the first 1280x800 viewport" budget the fold rule sets for this page (see
   docs/ai/mobile_table_cards_and_trust_strip_2026-08-02.md). The H1, intro and stat line
   inside are untouched — this is the block's own top padding and bottom margin. */
.dl-titleblock--tight {
  padding-top: var(--space-5);
  margin-bottom: var(--space-5);
}

/* Same page, same reasoning: the section head's own 3px rule and padding are pure
   chrome around "All tracked providers" — not the label text, not the findings or
   coverage strip that follow it. */
.dl-index-tight > .dl-section-head {
  padding-block: var(--space-2);
}

/* The eyebrow over a page's H1 — the pack's `.eyebrow` exactly: 13px, 800, uppercase, 0.14em of
   tracking. One of the few places v5 keeps caps, and `tools/type_scale.py` exempts this shape by
   measurement rather than by class, for the reason the pack gives: at that tracking, in caps, heavy
   type reads as a label rather than as a heading that lost its line. */
.dl-titleblock__eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--step-micro);
  font-weight: var(--weight-black);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-2);
}

.dl-titleblock__title {
  max-width: 32ch;
  /* **Room for the descenders** (2026-10-01).
   *
   * The margin was 0 and nothing below it supplied one, so the element underneath started at the
   * H1's exact box bottom. That was harmless for a 56px serif at 1.06 and is not for Manrope at
   * 79px and 1.05: the line box leaves about four pixels of leading, and the tails of p, g and y
   * run straight through whatever sits beneath. On /methodology/ the "p" of "prices" crossed the
   * border of the lead box.
   *
   * The box model said there was no overlap — bottom 288, top 288 — which is why no geometry tool
   * here reports it. Ink is not the box. */
  margin-bottom: var(--space-4);
}

/* `--product`: the index and tool pages' H1, one step down and in the text face.
 *
 * A modifier rather than a change to the base class, because `.dl-titleblock` is shared. The
 * provider review, cost guide, comparison and switching guide all use it too, and their H1 *is*
 * editorial — a sentence someone wrote, which is what the 56px serif is for. What needed
 * demoting is the other kind: an index page's H1 names the list below it and should then get
 * out of the list's way. Applying this to the base class demoted both, which is how it was
 * caught. See tokens/typography.css for the size. */
.dl-titleblock__title--product {
  font-family: var(--font-sans);
  font-size: var(--type-h1-product-size);
  line-height: var(--type-h1-product-lh);
  letter-spacing: var(--type-h1-product-ls);
  font-weight: var(--weight-black);
  max-width: 34ch;
}

.dl-titleblock__dek {
  margin: 10px 0 0;
  font-size: var(--step-lede);
  color: var(--text-muted);
  max-width: 46rem;
}

.dl-titleblock__facts {
  margin-top: var(--space-3);
  font-size: var(--type-small-size);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
  line-height: 1.6;
}

/* --- sections --------------------------------------------------------------- */

.dl-section {
  margin-top: var(--section-gap);
}

.dl-section--tight {
  margin-top: var(--space-6);
}

.dl-section > h2 {
  margin-bottom: 6px;
}

.dl-section > .dl-sub-h {
  font-size: var(--step-body);
  color: var(--text-muted);
  margin-bottom: var(--space-6);
}

/* The ranked-card set counter ("3 of 9 priced providers") — 2026-08 competitor-card pass. States
   how much set sits behind the top-3 cards shown, always computed from the same list the cards
   and the table below render, never typed. Text only, no pill or rail: the section heading and
   `.dl-sub-h` line already carry this sentence's weight, and a second bordered shape here would
   be decoration standing in for a fact the words already state plainly. */
.dl-set-counter {
  display: inline-block;
  margin-left: var(--space-2);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
}

/* **A hairline, not a 3px ink bar** (v5, 2026-10-02).
 *
 * The 3px rule in `--text` was the heaviest ink on any page carrying two of them, and the owner's
 * side-by-side of /compare/ against the pack is what showed it: two black bars across the page,
 * each introducing a 14px label. The pack's own `.section-top` draws no rule at all — an eyebrow
 * over a heading is the boundary — and where it does separate sections it uses 1px of `--line`.
 *
 * 1px of `--line` here rather than nothing, because this component is the only thing marking the
 * boundary on the index pages that still use it; removing the rule outright would need each of
 * those sections to grow the pack's eyebrow-and-heading pair first. Named as the next step rather
 * than done blind across every page that includes it. */
.dl-section-head {
  border-top: 1px solid var(--line);
  padding: var(--space-4) 0 var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* Sentence case, not caps.
 *
 * The uppercase pass that stripped `text-transform` from fourteen components missed this one,
 * so "WHERE THEY DIFFER" was still shouting on every comparison page while the rest of the site
 * had stopped. v4 bans caps outright; the reason it gives is legibility, and the reason it holds
 * here is that a caps eyebrow is decoration doing the job a heading already does. Tracking goes
 * back to normal with it — positive letter-spacing exists to rescue caps and does nothing for
 * lowercase but loosen it. */
.dl-section-head__label {
  margin: 0;
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0;
  color: var(--text-body);
}

.dl-section-head__end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* --- two-column editorial split (pros/cons, changes+reviews on old pages) --- */

.dl-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--space-12);
  row-gap: var(--space-8);
  align-items: start;
}

/* --- review page: content below the fold, full width ------------------------ */

/* 2026-08 restructure (docs/ai/depth_and_second_accent_2026-08-04.md): the review page's
   two-column [250px sticky "In this review" TOC | content] grid is gone. The owner's own
   click-through named it directly — a nine-item vertical TOC taking a whole column above the
   fold earns nothing a reader needs before they have read anything, and it was the single
   biggest contributor to the page reading as chaos rather than a product page. It is replaced
   by `.dl-jumpnav` (anchor-nav.css), the same horizontal "jump to a section" line the homepage
   already uses, placed *below* the new first-screen fold rather than beside the content — see
   provider_page.html's own file-level comment. `.dl-review__content` is kept: every section
   below the jump nav still wants its `<h2>` spaced and scroll-margined the same way, now as a
   plain full-width block rather than a grid column. */
.dl-review__content {
  min-width: 0;
}

.dl-review__content h2 {
  margin: var(--section-gap) 0 14px;
  scroll-margin-top: var(--space-6);
}

.dl-review__content > section:first-child h2 {
  margin-top: 0;
}

/* --- review page: the first-screen fold — content column + score panel ------ */

.dl-review-fold {
  display: grid;
  grid-template-columns: 1fr minmax(300px, 340px);
  column-gap: var(--review-gap);
  align-items: start;
  margin-top: var(--space-5);
}

.dl-review-fold__side {
  position: sticky;
  top: calc(60px + var(--space-4));
}

/* ==========================================================================
   Tier B — 900px to 1139px.
   ========================================================================== */

@media (max-width: 1139px) {
  .dl-review-fold {
    grid-template-columns: 1fr minmax(260px, 300px);
    column-gap: var(--space-8);
  }
}

/* ==========================================================================
   Tier C — below 900px. One column everywhere.
   ========================================================================== */

@media (max-width: 899px) {
  :root {
    --page-pad: 20px;
    --gutter: 16px;
  }

  .dl-review-fold {
    grid-template-columns: 1fr;
  }

  .dl-review-fold__side {
    position: static;
  }

  .dl-split {
    grid-template-columns: 1fr;
    row-gap: var(--space-6);
  }

  .dl-hero__title {
    max-width: none;
  }
}

@media (max-width: 599px) {
  :root {
    --page-pad: 16px;
  }
}

/* --- css/components/watermark.css --- */
/* ==========================================================================
   Watermark — a huge, pale ghost letterform behind a banded hero. New in the 2026-08
   owner-directed depth pass (docs/ai/depth_and_second_accent_2026-08-04.md): the state page's
   hero carries the state's own two-letter code, oversized and barely-there, behind the H1 and
   byline.

   Decorative only (`aria-hidden`), and deliberately not a contrast concern: it sits *behind*
   the hero's real text at 6% white-on-navy opacity, which cannot move the measured contrast of
   the foreground type sitting in front of it — tools/dl_contrast_audit.py checks token pairs,
   which this does not add one of. `.dl-hero` gains `overflow: hidden` to clip the oversized glyph
   to the band's own box; nothing about the band's height or the content's position changes.
   ========================================================================== */

.dl-hero {
  position: relative;
  overflow: hidden;
}

.dl-watermark {
  position: absolute;
  top: 50%;
  right: -0.06em;
  transform: translateY(-50%);
  z-index: 0;
  margin: 0;
  font-size: clamp(140px, 22vw, 260px);
  line-height: 1;
  font-weight: var(--weight-black);
  letter-spacing: -0.04em;
  color: rgba(255, 255, 255, 0.06);
  pointer-events: none;
  user-select: none;
}

/* Every other direct child of the band stays above the watermark. `.dl-page` is the band's own
   content wrap on every page that uses `.dl-hero`. */
.dl-hero > .dl-page {
  position: relative;
  z-index: 1;
}

@media (max-width: 599px) {
  .dl-watermark {
    display: none;
  }
}

/* --- css/components/icon.css --- */
/* ==========================================================================
   Icon — outline sprite, 1.5px stroke, inheriting currentColor.

   Inlined `<symbol>` sprite (templates/includes/_icon_sprite.html) referenced with
   `<use>`: zero requests, real stroke rendering, `stroke: currentColor` so an icon
   inside a button or a link picks up the text colour including on hover.

   Icons are informational here — they label a source, a direction or an external
   link. They never decorate a heading or fill space, and none of them stand in for
   the written status words the spec requires (a status is always a word, never a
   dot or an icon alone).
   ========================================================================== */

.dl-icon {
  display: inline-block;
  width: var(--icon-size);
  height: var(--icon-size);
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.15em;
}

.dl-icon--sm {
  width: var(--icon-size-sm);
  height: var(--icon-size-sm);
}

.dl-icon--lg {
  width: var(--icon-size-lg);
  height: var(--icon-size-lg);
  stroke-width: 1.5px;
}

/* The direction marks (mark-up / mark-down / mark-flat) are filled, not stroked, and
   sized to sit on the numeral baseline beside a changed value. They carry no colour
   of their own (see ledger-list.css) — a rising or falling price is never red or
   green on this site. */
.dl-icon--mark {
  width: 0.62em;
  height: 0.62em;
  stroke: none;
  fill: currentColor;
  vertical-align: 0.02em;
}

.dl-icon--mark-flat {
  fill: none;
  stroke: currentColor;
}

/* --- css/components/wordmark.css --- */
/* ==========================================================================
   Wordmark — a small navy check-mark badge plus the site name in navy, per
   design-system.html's own masthead SVG (a rounded navy square, a white checkmark).

   No status dot beside it: provider status is a per-provider fact and belongs beside
   a provider's name, never beside the site's own name.

   The brand string comes from settings via the `brand` context processor
   ({{ SITE_NAME }}) — the domain and exact wordmark are not final (see Open Questions).
   ========================================================================== */

.dl-wordmark {
  display: inline-flex;
  align-items: center;
  /* `gap: 10px`, `font-size: 1.5rem`, `letter-spacing: -0.06em` — the pack's `.brand`. Ours was
     22px at -0.02em, which reads as a heading rather than as a logotype: the tight tracking is
     most of what makes a wordmark look drawn rather than typed. */
  gap: 10px;
  font-weight: var(--weight-black);
  font-size: 1.5rem;
  letter-spacing: -0.06em;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

a.dl-wordmark:hover {
  color: var(--navy);
}

.dl-wordmark--sm {
  font-size: var(--step-lede);
}

/* **The pack's own mark** (2026-10-02): a 24px circle split down the middle, yellow on the left
 * and a muted lavender on the right — `linear-gradient(90deg, var(--yellow) 50%, #ada0d1 50%)`.
 *
 * Ours was a checkmark inside a dark rounded square. A tick is a claim ("verified"), and on a site
 * whose whole argument is that it does not assert what it cannot show, the logo was the one place
 * making that claim with no evidence behind it. The pack's mark asserts nothing; it is two halves
 * of the brand's own palette, which is what a logotype is for.
 *
 * The icon inside goes with it — the circle is the whole mark. */
/* **The drawn mark, not a placeholder circle** (2026-10-04).
 *
 * This was a 24px circle filled half yellow and half lavender — the reference pack's own
 * `.brand-mark` stand-in, used while the project had no logo. It also contained a `check` icon
 * that the gradient painted over completely.
 *
 * The real mark is an equals sign, so the box stops being a square. Its `viewBox` is cropped to
 * the artwork (`12 30 76 40`) rather than the pack's full 100x100 artboard: with the square
 * artboard a 26x18 box letterboxed the drawing down to 18x18 and the bars rendered 13.7px wide,
 * a quarter of their intended size.
 *
 * Sized in `em` so both wordmark sizes scale from one rule. The pack's own header mock draws the
 * bars 26px wide beside a 21px name — 1.24 times the font size — and this keeps that ratio. */
.dl-wordmark__mark {
  display: inline-flex;
  width: 1.24em;
  height: 0.65em;
  flex: 0 0 auto;
}

.dl-wordmark__svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* The small variant needs no size of its own now: `em` follows the smaller font. */

/* On the ink footer the plum bar is 1.08:1 against the ground — it would simply not be there.
   The pack's own `favicon.svg` solves the same problem the same way in its dark-mode rule: the
   upper bar takes the light value and the lower one steps lighter still. */
.dl-footer .dl-wordmark__mark {
  --brand-mark-1: var(--paper);
  --brand-mark-2: #b3a6dc;
}

.dl-wordmark__mark .dl-icon {
  display: none;
}

/* --- css/components/chrome.css --- */
/* ==========================================================================
   Page chrome — header, nav, breadcrumbs, footer.

   Header: white, plain, 60px row, per design-system.html's `.topbar`. Navigation
   labels come from the live page tree (see `dl_nav_items`/`NAV_DIVISIONS` in
   apps/core/templatetags/dl_components.py) mapped onto the mockups' own reader-facing
   words ("Provider reviews", "Comparisons", "By state", "Medicare Bridge") rather than
   the previous pass's abstract "Rates/Service/Continuity/Record" framing — a nav
   label should say what a reader clicks into, not sort it into a document category.
   "Costs" is in the mockup's nav but has no page type yet; it is omitted rather than
   linked to nothing (see Open Questions).

   Footer: solid navy, reversed text — the mockups' own footer, not a plain-background
   afterthought. The three-column "How we rate / Medically reviewed / How we make
   money" strip sits *above* the footer on a pale grey band (`.dl-method`, defined in
   editorial.css) — it is a distinct section in the mockups, not folded into the navy
   footer itself.

   Below 900px the nav collapses behind a labelled button that toggles
   `aria-expanded` on the list. With JavaScript off the toggle never appears and the
   nav stays a visible wrapped list of links.
   ========================================================================== */

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

.dl-header {
  /* `--border-2` at 2px, not `--rule-hairline` (2026-08-18 white-background fallout sweep).
     `--page-bg` flipped from a warm cream to pure white the same day, and a 95%-white header
     over a now-plain-white page left `border-bottom` as the ONLY thing separating the two —
     `--border` (#E4E7E3) measures 1.25:1 against white, which a screenshot showed reading as no
     line at all once the page below it stopped being a different hue. `--border-2` (#D3D8D3,
     1.45:1) plus doubling the stroke to 2px is the same "no new hex" fix the sticky bar and the
     table wrapper could use if they showed the same defect — a structural rule, not a text
     colour, so AA does not apply, but "the header floats with no visible edge" is a real defect
     a border this faint could not fix by hue alone. */
  /* **1px of `--line` on `--paper`** (2026-10-02), which is the pack's `.site-header` exactly.
     This was 2px of `--border-2` on a 95%-white blur, and the comment above it explains why: the
     page had just turned pure white and a hairline vanished against it. The page is `--paper`
     now, a warm off-white, and the header is the same colour — so the line separates two surfaces
     of the same hue, which is what it is for, and does not have to carry the whole boundary by
     weight. */
  border-bottom: 1px solid var(--line);
  /* 2026-08 depth pass: 95% white + a background blur, measured off the reference build's own
     sticky-header component code (`bg-white/95 backdrop-blur-md`) — a page that has scrolled
     shows a soft blur of its own content through the header rather than a flat opaque bar. Falls
     back to solid white automatically wherever `backdrop-filter` is unsupported, since the alpha
     channel alone still reads as white against anything behind it. */
  /* `--paper`, flat. The translucent white and the blur were drawn for a page that was pure white
     and a header that had to feel lifted off it; the pack's header is the same colour as its page
     with one hairline between them, and a blurred bar over a warm ground read as a second,
     colder surface. */
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 40;
}

.dl-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  /* 88px, the pack's own `.header-inner`. 60px was the previous pack's figure and it is the
     reason our header reads as a toolbar where the reference's reads as a masthead. Steps to 74px
     under 1024 and 64px on a phone, per its own breakpoints. */
  min-height: 88px;
}

.dl-header__brand {
  flex: 0 0 auto;
}

/* **The nav does not absorb the free space any more** (v5, 2026-10-02). `flex: 1 1 auto` pinned
   it immediately to the right of the wordmark and pushed everything else to the far edge; the
   pack's masthead is `justify-content: space-between` over three items, which seats the nav
   between the brand and the action rather than against the brand. */
.dl-header__nav {
  flex: 0 1 auto;
}

/* No `margin-left: auto` (2026-10-02). It predates `justify-content: space-between` on the row and
   defeats it: an auto margin consumes all the free space itself, so the brand and the nav stayed
   packed against the left edge while the action went to the right — a toolbar, where the pack's
   masthead seats the nav in the middle of the row. */
.dl-header__end {
  flex: 0 0 auto;
  display: flex;
  justify-content: flex-end;
}

.dl-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.dl-nav__link {
  display: inline-block;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
  font-size: var(--step-body);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
  text-decoration: none;
}

.dl-nav__link:hover {
  color: var(--blue);
}

.dl-nav__link[aria-current="page"] {
  color: var(--blue);
  border-bottom-color: var(--blue);
}

.dl-header__meta {
  font-size: var(--type-small-size);
  color: var(--text-muted);
  white-space: nowrap;
}

.dl-header__disc-link {
  font-size: var(--step-micro);
  color: var(--text-muted);
  white-space: nowrap;
}

/* The pack's `.header-cta`: an outlined button at the end of the masthead, hidden under 1050px
   where the nav needs the width more than the action does — its destination is the first item in
   the nav anyway, so nothing becomes unreachable. */
.dl-header__cta {
  white-space: nowrap;
}

/* `.dl-header__end` in the selector, not `.dl-header__cta` alone: the button also carries
   `.dl-btn`, whose `display: inline-flex` has the same specificity and loads later, so the bare
   class rule matched and lost. Measured — the media query reported true while the button stayed
   visible and ran off the right edge of a narrow viewport. */
@media (max-width: 65.625rem) {
  .dl-header__end .dl-header__cta {
    display: none;
  }
}

/* --- mobile nav toggle ----------------------------------------------------- */

.dl-nav__toggle {
  display: none;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1.5px solid var(--border-2);
  border-radius: var(--radius);
  background: transparent;
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  color: var(--text-body);
  cursor: pointer;
}

@media (max-width: 899px) {
  .dl-header__inner {
    min-height: 74px;
    flex-wrap: wrap;
    column-gap: var(--space-4);
    padding-block: var(--space-3);
  }

  .dl-header__nav {
    order: 3;
    width: 100%;
  }

  .dl-nav__toggle:not([hidden]) {
    display: inline-flex;
  }

  .dl-nav__list {
    gap: var(--space-4);
    row-gap: var(--space-3);
    padding-top: var(--space-2);
  }

  .dl-nav[data-collapsible="true"] .dl-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-top: var(--space-2);
  }

  .dl-nav[data-collapsible="true"] .dl-nav__list li {
    border-top: var(--rule-row);
  }

  .dl-nav[data-collapsible="true"] .dl-nav__link {
    display: block;
    padding: var(--space-3) 0;
    border-bottom: 0;
    border-left: 3px solid transparent;
  }

  .dl-nav[data-collapsible="true"] .dl-nav__link[aria-current="page"] {
    border-left-color: var(--blue);
    padding-left: var(--space-3);
  }

  .dl-nav__list[hidden] {
    display: none;
  }
}

/* --- breadcrumbs --------------------------------------------------------- */

.dl-crumbs {
  margin-bottom: 16px;
}

.dl-crumbs__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--step-fine);
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.dl-crumbs__list li {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
}

.dl-crumbs__list li + li::before {
  content: "\203A";
  color: var(--text-muted);
}

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

.dl-crumbs a:hover {
  text-decoration: underline;
}

.dl-crumbs [aria-current="page"] {
  color: var(--text-muted);
}

/* On the homepage's navy hero, breadcrumbs reverse out lighter. */
.dl-hero .dl-crumbs__list,
.dl-hero .dl-crumbs a {
  color: var(--text-on-navy-muted);
}

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

.dl-footer {
  margin-top: var(--section-gap);
  background: var(--navy);
  color: var(--text-on-navy-muted);
  /* `--focus` is a plum ring, 8.70:1 on paper and 1.66:1 here — under the 3:1 WCAG 1.4.11 asks of
     a focus indicator, on every link in the footer. The dark grounds take their own ring; see
     `--focus-on-ink` in tokens/colors.css. Found by the 2026-10-02 Codex review, which asked the
     question about the key-numbers strip and led here. */
  --focus-color: var(--focus-on-ink);
}

.dl-footer__top {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: var(--space-8);
  padding-top: var(--space-8);
  padding-bottom: var(--space-8);
}

.dl-footer__brand {
  grid-column: 1 / 5;
}

.dl-footer__blurb {
  margin: var(--space-3) 0 10px;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-on-navy-muted);
  max-width: 320px;
}

.dl-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: flex-start;
  grid-column: span 2;
}

.dl-footer .dl-label {
  color: var(--white);
}

.dl-footer__col > .dl-label {
  margin-bottom: 2px;
}

/* **The footer's links are standalone targets** (2026-10-05). Each is a flex item in its own
   column, so it computes to a block and WCAG 2.5.8's exception for a link inside a sentence
   does not reach it. Measured at 22px high across all thirteen — two pixels under the floor,
   on every page, in the one place a reader uses a thumb. `min-height` with the text centred:
   the column already has an 8px gap, so nothing below it moves except by those two pixels. */
.dl-footer__col a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  font-size: var(--type-small-size);
  color: var(--text-on-navy-muted);
  font-weight: var(--weight-semibold);
  text-decoration: none;
}

/* The wordmark is a link too, and measured 128x18 in the footer. */
.dl-footer__brand .dl-wordmark {
  min-height: 24px;
}

.dl-footer__col a:hover {
  color: var(--white);
  text-decoration: underline;
}

.dl-footer .dl-byline__role,
.dl-footer .dl-meta {
  color: var(--text-on-navy-muted);
}

/* The wordmark reverses on the navy footer: white type, a white mark with the navy
   checkmark inverted inside it (rather than a navy-on-navy square that would vanish). */
.dl-footer .dl-wordmark {
  color: var(--white);
}

.dl-footer .dl-wordmark__mark {
  background: var(--white);
}

.dl-footer .dl-wordmark__mark .dl-icon {
  stroke: var(--navy);
}

.dl-footer__legal {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--gutter);
  row-gap: var(--space-3);
  border-top: 1px solid var(--border-on-navy);
  padding: 20px 0 28px;
}

.dl-footer__legal-text {
  grid-column: 1 / 10;
  font-size: var(--step-micro);
  line-height: 1.7;
  color: var(--text-on-navy-dim);
}

.dl-footer__copy {
  grid-column: 10 / 13;
  text-align: right;
  font-size: var(--type-small-size);
  color: var(--text-on-navy-dim);
}

@media (max-width: 899px) {
  .dl-footer__top,
  .dl-footer__legal {
    grid-template-columns: 1fr 1fr;
  }

  .dl-footer__brand {
    grid-column: 1 / -1;
  }

  .dl-footer__col {
    grid-column: 1 / -1;
  }

  .dl-footer__legal-text,
  .dl-footer__copy {
    grid-column: 1 / -1;
    text-align: left;
  }

  /* The row fix for this breakpoint lives in no-clinical-review.css, next to the rules that
     actually cause the conflict (`.dl-footer .dl-footer__copy` and `.dl-footer .dl-no-clinical`
     both carry a class-on-class selector, which beats the single-class one this file could add
     here) — see that file's own comment. */
}

/* Tap targets on a phone. Measured with tools/mobile_check.py at 390px: the nav links were 28px
 * tall and the wordmark 22px, against the 44px minimum Apple and Google both publish. Nothing
 * caught it because no probe here went below 500px, where the desktop nav is still in play.
 *
 * Padding rather than a fixed height, so the hit area grows without moving the text or changing
 * the bar's own rhythm; the underline stays on the text where it belongs.
 */
@media (max-width: 56rem) {
  .dl-nav__link {
    padding-top: 8px;
    padding-bottom: 10px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .dl-brand,
  .dl-wordmark {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  .dl-skip {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* Footer links on a phone: 16-19px tall in a stacked list, which is a column of targets a thumb
 * cannot separate. Padding, not line-height — line-height would space the text and still leave
 * the hit area at the glyph. */
@media (max-width: 56rem) {
  .dl-footer a {
    display: inline-block;
    padding-block: 12px;
  }
}


/* Breadcrumb and masthead links reach the 44px tap minimum on a phone — see provider-logo.css
   for the reasoning and tools/mobile_check.py for the measurement. Both are standalone links a
   thumb has to aim at, not links inside a sentence, so WCAG 2.5.8's in-sentence exemption does
   not cover them. */
@media (max-width: 42rem) {
  .dl-crumbs__list a,
  .dl-header__disc-link,
  /* `.dl-link-label` is the site's standalone "read on" link — "See the full change log →" — a
     block of its own under a section, not a link inside a sentence. */
  .dl-link-label {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* --- css/components/anchor-nav.css --- */
/* ==========================================================================
   AnchorNav — the homepage's "jump to" line (includes hand-written in home_page.html).

   Not in design-system.html: the pack's own precedent for an anchor list is the review page's
   vertical sticky "In this review" card (`.dl-toc-card`, ledger-list.css). This is the same
   mechanism turned sideways — a single row of plain text links, no card, no icons, no pill
   shapes — because the homepage has no sticky rail to hang a vertical list from and does not
   need one; it needs a single line telling a reader what is further down this specific page.

   Reuses `static/js/modules/toc.js` unchanged: that module's selector is `[data-dl-toc] a`
   generically, so giving this nav the same `data-dl-toc` attribute as the review page's TOC
   gets the active-section highlight for free, with no new script.
   ========================================================================== */

.dl-jumpnav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  padding: var(--space-3) 0;
  border-bottom: var(--rule-hairline);
  font-size: var(--step-small);
}

/* Blue, per the sitewide text-link rule (2026-08-10 owner pass): set in ink these read
   as labels, not as anything a reader could click. The active-section state keeps its
   underline bar; hover underlines like every other link on the site. */
.dl-jumpnav a {
  display: inline-block;
  color: var(--blue);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding: 2px 0;
}

.dl-jumpnav a:hover {
  text-decoration: underline;
}

/* 44px targets once the row starts wrapping onto several lines.
 *
 * On a phone this nav is the page's table of contents and its links were 28px tall, which
 * tools/mobile_check.py flags at both 390 and 360. The row already wraps there, so the extra
 * height lands in space the layout has, and the underline bar the active state draws stays
 * attached to the text because the padding is symmetric. */
@media (max-width: 56rem) {
  .dl-jumpnav a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0;
  }

  .dl-jumpnav {
    gap: 0 var(--space-6);
    padding: 0;
  }
}

.dl-jumpnav a.dl-toc__link--on {
  color: var(--blue);
  border-bottom-color: var(--blue);
}

/* The jump targets themselves — offset so the sticky 60px header (chrome.css) never covers the
   heading a reader just jumped to. */
#dl-top-cards,
#dl-master-table-section,
#dl-unpriced-toggle,
#dl-method-label {
  scroll-margin-top: calc(60px + var(--space-4));
}

/* --- css/components/button.css --- */
/* ==========================================================================
   Button — 4px-radius rectangle, 13px caps label, per design-system.html §3.

   One accent for the whole site: primary is solid --blue with white text (5.9:1, AA at
   every size), hover/press --blue-dark (7.9:1). Secondary is a 1.5px blue border on
   white that inverts to solid blue on hover — used for the "Read more"/expand pattern,
   never for an affiliate action.

   Nothing lifts, scales or gains a shadow. Row buttons never wrap. There is exactly one
   primary CTA action for the whole site — "Check your eligibility" — set in the CTA
   partial, not here; this file only draws the button shape.
   ========================================================================== */

.dl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* **The pack's own `.btn`** (2026-10-02): `min-height: 48px`, `padding: 12px 20px`, radius 8px,
     `gap: 12px`, 1px border. Ours was a fixed `height` with 16px of side padding and a 6px radius
     — a tighter, squarer control than the reference's, and `height` rather than `min-height` meant
     a wrapping label overflowed its own box instead of growing it. The wrap tier below this file
     already works around that; `min-height` removes the need. */
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-3) 20px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  font-family: var(--font-sans);
  font-size: var(--type-cta-size);
  font-weight: var(--weight-bold);
  letter-spacing: var(--type-cta-ls);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}

.dl-btn:hover {
  text-decoration: none;
}

/* --- sizes ------------------------------------------------------------------ */

.dl-btn--sm {
  height: var(--control-h-sm);
  padding: 0 var(--space-3);
  font-size: var(--step-micro);
}

.dl-btn--lg {
  height: var(--control-h-lg);
  padding: 0 22px;
}

.dl-btn--block {
  width: 100%;
}

/* --- variants ---------------------------------------------------------------- */

/* The site's one paid-shaped action stands 44px tall, not 36.
 *
 * `--control-h` (36px) is the right height for a select or a filter control — things a reader
 * adjusts. This is the thing a reader presses, it is the same control on a phone as on a desk,
 * and at 36px it was the most-repeated tap-target miss on the site: twelve of them per page at
 * 390px (tools/mobile_check.py). 44px is both the WCAG target-size floor and the height the
 * 2026-08-15 review asks for by name. */
.dl-btn--primary {
  height: var(--control-h-lg);
}

.dl-btn--primary {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--text-inverse);
}

.dl-btn--primary:hover,
.dl-btn--primary:active {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
  color: var(--text-inverse);
}

.dl-btn--secondary {
  background: var(--white);
  border-color: var(--blue);
  color: var(--blue);
}

.dl-btn--secondary:hover,
.dl-btn--secondary:active {
  background: var(--blue);
  color: var(--text-inverse);
}

.dl-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-body);
  padding-inline: 0;
}

.dl-btn--ghost:hover {
  text-decoration: underline;
}

.dl-btn[aria-disabled="true"],
.dl-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* --- the plain review link: "Read our TrimRX review", "See the full change log" --- */

/* Semibold, sentence case, NOT the CTA's caps treatment — design-system.html's own
   `.reviewlink`/`.vslist a` are plain semibold links, not a second button voice. */
.dl-link-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-sans);
  font-size: var(--type-small-size);
  font-weight: var(--weight-semibold);
  color: var(--blue);
  text-decoration: none;
}

.dl-link-label:hover {
  text-decoration: underline;
  color: var(--blue-dark);
}

/* **A link that is a cell's only content is a standalone target** (2026-10-05). The unpriced
   providers' table ends each row with "Visit <provider>'s official site" and nothing else, so
   WCAG 2.5.8's exception for a link inside a sentence does not reach it. Measured at 20px on
   all twelve rows. Scoped to the direct child of a cell: everywhere else `.dl-link-label` sits
   inside running text, where growing its line box would be wrong. */
td > .dl-link-label {
  min-height: 24px;
}

/* The microcopy under a CTA: "on TrimRX's website". */
.dl-oncopy {
  display: block;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  margin-top: 7px;
  text-align: center;
}

/* --- paired buttons: filled primary + outlined secondary, side by side --------- */

/* New in the 2026-08 owner-directed depth pass (docs/ai/depth_and_second_accent_2026-08-04.md):
   the review and comparison folds now show the one affiliate CTA beside a second, outlined
   button that is always an internal link (full pricing, alternatives, the comparison table) —
   never a second affiliate action. The secondary button borrows --accent2 rather than --blue so
   it reads as a distinct, lower-emphasis action next to the primary CTA's blue fill, not as a
   second "check eligibility" button in a paler shade of the same colour. */
.dl-btn-pair {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.dl-btn-pair .dl-cta-block {
  flex: 1 1 auto;
  min-width: 200px;
}

.dl-btn--accent2 {
  background: var(--white);
  border-color: var(--accent2);
  color: var(--accent2);
}

/* Both uses of this variant sit in a `.dl-btn-pair` beside the 44px primary CTA (the review
   fold's "Compare N alternatives" / "See full pricing"). `.dl-btn--accent2` carries no height of
   its own, so it fell back to `.dl-btn`'s base 36px next to a 44px neighbour — the shorter of the
   pair's two tap targets and, per tools/mobile_check.py, under the WCAG floor. */
.dl-btn-pair .dl-btn--accent2 {
  height: var(--control-h-lg);
}

.dl-btn--accent2:hover,
.dl-btn--accent2:active {
  background: var(--accent2);
  border-color: var(--accent2);
  color: var(--text-inverse);
}

@media (max-width: 480px) {
  .dl-btn-pair {
    flex-direction: column;
    align-items: stretch;
  }
}

/* --- css/components/tag-badge.css --- */
/* ==========================================================================
   Flag and Badge — the inline markers, per design-system.html §5.

   Flag is the regulatory-record chip: neutral grey ("No actions on record"), amber
   ("Lawsuit, Apr 2025" — an allegation on file, not yet a finding) or red ("FDA warning
   letter, Sep 2025"). Colour roles do not mix with anything else on the site — amber and
   red never appear for a price direction or a provider status.

   Badge is a small neutral marker for a plan attribute ("Compounded") — grey background,
   4px radius, no border box.
   ========================================================================== */

/* --- Flag ------------------------------------------------------------------ */

.dl-flag {
  display: inline-block;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-semibold);
  border-radius: var(--radius);
  padding: 3px 8px;
  white-space: normal;
  line-height: 1.3;
}

.dl-flag--none {
  color: var(--text-muted);
  background: var(--gray-bg);
  font-weight: var(--weight-medium);
}

.dl-flag--warn {
  color: var(--amber);
  background: var(--amber-bg);
}

.dl-flag--bad {
  color: var(--red);
  background: var(--red-bg);
}

/* --- Badge ------------------------------------------------------------------ */

.dl-badge {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 7px;
  background: var(--gray-bg);
  border-radius: var(--radius);
  font-size: var(--type-micro-size);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  color: var(--text-muted);
}

/* --- feature pills: computed facts, not merit badges ------------------------ */

/* A row of `.dl-badge`s — the master table's provider cell and the ranked cards
   (`dl_feature_pills`). Wraps rather than truncates: a provider with fewer pills simply
   has a shorter row, never a horizontal scroll of its own inside a table cell. */
.dl-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

/* --- css/components/badge-pill.css --- */
/* ==========================================================================
   BadgePill — includes/_badge_pill.html. New in the 2026-08 owner-directed depth pass
   (docs/ai/depth_and_second_accent_2026-08-04.md): a small pill directly above the H1 on the
   provider review, comparison, ranking and state pages, carrying a round mark, a short label
   and a dated fact on one line.

   Not in design-system.html — flagged the same way every other genuinely new component in this
   pass is flagged. A pill shape (999px radius) is used nowhere else in this system, which is
   deliberate: everywhere else radius tops out at --radius-2xl (14px, the new elevated cards) or
   --radius (4px, controls), so this one shape stays legible as "a small dated tag," never
   confused with a button or a card.
   ========================================================================== */

.dl-badge-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  padding: 6px 14px 6px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-elevated);
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-body);
}

.dl-badge-pill__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  background: var(--gray-bg);
  color: var(--blue);
}

/* `border-radius: 50%` was a circular mask on a 22px square — fine over a favicon, a crop over a
   wordmark, and cropping a provider's mark is the one thing the doctrine forbids outright. Width
   now follows the mark (2026-08-28), same reasoning as `.dl-plogo__img`. */
.dl-badge-pill__img {
  width: auto;
  max-width: 72px;
  height: auto;
  max-height: 22px;
  border-radius: 3px;
  object-fit: contain;
}

.dl-badge-pill__label {
}

.dl-badge-pill__sep {
  color: var(--text-muted);
}

.dl-badge-pill__meta {
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: normal;
  font-variant-numeric: var(--tabular);
}

@media (max-width: 480px) {
  .dl-badge-pill {
    font-size: var(--step-micro);
  }
}


/* The 22px mark's dark ground — see provider-logo.css for the measurement behind `logo_ground`. */
.dl-badge-pill__mark--dark {
  background: var(--navy);
}

/* --- css/components/forms.css --- */
/* ==========================================================================
   Input, Select, Checkbox, and the filter line — per design-system.html §13.

   Native `<select>`/`<input>` elements with a visible 1px --border-2 rectangle, 4px
   radius — not the previous pass's underlined "Tag" treatment. A real select rather
   than a custom popup means the filter works with a keyboard, a screen reader and a
   touch device without any JavaScript of ours, and it renders identically to a form
   control anywhere else on the page rather than inventing a second control language
   for "filter chips."
   ========================================================================== */

/* --- shared field shell (calculator inputs, any future form) --------------- */

.dl-field {
  display: block;
}

.dl-field__label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

select,
input[type="number"],
input[type="text"],
input[type="email"] {
  font-family: var(--font-sans);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
  border: 1px solid var(--border-control);
  border-radius: var(--radius);
  background: var(--white);
  padding: 9px 10px;
}

select:focus-visible,
input:focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

select option {
  background-color: var(--white);
  color: var(--text-body);
}

/* --- square checkbox (availability checker's "hide compounded" option) ------ */

.dl-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}

.dl-check__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.dl-check__box {
  width: 16px;
  height: 16px;
  margin-top: 3px;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--white);
  border: 1.5px solid var(--border-control);
  border-radius: 3px;
  color: transparent;
}

.dl-check__input:checked + .dl-check__box {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--text-inverse);
}

.dl-check__input:focus-visible + .dl-check__box {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

.dl-check__text {
  font-size: var(--type-body-size);
  line-height: 1.5;
}

.dl-check__hint {
  display: block;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* ==========================================================================
   The filter line — a slim row of selects above the table (US News/NerdWallet
   pattern), not pill groups. Hidden until the JS enhancement reveals it: the
   homepage renders every provider server-side and does no querystring filtering, so
   with JavaScript off the reader gets the complete table and a plain row count.
   ========================================================================== */

.dl-filters {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: var(--space-5) 0 0;
}

.dl-filters[hidden] {
  display: none;
}

.dl-filters__group {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
  display: contents;
}

.dl-filters__row {
  display: contents;
}

.dl-filter {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.dl-filter__label {
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  white-space: nowrap;
}

.dl-filter__select {
  cursor: pointer;
}

.dl-filter__wrap {
  display: inline-flex;
}

.dl-filters__count {
  margin-left: auto;
  font-size: var(--step-small);
  color: var(--text-muted);
  white-space: nowrap;
}

.dl-filters__reset {
  padding: 0;
  border: 0;
  background: transparent;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--blue);
  text-decoration: underline;
  cursor: pointer;
}

.dl-filters__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-3);
  flex-basis: 100%;
}

@media (max-width: 599px) {
  .dl-filters {
    gap: var(--space-3);
  }

  .dl-filter {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }

  .dl-filters__count {
    margin-left: 0;
    flex-basis: 100%;
  }
}

/* --- the /compare/ comparator: two labelled selects, a "vs", a secondary submit ----
   The site's first real <form> — server-handled GET (ComparisonIndexPage.serve reads it),
   so it is complete without JavaScript; _filters.html's "a GET form the server ignores"
   objection does not apply.

   A white card on the warm page surface, sized to its controls — the same
   `--surface-elevated` treatment every other tool on the site gets (the calculator
   band, the glance cards), so the form reads as an instrument rather than three bare
   controls floating in an empty column. Controls share `--control-h` so the selects
   and the submit sit on one line at one height. */
/* **The pack's own `.compare-selectors`** (2026-10-02): a filled lavender panel at 16px radius,
   26px of padding, no border. It was a white bordered card on a warm page — the same surface as
   every other panel on the site, which made the one control a reader has to use indistinguishable
   from the content around it. The pack fills it precisely because it is the page's instrument.

   `--space-12` of width beyond 46rem too: the pack gives its two selects a full-width row, and at
   46rem they were two short fields with a quarter of the row empty beside them. */
.dl-comparator {
  display: flex;
  align-items: flex-end;
  gap: var(--space-5);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding: var(--space-6);
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--lavender);
}

.dl-comparator .dl-field {
  flex: 1 1 200px;
  min-width: 160px;
}

.dl-comparator select {
  width: 100%;
  height: var(--control-h);
  padding-block: 0;
}

.dl-comparator__vs {
  align-self: flex-end;
  /* optically centres "vs" against the --control-h selects beside it */
  padding-bottom: 9px;
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  color: var(--text-muted);
}

.dl-comparator__note {
  margin: var(--space-3) 0 0;
  font-size: var(--type-small-size);
  color: var(--text-body);
  max-width: 46rem;
}

@media (max-width: 599px) {
  .dl-comparator {
    width: 100%;
  }

  .dl-comparator .dl-field {
    flex-basis: 100%;
  }

  .dl-comparator__vs {
    display: none; /* stacked selects don't need a divider word */
  }

  .dl-comparator .dl-btn {
    width: 100%;
  }
}

/* The /states/ finder shares the comparator's card treatment (both render as
   `.dl-comparator`); this only stops a single select stretching the whole card. */
.dl-statefinder .dl-field {
  flex: 0 1 260px;
}

/* --- css/components/filter-chip.css --- */
/* ==========================================================================
   FilterChip + Segmented — includes/_filters.html. New in the 2026-08 chip pass, per
   the owner's direct comparison against a competitor's table: filter chips that carry
   a count ("Accepts insurance 6"), so a reader sees how many rows a choice yields before
   clicking it, and a segmented sort control instead of a bare dropdown.

   Not in design-system.html: forms.css's own header comment previously argued for plain
   native selects over "a second control language for filter chips," and that reasoning
   still holds for a 51-option state list — it does not hold once the owner has clicked
   through a competitor's table and named this exact device by name. Built as real,
   native, keyboard-operable controls (radio/checkbox inputs, visually hidden, with a
   styled `<label>` as the visible face) rather than a `<div>` with a click handler, so
   the "second control language" cost forms.css warned about is paid in markup only, not
   in accessibility: every chip is still a real form control a screen reader announces
   correctly (checked/unchecked, radio-group membership) with no ARIA invented to fake it.

   Radius 4px (`--radius`, per shape.css: "controls: buttons, inputs, selects, flag
   chips") — not a fully rounded pill. The one 999px-radius pill in this system
   (badge-pill.css) is flagged there as "used nowhere else," and a filter control reads
   as a control, not a status badge, so it takes the same radius every other control on
   the page already uses.
   ========================================================================== */

.dl-chipgroup {
  /* `.dl-filters` is a wrapping flex row; a full-width basis forces the chip row onto
     its own line, the same technique `.dl-filters__options` already uses for the reset
     button below. */
  flex-basis: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

/* --- "All filters": the disclosure the secondary chips moved into (2026-08-15) --------- */

/* Six chips and four controls on one line read as an analytics console rather than as a way to
   answer "what will this cost me". The four dimensions a reader actually chooses on stay on the
   primary row; the rest live here, one click away, unchanged and still carrying their counts.
   `<details>` because the disclosure pattern is native — no script of this component's own, no
   ARIA, and it works before the enhancement module runs. */
.dl-filters__more {
  flex-basis: 100%;
  margin-top: var(--space-3);
}

.dl-filters__more-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  padding: 4px 0;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--blue);
  cursor: pointer;
  list-style: none;
}

/* The default triangle is replaced with one drawn in the accent, so the affordance matches the
   rest of the site's disclosure controls rather than showing the browser's own marker. */
.dl-filters__more-toggle::-webkit-details-marker {
  display: none;
}

.dl-filters__more-toggle::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--dur) var(--ease-standard);
}

.dl-filters__more[open] .dl-filters__more-toggle::before {
  transform: rotate(90deg);
}

.dl-filters__more-toggle:hover {
  text-decoration: underline;
}

.dl-filters__more[open] .dl-chipgroup {
  margin-top: var(--space-3);
}

.dl-chipgroup__legend {
  padding: 0;
  margin-bottom: 6px;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
}

.dl-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.dl-chip {
  display: inline-flex;
}

.dl-chip__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* 2026-08 touch-target pass, per `ui-ux-pro-max`'s 44px floor: the chip's own visible face used
   to be ~30-32px tall (7px padding either side of a 13px label at the body's inherited 1.6
   line-height) — a mouse-only measurement that never accounted for the touch device this same
   filter line renders on below 600px. `min-height: 44px` plus `line-height: 1` (the label no
   longer needs prose leading) hits the real floor at every width rather than only widening it in
   the small-viewport media query below, which is where it is needed most and used to do the
   opposite (see that rule's own comment). */
.dl-chip__face {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  background: var(--gray-bg);
  color: var(--text-body);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
}

.dl-chip__count {
  font-variant-numeric: var(--tabular);
  color: var(--text-muted);
  font-weight: var(--weight-bold);
}

/* **Selected is the strongest chip in the row, not the faintest** (2026-10-05).

   This was `background: var(--blue-a10)` — a 10% tint of the action colour — which reads as
   "filled" against white. The resting chip is not on white: `--gray-bg` resolved to the v5
   lavender (#E8E1F4) in the palette migration, and a 10% plum tint over the cream page is
   *paler* than that. Measured on the live page: the one selected chip came back
   `rgba(75,37,77,0.1)` while every unselected one was a solid `rgb(232,225,244)`.

   So the selected filter was the lightest thing in the row and the owner could not tell what
   was on. Solid action colour now, with the label and the count inverted on it — the same
   three-step the segmented sort control uses: page, resting control, chosen control. */
.dl-chip__input:checked + .dl-chip__face {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--paper);
}

.dl-chip__input:checked + .dl-chip__face .dl-chip__count {
  color: var(--paper);
  opacity: 0.78;
}

.dl-chip__input:checked + .dl-chip__face:hover {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
}

.dl-chip__input:focus-visible + .dl-chip__face {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

.dl-chip__face:hover {
  border-color: var(--border-focus);
}

/* --- segmented sort control -------------------------------------------------- */

/* Laid out like the two selects beside it: label to the left, control on the same centre line.
   A `<legend>` renders in the fieldset's own top slot no matter what, which pushed "Sort by" above
   the segmented control and made the whole cluster taller than the row — the owner's "Sort by
   вообще поехал". `float: left` is the standard escape from the legend slot; the fieldset then
   lays out as an ordinary flex row. The fieldset/legend semantics stay — this is layout only. */
.dl-sortgroup {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.dl-sortgroup > legend {
  float: left;
}

.dl-segmented {
  display: inline-flex;
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
  overflow: hidden;
}

.dl-segmented__opt {
  display: inline-flex;
}

.dl-segmented__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* Same 44px floor as `.dl-chip__face` above, same reasoning. */
.dl-segmented__face {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 15px;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  line-height: 1;
  color: var(--text-body);
  background: var(--white);
  border-left: 1px solid var(--border-2);
  cursor: pointer;
  white-space: nowrap;
}

.dl-segmented__opt:first-child .dl-segmented__face {
  border-left: 0;
}

.dl-segmented__input:checked + .dl-segmented__face {
  background: var(--blue);
  color: var(--text-inverse);
}

.dl-segmented__input:focus-visible + .dl-segmented__face {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: -2px;
}

@media (max-width: 599px) {
  /* 2026-08 touch-target pass: this used to also shrink `.dl-chip__face`'s own padding and font
     size — exactly backwards, since this breakpoint is the one this filter line is most likely
     to be operated by touch rather than a mouse. The chip keeps its full 44px face at every
     width now; only the gap between chips tightens here, to fit more of them per line on a
     narrow viewport without shrinking any single one below the floor. */
  .dl-chips {
    gap: 6px;
  }

  /* **The sort control was unusable on a phone** (2026-10-06). `.dl-segmented` is an
     `inline-flex` pill with `overflow: hidden` so its children clip to the radius, and as a flex
     item it shrinks. Measured at 390px: the pill was 109px wide around 536px of content, so of
     "Price history first", "Lowest price", "Highest price" and "VerifiedDose score" a reader
     could reach the first and nothing else, with no scrollbar and no hint that the rest existed.

     Scrolling a 109px window was the other option and it is worse: a horizontal scroll a reader
     has to discover, inside a row that already scrolls vertically. So at this width the segment
     stops being one pill and becomes four wrapping buttons, which is exactly what the filter
     chips beside it already do. The fieldset and legend are untouched; this is layout only. */
  .dl-segmented {
    flex-wrap: wrap;
    gap: 6px;
    border: 0;
    overflow: visible;
  }

  .dl-segmented__face {
    border: 1px solid var(--border-2);
    border-radius: var(--radius);
  }

  .dl-segmented__opt:first-child .dl-segmented__face {
    border-left: 1px solid var(--border-2);
  }

  .dl-sortgroup {
    flex-wrap: wrap;
  }
}

/* A chip that can yield nothing. Still legible — it carries a real fact — but plainly not a
   control: filled rather than white, no pointer, no hover, and out of the tab order via the
   `disabled` attribute itself.

   Not a dashed border, which was the first attempt: `.claude/rules/design-system.md` forbids them
   outright. A filled face against the white ones beside it is the distinction this system allows. */
.dl-chip--empty .dl-chip__face {
  color: var(--text-muted);
  background: var(--gray-bg);
  border-color: var(--border);
  cursor: default;
}

.dl-chip--empty .dl-chip__face:hover {
  border-color: var(--border);
  background: var(--gray-bg);
}


/* The one line explaining the default order (2026-10-05).

   The sort control is a segmented button whose faces can carry three or four words each, and the
   default order has four keys. Two of them — insurance and prepayment — are not visible on a row,
   so a 9.3-scoring provider at $99 sits ninth and the table reads as unsorted. The button names
   the first key; this names the rest. Micro, muted, directly under the control it describes. */
.dl-sortgroup__note {
  max-width: 46ch;
  margin: var(--space-2) 0 0;
  color: var(--muted);
  font-size: var(--text-micro);
  line-height: 1.4;
}


/* The visible label over each chip group (2026-10-05).

   The legend used to be `dl-sr`. Four radios and two checkboxes rendered identically in one row,
   so nothing said which chips were alternatives and which stacked — see `_filters.html` for the
   report that prompted this. Micro, muted, and a real `<legend>` so the grouping a screen reader
   already had is now the grouping everyone has. */
.dl-chipgroup__label {
  padding: 0;
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-micro);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.02em;
}

.dl-chipgroup + .dl-chipgroup {
  margin-top: var(--space-4);
}

/* --- css/components/status.css --- */
/* ==========================================================================
   Provider status — a dot plus the word, per design-system.html §5.

   "Active" (green dot), "Degraded since 12 Jul" (amber dot), "No longer operating"
   (red dot). The dot is `aria-hidden` and purely a fast visual scan aid; the word is
   what a screen reader hears and what actually carries the meaning — the dot is
   decoration on top of a real written status, never a substitute for one.

   Colour discipline, per tokens/colors.css: --green means verified-active and nothing
   else; --red means dead/degraded-to-shutdown and regulatory alerts and nothing else.
   The degraded dot uses its own --status-dot-degraded tone (amber) rather than reaching
   for --amber (the regulatory-flag colour) or plain ink, so a status dot is never
   visually confused with a regulatory-flag chip in the same row.
   ========================================================================== */

.dl-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--type-small-size);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-status__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--text-muted);
}

.dl-status--active .dl-status__dot {
  background: var(--status-dot-active);
}

.dl-status--degraded .dl-status__dot {
  background: var(--status-dot-degraded);
}

.dl-status--dead .dl-status__dot {
  background: var(--status-dot-dead);
}

.dl-status--lg {
  font-size: var(--type-h3-size);
}

/* --- provider status banner -------------------------------------------------- */

/* Shown whenever Provider.status is not active. Carries switching guidance, and
   cites the record that triggered it. */
.dl-status-banner {
  border-left: 3px solid var(--text);
  padding: var(--space-2) 0 var(--space-2) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 46rem;
}

.dl-status-banner--dead {
  border-left-color: var(--red);
}

.dl-status-banner__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.dl-status-banner__date {
  font-size: var(--type-small-size);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
}

.dl-status-banner__body {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

/* --- css/components/price-cell.css --- */
/* ==========================================================================
   PriceCell — 17px bold price, left-aligned (design-system.html has no right-aligned
   numeric columns anywhere — a deliberate change from an earlier pass's ledger-style
   right alignment), with the plan's terms underneath and the verified date under that
   in green: "$119 / first month, then $139. Dose escalation included / ✓ Price checked
   Jul 28, 2026".

   The hard rule: a price never renders without the date it was verified on, and a
   price that is not publishable renders the words "Not published" rather than a bare
   number — enforced in `dl_price_cell` (dl_components.py), not here.

   Stale-but-verified handling — the one gap the source brief leaves open, resolved
   with typography rather than a fourth colour:

     current (within the freshness window)   a green checkmark plus the date — --green
                                              is reserved for "verified," so it is exactly
                                              the right colour for a fresh check.
     aging (past the window, still real)      the checkmark and date stay, but in muted
                                              ink rather than green — still a real,
                                              dated price, just not reconfirmed lately.
     badly stale                             muted ink, no checkmark glyph, plus an
                                              explicit "not verified in over N days" note.
     unverified / not published              "Not published", muted ink, no colour, no
                                              mark.

   Price changes (the public change log) render in ink by default; the change-log
   partial itself decides when a red/green delta is warranted (a price rise/fall is
   not a provider-status colour, so this file never applies --red/--green to a plain
   "changed from X to Y" line on its own).
   ========================================================================== */

.dl-price {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.dl-price__row {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
}

.dl-price__value {
  font-size: var(--type-money-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
  white-space: nowrap;
}

.dl-price--sm .dl-price__value {
  font-size: var(--step-body);
}

.dl-price__unit {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* The footnote marker inherits from the figure it sits on, which stopped being harmless when the
   table's price went to weight 800: a superscript "3" at 13.6px/800 is a blob, not a reference.
   Found by `tools/type_scale.py` after it was rewritten to measure weight — the second defect it
   caught on its first real run, and the one no screenshot would have shown at that size. */
.dl-price__sup {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

/* "Not published" — the honest rendering of a price we cannot date. */
.dl-price__unpublished {
  font-size: var(--type-body-size);
  color: var(--text-muted);
  white-space: nowrap;
}

/* An advertised figure whose billing term the provider does not state.
   Deliberately NOT the price treatment: body size and muted ink, not the tabular display
   numeral, because a reader scanning a column must not read it as a comparable price. It is a
   real, dated observation (see Plan.is_advertised_only) and it is never ranked or totalled. */
.dl-price__advertised {
  font-size: var(--type-body-size);
  font-weight: 500;
  color: var(--text-2);
  white-space: nowrap;
}

.dl-price__change {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  font-size: var(--type-small-size);
  color: var(--text-body);
  white-space: normal;
}

/* --- the verified date under a price ---------------------------------------- */

.dl-price__sub {
  display: block;
  font-size: var(--type-small-size);
  color: var(--text-muted);
  max-width: 22rem;
}

/*
 * What the figure above is contingent on: a multi-month prepayment, or the reader's insurance
 * covering it. Set in body ink rather than muted, unlike every other secondary line in this
 * component — muted is for context a reader may skip, and this changes the number's meaning.
 * No colour of its own: amber and red are reserved for regulatory flags, and a commitment is
 * not a warning about the provider, it is part of the price.
 */
.dl-price__conditions {
  display: block;
  font-size: var(--type-small-size);
  color: var(--text);
  max-width: 22rem;
}

.dl-price__verified {
  display: inline-flex;
  /* A flex container is single-line unless told otherwise, so without this the stamp could not wrap
     no matter what `white-space` said — it simply overflowed its column and printed on top of the
     next one when the age note made it longer. The segments inside carry `nowrap` individually
     (data-table.css), so this wraps between the words, the date and the age, never inside them. */
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px;
  row-gap: 1px;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
}

/* Current: the checkmark stays, the green does not.
 *
 * v4 (docs/ui_ref_v4) moves green to the identity — money, actions, the price result — and asks
 * evidence to be "quiet but present". Under v3 every dated price printed a green stamp, so a
 * table of twelve verified prices was twelve green lines and the accent had nowhere left to
 * stand out. The tick still says the price is current; muted ink says it without shouting. */
.dl-price__verified--current {
  color: var(--verified-ink);
}

.dl-price__stale-note {
  color: var(--text-muted);
  font-weight: var(--weight-semibold);
}

.dl-price__note {
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* "from GobyMeds" — the provider a state's cheapest verified price belongs to,
   rendered directly under the dl_price_cell it qualifies on /states/. */
.dl-cheapest__who {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size, 12px);
  color: var(--text-muted);
}

/* --- css/components/price-receipt.css --- */
/* ==========================================================================
   PriceReceipt — the month's charges as a paper receipt, on the provider page's price band.

   Added with the v5 migration, phase 3. The reference pack calls it "the price receipt" and makes
   it the one white surface sitting on the dark band: a document held up against the page, which is
   the metaphor the whole site runs on — we did not ask the provider what it charges, we read its
   page and wrote down what it said.

   The layout is a two-column list with the amounts right-aligned and tabular, because that column
   is the point. An equation reads left to right and answers "what is this made of"; a right-aligned
   stack answers "what costs what", and on Mochi the answer is that the $79 membership outweighs
   the $60 of medication. No arrangement of `$60 + $79` shows that as quickly.
   ========================================================================== */

.dl-receipt {
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  /* The one shadow on this page, and one of exactly two the doctrine allows outside a true
     overlay — see `.claude/rules/design-system.md`, which was amended to name it rather than
     have this comment claim an exception the rule did not contain. The card sits *on* the dark
     band rather than in the flow of it, and with no depth at all it reads as a hole cut in the
     band instead of a document laid on it. */
  box-shadow: 0 12px 36px rgb(54 33 57 / 12%);
}

.dl-receipt__eyebrow {
  margin: 0;
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.dl-receipt__title {
  margin: var(--space-1) 0 var(--space-4);
  font-size: var(--step-title);
  line-height: 1.2;
  color: var(--text-body);
}

/* **Two rules per receipt, not one per row** (2026-10-02).
 *
 * Every row used to carry its own `border-bottom`, so a three-row receipt drew four horizontal
 * lines and a two-row receipt drew three. Side by side in the home page's three cards that was
 * the defect the owner reported as "линии в них не ровные": the rules sat at different heights
 * from card to card, and since the row shape also differed per provider, nothing lined up with
 * anything. The row shape is fixed now (`dl_price_receipt`), and this is the other half — the
 * pack draws one rule above the group and one below it and none between, which is both calmer
 * and, at 47px per row against 30, a great deal shorter.
 *
 * Geometry straight from the pack's `.cost-lines`: 17px of padding inside the two rules, 7px
 * between rows, 14px type. */
.dl-receipt__lines {
  margin: 0;
  padding: var(--space-4) 0;
  border-top: var(--border-width) solid var(--line);
  border-bottom: var(--border-width) solid var(--line);
}

.dl-receipt__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: 7px 0;
}

.dl-receipt__label {
  margin: 0;
  font-size: var(--step-small);
  color: var(--text-muted);
}

.dl-receipt__figure {
  margin: 0;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
  /* Tabular, so the amounts form a true column. This is the single reason the receipt beats the
     equation at its own job, and it stops being true the moment the figures set proportionally. */
  font-variant-numeric: var(--tabular);
  white-space: nowrap;
}

/* The calculation, behind a disclosure — the pack's own closing move for this panel. Replaces the
   repeated total; see includes/_price_receipt.html for why that had to go. */
.dl-receipt__calc {
  margin-top: var(--space-3);
}

.dl-receipt__calc > summary {
  cursor: pointer;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--blue);
}

.dl-receipt__sum-line {
  margin: var(--space-2) 0 0;
  font-size: var(--step-small);
  color: var(--text-2);
  font-variant-numeric: var(--tabular);
}


/* **The gap row is muted, not amber** (2026-10-02, Codex review).
 *
 * It carried `--caution` ink, on the argument that a reader who misses this row will read the
 * total as the whole bill. The argument is sound and the colour was not available to make it:
 * `.claude/rules/design-system.md` reserves amber for regulatory findings and provider status,
 * with exactly one named exception — the billing-term band — and ends the clause "Anything else
 * wanting amber needs this line changed first". A hole in our research is neither of those, and
 * amber beside a provider's name on a monetized page reads as a finding against the company
 * rather than an admission about us. That is a worse error than the one the colour was guarding.
 *
 * Nothing is lost, because the row never signalled by colour alone and the Law forbids that
 * anyway: the figure column reads "Not established" where every other row reads an amount, and
 * the label of the total below now says "medication only" wherever this row is the reason. Words
 * in both places, colour in neither.
 */
.dl-receipt__line--gap .dl-receipt__figure {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
  font-variant-numeric: normal;
}

/* The commitment, on its own filled line. It is the only element here that is not a number, and it
   carries a surface because it is the fact most often skipped: the pack's own receipt puts the
   billing term in a peach band under the arithmetic for exactly this reason. */
/* **The pack's own surface for the billing term** (2026-10-02, on the owner's instruction that the
 * reference governs).
 *
 * Phase 5 put this on `--sky` and `--peach` instead, reasoning that `design-system.md` reserves
 * amber for regulatory findings and provider status, and that an amber band beside a price reads
 * as a warning about the company. Codex agreed. The owner looked at the two screenshots and said
 * the reference governs, which is his call to make about his own doctrine — so the band takes
 * `--caution-bg` with `--caution` ink, and the Law line is amended to name this exception rather
 * than being quietly broken here.
 *
 * The committed variant is gone with it: the pack uses one surface for every term and lets the
 * words carry the difference ("1 month · paid monthly" against "12 months · prepaid"), which is a
 * non-colour signal and the stronger one. */
.dl-receipt__term {
  margin: var(--space-4) 0 0;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--caution-bg);
  font-size: var(--step-small);
  line-height: 1.5;
  color: var(--caution);
}

.dl-receipt__verified {
  margin: var(--space-3) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
}

@media (max-width: 599px) {
  .dl-receipt {
    padding: var(--space-4);
  }
}

/* --- css/components/provider-name.css --- */
/* ==========================================================================
   ProviderName — the plain name line, shown as a caption under a *real* logo image
   (ProviderLogo, provider-logo.css). When there is no logo on file, the wordmark
   fallback already carries the name, so this line is suppressed there (`show_name`) —
   see dl_provider_name's docstring for the "mark plus caption" reasoning.

   The optional regulatory-flag marker is a small red-bordered mark, shown only when a
   flag exists. It states the fact and links to the source elsewhere on the page; it
   never characterises.
   ========================================================================== */

.dl-provider {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  row-gap: var(--space-1);
}

.dl-provider__name {
  font-family: var(--font-sans);
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.02em;
  color: var(--text-body);
}

/* 2026-08 clickability fix: neutral ink at rest, blue and underlined on hover/focus — not
   blue at rest the way `design-system.html`'s own generic `a{color:var(--blue)}` rule reads.
   A master table lists up to 21 providers; painting every one of their names blue at rest
   would repaint the table's entire left edge in the one accent colour the doctrine reserves
   for actions and links elsewhere on the page, directly against "colour is carried by the
   provider logos, the site itself is neutral" (.claude/rules/design-system.md). The name is
   still unambiguously a link: a real `<a>` (cursor, tab stop), the row's own brand mark sitting
   beside it, and hover/focus turning it blue and underlined — the same two-stage disclosure
   the "Read our X review" and "Visit X's official site" labels already use elsewhere in the
   row, just inverted (those are blue at rest because they carry no other visual weight; the
   name is bold and 21px, which already reads as the row's heading without needing colour too).
   `:focus-visible` is unstyled here on purpose — see provider-logo.css's own note; the
   universal rule in tokens/base.css already outlines every focusable element on the site. */
/* Overruled after the owner's review, and the reasoning above is worth keeping because it is not
   wrong — it is just outweighed here.
   The complaint was discoverability: "nowhere to click". A hover state cannot answer that, because
   a reader scanning a page has not hovered anything yet. `design-system.html`'s own rule for a text
   link is blue, and the provider name IS a text link — one accent colour doing the job it is
   reserved for is not decoration, which is what the neutrality doctrine actually forbids. The
   underline stays on hover only, so the column is not a field of underscores. */
a.dl-provider__name {
  color: var(--blue);
  text-decoration: none;
}

a.dl-provider__name:hover,
a.dl-provider__name:focus-visible {
  color: var(--blue-dark);
  text-decoration: underline;
}

.dl-provider--lg .dl-provider__name {
  font-size: var(--step-metric);
}

.dl-provider__flag {
  font-size: var(--type-micro-size);
  font-weight: var(--weight-semibold);
  color: var(--red);
  border: 1px solid var(--red);
  border-radius: var(--radius);
  padding: 1px 5px;
  white-space: nowrap;
}

/* The provider column in the master table stacks the name over its markers. */
.dl-provider-cell {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

/* --- css/components/provider-logo.css --- */
/* ==========================================================================
   ProviderLogo — a logo slot per row in the master table, and again in the review
   header. `Provider.logo` is nullable and today's dataset has none on file, so the
   fallback is not a rare edge case, it is the common case, and it is designed as one.

   The fallback is never a generic building/briefcase icon (the anti-list forbids
   icons in coloured squares outright) and never an empty cell. It is the provider's
   own name, set bold and slightly larger than running text — the same weight
   design-system.html's own `.plogo` demo uses for TrimRX/hims/ro/Mochi.

   Deliberately NOT given a guessed per-brand colour. The mockups' own fallback text
   is tinted per provider (TrimRX orange, Mochi purple) because those are the real
   brand colours the mockups' author already knew — this project has no sourced brand
   colour for a provider with no logo on file, and inventing one would be exactly the
   kind of unsourced claim the site's whole trust argument argues against. The fallback
   here stays neutral ink; colour arrives only with a real, uploaded logo image, which
   is nominative use only (never recoloured, cropped or given a coloured backdrop, per
   `Provider.logo`'s own help_text).

   Fixed-height box so every row keeps the same height regardless of which providers
   have supplied a mark and which have not — the same discipline the "not published"
   price cell already applies to a data gap.
   ========================================================================== */

/* Mark and name on one line, per the pack's `.plogo`. The mark is a square: sizing it by
   max-height as if it were a wide wordmark file is what made it a postage stamp.

   `min-height` is the row tile's height and has to move with it — it was 34px against a 34px tile
   and is 48px against the 48px one. A floor shorter than the tile stops being a floor. */
.dl-plogo {
  display: flex;
  align-items: center;
  min-height: 48px;
}

.dl-plogo--lg {
  min-height: 44px;
  margin-bottom: var(--space-2);
}

/* 2026-08 chip pass: a white, bordered, rounded tile behind a real uploaded mark — the
   owner's own "provider logos in white rounded tiles" note from a competitor's table. Sized
   slightly larger than the image it holds so a non-square or non-white brand mark gets a
   little breathing room against the tile's own edge, the same reasoning a favicon tray uses.
   Colour on this site is carried by the provider's own mark (`.claude/rules/design-system.md`
   — "the site itself is neutral"); the tile is white/bordered specifically so it never
   competes with whatever hue the logo itself brings in. */
/* **The tile stops being a square** (2026-08-28). It was 34x34 with the mark forced to a fixed
   26x26, and the old comment stated the assumption plainly: "a favicon-shaped mark has no aspect
   ratio to preserve". That was true of all six marks then on file, because all six came from a
   favicon service. `ingest_collected_logos` replaced them with the providers' own wordmarks, where
   it is false — PlushCare is 6.1:1 and Enhance MD is 10.3:1, and inside a fixed 26x26 box
   `object-fit: contain` letterboxes those to about 26x4px. Legible as a grey smudge, nothing more.

   So the height is fixed and the width follows the mark, between a square minimum (so a genuinely
   square favicon still gets the tile it was designed for) and a maximum that keeps the widest
   wordmark from pushing the provider name out of a table row. That is what the doctrine's "fit
   official marks at their optical size when supplied" asks for. */
/* **A fixed slot, with the mark letterboxed inside it** (2026-09-02).
   
   This went through both wrong answers first. It began as a fixed 34x34 square with the mark
   forced to 26x26, which cropped nothing only because all six marks then on file were square
   favicons; once real wordmarks landed, `object-fit: contain` squeezed PlushCare's 6.1:1 lockup
   into about 26x4px. The fix for that was `min-width: 34px; max-width: 104px` — the tile hugging
   its own mark — and it traded one visible defect for another: a favicon got a 34px tile and a
   wordmark a 104px one, so **the provider names started at three different x positions down a
   single column**. The owner's word for the result was the right one.
   
   Both problems come from letting one box serve two jobs. The slot is a column, so it is constant;
   the mark inside it is variable, so it scales. Fixed box, mark contained inside it, centred both
   ways: no crop, and every name begins at the same place. A square favicon simply gets more
   air around it than a wordmark does, which is what a reserved slot means. */
/* **76x48 in a table row** (v5, 2026-10-01). It was 96x34 — a letterbox, tuned when the only
   marks on file were six wordmarks. 39 marks later the set runs from 69x24 to 768x336, and a slot
   2.8 times wider than it is tall gives a square mark almost no height to live in: it shrinks to
   fit the 34px and ends up a third the optical size of the wordmark beside it.
   The pack's 1.58 ratio is the compromise across both shapes, and it is the same tile at three
   scales — 96x56 in a card, 76x48 in a row, 130x80 on a page.

   `--dl-logo-slot` in data-table.css carries this width plus the margin and must move with it.
   That token exists because the provider column's indented blocks hang off the slot, and the last
   time the two drifted the column had three different left edges. */
.dl-plogo__tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 76px;
  height: 48px;
  padding: 5px;
  flex: none;
  margin-right: 10px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* The reserved slot with nothing in it — see _provider_logo.html for why it exists at all.
   It holds the column and draws nothing: no border, no fill, no glyph. `visibility` is wrong here
   and `display: none` is worse; the element must occupy its width, which is its whole job. */
.dl-plogo__tile--empty {
  background: transparent;
  border-color: transparent;
}

/* The card tile, 96x56 (v5). Used by the home page's three leading picks, which is the one place
   on the site where a mark is doing recognition work rather than labelling a row. */
.dl-plogo--lg .dl-plogo__tile {
  width: 96px;
  height: 56px;
  margin-right: 12px;
}

/* The mark fills its tile's inner box and contains inside it.

   This replaces a pair of hand-tuned caps — `max-height: 26px` in a row, `34px` in a card — that
   had to be re-derived by hand every time a tile changed size, and silently stopped fitting when
   this pass made the row tile 48px tall instead of 34px. `object-fit: contain` against a
   100%/100% box is the same rule the pack states and needs no number: whichever axis runs out
   first stops the mark, so a 768x336 PNG and a 69x24 SVG both land inside the same padding. */
.dl-plogo__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 3px;
}

/* 2026-08 clickability fix: the mark and the wordmark now share one `<a>`
   (`.dl-plogo__link`, includes/_provider_logo.html) instead of the wordmark text being the
   anchor on its own — the tile is part of the click target now, not just the name beside it.
   `:focus-visible` is unstyled here on purpose: `tokens/base.css`'s own universal
   `:focus-visible` rule already outlines every interactive element on the site, this anchor
   included, so repeating it here would just be a second declaration of the same value. */
.dl-plogo__link {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* **An inset ring inside the scrolling table** (2026-10-04, `tools/focus_ring_clip.py`).
   Below 900px `.dl-table-wrap` becomes `overflow-x: auto` so the sticky provider column can
   work, and the provider link is the first thing in the first cell: measured at 390px it sits
   at exactly 0px from the wrapper's padding box on the home page, `/switching/` and
   `/costs/wegovy/` — 34 links across the three. The ring is 3px at a 4px offset, so all 7px of
   its left side fall in the clipped region and a keyboard user tabbing into the table sees
   nothing at all. It fails WCAG 2.4.11 in the quiet direction: the ring is painted, so the page
   looks correct to anyone using a mouse.

   Drawing the ring inside the border box is the fix `.dl-pcol` already uses for the same reason
   (`provider-column.css`, the card clips its own corners), and it keeps the table's left edge
   where it is — padding on the wrapper would move the first column away from the page gutter on
   the one screen size where that space is scarcest. Scoped to the scrolling tier: at >=900px the
   wrapper clips nothing and the standard offset ring is the better-looking one. */
@media (max-width: 899px) {
  .dl-table-wrap .dl-plogo__link:focus-visible {
    outline: var(--focus-width) solid var(--focus-color);
    outline-offset: calc(-1 * var(--focus-width));
  }
}

/* Blue at rest, not only on hover. The whole point of the clickability pass was that a reader
   scanning the page could not tell what was a link — and a hover state answers nothing for someone
   who has not hovered yet. `design-system.html`'s own rule for a text link is blue; the provider
   name is a text link, and an accent colour used for the job it is reserved for is not the
   decoration the neutrality doctrine forbids. The underline stays on hover so the column does not
   become a field of underscores, and the brand tiles keep carrying the actual colour. */
.dl-plogo__link .dl-plogo__wordmark {
  color: var(--blue);
}

.dl-plogo__link:hover .dl-plogo__wordmark,
.dl-plogo__link:focus-visible .dl-plogo__wordmark {
  color: var(--blue-dark);
  text-decoration: underline;
}

.dl-plogo__wordmark {
  display: inline-block;
  font-family: var(--font-sans);
  font-weight: var(--weight-black);
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--text-body);
}

.dl-plogo--sm .dl-plogo__wordmark {
  font-size: var(--step-title);
}

.dl-plogo--lg .dl-plogo__wordmark {
  font-size: var(--step-metric);
}

/* --- hero: the ranked card's identity line (2026-08-14 rebuild) ------------------------- */

/* Almost everything this block used to hold is gone with the bordered tile it styled — the
   `--hero` branch of includes/_provider_logo.html now emits either an image or a wordmark and
   nothing around them, and the slot's own geometry belongs to the card (`.dl-pcol__name`,
   provider-column.css) rather than to the logo component. What has to stay here is the one
   rule that is about the mark itself rather than about the card holding it. */

/* The fallback wordmark inherits its size, weight and colour from the heading it sits in, so it
   is declared with only what is specific to it: the wrapping behaviour a long provider name
   needs inside a 300px column. `word-break: break-word` rather than `anywhere` — "Medical
   Weight Loss Center" should break between words, and a single long brand name should break
   inside itself only as a last resort. */
/* The hero identity: the mark, then the name, on one baseline.
 *
 * Both are always present now (see _provider_logo.html) — the mark simply absent for the 33
 * providers who have none. That is what makes one row of cards one shape instead of two.
 *
 * The mark sits in a fixed 40px square, and — unlike the master table's provider cell — the slot
 * is **not** reserved when there is no mark. The two cases are genuinely different. In the table
 * the rows stack, so a name that starts 44px further left than the one above it draws a ragged
 * edge down the whole column, which is why `--dl-logo-slot` holds the space there. Three cards
 * side by side have no such column: a reader reads each card as its own block, and reserving the
 * slot would punch a 52px hole into the 33 cards that have no logo to put in it — the same "left
 * side empty" complaint, moved to the other 33. So the mark pushes the name right only where a
 * mark exists.
 *
 * No placeholder is ever drawn in the empty case: inventing a brand asset is this file's own
 * standing rule, and the reason the name set properly *is* the mark for those 33. */
.dl-plogo__hero {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* **The pack's `.logo-tile`: 96x56, white, 1px border, 8px radius, 10px of padding** (v5,
   2026-10-02). It was a bare 40px square with the mark floating on the coloured header. The tile
   is what gives every mark the same optical weight regardless of its own aspect ratio — a 69x24
   SVG wordmark and a square favicon both end up as one object of one size, which is the whole
   reason the reference's three card headers line up and ours did not. */
.dl-plogo__hero-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 96px;
  height: 56px;
  padding: 10px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* Same reasoning as `.dl-plogo__tile--dark`: a mark published in white renders as nothing on a
   white tile, and recolouring someone's mark is forbidden, so the tile moves instead. Which
   ground each mark needs is measured by `audit_logo_legibility`, not guessed. */
.dl-plogo__hero-mark--dark {
  background: var(--ink);
  border-color: var(--ink);
}

/* Name over descriptor, beside the tile — the pack's `.identity > div`. */
.dl-plogo__heroname {
  display: block;
  min-width: 0;
}

.dl-plogo__herokind {
  /* A wrapping flex row rather than a block: the model type and, on a ranked card, the crowd
     rating sit on this one line. It had 236px spare on a 413px card. */
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px var(--space-3);
  margin-top: 4px;
  font-size: var(--step-micro);
  font-weight: var(--weight-regular);
  letter-spacing: 0;
  color: var(--text-muted);
}

.dl-plogo__img--hero {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  /* Uncropped, whatever the mark's aspect ratio — `max-40x40` fits it inside the square rather
     than filling it. */
  object-fit: contain;
}

.dl-plogo__wordmark--hero {
  display: block;
  min-width: 0;
  /* A long name wraps rather than truncating. "WeightWatchers US" and "Measured Health" both fit
     one line at 22px in a 300px column; a longer one takes two, and the subgrid gives that extra
     height to all three cards at once so the row stays aligned. Truncation is the wrong trade
     here — a provider's name is the one string on the card a reader must be able to read whole. */
  word-break: break-word;
}


/* --- tap targets on a phone (2026-08-16) ------------------------------------------------- */

/*
 * `tools/mobile_check.py` flags every standalone link under 44px at 360px wide — the size Apple
 * and the WCAG 2.5.8 enhanced criterion both name. The provider link in a table row measured
 * 23-34px depending on whether the provider has a real mark or a set wordmark.
 *
 * A `min-height` rather than padding, and scoped to the phone tier: the row it sits in is already
 * ~130px tall on a phone, so the extra height falls inside space the cell already occupies and
 * nothing moves. On desktop the rows are dense by design and a mouse does not need the same
 * target, so the rule stays out of the way there.
 */
@media (max-width: 42rem) {
  .dl-plogo__link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}


/* **The dark tile, for marks drawn on a dark header** (2026-08-28). Three providers publish their
   wordmark in white or near-white — Sprout Health's file is named `Logo-sh-white.png` — and on the
   white tile they render as nothing. `Provider.logo`'s own help text forbids restyling or
   recolouring a mark, and `audit_logo_legibility` measures which ground each one needs rather than
   anyone guessing. So the mark is untouched and the tile moves under it: this is the background its
   designer assumed, not a treatment of ours.

   `--navy` rather than black, because it is the one dark surface the palette already reserves and
   it keeps a green-tinted mark from sitting on a competing hue. */
.dl-plogo__tile--dark {
  background: var(--ink);
  border-color: var(--ink);
}

.dl-plogo__herokind .dl-crowd {
  margin: 0;
}

/* --- css/components/score.css --- */
/* ==========================================================================
   Score — the VerifiedDose score: the number, "/10", and the words naming what it is.

   **The star is gone (2026-08-15).** It used to be one blue star plus a number on a 5-point
   scale, which is a defensible rating pattern in isolation and was indefensible here: the same
   glyph and the same denominator carry Trustpilot's crowd average two columns away. One is
   thousands of customers; the other is our arithmetic over published prices, filings and state
   lists. The star now belongs to the crowd alone (user-rating.css) and this number carries a
   label instead. See rubric.py's SCALE_MAX note for the reasoning and the objection to it.

   The figure goes into Schema.org unmanipulated, with the band declared honestly — see
   includes/_json_ld.html.

   Three sizes: `sm` in the master table's score column and the compare table; `lg` in the
   homepage's "Our pick" card; `xl` where the score is the anchor of its own block.
   ========================================================================== */

.dl-score {
  display: inline-flex;
  align-items: baseline;
  /* Wraps so `__what` can take its own line via `flex-basis: 100%`. Without it the label sat on
     the baseline beside "/10" and read as part of the figure — "7.0 /10 VerifiedDose score" on one
     line, which is a caption pretending to be a unit. */
  flex-wrap: wrap;
  gap: 3px;
  font-weight: var(--weight-black);
  color: var(--blue);
}

.dl-score--sm {
  font-size: var(--step-title);
}

.dl-score--lg {
  font-size: var(--step-metric);
}

/* --xl: the anchor size, where the score is the subject of its own block rather than a cell in
   a row. Font-size only — no colour change. */
.dl-score--xl {
  font-size: 40px;
}

.dl-score--xl .dl-score__max {
  font-size: var(--step-body);
}

.dl-score__max {
  font-size: var(--step-small);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

/* The words "VerifiedDose score", set small and quiet on their own line.
 *
 * Not optional and not a tooltip. A bare number beside a provider's name is read as a rating of
 * the provider by whoever is publishing the page; the only thing that stops that reading is
 * saying what the number is, every time it appears. It wraps to its own line at every size so a
 * narrow table cell cannot push it out of view. */
.dl-score__what {
  flex-basis: 100%;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-regular);
  line-height: 1.3;
  color: var(--text-muted);
  letter-spacing: 0;
}

/* "Best for …", one line, directly under the rating — not bold, not the accent. */
.dl-score__bestfor {
  display: block;
  margin-top: 2px;
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-body);
  max-width: 20rem;
}

/* `min-height: 24px` + centred flex, not a taller line-height on the text (2026-08-26). This link
 * had never rendered anywhere on the live site until the price band's score card started passing
 * `methodology_href` — no other caller of `dl_score` does — and `tools/mobile_check.py` caught it
 * the moment it did: 14px text at this component's own `--type-small-lh` (1.5) computes a 21-22px
 * box, under the 24px WCAG 2.5.8 AA floor, and this link sits on its own line rather than inside a
 * sentence, so it does not qualify for that criterion's inline-text exemption. The box grows, the
 * type does not. */
.dl-score__method {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-top: var(--space-1);
  font-size: var(--type-small-size);
  color: var(--blue);
  text-decoration: underline;
}

/* --- unscored state: a dash, never a fabricated number ----------------------- */

.dl-score--none {
  color: var(--text-muted);
  font-weight: var(--weight-bold);
}

.dl-score__none-note {
  display: block;
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

/* --- css/components/score-panel.css --- */
/* ==========================================================================
   ScorePanel — includes/_score_panel.html, built by dl_score_panel(). New in the 2026-08
   owner-directed depth pass (docs/ai/depth_and_second_accent_2026-08-04.md): a boxed panel with
   a big number and one row per rubric dimension, each with a bar — the highest-value item in
   that pass, since apps/providers/rubric.py already computes every input this needed.

   Not in design-system.html. The pack's `.dl-score` (score.css) — one blue star plus a number —
   stays exactly as it is and keeps its every existing use (the master table, cards, the compare
   table): this is a second, boxed presentation of the same underlying number for the one place
   (the review page's first screen) that has room for the full per-dimension breakdown, not a
   replacement of the inline component everywhere else.

   --accent2, not --blue, colours the big numeral and the closing link: this is the one narrowly-
   scoped use of the second accent this pass introduces (see tokens/colors.css's own comment) —
   the inline star rating used across hundreds of existing table rows and cards is untouched and
   stays --blue.

   The bar is 21 discrete width classes (`.dl-bar--0` … `.dl-bar--100`, step 5), not an inline
   `style` or a CSS custom property set inline — both are `style="` on the wire, which
   `test_no_inline_styles_or_handlers` forbids project-wide. `dl_score_panel` rounds each
   dimension's fraction to the nearest one before choosing a class.
   ========================================================================== */

.dl-score-panel {
  background: var(--surface-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-6);
  box-shadow: var(--shadow-elevated);
}

/* The "Best for …" chip — --blue at 10% alpha as the fill, --blue as the text, per the
   reference build's own "score badge pill" component code (bg-[accent]/10, text-[accent],
   8px radius). Ours substitutes --blue for their teal; see tokens/colors.css's --blue-a10. */
.dl-score-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--space-2);
  padding: 4px 10px;
  border-radius: var(--radius-lg);
  background: var(--blue-a10);
  color: var(--blue);
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
}

.dl-score-panel__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: var(--rule-hairline);
}

.dl-score-panel__num {
  font-size: 42px;
  line-height: 1;
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  font-variant-numeric: var(--tabular);
  color: var(--accent2);
}

.dl-score-panel__num--none {
  color: var(--text-muted);
}

.dl-score-panel__max {
  margin-left: 2px;
  font-size: var(--step-lede);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
}

.dl-score-panel__src {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-score-panel__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dl-score-panel__rowhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: 5px;
}

.dl-score-panel__rowlabel {
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-score-panel__weight {
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-score-panel__rowraw {
  flex: none;
  font-size: var(--step-micro);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
  white-space: nowrap;
  text-align: right;
}

.dl-score-panel__nodata {
  display: block;
  font-size: var(--step-micro);
  font-style: italic;
  color: var(--text-muted);
}

.dl-bar {
  display: block;
  height: 6px;
  border-radius: 999px;
  background: var(--gray-bg);
  overflow: hidden;
}

.dl-bar__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent2);
}

.dl-bar--0 { width: 0%; }
.dl-bar--5 { width: 5%; }
.dl-bar--10 { width: 10%; }
.dl-bar--15 { width: 15%; }
.dl-bar--20 { width: 20%; }
.dl-bar--25 { width: 25%; }
.dl-bar--30 { width: 30%; }
.dl-bar--35 { width: 35%; }
.dl-bar--40 { width: 40%; }
.dl-bar--45 { width: 45%; }
.dl-bar--50 { width: 50%; }
.dl-bar--55 { width: 55%; }
.dl-bar--60 { width: 60%; }
.dl-bar--65 { width: 65%; }
.dl-bar--70 { width: 70%; }
.dl-bar--75 { width: 75%; }
.dl-bar--80 { width: 80%; }
.dl-bar--85 { width: 85%; }
.dl-bar--90 { width: 90%; }
.dl-bar--95 { width: 95%; }
.dl-bar--100 { width: 100%; }

/* The date behind the panel's money rows (2026-10-05). Matches the receipt's own verified line
   — micro, muted, no box — because it makes the same statement about the same kind of figure.
   It sits above the foot's hairline so it reads as part of the numbers, not of the methodology
   sentence. */
.dl-score-panel__verified {
  margin: var(--space-3) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
}

.dl-score-panel__verified time {
  font-variant-numeric: tabular-nums;
}

.dl-score-panel__foot {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: var(--rule-hairline);
  font-size: var(--step-micro);
  line-height: 1.55;
  color: var(--text-muted);
}

.dl-score-panel__foot a {
  color: var(--accent2);
  font-weight: var(--weight-semibold);
  text-decoration: underline;
}

.dl-score-panel__foot a:hover {
  color: var(--accent2-dark);
}

/* "What stands out" — the four marks, under the rubric panel on a provider page. Bordered rather
   than boxed in ink: the 2px border belongs to the verdict box alone, and this is a summary, not
   a conclusion. */
.dl-marks-panel {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--surface-elevated);
}

/* 600, and `base.css` already says so.
 *
 * This selector is in that file's manual opt-down list — the one place the small-heading
 * exceptions are kept — and this rule sat one file later in the cascade setting the weight back to
 * 800, so the opt-out had no effect. Two rules disagreeing about the same selector, with the loser
 * being the one that documents the intent. Found by `tools/type_scale.py` after the probe learned
 * to exempt figures and wordmarks and only real labels were left in its output. */
.dl-marks-panel__label {
  margin: 0 0 var(--space-3);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
}

.dl-marks-panel .dl-marks {
  gap: 8px;
}

/* Marks: axis above statement, so the block reads as a fixed schema rather than five loose
 * sentences — the same four questions answered for all 41 providers.
 *
 * Tones, after a second-opinion review caught the palette being misused:
 *   yes         green tick. Green means verified and only that.
 *   none        muted dash. A neutral fact — cash-pay, support-only cancellation.
 *   watch       body-weight ink, statement in bold. A finding worth pausing on that is NOT a
 *               regulator's action: no published legal entity, undisclosed cancellation terms,
 *               a headline price that needs a prepaid year.
 *   regulatory  amber, the ONLY tone allowed to use it. An FDA letter or a docketed suit.
 *
 * Keeping watch-outs out of amber is what keeps amber loud when a real letter appears.
 */
.dl-marks__copy {
  display: grid;
  gap: 1px;
  min-width: 0;
}

/* Stepped up on 2026-08-18 (Codex audit): the rail read as fine print — an 11px axis over a
   14px finding — and the owner's word for it was "шрифт какой-то мелкий справа". 14/16 now.
   Deliberately NOT applied to the score panel's own raw values and footer below: the rail must
   stay quieter than the 42px price anchor, and those lines are provenance, not findings. */
.dl-marks__axis {
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

.dl-marks__statement {
  font-size: var(--step-body);
  line-height: 1.4;
  color: var(--text-body);
}

.dl-marks__item--watch .dl-marks__statement {
  font-weight: var(--weight-bold);
  color: var(--text);
}

.dl-marks__item--regulatory .dl-marks__chip {
  background: var(--amber-bg);
  color: var(--amber);
}

.dl-marks__item--regulatory .dl-marks__statement {
  font-weight: var(--weight-bold);
  color: var(--amber);
}

.dl-marks-panel .dl-marks {
  gap: 12px;
}

/* --- the icon chip (2026-08-17) ------------------------------------------------------------
 *
 * This panel used to be five rows of `✓`, `—` and `•` set at body size. The owner read it as text
 * pretending to be a component and he was right — five rows, one size, one colour, punctuation
 * doing the work of structure.
 *
 * The previous doctrine banned "icons in coloured squares" outright, which is why it stayed that
 * way. The 2026-08-17 rewrite allows an icon that does work, and this one does two jobs: the
 * glyph names the axis, so a reader finds "the insurance one" by shape rather than by reading
 * five labels, and the chip's fill separates a verified answer from a caution at a glance.
 *
 * **Status never rides on the fill alone.** The axis and the finding are both stated in words
 * beside it — the doctrine's Law requires that, and the SVG is `aria-hidden` so a screen reader
 * gets the text and nothing else.
 *
 * Circle, not a rounded square: a square chip at this size reads as a button. */
.dl-marks__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--gray-bg);
  color: var(--text-2);
}

/* Verified: the accent, at the tint the rest of the site uses for a quiet green surface. */
.dl-marks__item--yes .dl-marks__chip {
  background: var(--tip);
  color: var(--blue);
}

/* A watch-out: full-strength ink on the neutral fill. Not amber — amber is a regulator's action
   and nothing else, and spending it on "no legal entity published" is what makes it stop meaning
   anything when a real letter arrives. */
.dl-marks__item--watch .dl-marks__chip {
  color: var(--text);
}

/* --- css/components/rank-chip.css --- */
/* ==========================================================================
   RankChip — includes/_winner_card.html's numbered position mark. New in the 2026-08
   owner-directed depth pass (docs/ai/depth_and_second_accent_2026-08-04.md), replacing the plain
   "#1" text `.dl-winner-rank` used to draw.

   Solid navy, not a tinted circle: the anti-list forbids "icons in coloured squares" specifically
   (a decorative glyph standing in for content in a tinted box) — this is a numbered position
   mark with real informational content, the same category of thing as a table's row number, so
   it is drawn in the site's own --navy rather than invented pastel per rank. Round is one of the
   few shapes this system allows to be circular (the byline's photo/initials mark is the other),
   kept deliberately short rather than expanded into a general "chip" system.
   ========================================================================== */

.dl-rank-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 50%;
  background: var(--navy);
  color: var(--white);
  font-size: var(--step-body);
  font-weight: var(--weight-black);
  font-variant-numeric: var(--tabular);
  line-height: 1;
}

/* --- css/components/data-table.css --- */
/* ==========================================================================
   DataTable — the master comparison table, the hero object of the site.

   Per design-system.html §7: a 2px `--text` rule under the header row, 1px `--border`
   rules between body rows, generous vertical padding (22px/14px) rather than a fixed
   row height — air is part of the pattern, not a bug. No vertical rules between
   column groups; the mockups draw none.

   Column order: Provider (logo/name + one description line), Our rating, Price per
   month, Membership fee, Learn more (CTA). "Not published" gets a pale grey cell at the
   same typographic weight as a real price — not a washed-out, embarrassed placeholder.
   A gap in the data is stated, not hidden.

   There is no dedicated "Regulatory record" column any more (2026-08 owner review): on
   the seeded data 17 of 18 rows said "No actions on record", which is exactly the noise
   that review named. The one row that does carry a flag keeps it inline in the Learn
   more cell, next to that row's own CTA (`.dl-btncell .dl-flag` below) — a reader about
   to click has to see the letter without a separate column to scroll to — and the dated
   detail lives in `.dl-reg-actions` beneath the table instead.

   Implementation notes carried over unchanged from the previous build (still
   correct, still load-bearing, still covered by apps/core/tests/test_table_gutters.py
   and test_no_horizontal_scroll.py):

   - `border-collapse: separate` with `border-spacing: 0`, because collapsed borders
     vanish from `position: sticky` cells and the sticky provider column is how this
     table survives a phone.
   - Every cell gets padding on BOTH sides (`padding-inline`), zeroed only at
     `:first-child` / `:last-child` — never by column role — so no column can lose its
     gutter to a neighbour.
   - Below 900px the table scrolls horizontally inside a focusable region with the
     Provider column pinned via `position: sticky`, and `.dl-table-wrap` carries
     `contain: paint` so that sticky cell cannot leak into the document's own
     scrollable width. It is never converted into stacked cards and no column is ever
     dropped.

   Deviation from the previous pass, and why: that build froze Provider *and* Price
   together on mobile, reasoned as "the pair a reader compares first." The mockups'
   own column order puts Our rating between them, so freezing both would mean pinning
   two independently-sized sticky columns whose combined width shifts with the
   rating's own content. Freezing Provider alone is simpler, keeps the frozen region's
   width predictable, and the table is still a real, complete, horizontally-scrollable
   table either way — nothing here drops a column or turns it into cards.
   ========================================================================== */

.dl-table-wrap {
  max-width: 100%;
  /* 2026-08 depth pass (docs/ai/depth_and_second_accent_2026-08-04.md): the table now sits
     *inside* a rounded, bordered, corner-clipped box rather than being a bare table on the page
     — measured directly off the reference build's own "table container" component code
     (`rounded-xl border bg-white overflow-hidden`). `overflow: hidden` is safe unscoped: at
     >=900px the table never exceeds this wrapper's width (no forced min-width at that tier), so
     nothing is actually clipped; below 900px the mobile rule just below replaces it with
     `overflow-x: auto`, which clips the same rounded corners while still allowing the
     horizontal scroll the sticky-provider-column pattern requires. */
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

/* **`clip` instead of `hidden`, and only here** (2026-10-01).

   The two clip identically; only `hidden` makes the element a scroll container, and that is what
   stopped the sticky column headers from sticking to anything but a box that scrolls away with the
   page. So the desktop tier clips without becoming a scroll container.

   Scoped rather than applied to the base rule, which is where the first attempt put it: three
   lower tiers build on `overflow: hidden` deliberately — the frozen-column tier below needs
   `overflow-x: auto`, and the stacked tier needs `overflow-x: visible`, whose computed value
   depends on what the other axis holds. Changing the base moved 108 table elements outside the
   viewport at 360 and 390px, caught by tools/mobile_check.py on the next run. */
@media (min-width: 900px) {
  .dl-table-wrap {
    overflow: clip;
  }
}

.dl-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-family: var(--font-sans);
  /* 2026-08 depth pass: an explicit white fill, geometry-neutral (a background never changes a
     cell's box), so the table reads as one continuous sheet against --page-bg instead of an
     opaque-white header row sitting above body rows that show the warm page through them. The
     table itself keeps radius 0 — "a table is not a card," tokens/shape.css — this is fill only. */
  background: var(--surface-elevated);
}

.dl-table__caption {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* --- column gutters -------------------------------------------------------- */

.dl-table thead th,
.dl-table tbody th,
.dl-table tbody td {
  padding-inline: var(--row-pad-h);
}

.dl-table thead th:first-child,
.dl-table tbody th:first-child,
.dl-table tbody td:first-child {
  padding-left: 0;
}

.dl-table thead th:last-child,
.dl-table tbody th:last-child,
.dl-table tbody td:last-child {
  padding-right: 0;
}

/* ...but a table inside the bordered container gets its edge gutters back.
   The zeroing above is right for a bare table sitting directly on the page: it aligns the first
   column with the page's own text column instead of indenting it by 14px. The depth pass then put
   the master table inside a rounded, bordered box and did not revisit this, so the first column's
   logo and name pressed flat against that border and the last column's CTA against the other one.
   The owner spotted it immediately — "нет отступов же слева".

   **2026-08-10: no longer scoped to the container.** It was, on the reasoning that a bare table
   sitting directly on the page should align with the page's own text column — but that reasoning
   predates the depth pass, which gave `.dl-table` itself a white surface. A table with its own
   background is never "sitting directly on the page": its first column pressed against the edge
   of its own panel, on the provider review's two bare tables and on the comparator's preview.
   Measured across every table on the site: twelve had a 20px gutter and exactly those three had
   zero. The rule now keys off the table, which is what carries the surface.

   The gutter is larger than `--row-pad-h`: a column gutter separates two columns, but this
   separates content from the panel's own edge, which needs to read as the panel's padding.

   Confined to the table tier on purpose. Below 42rem the summary table becomes label/value cards
   with their own spacing system, which deliberately zeroes cell padding — and these selectors are
   specific enough (0,3,2) to have overridden it and reintroduced a stray indent inside every card. */
@media (min-width: 42.0625rem) {
  .dl-table thead th:first-child,
  .dl-table tbody th:first-child,
  .dl-table tbody td:first-child {
    padding-left: var(--space-5);
  }

  .dl-table thead th:last-child,
  .dl-table tbody th:last-child,
  .dl-table tbody td:last-child {
    padding-right: var(--space-5);
  }
}

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

/* **The header row, v5 (2026-10-01).** Three changes, each with a reason the old version had
   stopped having:

   - A `--lavender` fill rather than the page colour. The header had no surface of its own and was
     held apart from the body by a 2px rule in full-strength ink — a heavy line doing the work a
     quiet fill does better, and the single darkest horizontal mark on a money page.
   - The rule drops to 1px in `--line`. With a filled header, the boundary is the fill.
   - `vertical-align: top`, not `bottom`. A two-line header ("Commitment & fees") hung its first
     line in mid-air beside single-line neighbours.

   Sticky, because the table runs to 39 rows and a reader who has scrolled to row 30 is reading
   unlabelled numbers. `z-index: 3` clears the sticky *first column* below, which sits at 2.

   **It did not stick when it was first written, and the measurement is why this comment exists.**
   `position: sticky` resolves against the nearest ancestor scroll container, and `.dl-table-wrap`
   was `overflow: hidden` — which makes an element a scroll container even though nothing can
   scroll it. So the header stuck to a box that scrolled away with the page: measured at -900px
   after a 900px scroll, i.e. gone. The wrapper is `overflow: clip` at this tier now (see its own
   rule), which clips the rounded corners exactly as `hidden` did and is *not* a scroll container.

   `top` clears the site header rather than being 0. The site header is `position: sticky; top: 0;
   z-index: 40` (chrome.css), so a table header at `top: 0` comes to rest underneath it and is
   simply never seen. Both facts were found by the Codex review of this phase; neither is visible
   to any tool here, because every one of them measures a page that is not being scrolled. */
.dl-table thead th {
  position: sticky;
  top: var(--header-h);
  z-index: 3;
  text-align: left;
  padding-block: 14px;
  border-bottom: var(--border-width) solid var(--line);
  background: var(--lavender);
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  color: var(--text-body);
  white-space: nowrap;
  vertical-align: top;
}

.dl-table__col--num {
  text-align: right;
}

/* Kept unstyled: the mockups draw no vertical rule between column groups. */
.dl-table__col--group {
  border-left: 0;
}

/* --- body rows -------------------------------------------------------------- */

.dl-table tbody th,
.dl-table tbody td {
  padding-block: var(--row-pad-v);
  border-bottom: var(--rule-row);
  font-size: var(--step-body);
  line-height: 1.4;
  color: var(--text-body);
  vertical-align: top;
  text-align: left;
  font-weight: var(--weight-regular);
}

.dl-table tbody tr:last-child th,
.dl-table tbody tr:last-child td {
  border-bottom: 2px solid var(--text);
}

/* The provider cell's description line, under the logo/wordmark. */
.dl-pdesc {
  margin: 6px 0 0;
  font-size: var(--step-small);
  color: var(--text-muted);
  max-width: 220px;
  font-weight: var(--weight-regular);
}

/* The Learn more cell: CTA, "on X's website", the review link — all stacked, centred. */
.dl-btncell {
  text-align: center;
}

/* 2026-08 chip pass: the CTA now visually spans the row's own height rather than sitting
   a fixed 36px inside a cell that is often much taller (the provider cell's blurb + pills
   routinely make the row taller than the button alone needs) — the owner's own "full row
   height CTA" device. `.dl-btncell__inner` is a flex column filling the `<td>`'s box (a
   table cell already has a real, row-driven height; only its content's vertical position
   was ever "top" by default). The regulatory chip and the review link keep their own
   natural height at the top and bottom; the CTA block gets the flexible middle and
   centres its own button/microcopy inside it, so the button reads as the row's anchor
   action rather than a small control floating in empty space. The button's own box does
   not stretch arbitrarily tall on a very tall row — centring the block, not growing the
   button's own height without bound, keeps a 44px control looking like a button rather
   than an oversized bar. */
.dl-btncell__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  height: 100%;
}

.dl-btncell__inner .dl-cta-block {
  flex: 1 1 auto;
  justify-content: center;
  width: 100%;
}

.dl-reviewlink {
  display: block;
  margin-top: var(--space-2);
}

/* The row-level regulatory marker, when a row has one (see the column-order note
   above). Sits above the CTA it belongs beside, not in a column of its own. */
.dl-btncell .dl-flag {
  display: inline-block;
  margin-bottom: var(--space-2);
}

/* --- Insurance column: a written word, never a bare glyph or colour alone ------------ */

/* 2026-08 chip pass. The glyph is `aria-hidden`; "Yes"/"No" is the accessible fact. Plain
   ink for both states — this is neither "verified" (green) nor a status/regulatory mark
   (amber/red), so it gets no colour of its own, per tokens/colors.css's own "roles do not
   mix" rule. */
.dl-yn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--type-body-size);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
  white-space: nowrap;
}

.dl-yn--no {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

/* The unknown (2026-10-05). Quieter than the recorded "No", because a gap in our research must
   not read with the weight of a finding about the company. Same treatment the terms list gives
   its own "Not recorded" rows. */
.dl-yn--unknown {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
  font-size: var(--type-small-size);
}

/* Short, aligned values: state counts, insurance, model. */
.dl-table__mono {
  font-size: var(--type-small-size);
  font-variant-numeric: var(--tabular);
  white-space: nowrap;
}

.dl-table__mono--muted {
  color: var(--text-muted);
}

/* "Not published" — pale grey, full cell, same weight as a real price. */
.dl-table__cell--carbon {
  background: var(--gray-bg);
}

.dl-table__ref {
  margin-left: var(--space-2);
}

/* Compact variant — "What's included", plan detail lists. */
.dl-table--dense tbody th,
.dl-table--dense tbody td {
  padding-block: 12px;
  border-bottom: var(--rule-row);
}

.dl-table--dense tbody tr:last-child th,
.dl-table--dense tbody tr:last-child td {
  border-bottom: 2px solid var(--text);
}

/* A term/value facts table holds sentence-length provider-stated values, not a grid
   of numbers that must align down a column — so its value column wraps rather than
   inheriting the nowrap that is correct for short, aligned table values elsewhere. */
.dl-table--dense .dl-table__mono {
  white-space: normal;
}

/* Row that is filtered out by the JS enhancement. Kept in the DOM, hidden from
   layout and from assistive tech alike. */
.dl-table tbody tr[hidden] {
  display: none;
}

/* --- footnote + count line -------------------------------------------------- */

.dl-table-note {
  margin: 14px 0 0;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 60rem;
}

.dl-table-note sup {
  color: var(--text-body);
}

/* The empty state. Specific and dated, never "no results". */
.dl-table-empty {
  margin: var(--space-5) 0 0;
  padding-left: var(--space-4);
  border-left: var(--rule-row);
  font-size: var(--type-body-size);
  line-height: 1.6;
  color: var(--text-body);
  max-width: 44rem;
}

.dl-table-empty__meta {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* --- narrow viewports: scroll, pin the Provider column ---------------------- */

.dl-table-hint {
  display: none;
}

@media (max-width: 899px) {
  .dl-table-wrap {
    overflow-x: auto;
    outline-offset: 2px;
    -webkit-overflow-scrolling: touch;
    /* See tools/scroll_overflow.py: without paint containment, the sticky first
       column's constraint rect leaks into document.scrollWidth even though every
       ancestor's own scrollWidth is correct. This is the clipping boundary fix,
       carried over unchanged from the previous build. */
    contain: paint;
  }

  .dl-table-wrap .dl-table {
    min-width: 44rem;
  }

  /* Freeze Provider at ~42vw so the provider identity never leaves the viewport
     while the rest of the row (rating, price, membership, flag, CTA) scrolls
     underneath it. */
  .dl-table-wrap .dl-table thead th:first-child,
  .dl-table-wrap .dl-table tbody th:first-child,
  .dl-table-wrap .dl-table tbody td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 42vw;
    max-width: 42vw;
    background: var(--surface-page);
    padding-right: var(--space-3);
  }

  .dl-table-hint {
    display: block;
    margin: var(--space-2) 0 0;
    font-size: var(--type-small-size);
    color: var(--text-muted);
  }

  /* Mobile rows are taller so the frozen provider column stays legible under a
     thumb — the description line and rating both add height to the row already. */
  .dl-table tbody th,
  .dl-table tbody td {
    padding-block: 18px;
  }
}

/* ==========================================================================
   Below 42rem: the master (summary) table becomes a card per provider.

   Scoped to `.dl-table-wrap--summary`/`.dl-table--summary`, set only by
   includes/_master_table.html — every other `.dl-table` on the site (plan tables,
   cost-channel tables, the comparison "differences" table, the state index) keeps the
   sideways-scroll-with-pinned-column behaviour above, unchanged.

   Why this table specifically, and why it reverses an earlier decision. The previous
   pass (docs/ai/frontend_responsive_decisions.md §2) rejected a card transform outright,
   reasoning that it "destroys column-to-column comparison" and looks like every other
   comparison site. That is a real cost and it is still paid below. It is outweighed here
   by a fact about this table's own data: at the time of writing, 1 of the 18 providers
   tracked carries an open regulatory flag (Remedy Meds, FDA warning letter, 1 Sep 2025).
   Below 900px this table already scrolls sideways with the provider column pinned, and
   at the widths most traffic actually arrives at that scroll put the Regulatory record
   column *and* the entire CTA cell off-screen — so the one provider with an FDA action
   against it read identically to the other seventeen while its "Check your eligibility"
   button stayed on screen and clickable. That inverts the comparison the table exists to
   make. Two independent reviews were asked (docs/ai/review_fold_and_mobile_table_
   {codex,gemini}_2026-08-02.md); both, given that specific data point, recommended cards
   over hiding columns or keeping the sideways scroll. Nothing here drops a column,
   reorders one, or rebuilds a row in JS — every cell is the same server-rendered markup,
   only `display` changes.

   Whether a browser keeps announcing native table semantics once every element in it is
   `display: block` is disputed even between those two reviews — one cites the CSS
   Display spec (layout, not semantics, changes) and current engines; the other says
   WebKit/Blink/Gecko drop the accessibility-tree roles outright. Neither could be
   measured in this environment (no accessibility-tree driver in the dependencies), so
   `_master_table.html` sets `role="table"`/`"row"`/`"rowheader"`/`"columnheader"`/
   `"cell"` in the markup — each one matches its element's own implicit ARIA role
   exactly (`<thead>`/`<tbody>` skip the matching `role="rowgroup"` only because two of
   this project's own tests regex-match a bare `<tbody>...</tbody>`, see that file's own
   comment). Redundant ARIA that agrees with the native role is inert if the native
   semantics hold and a safety net if they don't; there is no version of this where
   adding it costs anything.
   ========================================================================== */

@media (max-width: 42rem) {
  .dl-table-wrap--summary {
    overflow-x: visible;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: 4px var(--space-6);
  }

  .dl-table-wrap--summary .dl-table--summary {
    min-width: 0;
  }

  .dl-table--summary,
  .dl-table--summary tbody,
  .dl-table--summary tbody tr,
  .dl-table--summary tbody th,
  .dl-table--summary tbody td {
    display: block;
    width: 100%;
    max-width: none;
  }

  /* Visually hidden, not `display: none` — the header row's text is still every
     cell's accessible name (via scope/headers), and it is also the exact source
     of each cell's visible `data-label` below, so the two voices can never drift. */
  .dl-table--summary thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }

  .dl-table--summary tbody tr {
    padding: var(--space-4) 0;
    border-top: var(--rule-row);
  }

  .dl-table--summary tbody tr:first-child {
    border-top: 0;
  }

  /* Cancel the desktop per-cell row rule and the heavy last-row rule: the card's
     own border-top (above) is the only separator now. */
  .dl-table--summary tbody tr:last-child th,
  .dl-table--summary tbody tr:last-child td,
  .dl-table--summary tbody th,
  .dl-table--summary tbody td {
    border-bottom: 0;
    padding-inline: 0;
    padding-block: var(--space-1);
  }

  /* The provider cell is the card's own heading, not a label/value row: cancel the
     sticky pin and the fixed 42vw width from the shared 899px rule above, and give
     it its own bottom breathing room before the label/value pairs start. */
  .dl-table-wrap--summary .dl-table--summary tbody th:first-child {
    position: static;
    left: auto;
    width: auto;
    max-width: none;
    background: transparent;
    padding-right: 0;
    padding-bottom: var(--space-3);
  }

  .dl-pdesc {
    max-width: none;
  }

  /* Every other cell but the CTA: a label/value pair. The label is `data-label`,
     not CSS `content` text authored a second time — it cannot drift from the
     column header it is copied from. */
  .dl-table--summary tbody td[data-label] {
    display: grid;
    grid-template-columns: minmax(7.5rem, 42%) 1fr;
    column-gap: var(--space-3);
    align-items: baseline;
  }

  .dl-table--summary tbody td[data-label]::before {
    content: attr(data-label);
    font-size: var(--step-small);
    font-weight: var(--weight-bold);
    color: var(--text-2);
  }

  /* "Not published" keeps its pale-grey cell (a stated gap, not a hidden one) but
     gets its own inset now that the column no longer has a table cell's gutter. */
  .dl-table--summary tbody td.dl-table__cell--carbon {
    padding: var(--space-2);
    border-radius: var(--radius);
  }

  /* The CTA cell: full width, last, no label prefix — the button reads as the
     card's own action, not one more value in the row. A hairline above it marks
     "the data ends, the action starts" without another bordered box. */
  .dl-table--summary tbody td.dl-btncell {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: var(--rule-hairline);
    text-align: center;
  }

  .dl-table--summary tbody td.dl-btncell .dl-btn {
    width: 100%;
    min-height: 44px;
  }

  /* Nothing scrolls sideways any more, so the scroll hint would be a lie. */
  .dl-table-hint {
    display: none;
  }

  /* The per-card "Paid link" line — see includes/_affiliate_cta.html and
     disclosure.css's own comments on why it only shows at this width. */
  .dl-table--summary .dl-paidnote {
    display: block;
  }
}

/* ==========================================================================
   Below 42rem: a provider's own plan table ("What it charges" / "Pricing, verified at
   checkout") becomes a stacked card per plan — the exact same display-only technique as
   `--summary` above, scoped to `.dl-table--plans` so every other `.dl-table--dense` table
   (terms, "How X compares", cost-channel, state index) keeps its ordinary
   sideways-scroll-with-focusable-region behaviour. Every cell is the same server-rendered
   markup; only `display` changes, and `data-label` is copied straight from each column's
   own header text, same discipline as `--summary`'s own cards.
   ========================================================================== */

@media (max-width: 42rem) {
  .dl-table-wrap:has(.dl-table--plans) {
    overflow-x: visible;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: 4px var(--space-4);
  }

  /* The real fix for this table not to overflow its card: `.dl-table-wrap .dl-table` above
     (line ~348) forces every table inside a wrap to `min-width: 44rem` for the sideways-scroll
     tier, which this rule never cancelled — the table stayed 704px wide and scrolled inside its
     own wrap exactly like the old dense layout, just with each cell turned to `display: block`
     first (visible as clipped/scrolling text, not full-width cards). Reset here, the same way
     `.dl-table-wrap--summary .dl-table--summary` already does above.

     `overflow-x: visible` two rules up is also not the whole story on its own: with `overflow-y`
     still `hidden` from this file's own unconditional `.dl-table-wrap { overflow: hidden }`
     (line ~58, never touched by either rule), the CSS Overflow spec computes an `overflow-x` of
     `visible` back down to `auto` whenever `overflow-y` isn't also `visible` — confirmed with a
     three-rule isolated reproduction outside this codebase before landing this comment, because
     the DevTools "Computed" panel disagreeing with the source rule looked like a caching problem
     at first and was not one. Moot once nothing is wide enough to overflow in the first place,
     which `min-width: 0` here guarantees. */
  .dl-table-wrap:has(.dl-table--plans) .dl-table--plans {
    min-width: 0;
  }

  .dl-table--plans,
  .dl-table--plans tbody,
  .dl-table--plans tbody tr,
  .dl-table--plans tbody th,
  .dl-table--plans tbody td {
    display: block;
    width: 100%;
    max-width: none;
  }

  /* Visually hidden, not `display: none` — see `--summary`'s identical rule above for why:
     the header row is still every cell's accessible name and the source of its own
     `data-label`, so the two can never drift apart. */
  .dl-table--plans thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }

  .dl-table--plans tbody tr {
    padding: var(--space-4) 0;
    border-top: var(--rule-row);
  }

  .dl-table--plans tbody tr:first-child {
    border-top: 0;
  }

  .dl-table--plans tbody th,
  .dl-table--plans tbody td {
    border-bottom: 0;
    padding-inline: 0;
    padding-block: var(--space-1);
  }

  /* The plan name is the card's own heading — bold, its own bottom breathing room before
     the label/value pairs start. Plain `tbody th` elsewhere in this file is deliberately
     `--weight-regular` (a table row header is not automatically a heading); this table's
     cards are the one place a `<th>` is genuinely acting as one. */
  .dl-table--plans tbody th {
    font-size: var(--step-lede);
    font-weight: var(--weight-black);
    padding-bottom: var(--space-2);
  }

  /* Form/Medication, Price, What's included: label/value pairs, the label copied from
     the column header via `data-label` rather than authored a second time in CSS. */
  .dl-table--plans tbody td[data-label] {
    display: grid;
    grid-template-columns: minmax(6rem, 38%) 1fr;
    column-gap: var(--space-3);
    align-items: baseline;
  }

  .dl-table--plans tbody td[data-label]::before {
    content: attr(data-label);
    font-size: var(--step-small);
    font-weight: var(--weight-bold);
    color: var(--text-2);
  }

  /* Nothing scrolls sideways any more, so the scroll hint would be a lie. */
  .dl-table-wrap:has(.dl-table--plans) .dl-table-hint {
    display: none;
  }
}

/* ==========================================================================
   The unpriced-providers toggle — a native `<details>`, not a JS-hidden `<tbody>`.

   Per the 2026-08 owner review: the master table shows only providers with a dated
   price. A provider with no price is collapsed here instead of dropped — the rows stay
   in the server-rendered HTML, closed by default, with the `<summary>` stating the
   count in plain words. That is why this is `<details>` and not `hidden`: it works with
   JavaScript off, and it gives assistive technology a real, native disclosure control
   instead of a script-dependent toggle button reimplementing one. Google's own spam
   policy permits accordions used for UX; what it penalises is hiding content to
   manipulate ranking, which a summary that states the count is the opposite of.
   ========================================================================== */

.dl-table-toggle {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule-row);
}

.dl-table-toggle > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-table-toggle > summary::-webkit-details-marker {
  display: none;
}

.dl-table-toggle > summary::after {
  content: "+";
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-table-toggle[open] > summary::after {
  content: "\2013";
}

.dl-table-toggle .dl-table-wrap {
  margin-top: var(--space-4);
}

/* --- the regulatory-actions detail section beneath the table --------------- */

.dl-reg-actions {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule-row);
}

.dl-reg-actions__list {
  margin-top: var(--space-3);
}

.dl-reg-actions__list li {
  margin-top: var(--space-2);
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-reg-actions__list .dl-flag {
  margin-right: var(--space-2);
}

/* The first-year cost span under the monthly price. The monthly figure is what providers
   advertise; this is what the year comes to, and it is the number the rubric scores. Small and
   secondary by design — it explains the row rather than competing with it. */
.dl-yearcost {
  display: block;
}

.dl-yearcost__value {
  display: block;
  /* Its own column now, so this is the cell's headline figure and is set like one — the same
     size as the monthly price beside it. It was small and bold because it used to be a fourth
     line inside the price cell, which is exactly what made that cell unreadable. */
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  color: var(--text);
  line-height: 1.25;
}

.dl-yearcost__note {
  display: block;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  line-height: 1.35;
}

/* --- mini range bar: where this row's first-year floor sits across the table -------- */

/* 2026-08 chip pass. `.dl-bar`/`.dl-bar__fill`/`.dl-bar--0`…`.dl-bar--100` are score-panel.css's
   own discrete-width-class bar, reused unchanged rather than a second bar component. The fill
   colour here is deliberately NOT `--accent2` (reserved narrowly for the score panel's own
   numeral/link/secondary-button, per tokens/colors.css) and NOT `--blue` (the one CTA/link
   accent) or a status/regulatory hue — this bar is neither a rating nor a status, so it takes a
   plain muted ink fill, the same "no new hue for a decoration" rule the rest of this system
   already follows. */
.dl-yearcost__bar {
  display: block;
  margin-top: 5px;
  width: 72px;
}

.dl-bar--range {
  height: 4px;
  border-radius: 999px;
}

.dl-bar__fill--muted {
  background: var(--text-2);
}

/* The full-coverage state list. A plain multi-column list, because every one of these states has
   the same provider count — rendering them as table rows repeated one value 33 times, which is
   precisely what made this page unreadable. */
.dl-statelist {
  columns: 4 12rem;
  column-gap: var(--space-5);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.dl-statelist li {
  break-inside: avoid;
  padding: 3px 0;
  font-size: var(--type-small-size);
  color: var(--text-body);
}

/* The verification stamp breaks by segment, never mid-segment.
   Two failure modes bracket this rule. Without any `nowrap` the stamp shredded into "✓ Price /
   checked" and "28 Jul / 2026" — four lines to say one thing. With `nowrap` on the whole phrase it
   could not wrap at all, so when the age note made it longer it overflowed a ranked card's narrow
   column and printed over the next column's text. Making each segment unbreakable and letting the
   phrase wrap between them is the only version that holds at both ends. */
.dl-price__vlabel,
.dl-price__date,
.dl-price__age-note,
.dl-price__stale-note {
  white-space: nowrap;
}

/* The age of a price past the fresh window. Muted ink, no colour — freshness is signalled by the
   presence of the green tick and by this text, never by colour alone. */
.dl-price__age-note {
  color: var(--text-muted);
  white-space: nowrap;
}

/* "same for all three" beside a compare-row label. Muted and small: it qualifies the row's own
   heading rather than competing with the figures. Not a colour — sameness is neither a status nor
   a verification, and the two reserved palettes stay reserved. */
.dl-compare-same {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

/* --- key-value terms table ("How it works" on the provider review) -----------
   A two-column facts list, capped at reading width: unconstrained it spanned the
   full 1128px content column and the value column started mid-page with dead air
   after it. `max-width`, never `min-width` — a bare table with no `.dl-table-wrap`
   must not be forced wider than its container (see test_no_horizontal_scroll.py). */
.dl-table--kv {
  max-width: 46rem;
}

/* The scroll container has to be capped with it. Capping only the table left the
   wrapper's own white panel at full width with the table sitting in its left half and
   an empty white rectangle beside it — visible on the provider profile, where this
   table has a wrapper (the review page's copy has none, which is why the first version
   of this rule looked correct). */
.dl-table-wrap:has(.dl-table--kv) {
  max-width: 46rem;
}

.dl-table--kv tbody th {
  width: 38%;
}

/* --- "How X compares": the metric column stops eating the provider columns --- */
.dl-table--compare thead th:first-child {
  width: 20%;
}

/* **The header row carries a mark now, not a bare name** (2026-08-25 — owner: "кликнуть тут
 * нельзя"). `.dl-table thead th`'s sitewide default is `vertical-align: bottom; white-space:
 * nowrap;`, right for a plain text label but wrong for `.dl-plogo`'s flex mark-and-wordmark row:
 * bottom-aligning a flex box against a short first-column label reads as misregistered, and a
 * long provider name had nowhere to go under `nowrap`. Middle-aligned and free to wrap instead —
 * the table already scrolls sideways for a column this can't fit (`.dl-table-hint`, above). */
.dl-table--compare thead th {
  vertical-align: middle;
  white-space: normal;
}

/* ==========================================================================
   The master table's own column widths and price footnotes — 2026-08-10
   density pass, measured with a DOM probe rather than judged by eye.

   Before: rows 201-272px tall, with the provider cell and the price cell carrying
   all of it while "Our rating", "Membership fee", "Insurance" and the 268px-wide
   action column were filled to 7-14% of the row's height. Width was allocated by
   content, which meant the widest column in the table sat behind a one-line text
   link while the price — the figure a reader came for — wrapped inside 148px.
   ========================================================================== */

/* `table-layout: fixed` is what makes the <colgroup> binding rather than advisory:
   with auto layout a <col> width is a hint the algorithm may ignore, and it did.

   Scoped to the wide tier on purpose. Below ~1140px seven columns of this table cannot
   hold their own headers at fixed widths — "Membership fee" overlapped "Insurance" at
   1024 and 900 (tools/cell_geometry.py). The auto algorithm has handled those widths
   correctly all along, and below 42rem the whole table becomes cards anyway, so the
   fixed grid is applied exactly where it was measured to be needed and nowhere else. */
@media (min-width: 1140px) {
  .dl-table--summary {
    table-layout: fixed;
  }

  /* Widths only. No padding in these classes: the gutter contract (test_table_gutters)
     requires horizontal padding to come from the shared th/td rule and to be zeroed
     only at the table's own first/last child. */
  /* **Re-balanced for the new text face (2026-08-14).** Host Grotesk sets about 8% wider than
     the Public Sans it replaced, and under `table-layout: fixed` a header cannot widen its own
     column to compensate: "Membership" ran 20px past its 8% cell and overlapped "Insurance"
     at 1440 (tools/cell_geometry.py). The two narrow columns take that width back from the
     four that had slack — the widths are a percentage set, so a column that needs more can
     only be paid for by one that needs less, never by adding to the total. Measured after the
     change: every header clears its neighbour by 26px or more at 1440. */
  /* **21%, up from 18% (2026-10-01).** Sized from the data rather than from the look: the longest
     medication chip the database can produce is "Cmpd. Oral tirzepatide", which measures ~146px at
     this chip's size. The provider cell spends 34px on its own gutters and another 86px on the
     logo slot, so 18% (236px) left the chips 116px and broke "Cmpd. Semaglutide" over two lines on
     every compounded provider — a wrap inside a word-pair, not a wrap between chips, and ~17px of
     row height on 21 of 39 rows.

     21% leaves 155px, which clears the longest label with 9px to spare. The widths are a
     percentage set, so this is paid for and not added: Insurance drops 9 -> 7 (it renders "Yes" or
     "No") and First year 14 -> 13. */
  .dl-table--summary .dl-col--provider {
    width: 21%;
  }

  /* 12%, up from 9% when the "On record" column's share was redistributed. The cell carries the
     score *and* the provider's "Best for …" line, and at 9% that line broke mid-word —
     "Watch the membershi / p math", which cell_geometry's shredding check exists to catch. */
  .dl-table--summary .dl-col--rating {
    width: 12%;
  }

  .dl-table--summary .dl-col--price {
    width: 20%;
  }

  /* Back to 14% after the 21% provider column was paid for out of this one and NewSelf's
     "$1,199.64-$1,733.88" split across two lines. A first-year range is one figure to a reader and
     breaking it is worse than any crowding it relieves. The percent comes out of the CTA column
     instead, whose widest content is a button label that does not change with the data. */
  .dl-table--summary .dl-col--year {
    width: 14%;
  }

  .dl-table--summary .dl-col--fee {
    width: 11%;
  }

  .dl-table--summary .dl-col--ins {
    width: 7%;
  }

  .dl-table--summary .dl-col--cta {
    width: 15%;
  }
}

/* With a fixed layout a long unbroken string can no longer widen its column, so it
   has to be allowed to break instead of spilling over the neighbour. */
.dl-table--summary tbody th,
.dl-table--summary tbody td {
  overflow-wrap: break-word;
}

/* The notes themselves: an ordinary numbered list under the table, in the same
   response as the figures they qualify. Not a tooltip and not an accordion — the
   terms of a price are not something a reader should have to uncover. */
.dl-table-footnotes {
  margin: var(--space-4) 0 0;
  padding-left: 1.4em;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 60rem;
}

.dl-table-footnotes li {
  margin-top: 4px;
}

.dl-table-footnotes li:target {
  color: var(--text-body);
}

.dl-table-footnotes__who {
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

/* The marker beside the figure. `scroll-margin-top` clears the sticky 60px header
   the same way the jump-nav targets do in anchor-nav.css. */
.dl-price__sup a {
  color: var(--blue);
  text-decoration: none;
}

.dl-price__sup a:hover {
  text-decoration: underline;
}

.dl-table-footnotes li {
  scroll-margin-top: calc(60px + var(--space-4));
}

/* Compact pills inside the table's provider column. Measured: at the shared 12px/7px size the two
   medication chips came to 244px against the column, so each took its own line and three pills
   cost 72px of row height. One step down fits two per line — same words, same count, one row
   less. Scoped to this table; the ranked cards have the width for the full size.

   **The width is stated, and that is the whole fix** (2026-10-01). The rule further down indents
   this list by the logo slot and caps it with `max-width`, which together describe the right box
   and never made the list occupy it: as a flex item that is not stretched, the `<ul>` shrank to
   its own content instead. Measured at 116px inside 150px of available column, which is just
   narrow enough to break "Cmpd. Semaglutide" over two lines — so every compounded provider paid
   ~17px of row height for a wrap that nothing in the layout actually required. A cap says how wide
   a box may be; it never says how wide it is. */
.dl-table--summary .dl-pills {
  gap: 4px;
  margin-top: 6px;
  width: calc(100% - var(--dl-logo-slot));
}

/* A chip in this column has to be able to wrap, for the same reason the CTA button below does.
   `.dl-badge` is `white-space: nowrap` with a fixed height — right everywhere a chip has room,
   wrong in a cell whose text starts 106px in. Strut Health's "Cmpd. Oral semaglutide" measured
   191->322 in a cell ending at 301: a flex item with `nowrap` will not shrink below its content,
   so it simply left the cell and landed on the score. It fit at the old 44px indent and stopped
   fitting when that indent was corrected to match the tile (2026-09-16).

   Wrapping rather than truncating: an ellipsis on "Cmpd. Oral semaglutid…" hides which molecule
   the provider actually sells, and the chips are the row's answer to that question. `min-width: 0`
   is what lets a flex item shrink at all; `height: auto` lets the second line have somewhere to
   go. */
.dl-table--summary .dl-pills .dl-badge {
  min-width: 0;
  height: auto;
  min-height: 18px;
  padding: 1px 6px;
  font-size: var(--step-fine);
  white-space: normal;
}

/* A button in this column has to be able to wrap. `.dl-btn` is `white-space: nowrap`
   with a fixed height — correct everywhere it was designed for, wrong here: with
   `table-layout: fixed` the column can no longer stretch to fit "Visit GobyMeds's
   official site", so the label ran straight out of the cell (caught by a screenshot,
   then by tools/cell_geometry.py). The label cannot be shortened — the outbound link
   naming its provider is a contract, not a style choice. */
.dl-table--summary .dl-btncell .dl-btn {
  white-space: normal;
  height: auto;
  min-height: var(--control-h);
  /* One inline padding for every button in the column, `--lg` included: the paid row's
     button is `--lg` (22px) and the rest are not (16px), so at 182px the same label fit
     on one line in four rows and broke onto two in the fifth. */
  padding-inline: var(--space-3);
  padding-block: 7px;
  line-height: 1.25;
}

.dl-table--summary .dl-btncell .dl-btn--lg {
  min-height: var(--control-h-lg);
}

/* Headers wrap in this table. They are `nowrap` sitewide, which is right when a column
   sizes itself to its content — but with fixed widths "Membership fee" could not fit
   its 9% column and overlapped "Insurance" (tools/cell_geometry.py, 15 collisions).
   `vertical-align: bottom` above already keeps a two-line header aligned with the
   one-line ones. */
@media (min-width: 1140px) {
  .dl-table--summary thead th {
    white-space: normal;
  }
}

/* --- provider cell: one left edge, one click target -----------------------------

   Two defects the owner named in one breath ("all over the place"), both in this
   column. Logos exist for 2 of the 12 tracked providers, so ten names started at the
   cell's left edge and two started 44px in — the column's left margin changed from row
   to row. And the row's own identity link was a 34px-tall strip in a 167px row, so the
   obvious thing to click was not clickable.

   The slot is reserved rather than filled: no placeholder mark is drawn for a provider
   without one (provider-logo.css is explicit that the name set properly *is* the mark),
   the space is simply held so every name shares one vertical. When the remaining logos
   are uploaded the column stops changing shape at all. */
.dl-table--summary tbody th {
  /* The mark's own tile plus its gap (provider-logo.css) — one number, so the reserved
     slot and the text indent below can never drift apart.

     **They drifted apart anyway, for two weeks** (2026-09-02 to 2026-09-16). The tile was
     34px wide when this was written, so 34 + 10 = 44 was right. Widening the tile to 96px to
     stop wordmarks being cropped was done in provider-logo.css and this number was not
     touched, so the cell ended up with three different left edges: the tile starting at 0, the
     chips under it indented 44px, and the name 106px in. The owner's close-up of NewSelf shows
     all three at once — "FDA letter, Feb 2026" hanging in a column of its own.

     A comment promising two numbers cannot drift is not a mechanism. They are still two
     numbers in two files; what this now has is the arithmetic written down beside it, and a
     test (`test_master_table_density`) that measures the chip indent against the name. */
  --dl-logo-slot: 86px; /* .dl-plogo__tile width 76px + its margin-right 10px */
}

/* The description, the pills and the app-rating flag line up with the name, not with the
   cell's edge — otherwise reserving the slot just moves the ragged edge from the name to the
   text under it, which is what the first attempt did.

   `.dl-uflag` was missing from this list, so on the two rows that carry it (Hims, Ivím) it
   started 44px to the left of the pills directly above it and read as though it had escaped the
   cell. Measured: pills at x=89, flag at x=45, in a cell whose content box starts at 45.

   **`.dl-flag` had the identical bug and it is the same story twice** (2026-08-16). The
   regulatory chip moved into this cell from its own column on 2026-08-15 and was not added
   here, so "FDA letter, Feb 2026" hung 44px left of the provider's name on every logo-less
   provider — which is 33 of 39 of them. Measured before the fix: flag at x=201, name and pills
   at x=245. The owner spotted it as "FDA letter вылазит — ужас".

   Anything that renders under the name in this cell belongs in this list. The slot exists so the
   name lines up whether or not a provider has a mark; anything left out of the list reintroduces
   the ragged edge one element lower down. */
.dl-table--summary tbody th .dl-pdesc,
.dl-table--summary tbody th .dl-uflag,
.dl-table--summary tbody th .dl-flag,
.dl-table--summary tbody th .dl-pills,
/* The crowd rating, added 2026-10-05 — and left out of this list on the day it was written, so
   it hung at the cell's left edge under the logo tile while the name and pills stood 86px in.
   The comment above says exactly what happens to anything omitted here, and the owner had
   already caught the same ragged edge once on the regulatory chip. */
.dl-table--summary tbody th .dl-crowd {
  /* margin, not padding: padding would indent the text while leaving the box at the
     cell's edge, which measures as aligned and looks wrong. */
  margin-left: var(--dl-logo-slot);
  /* The indent eats into the cell, so the cap has to account for it. This was `none`, which was
     right at a 44px indent and wrong at 106: `cell_geometry` measured "FDA letter, Feb 2026"
     crossing 4.6px into the score column on Strut Health and SkinnyRx, the two providers that
     carry both a long name and a regulatory chip. `none` does not mean "as wide as the column",
     it means "no limit", and with a 106px head start that is 106px past the column's edge. */
  max-width: calc(100% - var(--dl-logo-slot));
}

/* The whole row is the click target for our page about that provider — the same
   stretched-anchor pattern as `.dl-pcard` and the comparison index's ledger rows. The
   other controls in the row (the CTA button, the review link) are raised above the
   stretched layer so they keep their own destinations; `position: relative` on the row
   is what the overlay is measured against. */
.dl-table--summary tbody tr {
  position: relative;
}

.dl-table--summary tbody th .dl-plogo__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.dl-table--summary tbody tr:hover .dl-plogo__wordmark {
  color: var(--blue-dark);
  text-decoration: underline;
}

/* Above the stretched overlay, in source order after it: the button, the review link
   and the regulatory chip's own source link all keep working. */
.dl-table--summary tbody td .dl-btn,
.dl-table--summary tbody td .dl-reviewlink,
.dl-table--summary tbody td a {
  position: relative;
  z-index: 1;
}

/* A hover tint makes the whole row read as one target rather than as a name that
   happens to be blue. --gray-bg is the neutral fill the palette already reserves for
   exactly this kind of surface, so no new colour enters the table. */
.dl-table--summary tbody tr:hover {
  background: var(--row-hover);
}

/* Alternating rows, added with the v5 pass. 39 rows of six columns is where the eye starts losing
   its place travelling from a provider's name to its CTA, and a 1.04:1 stripe is the cheapest fix
   that costs the data nothing.

   **Driven by `[data-dl-stripe]`, not `:nth-child(even)`**, which is what it was for half a day.
   Filtering hides rows with the `hidden` attribute and leaves them in the DOM, and sorting moves
   the same nodes — so `nth-child` counted rows nobody could see and the stripes landed anywhere
   after the first filter. `table-filter.js` sets the attribute over the visible rows instead; see
   its `stripe()` for why that is JS's job rather than a CSS trick waiting to be found.

   Applied to `> *` rather than the row because the frozen first column below sets its own opaque
   fill and would otherwise punch a white stripe through the pattern. The cells that *should* keep
   their own surface are excluded by name: `--carbon` is the "Not published" fill, and a stated gap
   must not change colour because of its position in a list. */
.dl-table--summary tbody tr[data-dl-stripe] > *:not(.dl-table__cell--carbon) {
  background: var(--row-stripe);
}

/* **The leading figure in a row** (v5). `.dl-price__value` is the shared money treatment and sets
   18px at weight 700, which is right in a sentence and too quiet in a column of 39. The pack gives
   the table its own price size for the same reason this project gives the fold one: the figure is
   what the row is for, and it has to win against the provider's name two cells to its left.

   Scoped to the master table rather than lifted into `price-cell.css`, so a price inside running
   prose or a plan table keeps the size that suits its own context. */
.dl-table--summary .dl-table__col--price .dl-price__value {
  /* `--step-title` (22px), not the pack's literal 1.4rem. The two differ by 0.4px and the scale is
     binding: tokens/typography.css says components use the named steps and do not invent
     component-specific sizes, which is the rule that kept this file's type coherent through two
     palette changes. Caught by the Codex review of this phase. */
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.03em;
  font-variant-numeric: var(--tabular);
}

.dl-table--summary tbody tr:hover > *:not(.dl-table__cell--carbon) {
  background: var(--row-hover);
}

/* **The chips must not dissolve into the row they sit on** (2026-09-02).
   
   `.dl-badge` fills with `--gray-bg` and so does the row's hover tint, so on the hovered row the
   drug chips lost their shape entirely: same fill, no border, and what read as pills everywhere
   else read as loose grey words on one row out of thirty-nine. Visible in the owner's screenshot
   of Strut Health's row and in no static render, because a static render has no hover.
   
   White, not a third grey. The chip is a small surface on a tinted one, which is the same relation
   a white card has to the warm page — and it keeps the chip's own text contrast unchanged rather
   than introducing a fill nobody measured. */
/* Superseded by the v5 hover, and inverted by it. The hover tint was `--gray-bg`, the same fill
   the chips use, so on a hovered row the drug chips dissolved into it and this rule lifted them
   back out with white. The new hover is `--row-hover` at 1.10:1 — lighter than the chips, not
   equal to them — so painting them white now makes a chip *lighter* than the row it sits on and
   turns the fix into the defect it was written for. The chips keep their own fill. */
.dl-table--summary tbody tr:hover .dl-badge,
.dl-row--clickable:hover .dl-badge {
  background: var(--gray-bg);
}

/* The coverage bar under "N of M" on the state index — same discrete-width bar the
   score panel and the first-year column use, just narrower. */
.dl-covbar {
  display: block;
  max-width: 90px;
  margin-top: 5px;
}

/* --- the real monthly total, above the advertised figure -------------------------------------
   Printed only where a verified membership fee makes the two differ. The total is the number a
   reader pays and it leads; the advertised price keeps its own verification date beneath, because
   the date belongs to the figure we read, not to our arithmetic. */
.dl-allin {
  display: block;
  margin-bottom: 6px;
}

.dl-allin__total {
  display: block;
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  color: var(--text);
  font-variant-numeric: var(--tabular);
}

.dl-allin__unit {
  font-size: var(--step-small);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-allin__sum {
  display: block;
  font-size: var(--type-micro-size);
  line-height: 1.4;
  color: var(--text-muted);
}

/* The provider name is the row's own link to its review, and on a phone it measured 23-34px.
 * Padding rather than a min-height on the flex box, so the mark and the name stay vertically
 * centred against each other and only the hit area grows.
 *
 * `min-height` as well as the padding, because padding alone did not get there: 8px each side of
 * an 18px line is 34px, still under the floor, which is what mobile_check kept reporting after
 * the first attempt. The min-height sets the target and the padding keeps the text off its
 * edges. */
@media (max-width: 42rem) {
  /* Every table, not only `--summary`. The rule was scoped to the master table and the
     unpriced-providers table beneath it uses the plain `.dl-table`, so half the provider links
     on the home page were still 34px targets — which is what mobile_check kept naming after the
     first two attempts at this. */
  .dl-table tbody th .dl-plogo__link {
    min-height: 44px;
    padding-block: 8px;
  }
}

/* The regulatory chip, inside the provider cell rather than in a column of its own — see
   includes/_master_table.html for why the column went and why this placement serves the same
   argument better. It keeps its own line under the provider's name, above the feature pills.

   **One rule, and the left margin is part of it** (2026-08-16). There were two rules for this
   selector 20 lines apart, and this one's `margin` shorthand quietly zeroed the `margin-left`
   the indent list above sets — which is why adding `.dl-flag` to that list changed nothing and
   the chip stayed 44px left of the name. The shorthand is now written out with the slot in it,
   so the two cannot disagree again.

   It does not wrap: in a narrow cell "FDA letter, Feb 2026" broke to one word per line — four
   words, four lines — which reads as damage rather than as a flag. Held on one line at a smaller
   size it fits; if it ever stops fitting, cell_geometry catches the collision instead of the
   text quietly shredding. */
.dl-table--summary tbody th .dl-flag {
  display: inline-flex;
  margin: 6px 0 2px var(--dl-logo-slot);
  white-space: nowrap;
  font-size: var(--step-fine);
  padding: 2px 6px;
}

/* "First reading" — a provider priced once and not yet re-checked. Muted ink, no colour: this is
   a statement about the completeness of our own record, not a warning about the provider, and
   the amber/red palettes stay reserved for regulatory facts. */
.dl-firstread {
  display: block;
  margin-bottom: 4px;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* The CTA's destination microcopy — "on GobyMeds's website" — is required by the FTC, so it
 * cannot be dropped when the column narrows. In the table it was breaking one word per line
 * (three words, three lines, caught by cell_geometry's shredding check). Balanced wrapping and
 * a slightly tighter size keep it to two lines at every tested width. */
.dl-table--summary .dl-oncopy {
  text-wrap: balance;
  font-size: var(--step-fine);
  line-height: 1.3;
}


/* The two narrowest headers sit closest together; give their cells a little more side padding
   so the words keep a real gutter without widening the columns themselves. */
.dl-table--summary thead th:nth-child(6),
.dl-table--summary thead th:nth-child(7) {
  padding-right: var(--space-3);
}

/* A row the reader can click anywhere, from `static/js/modules/row-click.js`.

   The owner's complaint was that a click only worked on the link, which for a row standing for one
   medication is the wrong target. The anchor stays the keyboard path and the accessible name; this
   only widens where a mouse may land.

   The class is added by the module rather than written into the template, so a row is never
   signalled as clickable on a page where the script did not run — the point of a progressive
   enhancement is that its absence is invisible rather than misleading.

   Cursor and a quiet hover fill, nothing else. The design Law allows a quiet hover state on a
   surface and forbids lift, shadow and scale: a row that rises off the page reads as a card, and a
   table row is not one. */
.dl-row--clickable {
  cursor: pointer;
}

.dl-row--clickable:hover {
  background: var(--gray-bg);
}

/* --- css/components/cost-chart.css --- */
/* ==========================================================================
   CostChannelChart — includes/_cost_channel_chart.html, built by
   dl_cost_channel_chart() (apps/core/templatetags/dl_components.py). A decorative,
   aria-hidden restatement of the channel table beside it — see that function's own
   docstring for the linear-scale-with-a-floor reasoning and why aria-hidden is
   correct here.

   Bar width is 20 discrete CSS classes (`.dl-costchart__bar--5` … `--100`, step 5),
   never an inline `style` or a CSS custom property set inline — both are `style="` on
   the wire, which `test_no_inline_styles_or_handlers` forbids project-wide. Same
   discrete-class technique as `.dl-bar` (score-panel.css); a separate class family
   because this bar is never coloured by the rubric's second accent — it is a plain
   `--blue` fill, this page's one accent, not a rating.

   Head-then-track layout (label + figure on one flex line, the bar on its own line
   below), not a three-column grid: a fixed-width grid of label/track/figure is exactly
   the shape that collided at narrow widths on this project before (see
   tools/scroll_overflow.py's own history). Stacking the bar under its own row's head
   needs no breakpoint at all — every row wraps on its own terms at any width.
   ========================================================================== */

.dl-costchart {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5) var(--space-6);
  margin: var(--space-5) 0 var(--space-6);
  background: var(--surface-elevated);
}

.dl-costchart__label-row {
  margin: 0 0 var(--space-4);
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.dl-costchart__row {
  padding: 10px 0;
  border-top: var(--rule-hairline);
}

.dl-costchart__row:first-of-type {
  border-top: 0;
  padding-top: 0;
}

.dl-costchart__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: 7px;
}

.dl-costchart__label {
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-costchart__figure {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.dl-costchart__value {
  font-size: var(--type-money-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
  white-space: nowrap;
}

/* Same size, same weight as a priced row's own figure — only the ink is muted. A missing
   figure must never read as a smaller, less important number than one that exists. */
.dl-costchart__value--gap {
  color: var(--text-muted);
}

.dl-costchart__unit {
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-costchart__date {
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  white-space: nowrap;
}

.dl-costchart__track {
  display: block;
  height: 10px;
  border-radius: 999px;
  background: var(--gray-bg);
  overflow: hidden;
}

.dl-costchart__bar {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--blue);
}

.dl-costchart__bar--5 { width: 5%; }
.dl-costchart__bar--10 { width: 10%; }
.dl-costchart__bar--15 { width: 15%; }
.dl-costchart__bar--20 { width: 20%; }
.dl-costchart__bar--25 { width: 25%; }
.dl-costchart__bar--30 { width: 30%; }
.dl-costchart__bar--35 { width: 35%; }
.dl-costchart__bar--40 { width: 40%; }
.dl-costchart__bar--45 { width: 45%; }
.dl-costchart__bar--50 { width: 50%; }
.dl-costchart__bar--55 { width: 55%; }
.dl-costchart__bar--60 { width: 60%; }
.dl-costchart__bar--65 { width: 65%; }
.dl-costchart__bar--70 { width: 70%; }
.dl-costchart__bar--75 { width: 75%; }
.dl-costchart__bar--80 { width: 80%; }
.dl-costchart__bar--85 { width: 85%; }
.dl-costchart__bar--90 { width: 90%; }
.dl-costchart__bar--95 { width: 95%; }
.dl-costchart__bar--100 { width: 100%; }

.dl-costchart__note {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: var(--rule-hairline);
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* --- css/components/stat-block.css --- */
/* ==========================================================================
   StatBlock, the stat line, and the tipbox's two stat chips.

   `.dl-statchips` is the homepage tipbox's "$299 highest tracked / $79 lowest
   tracked" pair — small white cards with the mockups' own soft shadow
   (`--shadow-chip`), the only boxed numbers this system draws. `.dl-stat-line` is a
   single left-aligned line of tabular figures; `.dl-stat` is the stacked value-over-
   label form used in the review sidebar's "Key numbers".
   ========================================================================== */

.dl-statchips {
  display: flex;
  gap: 10px;
  /* `flex: 0 0 auto` refuses to shrink and refuses to wrap, so on a 390px phone the pair
     ran to 429px and took the whole page sideways with it — the defect
     tools/mobile_check.py was written to find, and one nothing caught because no probe went
     below 500px. Allowed to wrap and to shrink, the two chips stack instead. */
  flex: 0 1 auto;
  flex-wrap: wrap;
  min-width: 0;
}

.dl-statchip {
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-chip);
  padding: 12px 18px;
  text-align: center;
  min-width: 92px;
}

.dl-statchip__n {
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  color: var(--text-body);
}

.dl-statchip__l {
  font-size: var(--step-micro);
  color: var(--text-muted);
}

.dl-statchip--hl {
  background: var(--navy);
}

.dl-statchip--hl .dl-statchip__n {
  color: var(--white);
}

.dl-statchip--hl .dl-statchip__l {
  color: var(--text-on-navy-muted);
}

/* --- plain stat line (methodology strip) ------------------------------------- */

.dl-stat-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--type-small-size);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
}

.dl-stat-line__fact {
  display: inline;
}

.dl-stat-line__sep {
  color: var(--text-muted);
}

.dl-stat-line strong {
  color: var(--text-body);
  font-weight: var(--weight-bold);
}

/* --- stacked value / label ---------------------------------------------------- */

.dl-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.dl-stat__value {
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  font-variant-numeric: var(--tabular);
  color: var(--text-body);
}

.dl-stat--lg .dl-stat__value {
  font-size: var(--step-title);
}

.dl-stat__label {
  font-size: var(--step-micro);
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

/* --- the sidebar "key numbers" list -------------------------------------------- */

.dl-keynums {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 2;
  font-variant-numeric: var(--tabular);
}

.dl-keynums div {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.dl-keynums dt {
  color: var(--text-body);
  font-weight: var(--weight-bold);
  min-width: 3.5em;
}

.dl-keynums dd {
  margin: 0;
  color: var(--text-muted);
}

/* The rating tile's own figure. Sized between the price tiles' number and their caption so the
   fold reads as four comparable facts rather than three numbers and a footnote. A thin base
   greys the number rather than hiding it: the figure exists, it simply cannot be ranked on. */
.dl-tile__score {
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  font-variant-numeric: var(--tabular);
  color: var(--text);
  display: block;
}

.dl-tile__score--thin {
  color: var(--text-2);
}

.dl-tile__note {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size);
  line-height: 1.35;
  color: var(--text-muted);
}

/* No bar in the rating tile. Both reviewers rejected it and for the same reason: a bar reads as
   progress toward a fixed capacity, and a review base is evidence strength, not progress. It also
   put horizontal weight in the fourth tile that Price, First year and Membership do not have,
   breaking the fold's rhythm. The tile is now the same three-line stack as its siblings —
   label, value, note — and a thin base is carried by muted ink on the number itself. A dot strip
   encoding the base was proposed and rejected: a custom scale needs explaining, and a tile has
   no room to explain anything. */

.dl-tile__star {
  color: var(--rating-star);
  margin-right: 4px;
}

/* --- css/components/key-numbers.css --- */
/* ==========================================================================
   KeyNumbers — the homepage's hero fact strip (includes/_key_numbers.html).

   Not in design-system.html: the pack's own stat component stops at the small two-chip
   `.dl-statchip` pair (stat-block.css) used beside the "before you compare" callout. This is
   that same value-over-label idiom — a plain number, a small muted label, no icon, no tinted
   background, no shadowed box — scaled up to hero prominence and laid out as one undecorated
   row of facts, separated only by the hairline rule the rest of the site already uses for
   separation (`--border`), never by four boxed "stat cards" with their own shadows. That
   distinction is deliberate: a shadowed tile grid is the previous-generation pattern this system
   forbids outright; a row of plain numbers under a hairline reads as the ledger's own front page,
   not a dashboard bolted onto it.

   Sits directly under the navy hero band, before the trust strip — the fact a reader wants
   answered ("what does this site actually know") comes before the compliance microcopy, and both
   come before a single table row.

   A CSS Grid with a fixed column count at each tier, not a wrapping flex row: a flex row whose
   items wrap based on available width put a `border-left` divider on whichever item happened to
   wrap onto a line of its own — a vertical rule hanging at the start of a row with nothing to its
   left, dividing nothing. It first showed at 768px (3 of 4 items fit, one wrapped); moving the
   breakpoint to 900px only relocated the same bug one viewport over, because the underlying cause
   was content-dependent wrapping, not the specific pixel value. A grid with an explicit column
   count per tier and dividers assigned by column position (`nth-child`) cannot produce this: every
   item is always in a real row and a real column, at every width.
   ========================================================================== */

.dl-keyfacts {
  /* Trimmed from 24/20 in the 2026-08-15 density pass. The strip's own figures are unchanged and
     none is smaller; this is padding around them, which is the part a reader scrolls past rather
     than reads. */
  /* 24px above, 20px below. The top figure is not padding for its own sake: on the home page
     this strip sits directly beneath the full-bleed hero band, and the lead chip used to bleed
     upward into that gap so the two dark rectangles read as one shape with a bite out of it —
     "приклеено кверху, нет разделения между прямоугольником". Page surface between them is what
     separates an editorial band from the evidence strip under it. */
  padding: var(--space-6) 0 var(--space-5);
  border-bottom: var(--rule-hairline);
}

/* **The statistics strip, dark** (2026-10-02).
 *
 * The reference pack runs a full-bleed ink strip under the hero carrying three plain figures —
 * "39 active providers · 87 comparable plans · 222 recorded changes" — and nothing else: no cards,
 * no icons, no borders. It is the counterweight to the light hero above it, and the page reads
 * wrong without it: a pale band followed by four pale cards is two of the same thing.
 *
 * The dark surface comes back here rather than staying on the hero. Same amount of ink on the
 * page, in the place the pack puts it.
 *
 * The icon tints and card borders go. They were hierarchy invented when nothing else on the strip
 * had any; a figure at 32px on an ink ground needs neither. */
.dl-keyfacts--strip {
  background: var(--ink);
  border-bottom: 0;
  /* The pack's `.stat-inner` sets 15px of vertical padding — the band is a line a reader crosses,
     not a section they stop in. */
  padding: var(--space-4) 0;

  /* Scoped, so the ink ground's own ladder applies to everything nested without touching a rule. */
  --text-body: var(--text-on-ink);
  --text-muted: var(--text-on-ink-muted);
  --text: var(--text-on-ink);
  --text-2: var(--text-on-ink-muted);
  --border: var(--border-on-ink);

  /* **The hover fill and the link colour belong to the ground too** (2026-10-02).
   *
   * The two tile rules below are written once for both grounds and read `--band` and `--blue`,
   * which resolve to lavender and plum — a light surface and a dark ink. On the light four-up
   * grid that is correct. On this band it painted a lavender tile behind text set in
   * `--text-on-ink`, so hovering the lowest-price tile turned the figure, the provider's name and
   * its verification date into pale-on-pale: the owner's report was that the tile went completely
   * unreadable on hover, and it did. The ladder above already solved this for every other colour
   * on the band by scoping it; these two were simply missing from it.
   *
   * The lift is white at 10%, not a token surface: every named surface this project owns is a
   * light one, and the only correct hover on an ink ground is a lighter ink. */
  --band: rgb(255 255 255 / 10%);
  --blue: var(--link-on-ink);
  /* The keyboard ring, for the same reason and found by the same review: `--focus` is 1.66:1 on
     this ground and 1.22:1 on the hover lift above. See `--focus-on-ink` in tokens/colors.css. */
  --focus-color: var(--focus-on-ink);
}

/* The row holds the container while the section holds the ground. */
.dl-keyfacts--strip .dl-keyfacts__row {
  /* The same container arithmetic as `.dl-page` — see its comment in layout.css for why the
     gutter is subtracted from the viewport rather than paid for out of the 1280. The strip sits
     full-bleed on the ink ground and holds its own container, so it has to track that rule. */
  width: min(var(--page-max), 100% - var(--page-pad) * 2);
  margin-inline: auto;
  /* **Flexed, not four equal tracks** (2026-10-02). The base variant's `repeat(4, minmax(0,1fr))`
     is right for the light stat grid, where every item is a card of the same width. On the strip
     it gave "$3,576–$24,576" the same 276px as "39", so the range filled its track and its label
     shredded over two lines while the other three sat on one — the uneven row the owner's
     screenshot shows.

     The pack's own strip is `display: flex; justify-content: space-between`: each fact takes the
     width its own words need and the space between them absorbs the difference. */
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-5) var(--space-8);
  flex-wrap: wrap;
}

.dl-keyfacts--strip .dl-keyfacts__item {
  flex: 0 1 auto;
}

/* **One line per fact, number first** (2026-10-02). The pack's strip reads "39 active providers"
 * on a single baseline: the figure heavy, the words beside it in normal weight, and nothing
 * underneath. Ours stacked a label over a 32px figure over a sub-line, which is a stat *card* —
 * correct on a light page, three storeys too tall for a band whose whole job is to be read in one
 * pass on the way down. */
.dl-keyfacts--strip .dl-keyfacts__item {
  border: 0;
  padding-left: 0;
  display: block;
}

/* **Three parts on two lines**, which is as close as this strip gets to the pack's one line: the
   pack's items carry a figure and two words, ours carry a figure, a label and the attribution that
   makes the figure checkable. Dropping the attribution would make the layout match and the content
   worse.

   Every figure is wrapped in `.dl-keyfacts__figure` now (template, 2026-10-02) so all three parts
   are addressable. `display: contents` on the `<dd>` dissolves the box that used to trap the
   attribution in the figure's column — it is safe here precisely because nothing inside is a bare
   text node any more, which is what broke the first attempt. */
.dl-keyfacts--strip .dl-keyfacts__body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: var(--space-2);
}

.dl-keyfacts--strip .dl-keyfacts__value {
  display: contents;
}

.dl-keyfacts--strip .dl-keyfacts__figure {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  line-height: 1.2;
  /* No `nowrap`. It was the first version's and it pushed "$3,576–$24,576" and its caption past
     the right edge of a 360px phone — `auto` in the column track plus an unbreakable 160px string
     is wider than the screen, and the strip is full-bleed so nothing clipped it. A range may break
     at its dash; the figure beside it is short enough never to. */
  overflow-wrap: anywhere;
}

.dl-keyfacts--strip .dl-keyfacts__label {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--step-small);
  font-weight: var(--weight-regular);
  color: var(--text-on-ink);
}

/* A range gets the step below. The pack's strip holds four short figures and each label sits on
   one line beside its own; ours holds one 14-character range ("$3,576–$24,576") that took 160px
   of a 250px track and left "Widest first-year range" to shred across two lines while its three
   siblings sat on one. A range is two numbers and does not need a single count's optical weight —
   dropping it one step is what makes the four read as one row. */
.dl-keyfacts--strip .dl-keyfacts__value--range .dl-keyfacts__figure {
  font-size: var(--step-lede);
}

.dl-keyfacts--strip .dl-keyfacts__who {
  grid-column: 1 / -1;
  grid-row: 2;
  min-width: 0;
  overflow-wrap: anywhere;
  display: block;
  margin-top: 2px;
  font-size: var(--step-micro);
  color: var(--text-on-ink-muted);
}

/* The verified tick inside the strip. `--good` is a dark green and the ground is darker still —
   1.4:1 — so the one piece of evidence on the strip was the least readable thing on it. The
   on-ink cut of the same semantic, measured at 9.51:1. */
.dl-keyfacts--strip .dl-keyfacts__verified,
.dl-keyfacts--strip [class*="verified"] {
  color: var(--good-on-ink);
}

.dl-keyfacts--strip .dl-keyfacts__value {
  color: var(--text-on-ink);
}

.dl-keyfacts--strip .dl-keyfacts__label {
  color: var(--text-on-ink-muted);
}

/* No icon on the strip. The pack's own has none, and a tinted square beside a figure on ink is a
   second accent competing with the number it is meant to introduce. */
.dl-keyfacts--strip .dl-staticon {
  display: none;
}

.dl-keyfacts__row {
  margin: 0;
  padding: 0;
  display: grid;
  /* minmax(0, …): with bare 1fr the tracks' min-content floor let the two long-figure columns
     steal width from the two count columns, whose text then shredded one word per line —
     measured on the first render of the icon-tile version. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-6);
}

/* Icon tile + text stack. One structure for all four metrics — the owner's brief bans the old
   arrangement (a dark green card for the first, plain text for the rest) by name. */
.dl-keyfacts__item {
  position: relative;
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-6);
  margin: calc(var(--space-2) * -1) calc(var(--space-2) * -1) calc(var(--space-2) * -1) 0;
  border-left: var(--rule-hairline);
  border-radius: var(--radius-md);
  transition: background-color var(--dur) var(--ease-standard);
}

.dl-keyfacts__item:first-child {
  /* Flush with the page edge, unlike its three siblings: no left padding or divider to
     compensate for, so neither gets the negative-margin treatment the base rule applies. */
  padding-left: 0;
  margin-left: 0;
  border-left: 0;
}

@media (prefers-reduced-motion: reduce) {
  .dl-keyfacts__item {
    transition: none;
  }
}

.dl-keyfacts__body {
  min-width: 0;
}

/* The tinted data-icon tile. The four tints are the entirety of where the soft blue and purple
   tokens may appear — see tokens/colors.css. */
.dl-staticon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
}

.dl-staticon--green { background: var(--tint-green-bg); color: var(--tint-green-ink); }
.dl-staticon--blue { background: var(--tint-blue-bg); color: var(--tint-blue-ink); }
.dl-staticon--purple { background: var(--tint-purple-bg); color: var(--tint-purple-ink); }
.dl-staticon--neutral { background: var(--tint-neutral-bg); color: var(--tint-neutral-ink); }

.dl-keyfacts__label {
  margin: 0 0 6px;
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-keyfacts__value {
  margin: 0;
  font-size: var(--step-metric);
  line-height: 1.1;
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  font-variant-numeric: var(--tabular);
  color: var(--text-body);
}

.dl-keyfacts__who {
  display: block;
  margin-top: 5px;
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

/* On phones the strip is 2×2 and each track is ~150px of text beside a 32px icon. The
   fourteen-glyph range cannot hold 26px there — it broke mid-digit ("$24,57 / 6"), which reads
   as a wrong number, the worst failure a price site can print. One step down and nowrap: the
   figure either fits or the caption wraps under it, never the digits. */
@media (max-width: 48rem) {
  .dl-staticon {
    width: 32px;
    height: 32px;
  }

  .dl-keyfacts__value--range {
    font-size: 1.125rem;
  }

  .dl-keyfacts__figure {
    white-space: nowrap;
  }
}

/* Below 900px (the site's own existing tier boundary, reused rather than a second one
   introduced for this component alone — see data-table.css's `@media (max-width: 899px)`): two
   columns, two rows. Dividers are assigned by grid position, not by which item happens to wrap:
   the right column (items 2 and 4) gets the vertical rule, the bottom row (items 3 and 4) gets a
   horizontal one, so item 4 correctly gets both and no divider is ever orphaned. */
@media (max-width: 899px) {
  .dl-keyfacts__row {
    /* `minmax(0, 1fr)`, not `1fr`: a grid track's automatic minimum is its content's min-content
       width, so a figure like "$1,199.64-$1,733.88" refuses to shrink and pushes the whole strip
       past the viewport. Measured at 390px with tools/mobile_check.py — the row ended at 480px in
       a 390px viewport and took the entire page sideways with it. Nothing below 500px had ever
       been measured, which is why this survived: at 500 the untruncated figure still fits. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--space-6);
    row-gap: var(--space-4);
  }

  .dl-keyfacts__item {
    min-width: 0;
  }

  .dl-keyfacts__value,
  .dl-keyfacts__figure {
    overflow-wrap: anywhere;
  }

  .dl-keyfacts__item {
    border-left: 0;
    padding-left: 0;
  }

  .dl-keyfacts__item:nth-child(2n) {
    border-left: var(--rule-hairline);
    padding-left: var(--space-6);
  }

  .dl-keyfacts__item:nth-child(n + 3) {
    border-top: var(--rule-hairline);
    padding-top: var(--space-4);
  }

  .dl-keyfacts__value {
    font-size: var(--step-metric);
  }
}

/* --- 2026-08-10 owner design pass ------------------------------------------------ */

/* Range figures stay on one line, one type step down: "$1,188–$3,588" at the full
   34px wrapped at the dash and broke the strip's shared baseline. */
.dl-keyfacts__figure {
  white-space: nowrap;
}

/* ...but not once the strip is two columns wide.
 *
 * The `max-width: 899px` block above already grants this figure `min-width: 0` and
 * `overflow-wrap: anywhere` so it can shrink — and that grant did nothing, because this rule
 * sits later in the file and `overflow-wrap` cannot break a line that `white-space: nowrap`
 * forbids from breaking at all. It went unnoticed while the site was set in Public Sans: at
 * 500px "$3,576–$24,576" measured just inside its column. Host Grotesk sets about 8% wider, the
 * same figure became 228px against 213px of column, and the whole page scrolled sideways by
 * 15px (tools/scroll_overflow.py, 2026-08-14).
 *
 * A range is the one figure here with an honest break point in it — at the dash — so letting it
 * take two lines on a narrow screen costs nothing. The single figures beside it are short
 * enough to stay on one line by themselves. */
@media (max-width: 899px) {
  .dl-keyfacts__figure {
    white-space: normal;
  }
}

/* A hair smaller than its siblings only because "$3,576–$24,576" is twice as many glyphs as
   "39" — same step, no separate size. The sweep briefly put it on --step-section (30px, the
   serif heading step), which is a different job entirely. */
.dl-keyfacts__value--range {
  /* 26px, not the full metric step: "$3,576–$24,576" is fourteen tabular glyphs, and at 32px it
     overran its own track into the neighbouring tile's icon — visible in the owner's screenshot,
     the exact class of defect cell_geometry exists to catch in tables. The figure still reads as
     a metric beside its 12px label. */
  font-size: 1.625rem;
  letter-spacing: -0.02em;
}

/* The verification stamp in the lead tile speaks the sitewide green ✓ vocabulary —
   it was the one "verified" on the page set in muted grey. */
.dl-keyfacts__verified {
  color: var(--green);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
}

/* The lead tile is the page's answer, so it takes the pack's own dark stat chip.
   On-navy inks are the measured pairs from tokens/colors.css's own audit table:
   #D6DEEA / navy 10.4:1, #9FE3B9 / navy 9.5:1 — both clear AA with room. */
/* The box bleeds into the margins; the text stays on the strip's own grid. Without
   the negative margins the chip's padding pushed its label below and right of the
   neighbouring tiles' labels, and the box read as a patch stuck to the page edge
   rather than a highlight behind aligned content. */
/* `.dl-keyfacts__item--lead` is gone (2026-08-18): the dark chip it styled was the imbalance
   the owner rejected twice — first as "приклеено кверху", then via his brief's "do NOT make the
   first metric a giant green card". No template renders the class any more. */

/* A three-count strip (the state index). The homepage's is four; this keeps the same
   component and only says how many columns it has. */
.dl-keyfacts__row--3 {
  grid-template-columns: repeat(3, 1fr);
}

/* A two-count strip — the state index again, when no provider has a state list on file and
   the strip has one real fact plus one honest "not yet collected" note rather than two
   vacuous stats (`StateIndexPage.get_context`, 2026-08-25). Without this the base rule's
   four-track grid still applied, packing two items into half the row and leaving two empty
   tracks of dead space to their right. Below 899px the un-scoped rule already renders any
   two-item row correctly (two tracks, one item each), so no phone-width override is needed
   here the way `--row--3` needed one. */
.dl-keyfacts__row--2 {
  grid-template-columns: repeat(2, 1fr);
}

/* One column on a phone — but only the 3-metric variant (the state index strip). It used to
   apply to every `.dl-keyfacts__row`, which meant the homepage's own 4-metric strip (the "must
   read as 2x2 on a phone" one) also collapsed to a single column below 424px, at both of the
   widths tools/mobile_check.py actually measures (390, 360) — the opposite of the required
   layout. Scoped to `--row--3` now: two 155px columns held a five-figure range and its label,
   which wrapped to four lines each and made *that* strip taller than the fold it sits above; the
   homepage strip does not have that problem once `--value--range` (below) is sized down and its
   figure gets its own wrap point at the dash, so it stays 2x2 all the way to 360px instead. */
@media (max-width: 26.5rem) {
  .dl-keyfacts__row--3 {
    grid-template-columns: minmax(0, 1fr);
  }

  .dl-keyfacts__row--3 .dl-keyfacts__item:nth-child(2n) {
    border-left: 0;
    padding-left: 0;
  }
}


/* --- the tiles lead somewhere (2026-08-17), then the whole tile does (2026-08-18) ---------- */

/*
 * Every tile that names a provider or counts a set links to it: the cheapest provider's page,
 * the widest-range provider's page, the table itself, the unpriced list below it. The owner's
 * complaint about the lead tile was exact — "и что это мне даёт, я даже кликнуть не могу" — and
 * it applied to all four.
 *
 * **The click target is the whole tile now, not just the name or the caption.** The reasoning
 * that used to sit here argued the opposite — that wrapping the figure and its verification date
 * in one anchor would claim the date leads somewhere too — and the owner overruled it directly:
 * "в идеале чтобы кликались каждый dl-keyfacts__item, а не ссылка" (2026-08-18). The date still
 * is not itself a link and states no separate destination of its own; the stretched-link
 * technique below doesn't change what is clickable in the sense of "what has its own href," only
 * how much of the tile's surface reaches the one destination the tile already has. Same pattern
 * already used for the ranked card (`.dl-pcol .dl-plogo__link--hero::after`, provider-column.css)
 * and the summary table's provider cell (`.dl-table--summary tbody th .dl-plogo__link::after`,
 * data-table.css) — one real anchor, one tab stop, a transparent `::after` stretched to the
 * tile's own positioned box via `inset: 0`.
 */
.dl-keyfacts__link {
  color: inherit;
  text-decoration: none;
  border-bottom: 0;
}

.dl-keyfacts__link:hover,
.dl-keyfacts__link:focus-visible {
  color: var(--blue);
  text-decoration: underline;
}

.dl-keyfacts__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* The quiet hover the ranked card already uses for the same "the whole surface is one link"
   pattern — a --band fill, nothing moving, no shadow or scale (provider-column.css's own
   `.dl-pcol:hover`). */
.dl-keyfacts__item:has(.dl-keyfacts__link:hover) {
  background: var(--band);
}

/* Keyboard parity: tabbing to the tile's link rings the whole tile, not just the name —
   `:focus-visible` so a mouse click leaves no ring behind, matching `.dl-pcol`'s own rule. */
.dl-keyfacts__item:has(.dl-keyfacts__link:focus-visible) {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: calc(-1 * var(--focus-width));
  background: var(--band);
}


/* The strip on a narrow screen: the pack's own phone tier — two columns, the figure on its own
   line above the words, and no dividers.
 *
 * Three things were wrong before (measured at 500px, 2026-10-02, after the desktop row became a
 * flex row):
 *
 * - `display: block` on the body put the label *above* the figure, because the `<dt>` comes first
 *   in the DOM and only the grid was reordering them. The pack sets `strong { display: block }` —
 *   the figure leads at every width.
 * - the base `899px` tier's dividers came back: a vertical rule between the first two items and a
 *   bordered, rounded box around the first. They are drawn for the light four-up stat grid, where
 *   each item is a panel; on the ink band they are the box the band exists instead of.
 * - the row was still the base grid, so the flex row this file now sets for the strip stopped
 *   applying and the two shapes disagreed about which was in force. */
@media (max-width: 599px) {
  .dl-keyfacts--strip .dl-keyfacts__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-4) var(--space-5);
  }

  .dl-keyfacts--strip .dl-keyfacts__item,
  .dl-keyfacts--strip .dl-keyfacts__item:nth-child(2n),
  .dl-keyfacts--strip .dl-keyfacts__item:nth-child(n + 3) {
    border: 0;
    padding: 0;
    margin: 0;
  }

  .dl-keyfacts--strip .dl-keyfacts__body {
    grid-template-columns: minmax(0, 1fr);
  }

  .dl-keyfacts--strip .dl-keyfacts__figure {
    grid-row: 1;
    font-size: var(--step-lede);
  }

  .dl-keyfacts--strip .dl-keyfacts__label {
    grid-column: 1;
    grid-row: 2;
  }

  .dl-keyfacts--strip .dl-keyfacts__who {
    grid-row: 3;
  }
}

/* --- css/components/home-hero.css --- */
/* ==========================================================================
   HomeHero — the homepage's dark banded hero (2026-08-18: dark again, second reversal the same
   day — see home_page.html's own comment for why this contradicts the "went light" pass this
   file used to open with).

   The composition is unchanged from the light pass and that pass's own reasoning still holds:
   the hero caps at 1200px inside the wider 1360px page, the editorial column is a fixed 560px so
   the claim's measure never drifts, and the card owns the remainder. From 1280 to 1800 the block
   is the same shape, just more margin around it.

   What changed: `.dl-hero.dl-hero--home` wraps this grid in the full-bleed navy band
   (`.dl-hero`, layout.css) and a tonal SVG duotone sits behind it, anchored to the section's own
   right edge — see `_hero_art_duotone.html`. On-dark ink for the title/tag/sub below; the byline
   needs no override here, because it already reuses `.dl-hero .dl-byline*` (layout.css) the
   moment it is a descendant of `.dl-hero` — which this markup now is.
   ========================================================================== */

.dl-hero--home {
  position: relative;
  overflow: hidden;
  /* **Flat, not a gradient** (2026-10-02, the side-by-side pass).

     `.dl-hero` (layout.css) paints a 115deg lavender-to-peach gradient, which is the pack's
     *provider* hero. The pack's home hero is a flat `#eadff1` with the photograph covering the
     whole band and a horizontal wash laid over it — the colour a reader sees on the left is the
     wash, not the ground. Painting a diagonal gradient *and* a horizontal wash over a photograph
     cropped to the right 42% is three treatments fighting, and it is what the owner's screenshots
     showed: our left half was flat lavender where the reference's is lit by the picture through
     the wash. */
  background: #eadff1;
  /* `.dl-hero`'s own 20/22px band chrome padding is right for a plain article title sitting
     directly on it; this hero's own grid (`.dl-homehero`, below) carries its own generous
     vertical rhythm instead, so the band itself adds none of its own or the two would stack. */
  padding: 0;
}

.dl-hero--home__inner {
  position: relative;
  /* Above both the art (z-index 0) and the wash over it (the `::before`, z-index 1). It was 1,
     which tied with the wash and left the copy's stacking order decided by document order — it
     happened to win, but a legibility layer and the text it protects must not be able to swap. */
  z-index: 2;
  /* The pack's `.hero-inner`: 570px, stepping to 510 under 1280 and 670 on a phone (where the
     copy stacks over the picture). It is the band's whole proportion — ours was whatever the
     content came to, which is why the hero read as a strip and the reference's reads as a cover. */
  min-height: 570px;
  display: flex;
  align-items: center;
}

/* The tonal duotone. Absolutely positioned against the full-bleed band, not the 1360px content
   column, so it can run to the true viewport edge the way the reference's own tinted photo does
   — cropped only by this element's own bounds (`overflow: hidden` on `.dl-hero--home`). The mask
   fades its own left edge into the band rather than ending on a hard vertical seam, which is what
   "melts into the band" means in practice: no visible box, just a wash of a few darker/lighter
   greens. */
/* **The photograph covers the whole band** (2026-10-02), which is the pack's `.hero-media`:
   `position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover`. It was confined to
   the right 42% behind a left-edge mask — a composition written for a dark band where the picture
   was an accent in the margin. The pack treats it as the hero's ground: the copy sits *on* the
   photograph and the wash below is what makes it readable. With the picture cropped to 42% the
   wash had nothing to act on for the first 58% of the band, so the left side rendered as flat
   colour. That is the "задний фон не тот" the owner was pointing at. */
.dl-hero--home__art {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.dl-hero--home__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The pack's own framing value. With the picture full-bleed the subject has to be held off to
     the right of centre or the copy column lands on top of it. */
  object-position: 60% center;
  /* The wash: the photo is a pale grey-blue flat-lay, and the band is deep green. `luminosity`
     keeps the photograph's light and shade while taking its colour from the band underneath, so
     it reads as one tinted surface rather than a picture pasted onto a colour — exactly the
     treatment the owner's reference uses. `opacity` then sets how far it comes forward. */
  /* **No wash** (2026-10-02). `mix-blend-mode: luminosity` at 28% existed to make a colour
     photograph take the dark band's own green and sit *behind* white text. The band is a light
     gradient now and the text is ink, so the blend was draining every colour out of the picture
     and the opacity was holding what was left at a quarter strength: the reference's hero shows
     wood, a hand and a paper receipt, ours showed a pale grey smudge.
     Full strength, and the gradient overlay below carries the legibility instead — which is how
     the pack does it. */
  opacity: 1;
}

/* The overlay that keeps the copy readable over the photograph, from the pack's own hero: a
 * left-to-right wash that is opaque under the text and gone by the time it reaches the picture.
 * This replaces the blend above — legibility by covering the photograph where words are, rather
 * than by draining it everywhere. */
.dl-hero--home::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    #e2dff9 0%,
    #eadff4 30%,
    rgb(245 226 232 / 94%) 47%,
    rgb(246 221 214 / 38%) 66%,
    rgb(246 221 214 / 0%) 78%
  );
}

/* The art is no longer hidden on narrow screens. It used to be, because it was an accent in the
   right margin and below 1200px there is no right margin; as the band's ground there is no width
   at which it has nowhere to go. What changes instead is the wash: the `68.75rem` tier below
   swaps the horizontal gradient for the pack's vertical one, so the copy stacked over the picture
   stays readable. */

.dl-homehero {
  /* `width: min(100%, 1200px)` and `margin-inline: auto` stood here, dead since the hero stopped
     being a grid (see the note below) and overridden by the `width: 100%` nine lines down. The
     2026-10-02 Codex review read them as live and reported the hero as capped 80px narrower than
     the rest of the page — measured, it is not, but a rule that contradicts itself inside its own
     block is how that reading happens. The hero takes its width from `.dl-page` like everything
     else on the page. */
  display: grid;
  /* The card's own column is capped at 460px, not "fill whatever is left of the 1200px box"
     (2026-08-18, the art pass). A card stretched to fill the box left the art nothing to occupy
     but the sliver beyond `.dl-page`'s own 1360px cap — ~175px at 1550px wide, close to nothing
     at 1280px. Capping the card leaves real empty space inside this box's own right edge (grid's
     default `justify-content: start` packs the two fixed tracks to the left, so the remainder is
     unused canvas here, not stretched into the card) — empty because nothing is drawn there, not
     because anything is hidden, so the art layer behind it (lower z-index, same stacking
     context) shows straight through. */
  /* **58% to the copy, the card beside it** (v5, 2026-10-02). The pack gives its hero copy 58% of
     the page and nothing else a fixed track; ours gave the copy a flat 560px, which at 1440 is 43%
     — so the headline that sets on two lines in the reference broke to three in ours, and the
     whole hero read as narrower and taller than it should. */
  /* **Not a grid any more** (2026-10-02). The pack's hero is `display: block` with the copy at
     58% and the card absolutely positioned against the band's bottom-right corner. Ours reserved
     a second column for the card, which did three things the reference does not: it pinned the
     card to the *top* right, it took the card's width out of the band before the picture could
     use it, and it made the hero's height the taller of two stacked columns. The card is a panel
     laid over the corner of a cover, not a column beside the headline. */
  display: block;
  position: relative;
  width: 100%;
  padding: var(--space-10) 0 var(--space-8);
}

/* The pack's `.hero-copy`: 58% of the band, capped so the measure cannot run away on a wide
   screen. */
.dl-homehero__main {
  width: 58%;
  max-width: 720px;
}

/* The eyebrow over the headline: the pack's `.eyebrow` — 13px, 800, uppercase, 0.14em tracked. */
.dl-homehero__eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--step-micro);
  font-weight: var(--weight-black);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}

/* The hero's own action pair: one filled button and one plain link, on a line under the lead. */
.dl-homehero__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  /* Tightened 2026-10-02. The pack's hero is copy, lead, actions and nothing else; ours carries
     two more lines below the actions — the named byline and the publication line, both required
     — so the rhythm between the four has to be closer than it would be with two. */
  margin: var(--space-4) 0 var(--space-3);
}

.dl-homehero__jump {
  font-weight: var(--weight-semibold);
  text-decoration: underline;
}

.dl-homehero__title {
  font-family: var(--font-sans);
  /* **The pack's own hero size and weight** (2026-10-02): `clamp(3.7rem, 5.25vw, 5.5rem)` at 800,
     which is 59px at 1120 and 88px at 1680. It was 52px at weight 400 — a flat size chosen when a
     single-weight serif set this line in a 560px column. The column is 58% of the page now and
     the face has a real 800, so the constraint that produced 52/400 is gone and what it produced
     is the single biggest difference between our home page and the reference's. */
  font-size: clamp(3.7rem, 5.25vw, 5.5rem);
  line-height: 1.01;
  letter-spacing: -0.04em;
  font-weight: var(--weight-black);
  font-synthesis-weight: none;
  /* Ink. It said `--white` — left from the dark band — and only rendered correctly because
     `.dl-hero h1` in layout.css outranks it by one class. A colour that is wrong and invisible
     because a more specific rule happens to cover it is a trap for the next edit. */
  color: var(--ink);
  text-wrap: balance;
}

/* The pack's one typographic signature in the headline: a single word set in the editorial serif,
   italic, at weight 500 against the grotesk's 800 — "A *clearer* price." Ours set the whole line
   in one face and one weight, which is the difference a reader notices first and the reason the
   two headlines read as different brands. See `dl_accent` (apps/core/headings.py) for how the
   word is marked in the heading field. */
.dl-homehero__title em {
  font-family: var(--font-editorial, var(--font-serif));
  font-weight: var(--weight-medium);
  font-style: italic;
  letter-spacing: -0.02em;
}

/* The left side is one intentional stack: claim → positioning line → named verification →
   supporting paragraph. Margins tuned so the four read as one column, not four siblings. */
.dl-homehero__tag {
  margin: var(--space-4) 0 var(--space-3);
  font-size: var(--step-lede);
  line-height: 1.45;
  font-weight: var(--weight-medium);
  /* The same ink `.dl-hero__sub` (layout.css) already uses on this band. Both now read the
     token rather than repeating a hex; see tokens/colors.css for why it stopped being blue. */
  color: var(--text-on-navy);
}

.dl-homehero__main .dl-byline {
  margin-top: 0;
}

.dl-homehero__sub {
  margin: var(--space-3) 0 0;
  max-width: 38rem;
  font-size: var(--step-body);
  line-height: 1.6;
  /* #D6DEEA, the hero byline's own muted line (tokens/colors.css: 8.99:1 on --navy) — a second
     supporting line on the same band takes the same ink rather than a third invented for it. */
  color: var(--text-on-navy-2);
}

/* --- the decision column ------------------------------------------------------------------ */

/* **Bottom-right, over the picture** (2026-10-02) — the pack's `.hero-receipt` position, down to
   the `bottom: 24px`. It sat top-right in a grid column of its own; the reference anchors it to
   the corner the photograph's subject leads the eye to, and leaves the top of the band to the
   headline alone.

   `max-width` rather than a track width: this card holds a form, and a form that grows (the
   questions open behind a `<details>`) must grow upward from a fixed bottom edge rather than push
   the band taller. */
.dl-homehero__side {
  position: absolute;
  right: 0;
  bottom: var(--space-5);
  z-index: 2;
  width: min(320px, 38%);
}

.dl-homehero__side .dl-match {
  width: 100%;
  min-width: 0;
}

/* --- the helper card ------------------------------------------------------------------------ */

.dl-match {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5) var(--space-5) var(--space-4);
}

/* 700 at 18px. "Not sure where to start?" heads a small helper card, not a page section, and 800
   below the 20px floor is what `tools/type_scale.py` measures. */
.dl-match__title {
  font-family: var(--font-sans);
  font-size: var(--step-lede);
  font-weight: var(--weight-bold);
  margin: 0;
}

.dl-match__sub {
  margin: 6px 0 var(--space-4);
  font-size: var(--step-small);
  line-height: 1.45;
  color: var(--text-2);
}

.dl-match__grid {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.dl-match__field {
  display: grid;
  gap: 4px;
}

.dl-match__label {
  font-size: var(--step-micro);
  font-weight: var(--weight-medium);
  color: var(--text-2);
}

/* The same control anatomy as the table's own filter selects — one control vocabulary. */
.dl-match select {
  width: 100%;
  min-height: 40px;
  padding: 0 var(--space-3);
  font: inherit;
  font-size: var(--step-small);
  color: var(--text-body);
  background: var(--white);
  border: 1px solid var(--border-2);
  border-radius: var(--radius);
}

.dl-match select:focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
}

.dl-match__go {
  width: 100%;
}

.dl-match__micro {
  margin: var(--space-3) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
  text-align: center;
}

/* The three questions live behind the disclosure, closed by default — the approved reference's
   card is text plus one button. The summary IS the primary button; open, the questions and the
   apply button take over and the summary steps aside. */
.dl-match__details > summary {
  list-style: none;
  cursor: pointer;
  width: 100%;
}

.dl-match__details > summary::-webkit-details-marker {
  display: none;
}

.dl-match__details[open] > summary {
  display: none;
}

.dl-match__details[open] .dl-match__grid {
  margin-top: var(--space-2);
}

.dl-match__apply {
  width: 100%;
}

/* --- responsive ------------------------------------------------------------------------------ */

/* The pack's own middle tier: the copy takes more of the band, the headline steps down, the card
   tucks closer to the corner. Still one composition, just tighter. */
@media (max-width: 75rem) {
  .dl-hero--home__inner {
    min-height: 510px;
  }

  .dl-homehero__main {
    width: 62%;
  }

  .dl-homehero__title {
    font-size: clamp(3.35rem, 6vw, 4.5rem);
  }
}

/* Below this the two cannot share a line, so the composition stacks: copy first, card under it,
   both over the photograph — and the wash turns vertical so the ink at the top of the band stays
   on the pale end of it. This is the pack's `max-width: 760px` tier. */
@media (max-width: 68.75rem) {
  .dl-hero--home::before {
    background: linear-gradient(
      180deg,
      #e3e0fa 0%,
      rgb(238 225 244 / 98%) 38%,
      rgb(247 224 218 / 72%) 56%,
      rgb(247 224 218 / 8%) 76%
    );
  }

  .dl-hero--home__inner {
    min-height: 0;
    display: block;
  }

  .dl-homehero {
    padding-top: var(--space-8);
  }

  .dl-homehero__main {
    width: 100%;
    max-width: none;
  }

  .dl-homehero__side {
    position: static;
    width: 100%;
    max-width: 28rem;
    margin-top: var(--space-8);
  }
}

@media (max-width: 42.5rem) {
  .dl-homehero {
    padding-top: var(--space-6);
  }

  .dl-homehero__side {
    max-width: none;
  }
}

/* --- css/components/provider-identity.css --- */
/* ==========================================================================
   ProviderIdentity + ProviderTerms — the provider page's head and its key/value panel
   (2026-08-18, owner-approved reference). Trimmed to mark + H1 + crowd rating on
   2026-08-25 when the action, the dek, the byline and the fact chips all moved out — see
   the "trimmed back" note below and docs/ai/review_price_composition_codex_2026-08-25.md.

   The mark tile: the uploaded logo fitted uncropped, or the provider's initial set in the
   display serif. The initial is a typographic mark, not a fake logo — one letter, ink on
   white. 90px: the owner's second reference enlarges the mark from the first pass's 64px,
   which read small beside the taller two-column row this pass adds beside it.
   ========================================================================== */

/* **Trimmed back to mark + text on 2026-08-25** (Codex's price-composition read,
   docs/ai/review_price_composition_codex_2026-08-25.md). This briefly (2026-08-18) held a second
   flex child — `.dl-idhead__actions`, the fold's primary button, top-aligned with the H1 — but
   the action moved out of the identity row entirely once the disclosure and the button both had
   to sit after the new price band instead. `.dl-idhead` is a plain mark+text row again; there is
   no second column to keep from competing with it. */
.dl-idhead {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
}

.dl-idhead__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 90px;
  height: 90px;
  margin-top: 6px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* The typographic-initial branch only, not a real logo: white-on-white with a single hairline
   (2026-08-18 fallout sweep, `--page-bg` went from a warm cream to pure white) left the tile
   reading as a stray letter rather than a deliberate mark once page and tile shared one hex
   exactly. A real uploaded logo keeps the plain white ground — `Provider.logo`'s own help text
   is "never restyled, recoloured," and a brand mark needs a neutral backdrop, not a tinted one. */
.dl-idhead__mark:has(.dl-idhead__initial) {
  background: var(--gray-bg);
}

.dl-idhead__img {
  max-width: 76px;
  max-height: 76px;
  object-fit: contain;
}

.dl-idhead__initial {
  font-family: var(--font-display);
  font-size: 46px;
  line-height: 1;
  color: var(--text-body);
}

.dl-idhead__text {
  min-width: 0;
}

/* --- the crowd rating row: H1, this, then the dek --------------------------------------- */

/* A short row, not the boxed two-window app rating: see includes/_crowd_rating.html for the
   full reasoning. **Every status renders this same shape** (2026-08-19, owner: "не вижу
   элемента с рейтингом" — the first pass's fallback was one line of plain grey prose at the
   dek's own size and weight, and read as the component being missing rather than a rating
   withheld). Glyph, bold headline figure, small qualifier — then a second line underneath
   naming the source and the date, in its own paragraph so it never competes with the headline
   for size. */
.dl-idrating {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin: var(--space-2) 0 0;
  font-size: var(--step-lede);
  line-height: 1;
}

.dl-idrating__num {
  font-weight: var(--weight-black);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-idrating__max {
  font-size: var(--step-small);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
}

/* The outline star (`dl_rating_star_outline`) — the one thing that differs visually from the
   published row. Muted, never the brand accent: a hollow glyph beside a real figure says "here
   is what we know, and it is not a rating" without needing a second sentence to say so. */
.dl-idrating__glyph {
  color: var(--text-muted);
  margin-right: 2px;
}

.dl-idrating__src {
  margin: 3px 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* --- the fold's one action — its own block now, after the price band and disclosure -------- */

/* **Moved out of the identity row on 2026-08-25** (docs/ai/review_price_composition_codex_2026-08-25.md).
   It used to be `.dl-idhead__actions`, a `flex: 0 0 auto` column top-aligned with the H1 — the
   fix for the owner's older complaint, "кнопка перехода вообще теряется внизу" (2026-08-18). The
   price band that now sits between identity and this block made that position wrong for a
   different reason: the FTC disclosure has to sit immediately before the action, and the
   disclosure cannot sit inside the identity row above the price it is disclosing. `.dl-fold-actions`
   is a plain block; there is no longer a sibling column for it to avoid competing with. */
.dl-fold-actions {
  margin-top: var(--space-6);
}

/* The "on <Provider>'s website" / "not a paid placement" line, once, below the whole pair —
   see includes/_provider_actions.html's own comment for why this replaced the built-in copy each
   button used to carry under itself alone. Left-aligned under a left-aligned row of buttons,
   unlike `.dl-oncopy`'s own centred default (built for sitting under one full-width button). */
.dl-idhead__actions-note {
  margin: 8px 0 0;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  text-align: left;
}

/* --- the fact chips ------------------------------------------------------------------------ */

/* Verified facts only, each with its axis icon at 16px. White chips, 1px border — credentials,
   not buttons, per the brief: "Factual credentials may be compact neutral chips when the fact
   and date are explicit." */
.dl-factchips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

/* Tinted, not white-on-white (2026-08-18). Three bordered white chips sitting on a white page,
   a few pixels above the deep-green price card, read as unfinished — the owner's "колхозный".
   Each now carries the same data-category tint its icon already uses in the stat strip
   (tokens/colors.css `--tint-*`), so the chip and its glyph are one object.
   The tint is by *category*, never by judgement — a cash-pay chip is not "worse" than an
   insurance one, so they share the neutral/green vocabulary rather than a good/bad scale. */
.dl-factchip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 0 var(--space-4);
  font-size: var(--step-small);
  font-weight: var(--weight-medium);
  color: var(--tint-green-ink);
  background: var(--tint-green-bg);
  border: 1px solid transparent;
  border-radius: var(--radius);
}

.dl-factchip .dl-mark-icon {
  color: currentColor;
}

/* --- the Terms panel ------------------------------------------------------------------------ */

.dl-terms {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  margin-top: var(--space-5);
}

.dl-terms__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.dl-terms__title {
  font-family: var(--font-sans);
  font-size: var(--step-lede);
  font-weight: var(--weight-black);
  margin: 0;
}

.dl-terms__note {
  font-size: var(--step-micro);
  color: var(--text-muted);
}

.dl-terms__list {
  margin: 0;
  display: grid;
}

.dl-terms__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: var(--space-3);
  padding: 9px 0;
  border-top: 1px solid var(--border);
  font-size: var(--step-small);
}

.dl-terms__row dt {
  color: var(--text-2);
}

.dl-terms__row dd {
  margin: 0;
  color: var(--text-body);
  font-weight: var(--weight-medium);
}

/* An honest gap is quiet ink, never a negative: "not published" and "no" are different states,
   and the brief's own semantics table makes the distinction load-bearing. */
.dl-terms__gap {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

@media (max-width: 42rem) {
  /* `.dl-idhead` is mark + text only now (2026-08-25) — see this file's own comment above — so
     its one gap is the mark/text gap, tightened for a narrow phone. */
  .dl-idhead {
    gap: var(--space-3);
  }

  .dl-idhead__mark {
    width: 48px;
    height: 48px;
  }

  .dl-idhead__img {
    max-width: 40px;
    max-height: 40px;
  }

  .dl-idhead__initial {
    font-size: 26px;
  }
}


/* **The provider H1 takes the pack's own provider step** (2026-10-02, measured).
 *
 * It was taking `--type-h1-size` (the hero step) by inheritance from `h1`, because
 * `.dl-titleblock__title` sets no size of its own and the profile page applies no variant. Measured
 * on /providers/telosrx/: 79.2px at weight 800, breaking "TelosRx: GLP-1 pricing and terms" over
 * two lines, 166px tall. The pack's `.provider-heading h1` is `clamp(2rem, 4vw, 3.6rem)` — 57.6px
 * at the top — and its own H1 measures 65px on one line. 100px of a provider page's first screen,
 * spent on type size.
 *
 * The hero step belongs to a page whose H1 *is* the content (the home page, a ranking). A provider
 * page's H1 is a label over a price band, and the band is the answer. */
.dl-idhead .dl-titleblock__title {
  font-size: clamp(2rem, 4vw, 3.6rem);
  line-height: 1.08;
  max-width: 30ch;
}

/* The review variant's H1: one size down from the profile's product title. The four review
   pages carry a byline and a verdict below; a 56px two-line headline on top of those was the
   text wall the owner rejected. 30px serif holds the keyword and the figure on one line. */
.dl-idhead .dl-titleblock__title--review {
  font-family: var(--font-display);
  font-size: var(--step-section);
  line-height: 1.15;
  letter-spacing: -0.02em;
  max-width: 26ch;
}


/* The identity block's own version of the dark tile — see provider-logo.css for the measurement
   and the reasoning. Scoped to a real uploaded mark: the typographic-initial fallback keeps
   `--gray-bg`, since that is our own lettering and has no designer's assumption to respect. */
.dl-idhead__mark--dark {
  background: var(--navy);
}

/* --- css/components/fact-coverage.css --- */
/* ==========================================================================
   FactCoverage — how many of the tracked providers publish each fact.

   The one graphic on this site whose subject is the industry rather than a provider. It
   replaces two bare "N of M" stat chips that stated the same idea in text and had no shape:
   a reader could read them and not notice that the numbers fall off a cliff.

   Drawn with a track and a fill, no SVG and no library. Five bars do not need a runtime, and
   a width in percent is the whole geometry.

   Colour: the fill is the accent green and the track is the neutral fill, which is the site's
   existing vocabulary for "measured quantity" (the score panel's own weighted bars use it).
   No second hue, no gradient, no per-row colour — a red bar on "How to cancel" would be the
   amber/red palette leaking out of regulatory findings, which is the one thing it is for.
   ========================================================================== */

/* Room on both sides: the block sits between the ranked cards and the master table, and with a
   top margin only it read as the cards' footer rather than as its own statement. */
.dl-coverage {
  margin: var(--space-6) 0 var(--space-5);
}

.dl-coverage__cap {
  margin-bottom: var(--space-3);
  font-size: var(--step-small);
  font-weight: var(--weight-medium);
  color: var(--text-body);
}

.dl-coverage__rows {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 7px;
}

/* Label, bar, figure. The label column is sized in `ch` off the longest label so the bars all
   start on one vertical without a magic pixel value; the figure column is fixed so the bars all
   end on one too, whether the count is 9 or 24. */
.dl-coverage__row {
  display: grid;
  grid-template-columns: minmax(0, 22ch) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--step-small);
}

.dl-coverage__label {
  color: var(--text-2);
}

.dl-coverage__track {
  height: 8px;
  border-radius: 2px;
  background: var(--gray-bg);
  overflow: hidden;
}

/* Width comes from `.dl-bar--NN` (score-panel.css), the 21 discrete classes in steps of 5 that
   this project uses everywhere instead of an inline `style` — which `test_no_inline_styles_or_
   handlers` forbids. Reusing those classes rather than minting a second set means one place
   defines what a proportion bar's width means. */
.dl-coverage__fill {
  display: block;
  height: 100%;
  background: var(--blue);
}

.dl-coverage__n {
  font-variant-numeric: var(--tabular);
  font-weight: var(--weight-medium);
  color: var(--text-body);
  white-space: nowrap;
}

/* "of 39" repeats on every row, so it is set quiet — the varying number is the information and
   the denominator is the constant it is measured against. */
.dl-coverage__of {
  font-weight: var(--weight-regular);
  color: var(--text-2);
}

/* Below the tier where the label column has room to be a column at all, the bar and figure move
   under the label. The bar keeps its full width there, which makes the comparison easier to read
   on a phone rather than harder. */
@media (max-width: 40rem) {
  .dl-coverage__row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .dl-coverage__label {
    grid-column: 1 / -1;
  }
}


/* Under the key-numbers strip: two columns, so five short rows do not become a tall block on the
   one screen the whole page is competing for. The strip above it is four columns of figures; this
   is the same width divided differently, and it reads as the same family of evidence. */
.dl-coverage__rows {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-8);
}

@media (max-width: 60rem) {
  .dl-coverage__rows {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- css/components/metric-tile.css --- */
/* ==========================================================================
   MetricTile — includes/_metric_tile.html. New in the 2026-08 owner-directed depth pass
   (docs/ai/depth_and_second_accent_2026-08-04.md): four boxed facts in their own grid
   ("COMPOUNDED SEMAGLUTIDE, ALL IN — $179/mo"), replacing a bare label-over-value grid with
   only hairlines between cells.

   Not in design-system.html — the pack's own precedent for this exact value-over-label idiom is
   `.dl-qfacts` (editorial.css), a single bordered strip with hairline dividers between cells.
   `.dl-qfacts` itself is unchanged and still used everywhere this pass did not touch; this is
   the same content, drawn with the technique the owner pulled directly from the reference
   build's own component code rather than a per-tile border: a 1px grid **gap** filled with
   `--border`, with each tile's own white fill sitting in the gaps between — the gap *becomes*
   the hairline, so no tile carries its own border or shadow. The whole grid is one component
   (rounded, bordered, clipped), the same "one wrapper, not N bordered boxes" shape
   `.dl-table-wrap` now also uses (data-table.css) — see this pass's own note there.
   ========================================================================== */

.dl-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  margin: var(--space-4) 0;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  /* One of the two surfaces in this pass that carries the elevated shadow (the score panel is
     the other) — see tokens/shape.css's own comment on why elevation is scoped to these two. */
  box-shadow: var(--shadow-elevated);
}

.dl-tile {
  background: var(--surface-elevated);
  padding: var(--space-4);
  min-width: 0;
}

.dl-tile__label {
  margin: 0 0 6px;
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

.dl-tile__value {
  margin: 0;
  font-size: var(--step-title);
  line-height: 1.2;
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  font-variant-numeric: var(--tabular);
  color: var(--text-body);
  word-break: break-word;
}

/* 2026-08 chip pass: a few tiles hold a short phrase rather than a number (the ranking
   page's "Ranked by" tile states the criterion in words, e.g. "Lowest verified all-in
   monthly cost") — the same 20px/black weight the numeric tiles use reads as shouting
   for a sentence-length value, so this modifier steps it down without giving the tile
   a second typographic voice. */
.dl-tile__value--sm {
  font-size: var(--step-body);
  font-weight: var(--weight-bold);
  line-height: 1.3;
}

.dl-tile__value small {
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
}

.dl-tile__note {
  display: block;
  margin-top: 4px;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

@media (max-width: 899px) {
  .dl-tiles {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 420px) {
  .dl-tiles {
    grid-template-columns: 1fr;
  }
}

/* 2026-08 chip pass: fewer-than-four-tile grids (the state page's two-fact pair, the
   ranking page's three-fact row). Placed after the base tiers above on purpose — same
   specificity (one class each), so the later rule wins at every width the two overlap,
   which is what keeps a 3-tile grid from ever landing on the base rule's 2-column tier
   and leaving one cell empty (showing the gap's own border-colour fill, which reads as
   a broken layout rather than a deliberately smaller grid). Two tiles stay two columns
   at every width tested (500px was legible in dev); three stay three down to 560px,
   then stack — a criterion phrase like "Lowest verified all-in monthly cost" wrapping
   across three narrow columns below that width read cramped rather than merely small. */
.dl-tiles--2 {
  grid-template-columns: repeat(2, 1fr);
}

.dl-tiles--3 {
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 560px) {
  .dl-tiles--3 {
    grid-template-columns: 1fr;
  }
}

/* --2up: a page-level strip of exactly two tiles (best_page.html's "providers ranked / ranked by"
   pair). Distinct from `--2`, which is the ranked card's fixed 2x2 nested grid: this one needs the
   same stacking behaviour `--3` has and that one must not get it. Named rather than reusing `--2`
   because the third tile was removed from that strip and leaving `--3` in place published a
   visibly empty third cell. */
.dl-tiles--2up {
  grid-template-columns: repeat(2, 1fr);
}

@media (max-width: 560px) {
  .dl-tiles--2up {
    grid-template-columns: 1fr;
  }
}

/* --nested: the ranked-provider card's own 2x2 fact grid (includes/_winner_card.html), sitting
   inside an already-bordered, already-elevated `.dl-pcard`. The base `.dl-tiles` component draws
   its own border and `--shadow-elevated` because everywhere else it is used, it sits directly on
   the page background (best_page.html's "Providers ranked" strip) — stacking that same chrome a
   second time immediately inside the card's own border/background would read as a frame within a
   frame rather than one bounded fact block. This modifier removes both and the outer margin
   (the card's own flow controls spacing here), keeping only the grid mechanics and the
   `.dl-tile__label`/`.dl-tile__value` typography every other tile grid already uses. Paired with
   `.dl-tiles--2` for a fixed 2-column, 2-row layout — see that component's own comment on why a
   fixed count, not a responsive one, is what keeps exactly four tiles from ever orphaning a cell. */
.dl-tiles--nested {
  margin: 0;
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: none;
}

/* --- css/components/user-rating.css --- */
/* ==========================================================================
   UserRating — the two-window user score.

   Deliberately not styled like our own rating: no blue star, no large numeral. Our score is a
   published rubric we can defend line by line; this is somebody else's crowd, and letting the two
   look alike would invite a reader to read them as one thing. Muted ink, small type, sitting with
   the provider's identity rather than in the score column.

   No colour at all. A first pass reached for amber on a fallen recent window, and `tokens/colors.css`
   says in its own words that amber is for regulatory flags only — a crowd's mood is not a
   regulatory fact. The direction is carried by the arrow between the two figures and by the
   "% at 1-2 stars" that follows, which is also the only version that survives being read in
   greyscale.
   ========================================================================== */

.dl-urating {
  margin-top: var(--space-2);
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-urating__row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}

.dl-urating__label {
  font-size: var(--type-micro-size);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-2);
}

.dl-urating__pair {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  white-space: nowrap;
}

.dl-urating__lifetime,
.dl-urating__recent {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-urating__max {
  font-weight: var(--weight-regular);
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

.dl-urating__sep {
  color: var(--text-muted);
}

/* A recent window materially below the lifetime average: underlined, not coloured. The arrow and
   the low-star share already state the direction in words. */
.dl-urating__recent--down {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

.dl-urating__meta {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size);
  line-height: 1.45;
  color: var(--text-muted);
}

.dl-urating__themes {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--space-3);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--type-micro-size);
}

.dl-urating__count {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-urating--compact .dl-urating__meta {
  max-width: 22rem;
}

/* The table's one-line flag. A link, so it looks like the one thing it is; no colour, because the
   words carry the direction and the reserved palettes stay reserved. */
.dl-uflag {
  display: inline-block;
  margin-top: var(--space-2);
  font-size: var(--type-micro-size);
  font-weight: var(--weight-semibold);
  color: var(--text-2);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 3px;
}

.dl-uflag:hover,
.dl-uflag:focus-visible {
  color: var(--blue);
  text-decoration-color: currentColor;
}

/* --- "What recent app reviewers say" ---------------------------------------------------------
   A list, not a table: two labelled figures with a direction between them, because a reviewer's
   point was that `4.7 -> 1.5` reads as a range until each number is given its own label. */

.dl-appreviews {
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
  border-top: var(--rule-hairline);
}

.dl-appreviews__item {
  display: grid;
  grid-template-columns: minmax(9rem, 1.2fr) auto minmax(0, 2fr);
  gap: var(--space-3) var(--space-6);
  align-items: start;
  padding: var(--space-4) 0;
  border-bottom: var(--rule-hairline);
}

.dl-appreviews__gap {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.dl-appreviews__fig {
  display: flex;
  flex-direction: column;
}

.dl-appreviews__num {
  font-size: var(--step-metric);
  font-weight: var(--weight-black);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--text);
  font-variant-numeric: var(--tabular);
}

.dl-appreviews__lbl {
  font-size: var(--type-micro-size);
  line-height: 1.3;
  color: var(--text-muted);
  max-width: 9rem;
}

.dl-appreviews__arrow {
  font-size: var(--step-title);
  color: var(--text-muted);
}

.dl-appreviews__low {
  margin: 0;
  font-size: var(--type-small-size);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-appreviews__themes {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--space-4);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

.dl-appreviews__count {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-appreviews__src {
  margin: var(--space-2) 0 0;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* One column on a phone: three columns of two-line labels collapse into unreadable slivers. */
@media (max-width: 47rem) {
  .dl-appreviews__item {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }
}

/* The section's intro. Not `.dl-pdesc`, which is capped at 220px because it describes a table
   cell — reusing it here squeezed eleven lines into a narrow column beside two-thirds of empty
   row. A prose measure, the same one the rest of the site's body copy uses. */
.dl-appreviews__intro {
  margin: var(--space-3) 0 0;
  max-width: 46rem;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-muted);
}

/* --- quoted reviews -------------------------------------------------------------------------
   A quotation, set as one. Not a card, not a tinted panel: a rule down the left edge and the
   author's own words, which is how this site already renders a sourced claim. No colour — the
   stars carry the sentiment and the reserved palettes stay reserved. */

.dl-quotes {
  margin-top: var(--space-3);
}

.dl-quotes__label {
  display: block;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-2);
}

.dl-quotes__list {
  display: grid;
  gap: var(--space-3);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.dl-quotes__item {
  border-left: 2px solid var(--border);
  padding-left: var(--space-3);
}

.dl-quotes__stars {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  font-size: var(--type-small-size);
  color: var(--text-2);
  letter-spacing: 0.06em;
}

/* One blue star plus the number, matching the site's single rating mark. The five-glyph row this
   replaces was a design-system violation and also unreadable at 13.5px: five characters with
   letter-spacing read as a texture, not a score. `gap` is zeroed across these three spans so the
   figure sets as one token ("★ 1 /5") inside the flex row's 6px gaps. */
.dl-quotes__star {
  color: var(--blue);
  margin-right: 2px;
}

.dl-quotes__num {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  letter-spacing: normal;
}

.dl-quotes__max {
  color: var(--text-2);
  letter-spacing: normal;
}

.dl-quotes__title {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  letter-spacing: normal;
}

.dl-quotes__body {
  margin: 3px 0 0;
  font-size: var(--type-small-size);
  line-height: 1.55;
  color: var(--text-body);
  max-width: 44rem;
}

.dl-quotes__by {
  margin: 3px 0 0;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* Two abreast where there is room; stacked on a phone. */
@media (min-width: 60rem) {
  .dl-quotes__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- theme chips: boxed numerals (2026-08 winner-card rebuild) ---------------------------
   The ranked card's left column restates `rating.top_themes` as three small bordered boxes,
   the count set large above the word — the owner's reference card's own idiom, and the
   opposite emphasis of `.dl-tile` (word-label-over-big-value) elsewhere on this site, which is
   why this is its own pair of classes rather than a `.dl-tile` reuse. Same "1px grid gap
   filled with --border" technique `.dl-tiles` already uses (metric-tile.css), at a smaller
   scale — no shadow, no elevation: this is a sub-component of an already-elevated `.dl-pcard`,
   not a surface of its own. */
.dl-theme-chips {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.dl-theme-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-2) var(--space-1);
  background: var(--surface-elevated);
  text-align: center;
}

.dl-theme-chip__n {
  font-size: var(--step-lede);
  font-weight: var(--weight-black);
  line-height: 1;
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-theme-chip__label {
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  text-transform: lowercase;
}

/* The lightweight aggregate block: one platform number with its base and its caveat, for the
   36 providers that have a Trustpilot score and no app rating. Deliberately not `.dl-score` and
   deliberately not the two-window block above — a lifetime average is a different measurement
   and must not borrow the visual language of either. */
.dl-urating-lite__figure {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.dl-urating-lite__num {
  font-size: var(--step-metric);
  font-weight: var(--weight-black);
  font-variant-numeric: var(--tabular);
  color: var(--text);
}

.dl-urating-lite__max {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-urating-lite__base {
  font-size: var(--type-small-size);
  color: var(--text-2);
}

.dl-urating-lite__note {
  margin: var(--space-2) 0 0;
  max-width: 44rem;
  font-size: var(--type-small-size);
  line-height: 1.55;
  color: var(--text-body);
}


/* The one-line crowd rating in a table row and on a ranked card (2026-10-05).

   One star, one number, one count — the only shape the doctrine's Law allows for a third-party
   rating, and the shape that fits a dense cell. The previous attempt at a figure in this table
   was the app-store trend (`4.7 → 1.5`), which two reviews rejected as unreadable at 13px; a
   single token is a different object and reads at a glance.

   Micro type and muted ink: this is somebody else's number, and it must not compete with the
   VerifiedDose score in the next column. Only the star carries colour. */
.dl-crowd {
  /* `inline-flex`, not `flex`: a block-level flex fills its line and forces whatever shares the
     row onto the next one, which is how the first attempt cost 22px per row while looking like
     it sat beside the name. */
  display: inline-flex;
  /* **One line, always** (2026-10-05). On a ranked card the figure shares the model-type row,
     and that row wraps: measured, the card stood 715px at 1440 and 872px at 900 because the
     figure broke across two and then three lines. It is six words; it wraps as a block onto the
     next line or not at all. */
  white-space: nowrap;
  align-items: baseline;
  gap: 3px;
  margin: 5px 0 0;
  font-size: var(--text-micro);
  color: var(--text-muted);
  line-height: 1.2;
}

.dl-crowd__num {
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

/* The count, set apart so the score reads as the figure and the base as its footnote. The
   separator is a real character in the markup — a `::before` is invisible to `textContent` and
   so to every probe that reads this page, which is how "★ 4.4/511" passed a check once. */
.dl-crowd__count {
  font-variant-numeric: var(--tabular);
  /* The count never breaks away from its own word: "2,945" on one line and "reviews" on the next
     cost a second line in the table and read as two separate facts. */
  white-space: nowrap;
}


/* The row's secondary line: the feature pills and the crowd figure together.

   Both are small, both wrap, and neither needs a line of its own. Keeping them in one wrapping
   row is what makes the rating free in a table whose height the owner has objected to three
   times. */
.dl-provcell__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px var(--space-3);
}

.dl-provcell__meta .dl-crowd {
  margin: 0;
}

/* --- css/components/regulatory-panel.css --- */
/* ==========================================================================
   RegulatoryPanel — the regulatory record on a review page.

   Every entry states the fact, dates it with a real Flag chip (amber/red, per
   tag-badge.css), and links the primary source. The copy never characterises. The
   empty state is specific and dated: "No regulatory flags on file for <provider> ·
   last checked 28 Jul 2026".
   ========================================================================== */

.dl-reg {
  border-left: 3px solid var(--border-2);
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-width: 46rem;
}

.dl-reg--flagged {
  border-left-color: var(--red);
}

.dl-reg__label {
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-reg--flagged .dl-reg__label {
  color: var(--red);
}

.dl-reg__fact {
  font-size: var(--type-body-size);
  line-height: 1.6;
  color: var(--text-body);
}

.dl-reg__date {
  font-size: var(--type-small-size);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
}

/* Its own line under the finding's prose, not a link inside a sentence — WCAG 2.5.8's "link in
   running text" exemption (tools/mobile_check.py's own reasoning) does not apply here, and at
   22px this was the smallest tap target the provider review page had (found by adding that page
   to the tool's list this pass). `min-height` widens the box a source link sits in without
   changing the text's own size. */
.dl-reg__source {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h-lg);
  gap: var(--space-1);
  align-self: flex-start;
  font-size: var(--type-small-size);
  color: var(--blue);
}

.dl-reg__source:hover {
  text-decoration: underline;
}

/* --- a list of flags --------------------------------------------------------- */

.dl-reg-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.dl-reg-list > li {
  padding: var(--space-4) 0;
  border-top: var(--rule-row);
}

.dl-reg-list > li:first-child {
  border-top: 0;
  padding-top: 0;
}

.dl-reg-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 46rem;
}

.dl-reg-item__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.dl-reg-item__summary {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

/* --- css/components/availability-checker.css --- */
/* ==========================================================================
   The per-provider state availability checker.

   A plain native <select>, not a chip or a map graphic — the same filter-line
   vocabulary as _filters.html.
   ========================================================================== */

.dl-availability {
  margin: var(--space-5) 0;
  padding-top: var(--space-4);
  border-top: var(--rule-hairline);
}

.dl-availability__label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-availability__row {
  display: inline-flex;
}

.dl-availability__result {
  margin: var(--space-2) 0 0;
  min-height: 1.5em;
  font-size: var(--type-small-size);
  line-height: 1.5;
  color: var(--text-body);
}

/* --- css/components/byline.css --- */
/* ==========================================================================
   The byline strip — visible evidence that a specific desk checks the prices.

   "Written by the &lt;desk&gt; · Last updated on &lt;date&gt; · ✓ Prices hand-checked" —
   copies the mockups' byline *form* (avatar-shaped mark, a date, a green checkmark
   fact) without the credential neither exists: see docs/ai/adr_1_no_named_humans.md.
   There is no reviewer segment — the honest counter-statement is
   includes/_no_clinical_review.html, rendered alongside this strip.

   The mark stays a square, not a circle, for both a `person` and a `desk` author:
   ADR-1 forbids inventing author photographs, and a circular mark with no photo inside
   it reads as a portrait waiting to happen. A desk gets a small clock mark ("checked on
   a schedule"); a `person` byline (kept for a future real hire) gets a photo or
   initials in the same square footprint, so switching `AuthorProfile.kind` causes no
   layout shift.

   Two colour contexts: on white (review header, footer) this file's own colours apply
   directly; on the homepage's navy hero band, layout.css's `.dl-hero .dl-byline`
   overrides reverse it to the mockup's `#D6DEEA` muted line with a white name — both
   still clear AA against navy (measured in tokens/colors.css's header comment).
   ========================================================================== */

.dl-byline {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  row-gap: var(--space-2);
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-byline__photo {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: var(--radius);
  object-fit: cover;
  background: var(--gray-bg);
}

.dl-byline__initials,
.dl-byline__mark {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: var(--radius);
  border: 1px solid var(--border-2);
  background: transparent;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-bold);
}

.dl-byline__text {
  min-width: 0;
}

.dl-byline__name {
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-byline__date {
  font-variant-numeric: var(--tabular);
}

/* The optional "Last updated on <date>" -> methodology-section link. */
.dl-byline__link {
  color: inherit;
  text-decoration: underline;
}

/* The green checkmark fact — "✓ Prices hand-checked". design-system.html's own
   `.fact` on navy is `#9FE3B9` (a paler mint so it still clears AA on the dark band);
   on white it is plain --green, both bold and uppercase-tracked. */
.dl-byline__fact {
  font-weight: var(--weight-bold);
  font-size: var(--type-micro-size);
  letter-spacing: 0.05em;
  color: var(--green);
  white-space: nowrap;
}

/* --- stacked form: the methodology strip and author cards ------------------- */

.dl-byline--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}

.dl-byline__role {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

@media (max-width: 599px) {
  .dl-byline {
    align-items: flex-start;
  }
}

/* --- css/components/disclosure.css --- */
/* ==========================================================================
   FTC affiliate disclosure, and the CTA it sits above.

   "Some links on this page are affiliate links. We're paid if you subscribe. It
   doesn't change the price you pay, where a provider sits in our tables, or what this
   review says." Plain and unhedged — no parentheses, no gratitude, no small print
   games, and no collapsing behind a "disclosure" toggle.

   Renders at body size in ink, not at the mockups' own 12.5px italic "affnote" size —
   this is one of the two places the project's compliance rules explicitly outrank the
   mockup's literal CSS (the other is the accessibility floor): "FTC disclosure above
   the first affiliate CTA — plain, unhedged, readable contrast" is trust furniture, not
   taste. The italic "affnote" voice from the review header is kept as a second, lighter
   repeat (`.dl-affnote`) directly under the byline, matching the mockups' Innerbody
   pattern — but it is never the *only* disclosure on the page.
   ========================================================================== */

/* 2026-08 owner review: one line, not a bordered block.
   The rule is that the disclosure is clear, conspicuous and above the first affiliate link — not
   that it occupies a panel at the top of every money page. The heavy left rule and the stacked
   label are gone; the sentence runs inline. What stays is the part the rule is actually about:
   body size, ink not grey, above the first CTA, never behind a toggle. */
.dl-disclosure {
  padding: var(--space-2) 0;
  max-width: 46rem;
}

.dl-disclosure__label {
  /* Inline, so the sentence reads as one line instead of a heading with a paragraph under it. */
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-disclosure__text {
  margin: 0;
  font-size: var(--type-small-size);
  line-height: 1.55;
  color: var(--text-body);
}

/* The lighter, italic repeat under a review's byline — Innerbody's own voice for it. */
.dl-affnote {
  font-style: italic;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 54rem;
}

/* 2026-08 second pass: the same sentence, same weight, moved to sit directly above
   the review page's verdict CTA instead of under the byline (see provider_page.html's
   file-level comment) — this only fits the 210px `.dl-verdict__side` column, so the
   54rem max-width above is dropped rather than left to do nothing. */
.dl-affnote--cta {
  max-width: none;
  margin: 0;
}

/* The compact repeat directly under a CTA button. Still a full sentence. */
.dl-cta-note {
  margin: var(--space-2) 0 0;
  font-size: var(--type-micro-size);
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 30rem;
  text-align: center;
}

/* The master table's per-card "Paid link" line — see includes/_affiliate_cta.html's
   own comment. Hidden by default and shown only at the card breakpoint
   (data-table.css's `@media (max-width: 42rem)`), where the page-level disclosure can
   be a long scroll away from any one card's CTA. Full body-ink contrast and >=15px,
   same floor as the disclosure and the no-clinical-review statement: this is
   compliance microcopy, never a quiet aside. */
.dl-paidnote {
  display: none;
  margin-top: var(--space-1);
  font-size: var(--step-body);
  line-height: 1.4;
  color: var(--text-body);
  text-align: center;
}

.dl-cta-block {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.dl-cta-block--inline {
  align-items: flex-start;
  margin-top: var(--space-6);
}

/* The standing statement in the footer: not a substitute for the page-level
   disclosure, which always appears above the first link. */
.dl-affiliate-standing {
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-on-navy-dim);
}

/* --- the unmonetised state (2026-08-17) --------------------------------------------------- */

/*
 * While no offer is live, the boxed FTC disclosure is replaced by one quiet line — see
 * `_affiliate_disclosure.html` for why the box goes away and how it comes back.
 *
 * Deliberately not a box, not a fill, not a border: the boxed treatment is the FTC signal, and
 * spending it on a sentence that is *not* that disclosure would blunt it for the day it matters.
 * Small, muted, in the flow — the "где-то вниз мелким текстом" the owner asked for, kept in place
 * rather than exiled to the footer so it still sits where a reader meets the first outbound link.
 */
.dl-disclosure__none {
  margin: 0 0 var(--space-4);
  font-size: var(--step-micro);
  line-height: 1.4;
  color: var(--text-muted);
}

.dl-disclosure__none a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.dl-disclosure__none a:hover,
.dl-disclosure__none a:focus-visible {
  color: var(--blue);
}

/* --- css/components/no-clinical-review.css --- */
/* ==========================================================================
   The no-clinical-review statement — the honest counter-statement to a "Medically
   reviewed by <clinician>" byline this project does not carry (docs/ai/adr_1_no_named_humans.md).

   Body size, the same register as the FTC disclosure text — never the muted, smaller
   legal-print register used for the footer's standing affiliate statement. A statement
   whose entire purpose is to be read cannot also be the page's smallest, quietest
   paragraph. On white it is plain ink; in the navy footer it reverses to white so it
   stays exactly as legible as everywhere else it appears (13.9:1, see tokens/colors.css).
   ========================================================================== */

.dl-no-clinical {
  max-width: 54rem;
  margin: 0 0 var(--space-4);
  font-size: var(--type-body-size);
  line-height: 1.6;
  color: var(--text-body);
}

.dl-byline + .dl-no-clinical {
  margin-top: var(--space-3);
}

/* The sitewide repeat in the footer's legal row: full width, own row, white on navy.
   `.dl-footer__legal` is a 12-column grid whose other two children
   (`.dl-footer__legal-text`, `.dl-footer__copy`) both carry an explicit `grid-column`
   (see chrome.css) — this paragraph needs one too, or auto-placement drops it into
   whatever single track is left over instead of a full row. */
.dl-footer .dl-no-clinical {
  max-width: none;
  margin-bottom: 0;
  color: var(--white);
  grid-column: 1 / 13;
  grid-row: 1;
}

.dl-footer .dl-footer__legal-text {
  grid-row: 2;
}

.dl-footer .dl-footer__copy {
  grid-row: 2;
}

@media (max-width: 899px) {
  .dl-footer .dl-no-clinical {
    grid-column: 1 / -1;
  }

  /* Found while verifying an unrelated pass's own screenshots (2026-08-05), pre-existing and
     not caused by that pass: `.dl-footer__legal-text` and `.dl-footer__copy` both got reset to
     the same full-width `grid-column` at this breakpoint (chrome.css), but nothing gave them
     different *rows* — this file's own unconditional rule below pins both to `grid-row: 2`,
     which only avoided a collision on the desktop grid because legal-text and copy also had
     different columns there (1/10 and 10/13). Below 899px they share both column and row, so
     the copyright line and the affiliate-status line were drawn on top of each other — a real,
     visible collision, not a hypothetical one. Needs `.dl-footer .dl-footer__copy` (not the
     single-class selector chrome.css's own media query could offer) to outrank the unconditional
     rule below at equal specificity by coming later in this file. */
  .dl-footer .dl-footer__copy {
    grid-row: 3;
  }
}

/* Inline variant, used in the article header where the italic FTC note sits directly above.
   Two full-width compliance blocks stacked there added ~180px of near-repetition before the
   reader reached anything about the provider; the reference mockup has one line. Same size and
   same ink — the trust argument still depends on this being as legible as the note beside it —
   just no block of its own. */
.dl-no-clinical--inline {
  margin: 6px 0 0;
  font-size: var(--fs-small);
  color: var(--text-2);
  max-width: 68ch;
}

/* --- css/components/trust-strip.css --- */
/* ==========================================================================
   TrustStrip — the FTC disclosure and the no-clinical-review statement side by side.

   Used only on the home page today, where the master table is meant to land in the
   first 1280x800 viewport (docs/ai/mobile_table_cards_and_trust_strip_2026-08-02.md).
   Two columns recover roughly one stacked block's worth of height versus rendering the
   same two partials full-width, one above the other. Nothing about either statement's
   own type size, colour or left rule changes here — this file only arranges them.

   Not used on the provider index: that page never carried a second, no-clinical-review
   block to merge with the disclosure in the first place (see
   templates/content/provider_index_page.html's own comment), so wrapping its lone
   disclosure in this strip would add new content height rather than compact two
   existing blocks — working against the exact budget this component exists to protect.

   Each child keeps its own left rule (`.dl-disclosure`'s 3px ink bar; `.dl-no-clinical`
   carries none of its own, which is fine — it reads as the second column of one strip,
   not a second, competing callout).
   ========================================================================== */

.dl-trust-strip {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-8);
  row-gap: var(--space-3);
  margin: var(--space-3) 0 var(--space-5);
}

.dl-trust-strip > .dl-disclosure,
.dl-trust-strip > .dl-no-clinical {
  flex: 1 1 320px;
  max-width: 38rem;
  margin: 0;
}

/* Below the point a two-column strip has room to breathe, fall back to the ordinary
   full-width stack — the same shape the two blocks already have everywhere else on
   the site, so nothing about this pattern needs a new narrow-viewport idea. */
@media (max-width: 640px) {
  .dl-trust-strip {
    flex-direction: column;
  }

  /* `flex: 1 1 320px` above sets each item's flex-basis along the flex container's
     *main* axis — width in row mode, but height once `flex-direction` flips to
     column here. Left as `1 1 320px` at this breakpoint, both statements were
     stretched to a 320px-tall box with a large blank gap under their own text,
     border-left included: a real defect this pass caught in a screenshot, not
     just a naming label. Reset to a plain block: no grow, no forced basis. */
  .dl-trust-strip > .dl-disclosure,
  .dl-trust-strip > .dl-no-clinical {
    flex: 0 1 auto;
    max-width: none;
  }
}

/* --- css/components/ledger-list.css --- */
/* ==========================================================================
   The bordered list — "Latest price and status changes", the sidebar alternatives,
   the FAQ, the review's table of contents. Per design-system.html §11, a rounded
   1px-bordered box with rows separated by a hairline rule (`.updates` in the
   mockups), not the previous pass's rule-only ledger.

   Deliberate deviation from the mockups' own CSS, and not negotiable: their
   `.updates .up`/`.dn` colour a price increase red and a decrease/new-lower-price
   green. The harness's hard rules are explicit that a status colour is never used for
   anything but provider status and red never marks a price change, so this file keeps
   the previous pass's ink-only direction mark (a small up/down/flat glyph, no colour)
   instead of the mockups' red/green deltas — see dl_change_row / dl_icon's "mark-up"
   family in dl_components.py.
   ========================================================================== */

.dl-updates {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 4px 24px;
}

/* The "See the full change log" link under .dl-updates — was an inline `style="margin-top:12px"`
   in home_page.html, which `test_no_inline_styles_or_handlers` forbids project-wide. */
.dl-updates__more {
  margin-top: 12px;
}

/* The only content of its own paragraph, so it is a standalone target rather than a link in a
   sentence: 169x22 measured, two pixels under the WCAG 2.5.8 floor. Scoped here rather than on
   `.dl-link-label`, which is used inside running text everywhere else and must not grow its
   line box there. */
.dl-updates__more .dl-link-label {
  min-height: 24px;
}

.dl-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.dl-list > li {
  border-top: var(--rule-row);
}

.dl-list > li:first-child {
  border-top: 0;
}

/* --- row with a figure in the left column (comparison index, coverage hub) --- */

.dl-row {
  display: grid;
  grid-template-columns: 130px minmax(0, 1fr) 220px;
  column-gap: var(--gutter);
  row-gap: var(--space-2);
  align-items: baseline;
  padding: var(--space-4) 0;
}

.dl-row--tight {
  grid-template-columns: 90px 14px minmax(0, 1fr);
  column-gap: var(--space-3);
  padding: 10px 0;
  align-items: baseline;
}

.dl-row__figure {
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  font-variant-numeric: var(--tabular);
  color: var(--text-body);
  white-space: nowrap;
}

.dl-row__figure--muted {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

.dl-row__title {
  display: block;
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  margin-bottom: 3px;
}

.dl-row__desc {
  display: block;
  font-size: var(--type-body-size);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 44rem;
}

.dl-row__action {
  justify-self: end;
  font-size: var(--type-small-size);
}

@media (max-width: 899px) {
  .dl-row,
  .dl-row--tight {
    grid-template-columns: minmax(0, 1fr);
  }

  .dl-row__action {
    justify-self: start;
  }
}

/* --- the changes feed: date, direction mark, sentence ------------------------ */

/* The record itself: a chronology, one rule between entries and none above the first or below the
 * last. The rules are the only structure it needs — the pack draws no card around an entry and no
 * container around the list, because a history with a frame around it reads as an excerpt. */
.dl-ledger {
  margin: 0;
  padding: 0;
  list-style: none;
}

.dl-ledger > li + li {
  border-top: var(--border-width) solid var(--line);
}

/* **One entry of the public price record** (v5, 2026-10-01).
 *
 * The shape it replaces was a single baseline-aligned line: date, mark, then "provider field old
 * -> new" as running text. It read as a diff. The pack's price record leads each entry with the
 * event in words and puts the figures under it, so a column of them reads as a history — which is
 * what this page is for and what makes it the one thing a competitor cannot reproduce.
 *
 * A fixed date column, because the dates are the spine of the list and a ragged left edge on them
 * costs a reader the scan. 92px holds "23 Sep 2026" at this size without wrapping. */
.dl-change {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-4) 0;
  font-size: var(--type-body-size);
  line-height: 1.5;
}

.dl-change__date {
  font-size: var(--step-micro);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  font-variant-numeric: var(--tabular);
  /* **No `white-space: nowrap`** (2026-10-04). The track beside this is a hard 92px, so `nowrap`
     meant any string wider than that left its column and ran underneath the event heading
     instead of wrapping. Dates fit — "12 Aug 2026" measures 76px — but the failure is silent and
     the first thing to trip it was not a date: an undefined `shown_on` rendered
     `!!MISSING:shown_on!!` under dev settings and sat across the headline on every row of every
     routed profile.
     Wrapping is the right degradation for a column whose whole job is to line up: a long value
     takes two lines inside its own track and the column survives. `overflow: hidden` would have
     kept the column and eaten the value, which on a dated-evidence site is the worse trade. */
  overflow-wrap: anywhere;
  /* Optically aligned with the first line of the event beside it, which is 22px/1.25 against this
     13px/1.5 — baseline alignment in a grid is not available and `align-items: baseline` on the
     row pulled the whole body cell. */
  padding-top: 3px;
}

.dl-change__body {
  min-width: 0;
}

/* The event. An h3 by the heading rule, so it inherits 22px/800 — which is the right weight for a
 * list where each item is its own small story, and the reason the figures below it can stay at
 * body size without the entry losing its head. */
.dl-change__event {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--step-lede);
  line-height: 1.3;
  letter-spacing: -0.01em;
  /* 700, not the 800 an h3 inherits. This is a list-item heading at 18px, repeated 200 times down
     the page, and 800 below the 20px floor is the exact shape `tools/type_scale.py` exists to
     catch — it caught this one. The pack sets its own list headings at the same weight. */
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

/* Direction only, never colour. A price moving is not a status, and status is what colour means
 * here — see `_change_row.html`'s own comment. */
.dl-change__mark {
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

.dl-change__what {
  margin: var(--space-1) 0 0;
  color: var(--text-muted);
  font-size: var(--step-small);
  font-variant-numeric: var(--tabular);
}

.dl-change__what a {
  color: var(--text-body);
}

/* "Observation and source" — the pack's own label, and the right place for the two dates. A
 * `<details>`, so it costs no JavaScript and a crawler reads it closed or open. */
.dl-change__detail {
  margin-top: var(--space-2);
}

.dl-change__summary {
  cursor: pointer;
  color: var(--text-brand);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  /* 21px measured, under WCAG 2.5.8's 24px floor. Padding rather than `min-height` with a
     flex display: `list-item` is what draws the disclosure triangle, and a reader needs the
     affordance more than the row needs to be a flex container. */
  padding-block: 2px;
}

.dl-change__disclosure {
  margin-top: var(--space-2);
  padding-left: var(--space-3);
  border-left: var(--border-width) solid var(--line);
  color: var(--text-muted);
  font-size: var(--step-small);
}

.dl-change__disclosure p {
  margin: 0 0 var(--space-1);
}

.dl-change__source {
  color: var(--text-muted);
  margin-left: var(--space-1);
  font-size: var(--type-small-size);
}

@media (max-width: 599px) {
  /* The date goes above the event rather than beside it: 92px of a 360px screen spent on a column
     of dates is a third of the width, and the entry's own text is what has to survive. */
  .dl-change {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-1);
  }

  .dl-change__date {
    padding-top: 0;
  }
}

/* --- the reviews list / alternatives ------------------------------------------ */

.dl-teaser {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: var(--space-3) 0;
  align-items: flex-start;
}

.dl-teaser__head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  flex-wrap: wrap;
}

.dl-teaser__date {
  margin-left: auto;
  font-size: var(--type-small-size);
  color: var(--text-muted);
  white-space: nowrap;
}

.dl-teaser__line {
  font-size: var(--type-small-size);
  line-height: 1.55;
  color: var(--text-muted);
}

/* --- index-page teaser row: title/description left, real numbers right ------ */

/* 2026-08 second pass, per the owner's click-through review: the comparison, switching,
   costs and ranking index pages opened with plain text rows — a date, a title, a
   truncated sentence — and nothing a reader could act on without opening the child
   page first. This keeps the established bordered-ledger shape (`.dl-updates` +
   `.dl-list`, the same box "Latest price and status changes" already uses) rather than
   turning these listings into a card grid: the row now carries the actual number
   (a price, a rating, a provider count) instead of a second thing to click through to
   find one. `.dl-idxrow__stats` deliberately reuses whatever component the number
   belongs to elsewhere on the site — `.dl-cmp-ctas__side` for two providers'
   logo/rating/price, `.dl-stat` for a single figure — rather than inventing a stat
   format specific to this row. */
.dl-idxrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-6);
  padding: var(--space-4) 0;
}

.dl-idxrow__main {
  flex: 1 1 260px;
  min-width: 0;
}

.dl-idxrow__stats {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex: 0 0 auto;
}

@media (max-width: 599px) {
  .dl-idxrow__stats {
    flex-basis: 100%;
    justify-content: flex-start;
  }
}

/* --- a two-value row: name left, value right --------------------------------- */

.dl-pair {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  flex-wrap: wrap;
}

.dl-pair__value {
  margin-left: auto;
  font-size: var(--type-small-size);
  white-space: nowrap;
}

.dl-pair__value--muted {
  color: var(--text-muted);
}

/* --- FAQ (details/summary) ----------------------------------------------------- */

.dl-faq {
  max-width: 48rem;
}

.dl-faq details {
  border-bottom: var(--rule-hairline);
}

.dl-faq summary {
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  padding: 15px 0;
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
}

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

.dl-faq summary::after {
  content: "+";
  font-weight: 400;
  color: var(--text-muted);
}

.dl-faq details[open] summary::after {
  content: "\2013";
}

.dl-faq__a {
  margin: 0 0 15px;
  font-size: var(--type-body-size);
  line-height: 1.65;
  color: var(--text-body);
}

/* --- review page table of contents -------------------------------------------- */

.dl-toc-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
}

.dl-toc-card > .dl-label {
  display: block;
  margin-bottom: var(--space-3);
}

.dl-toc {
  margin: 0;
}

.dl-toc > li {
  border-top: 0;
  padding: 5px 0;
}

.dl-toc a {
  display: block;
  font-size: var(--type-small-size);
  color: var(--text-body);
  border-left: 2px solid transparent;
  padding-left: 10px;
  margin-left: -10px;
  text-decoration: none;
}

.dl-toc a:hover {
  color: var(--blue);
}

.dl-toc a.dl-toc__link--on {
  border-left-color: var(--blue);
  color: var(--blue);
  font-weight: var(--weight-bold);
}

@media (max-width: 599px) {
  .dl-change {
    flex-wrap: wrap;
  }

  .dl-change__date {
    min-width: 0;
  }
}

/* A list of provider marks, used where a table would have had one column.
   Not `.dl-statelist`: that styles inline text links, and here each item is a logo tile plus a
   name. A provider with an uploaded logo and one without have differently-shaped items, so the row
   is centred rather than baseline-aligned — otherwise the two names sit at visibly different
   heights beside each other, which is what happened on /costs/wegovy/. */
.dl-provlist {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

.dl-provlist > li {
  display: flex;
  align-items: center;
  min-height: 30px;
}

/* --- comparison-index row: title, the glance cards, the full verdict ------------ */

/* 2026-08 third pass, per the owner's screenshot review. The row is a block, not the
   base `.dl-idxrow` flex split: the title sits on top, the two providers render as the
   comparison page's own `.dl-cmp-glance` bordered side cards (editorial.css — reused,
   not imitated), and the verdict runs beneath at the row's full width, so a 40-word
   two-sided verdict sets in two lines instead of seven in a narrow column.

   The whole row is the click target for the comparison it names — one real <a> (the
   title), stretched over the row the same way `.dl-pcard .dl-plogo__link::after` does
   it. Everything else in the row (logos via `linked=False`, scores, prices) is plain
   content, so there is nothing for the stretched area to trap. */
.dl-idxrow--cmp {
  position: relative;
  display: block;
}

.dl-idxrow--cmp .dl-row__title {
  font-size: var(--step-lede);
}

.dl-idxrow--cmp .dl-row__title::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Above the cards inside the row. They became `position: relative` when the
     comparison *page*'s copies of the same component were made click targets, and a
     positioned child paints over a statically-stacked overlay — measured with
     `elementFromPoint`: the middle of a card stopped resolving to this link. */
  z-index: 1;
}

.dl-idxrow--cmp:hover .dl-row__title,
.dl-idxrow--cmp:focus-within .dl-row__title {
  text-decoration: underline;
}

.dl-idxrow--cmp .dl-cmp-glance {
  margin: var(--space-3) 0 0;
}

.dl-idxrow--cmp .dl-row__desc--full {
  margin-top: var(--space-3);
  max-width: none;
  font-size: var(--type-small-size);
}


/* ==========================================================================
   CompareSummary — the two provider sides of a published comparison, per the v5 pack's
   `.compare-summary` / `.summary-card` (2026-10-02).

   The pack fills each side rather than bordering it: `background: var(--sky)` on the first and
   `var(--peach)` on the second, 26px of padding, 16px radius, no rule. That is the whole
   difference between its comparison listing and ours — ours drew two white bordered boxes on a
   warm page and filled them with labelled fact grids, so the two sides read as one form rather
   than as two options.

   The surfaces are assigned by position and carry no judgement: the doctrine's Law forbids a fill
   that implies a winner, and these are safe for the same reason the ranked cards' headers are —
   swap the pair's order and the colours swap with it.
   ========================================================================== */

.dl-cmpsum {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
  margin: var(--space-5) 0;
}

.dl-cmpsum__card {
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--sky);
  min-width: 0;
}

.dl-cmpsum__card:nth-child(2) {
  background: var(--peach);
}

.dl-cmpsum__card .dl-plogo__hero {
  margin-bottom: var(--space-5);
}

/* The pack's `.summary-card .identity h3` is 1.2rem. Ours inherits body size from the card, so a
   96x56 tile sat beside a 16px name and the mark read as the subject with the brand as its
   caption. */
.dl-cmpsum__card .dl-plogo__wordmark--hero {
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.dl-cmpsum__label {
  margin: 0 0 var(--space-1);
  font-size: var(--step-small);
  color: var(--text-2);
}

.dl-cmpsum__year {
  font-size: 2rem;
  font-weight: var(--weight-black);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
  overflow-wrap: anywhere;
}

/* `dl_cost_range` renders its own caption and bar for the provider page; inside this card the
   figure alone is wanted, as in the ranked card. */
.dl-cmpsum__year .dl-yearcost__note,
.dl-cmpsum__year .dl-yearcost__bar {
  display: none;
}

.dl-cmpsum__year .dl-yearcost__value {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  color: inherit;
}

.dl-cmpsum__monthly {
  margin: var(--space-2) 0 0;
  font-size: var(--step-lede);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-cmpsum__checked {
  margin: var(--space-1) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
}

@media (max-width: 42rem) {
  .dl-cmpsum {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The action inside a summary card — the pack's `.summary-card .actions`: the primary button at
   the card's full width, the breakdown link beneath it. Only the comparison page passes an offer;
   the /compare/ listing's rows have one destination and pass none. */
.dl-cmpsum__act {
  margin-top: var(--space-5);
}

.dl-cmpsum__act .dl-btn,
.dl-cmpsum__act .dl-cta-block {
  width: 100%;
}

/* 44px of target, not 22px of text. `tools/mobile_check.py` measured this link at 22px on a phone
   — under the 24px WCAG 2.5.8 AA floor, not just under the 44px platform guideline — the moment it
   landed, because it is a standalone action sitting on its own row rather than a link inside
   prose. `inline-flex` with a minimum height costs nothing visually: the space below the button is
   the card's own padding. */
.dl-cmpsum__more {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--space-2);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
}

/* The score, under the date it follows rather than over the price it judges. */
.dl-cmpsum__card .dl-score {
  margin-top: var(--space-4);
}


/* The change log's pager (2026-10-05).

   The page used to take the newest 200 entries and stop, with no link to the rest. Paginating it
   needs exactly three things on screen: a step in each direction, and where you are. Anything
   more — numbered pages, a jump box — is furniture on a chronology nobody navigates by index.

   `--focus-offset` worth of gap is not a concern here: the pager sits in normal flow, nothing
   clips it, and `tools/focus_ring_clip.py` measures that rather than assuming it. */
.dl-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.dl-pager__step {
  /* 44px of target, per WCAG 2.5.8 — the step links are the only way to the older entries and
     they sit at the bottom of a long page, where a mis-tap costs the whole scroll back. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--control);
  border-radius: var(--radius-control, 6px);
  color: var(--plum);
  font-size: var(--text-small);
  font-weight: 600;
  text-decoration: none;
}

.dl-pager__step:hover {
  border-color: var(--plum);
  color: var(--plum-hover);
}

/* The unavailable direction stays in place rather than disappearing, so the row does not reflow
   between the first page and the rest. It is not a link and is not focusable. */
.dl-pager__step--off {
  border-color: var(--line);
  color: var(--muted);
  font-weight: 500;
}

.dl-pager__count {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-micro);
  font-variant-numeric: tabular-nums;
}

/* --- css/components/provider-column.css --- */
/* ==========================================================================
   ProviderColumn — the ranked provider card, per docs/ui_ref_v4.

   v4's central structural idea: the comparison is ONE object. A single bordered frame divided
   into equal columns by 1px rules, so it reads as a comparison rather than as four cards
   floating near each other. The emphasised column carries a 3px green top edge — never a tinted
   fill, because a filled row is a thumb on the scale and this site sorts by verified price.

   Radius is restrained by the same spec: 10px on the frame, 6px on controls, 0 everywhere else.
   No shadows (v4 allows them only on true overlays), no card elevation, no glass.
   ========================================================================== */

/* **Four zones, aligned across every card by subgrid** (2026-08-16).
 *
 * The cards were a flex column of paragraphs, and the owner's verdict on the result was exact:
 * "это вообще блядь один какой то текст" — one blob of text. Two things were wrong and only the
 * second is about styling.
 *
 * The first: the cards stopped lining up. A flex column stacks each card's own content at its
 * own heights, so with a two-line price story in one card and a four-line one in the next, the
 * rating sat at a different height in each of the three, the pills at another, the button at
 * another. Three columns whose rows do not correspond are not a comparison — they are three
 * separate cards that happen to be adjacent. The owner had already named this once:
 * "ты можешь что бы все было одинаково".
 *
 * `grid-template-rows: subgrid` fixes it structurally rather than by hand: each card spans the
 * same four row tracks of the parent, so identity, price, what-you-get and action begin and end
 * at the same y in all three. Nothing needs a fixed height, and a card with a longer price story
 * simply makes that track taller for everyone.
 *
 * The second: with the zones aligned, a 1px rule between them runs straight across the whole
 * frame. That is what gives the block visible structure — the same job Trustpilot's bordered
 * boxes do on the page the owner held up, done in this system's own neutral language instead of
 * with cards, fills or shadows.
 */
/* **Separate cards, not one divided frame** (v5, 2026-10-02).
 *
 * The frame above is v4's idea and it was a good one for v4. v5 draws three bordered, rounded
 * cards with 20px between them (`.cards { gap: 20px }` / `.provider-card { border: 1px solid
 * var(--line); border-radius: var(--radius-md) }`), and the owner put the two side by side: ours
 * ran edge to edge with square corners and no gap, so the three read as one striped panel rather
 * than as three things to choose between.
 *
 * The subgrid survives the change and is the reason this is a column gap only. Row gap stays 0,
 * because the four tracks are the *insides* of each card — a row gap would prise each card's own
 * zones apart and put the border around nothing. */
.dl-pcompare {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  /* Three tracks, matching the pack's card-top / card-body / card-footer — see
     includes/_winner_card.html for why the fourth was folded away. */
  grid-template-rows: repeat(3, auto);
  gap: 0 var(--space-5);
  margin-bottom: var(--space-6);
}

.dl-pcol {
  position: relative;
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  min-width: 0;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg, 10px);
  /* So the coloured identity zone takes the card's top corners. */
  overflow: hidden;
  transition: background-color var(--dur) var(--ease-standard);
}

/* --- the whole card is the link to the review (2026-08-16) --------------------------------- */

/*
 * The owner: "при наведении надо выделять, а при клике переходит на карточку". The card had no
 * hover state at all and only its small "View details" link navigated anywhere.
 *
 * **The constraint that decides the technique**: each card also holds a primary CTA going
 * somewhere else entirely — the provider's own website. Wrapping the card in an `<a>` would nest
 * a link inside a link, which is invalid HTML and which browsers resolve unpredictably. A JS
 * click handler on the card would be worse: it would make the card unreachable with a keyboard
 * and invisible to a crawler.
 *
 * So the anchor that already exists — the provider name in the `<h3>` — grows a transparent
 * `::after` that covers the card. One real link, one tab stop, a real `href` in the markup, and
 * the CTA stays a separate element by sitting above the overlay on `z-index`.
 *
 * The known cost, accepted: the overlay prevents selecting text inside the card with the mouse.
 * That is the standard trade for this pattern; the same text is selectable on the review page
 * the card links to.
 */
.dl-pcol .dl-plogo__link--hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* Anything a reader must still be able to reach sits above the overlay. */
.dl-pcol__zone--act,
.dl-pcol__catch a,
.dl-pcol__dist a {
  position: relative;
  z-index: 2;
}

/* The hover state, in v4's own words: "Rows and quiet controls fill --surface on hover; links
   underline." No shadow, no lift, no scale — nothing moves, which is what the anti-list asks
   for and what keeps a dense comparison from feeling like a toy. */
.dl-pcol:hover {
  background: var(--band);
}

.dl-pcol:hover .dl-plogo__wordmark--hero,
.dl-pcol:hover .dl-pcol__name {
  text-decoration: underline;
  text-underline-offset: 0.14em;
}

/* Keyboard parity: tabbing to the card's link rings the whole card, not the few pixels of the
   wordmark. `:focus-visible`, so a mouse click does not leave a ring behind. */
.dl-pcol:has(.dl-plogo__link--hero:focus-visible) {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: calc(-1 * var(--focus-width));
  background: var(--band);
}

@media (prefers-reduced-motion: reduce) {
  .dl-pcol {
    transition: none;
  }
}



/* The zone. Padding lives here rather than on the card, so the rule between two zones is a full
   edge-to-edge line rather than a line inset by the card's own padding. */
.dl-pcol__zone {
  display: flex;
  flex-direction: column;
  padding: var(--space-5);
  min-width: 0;
}

/* **No rule between the zones at all** (2026-10-02). The last one left was under the coloured
   identity zone, and the pack draws none there: the colour change from the header's surface to the
   white body IS the boundary, so the hairline read as a seam crossing the card a few pixels below
   a boundary that was already visible. The only rules inside a card belong to the receipt's line
   items, which is where the pack puts its own. */

/* **The identity zone takes a brand surface** (v5, 2026-10-01).
 *
 * The pack's three leading cards open with a filled header — lavender, peach, sky, in that order —
 * and its UX notes are explicit about what the fill means: "Цвета фонов — ритм бренда, не оценка
 * качества". The three cards are the first three comparable results of the current sort, not three
 * editorial picks, and nothing about the colour may suggest otherwise. All three carry the same
 * CTA, the same fields and the same size; the surfaces rotate by position, so the same provider
 * takes a different colour the moment the sort changes.
 *
 * That property is the reason this is safe on a monetized comparison: a colour a provider cannot
 * earn or buy cannot read as a recommendation. It is also why the rotation keys off `nth-child`
 * rather than off anything about the provider.
 *
 * The three fills clear 11.1:1 or better against `--ink`, so the identity text needs no variant.
 */
.dl-pcol:nth-child(3n + 1) .dl-pcol__zone--id {
  background: var(--lavender);
}

.dl-pcol:nth-child(3n + 2) .dl-pcol__zone--id {
  background: var(--peach);
}

.dl-pcol:nth-child(3n) .dl-pcol__zone--id {
  background: var(--sky);
}

/* The hover fill must not reach inside the coloured header: the row's hover tint is lighter than
   all three surfaces, so letting it win turned the header pale on the hovered card only. */
.dl-pcol:hover .dl-pcol__zone--id {
  background: inherit;
}

.dl-pcol:nth-child(3n + 1):hover .dl-pcol__zone--id {
  background: var(--lavender);
}

.dl-pcol:nth-child(3n + 2):hover .dl-pcol__zone--id {
  background: var(--peach);
}

.dl-pcol:nth-child(3n):hover .dl-pcol__zone--id {
  background: var(--sky);
}

/* The card's own term band was removed with the one in its template (2026-10-02): the price
 * receipt moved inside the card and carries the term from the same `Plan.conditions`, so the two
 * printed the identical sentence twice. Its surface rules live in `price-receipt.css` now. */

.dl-pcol__rank {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  color: var(--ink);
}

.dl-pcol__rank-note {
  font-weight: var(--weight-regular);
  color: var(--muted);
}

/* **The first-year figure leads the body** (v5). The pack's card opens with the modelled
 * first-year cost at card-price size and puts the monthly under it; ours opened with the monthly
 * and never showed the year at all. */
.dl-pcol__yearlabel {
  margin: 0 0 var(--space-1);
  font-size: var(--step-small);
  color: var(--text-muted);
}

.dl-pcol__year {
  /* 32px, not the 38px metric step the pack sets on its own `.price`. The pack's figure is a
     single number ("$1,668"); ours is a range, because a provider publishing four doses has a
     first-year cost that depends on which one a reader needs. "$1,199.64–$1,733.88" at 38px is
     wider than a third of a 1440px page, so it broke to two lines in one card and not the others,
     and the receipts underneath stopped lining up across the row. Same type system, the step
     below. */
  font-size: 2rem;
  font-weight: var(--weight-black);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

/* `dl_cost_range` renders its own label and bar for the table; inside the card only the figure
   is wanted — the label is the line above it and the bar is the receipt below. */
.dl-pcol__year .dl-yearcost__note,
.dl-pcol__year .dl-yearcost__bar {
  display: none;
}

.dl-pcol__year .dl-yearcost__value {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  color: inherit;
}

.dl-pcol__monthly {
  margin: var(--space-2) 0 0;
  font-size: var(--step-lede);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-pcol__checked {
  margin: var(--space-1) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
}


/* --- the receipt inside a card ---------------------------------------------------------------
 *
 * The receipt is a panel on a provider page — white, shadowed, with its own eyebrow and title. In
 * a card it is already inside a panel, so it keeps only its line items and sheds the chrome: no
 * second surface, no second shadow, and no "The price receipt / What makes up the total" pair over
 * three rows that are self-evidently a receipt.
 *
 * **Restored 2026-10-02 after I deleted it by accident.** Removing the card's `__total` override
 * (the element itself is gone — see includes/_price_receipt.html) I cut back to the preceding
 * comment and took these four rules with it. The card went from 739px to 862px and
 * `tools/card_balance.py` failed on its 860px budget within the same session, which is the only
 * reason the mistake did not ship: nothing visual about it was obvious at a glance. */
.dl-pcol .dl-receipt {
  margin-top: var(--space-4);
  padding: 0;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}

.dl-pcol .dl-receipt__eyebrow,
.dl-pcol .dl-receipt__title {
  display: none;
}

/* The date is printed once per card, by `.dl-pcol__checked` above the receipt. */
.dl-pcol .dl-receipt__verified {
  display: none;
}

/* **No "See the calculation" on a card** (2026-10-02).
 *
 * The disclosure earns its place on the provider page, where the receipt panel sits beside a
 * 75px figure and a reader may reasonably ask which addends produced it. On a card the three
 * rows directly above it *are* the addends, so it restates them behind a click.
 *
 * It was also the last per-provider height difference left in the price zone, and in a row of
 * three cards that is not a cosmetic point: the zones are subgrid-aligned, so one card carrying
 * an extra 22px line made the other two carry 22px of white space. Mochi had it and the two
 * beside it did not. */
.dl-pcol .dl-receipt__calc {
  display: none;
}

/* The one filled action spans the card (v5). It was half a row beside a secondary button; a CTA
   sized to the card is the pack's shape and reads as the card's own action rather than as one of
   two choices. */
.dl-pcol__actions .dl-btn {
  width: 100%;
}

/* Our review, as a link under the button. The secondary action, in the pack's words. */
/* A quick-fact value holding a sentence rather than a figure.
 *
 * `.dl-qf__v` is set at the heading weight because almost every one of them is a number, and the
 * reference pack sets figures heavy at any size. "5 changes on file" is a link, not a figure, and
 * at 16px/800 it was the heaviest thing in the panel. Scoped to the link so the numbers around it
 * keep the weight they are meant to have. */
.dl-qf__v a {
  font-weight: var(--weight-semibold);
}

/* The pack's `.minor-actions`: the secondary link and the lesser fact on one row, pushed to
   opposite ends, wrapping rather than shrinking when the column is narrow. */
.dl-pcol__minor {
  position: relative;
  z-index: 2;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: var(--space-3) 0 0;
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
}

/* The action zone anchors from both ends: the button at the top of the track, the verification
   date at the bottom.
 *
 * Anchoring the whole group to the bottom looked right and was not — a card carrying an extra
 * "View details" line made its group taller, which pushed its button *up* out of line with the
 * other two. The button is the element that must align, so it is the one that gets a fixed edge;
 * the date is pushed to the floor by `margin-top: auto` and any slack falls between them. */
.dl-pcol__zone--act {
  justify-content: flex-start;
}

/* The row, not the date alone, anchors to the bottom of the action track now that the two share
   a line. */
/* **"Read the breakdown" is a standalone target, not a link inside a sentence** (2026-10-05).
   It sits in a flex row beside "on <provider>'s website", so it computes to `display: block`
   and WCAG 2.5.8's exception for a target "in a sentence or block of text" does not reach it.
   Measured at 136x22 — two pixels under the 24px floor, on all three cards.

   `min-height` rather than `height`, and on the link alone: the row keeps `align-items:
   baseline`, so the text does not move and the card grows by 2px. That is the same trade the
   secondary button in `.dl-pcol__actions` already made below. */
.dl-pcol__minor .dl-pcol__details {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

.dl-pcol__zone--act .dl-pcol__minor {
  margin-top: auto;
  padding-top: var(--space-3);
}

/* **The 3px green top edge on card 1 is gone** (2026-08-16).
 *
 * It marked the cheapest card, and the section heading directly above already says "Same
 * cheapest-first order as the table below — not a separate editorial pick". The order carries
 * that information; the edge only repeated it in the visual grammar of an endorsement, on a page
 * that earns a commission when a reader leaves. Emphasis moved to the thing actually being
 * compared instead — see `.dl-pstory__result`.
 */

/* --- identity ------------------------------------------------------------- */

/* The identity slot: the heading IS the mark.
 *
 * v4 places marks "at optical size with their own clear space, never normalised into equal boxes
 * and never inside a bordered chip" — the chip is what made every card look like a form. What it
 * does need is a constant *height*, so the line below it starts at the same y on every card in
 * the row: the mark's own width varies with the brand, which is the point, but the card's rhythm
 * must not. A logo is capped rather than stretched, so a wide wordmark and a square favicon both
 * sit on the same baseline without either being distorted.
 *
 * 48px is the smallest height that holds both a 40px mark and the fallback wordmark's two lines
 * at the widest provider name in the set ("Medical Weight Loss Center"). */
.dl-pcol__name {
  display: flex;
  align-items: center;
  /* 56px, the height of the pack's logo tile, so the identity row is the same height on a card
     whose provider has a mark and one whose provider has none. */
  min-height: 56px;
  margin: 0;
  /* The heading's own font settings are inherited by the fallback wordmark below rather than
     restated there. Grotesque, not the display serif: a brand name set in our headline face
     reads as our voice rather than the provider's. */
  font-family: var(--font-sans);
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--text-body);
}

/* The mark fits inside the tile's own padding box; the tile sets the size now, not a hand-tuned
   cap that had to be re-derived whenever the slot changed. */
.dl-pcol__name .dl-plogo__img--hero {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* `min-height: 44px` is the tap target, not a visual measurement: the slot around it is 48px
   already, so this changes nothing on screen and everything for a thumb. Without it the hit area
   was the height of the name's own text — 25px for "TelosRx", and this is the link a reader on a
   phone is most likely to aim at. */
.dl-pcol__name .dl-plogo__link--hero {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.dl-pcol__name .dl-plogo__link--hero:hover,
.dl-pcol__name .dl-plogo__link--hero:focus-visible {
  color: var(--blue);
}

/* A logo image must not pick up an underline on hover — it has no text to underline and the
   line would draw itself across the brand's mark. */
.dl-pcol__name .dl-plogo__link--hero:hover .dl-plogo__wordmark--hero {
  text-decoration: underline;
}

/* The crowd's rating: a figure on one line, its provenance in small ink beneath.
 *
 * It used to be a single run-on line — "★ 4.4 on Trustpilot, 11 reviews — too few to publish" —
 * which wrapped to two lines at one size and one colour and read as prose. Splitting the figure
 * from its source is what makes it scannable, and it is the shape the reference page the owner
 * pointed at uses for the same information.
 *
 * Never five drawn stars: one star and a number. A five-star row invites a reader to compare a
 * 39,000-review average with an 11-review one as if they were the same measurement.
 */
.dl-pcol__rating {
  margin-bottom: var(--space-4);
  margin-top: var(--space-4);
}

.dl-pcol__rating-fig {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin: 0;
  /* 15px, down from 19px. It competed with the price for the same job and lost on merit: the
     rating is provenance, the price is the decision. It stays because the owner asked for the
     user rating to be visible — demoted, not dropped. */
  font-size: var(--step-body);
  line-height: 1.2;
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-pcol__rating-fig b {
  font-weight: var(--weight-semibold);
}

.dl-pcol__rating-fig--none {
  color: var(--text-2);
}

.dl-pcol__rating-max {
  font-size: var(--step-small);
  color: var(--text-2);
}

.dl-pcol__rating-src {
  margin: 3px 0 0;
  font-size: var(--type-micro-size);
  line-height: 1.35;
  color: var(--text-2);
}

/* One distinction, in green, phrased as navigation rather than ranking. */
.dl-pcol__dist {
  margin: 7px 0 0;
  font-size: var(--type-small-size);
  font-weight: var(--weight-medium);
  color: var(--blue);
}

/* --- the price story ------------------------------------------------------ */

.dl-pstory {
  margin-top: var(--space-4);
}

/* Terms, in quiet ink, with the figures picked out. The operator is green: it is the only mark
   in the arithmetic that carries the identity, and it is what makes the shape readable at a
   glance — a `+` means two charges, a `÷` means a prepaid year. */
.dl-pstory__eq {
  margin: 0;
  font-size: var(--step-small);
  line-height: 1.45;
  color: var(--text-2);
  letter-spacing: -0.01em;
}

.dl-pstory__eq i {
  font-style: normal;
  font-weight: var(--weight-medium);
  color: var(--text-body);
}

.dl-pstory__op {
  color: var(--blue);
  font-weight: var(--weight-medium);
}

.dl-pstory__rule {
  width: 34px;
  height: 1px;
  background: var(--border-2);
  margin: 9px 0;
}

/* The result. Green, because under v4 green carries money — and set in the display serif,
   the only place outside a headline where that face appears.
 *
 * The reasoning is that this number IS the headline of the card. Everything above it is the
 * arithmetic that produces it and everything below is qualification. Setting it in the same
 * face as the page's H1 says so without a label, a box or a badge — all three of which v4
 * bans. Tabular figures are still requested; where the serif lacks them the fallback is its
 * proportional set, which is correct here because these numbers are read one at a time and
 * never stacked into a column. (Table cells keep the grotesque and its real tabular figures —
 * see price-cell.css. A column of prices must align; a card's single figure must not.) */
.dl-pstory__result {
  margin: 0;
  font-family: var(--font-sans);
  /* **38px at weight 800** (v5). The three providers on this row are genuinely close — $99, $139,
     $149, same molecules, same cash-pay terms — so there is no dramatic difference to dramatise
     and inventing one would be dishonest. What is left is to make the figure being compared
     unmistakably the largest thing in every card, and let $99 against $149 create the hierarchy
     by itself.

     It was 42px at weight 400, which was correct for a single-weight serif and wrong the moment
     the serif left: the site's one anchor figure came out of the font swap as a large, light
     grotesque number — the weakest thing in a card whose whole job is to carry it. Caught by the
     Codex review of this phase, not by any audit here, because nothing measures emphasis. */
  font-size: var(--step-metric);
  font-weight: var(--weight-black);
  font-synthesis-weight: none;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--blue);
  font-variant-numeric: var(--tabular);
}

/* "Not published" is not a price, so it is not green, not large, and not the display face —
   the serif is reserved for figures we can actually stand behind. */
.dl-pstory__result--none {
  font-family: var(--font-sans);
  font-size: var(--type-body-size);
  font-weight: var(--weight-regular);
  color: var(--text-2);
  letter-spacing: 0;
}

/* The unit stays in the text face: "/mo" set in a serif at 15px next to a 38px serif figure
   reads as part of the number and blurs the boundary between what is priced and what is
   billed. */
.dl-pstory__unit {
  font-family: var(--font-sans);
  font-size: var(--step-body);
  font-weight: var(--weight-regular);
  color: var(--text-2);
  letter-spacing: 0;
}

.dl-pstory__qual {
  margin: 6px 0 0;
  font-size: var(--type-small-size);
  color: var(--text-2);
}

/* --- the story at anchor size, and on pages that are not the card ---------- */

/* `--lg`: where the price story is the page's price anchor rather than one element of a card —
   the provider review's fold, above the verdict. Same component, same arithmetic, one step up
   in scale so it holds a full-width column instead of a 300px one. */
.dl-pstory--lg .dl-pstory__eq {
  font-size: var(--step-body);
}

.dl-pstory--lg .dl-pstory__result {
  font-size: 48px;
}

.dl-pstory--lg .dl-pstory__unit {
  font-size: var(--step-lede);
}

/* The medication-only figure: same size, different ink.
 *
 * Green is the site's money colour and it means a verified all-in total. Where the membership fee
 * is unknown this number is not that, so it keeps the size — the three cards have to stay in
 * rhythm — and gives up the accent. The label beneath states the missing part in words, because
 * ink alone is not a status signal (doctrine, Law: never communicate status by colour alone).
 *
 * `dl_price_position` refuses to plot this figure at all; a partial number on an axis of all-in
 * totals is a false comparison. */
.dl-pstory__result--partial {
  color: var(--text-body);
}

.dl-pstory__total {
  margin: 6px 0 0;
  font-size: var(--step-small);
  color: var(--text-2);
}

.dl-pstory__total b {
  font-weight: var(--weight-medium);
  color: var(--text-body);
}

/* "Not published" does not scale with the anchor. The `--lg` rule above outranks
   `.dl-pstory__result--none` on specificity, so without this the words rendered at 48px — a
   provider with no verifiable price got the largest type on its own page, which reads as an
   accusation rather than as a gap in what they publish. */
.dl-pstory--lg .dl-pstory__result--none {
  font-size: var(--type-body-size);
}

.dl-pstory--lg .dl-pstory__rule {
  width: 44px;
  margin: 11px 0;
}

/* The verification date under the arithmetic. Muted, not green: v4 moved green to money and
   actions, and this line is evidence — "quiet but present", in the spec's own words. */
.dl-pstory__verified {
  margin: var(--space-3) 0 0;
  font-size: var(--type-micro-size);
  color: var(--verified-ink);
  font-variant-numeric: var(--tabular);
}

/* The recommendation label: small caps in the accent, the first thing in the card. Computed
   from verified fields (`dl_provider_distinction`) — never a rank, never "best overall". */
/* **A chip, not an eyebrow** (v5, 2026-10-02).
 *
 * This was 12px tracked uppercase in the accent — correct while it was the first line of the
 * coloured header, where an eyebrow introduces what follows. Moved into the body it had nothing
 * to introduce: it sat under the billing-term band as a stray shout, and "WATCH THE MEMBERSHIP
 * MATH" set that way read as a warning label rather than as the navigation line it is.
 *
 * The pack has a component for exactly this shape of fact — `.status`, a compact filled chip in a
 * semantic pair — so the distinction takes it rather than inventing a treatment. Green because it
 * is a verified positive; amber and red stay reserved for regulatory findings and status, which
 * `.dl-pcol__catch` below carries. */
.dl-pcol__flag {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  margin: 0 0 var(--space-3);
  padding: 5px 9px;
  border-radius: var(--radius);
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  background: var(--good-bg);
  color: var(--good);
}

/* Two actions on one row: the bordered View details beside the filled CTA. Both above the
   card-wide overlay; the row wraps on narrow columns rather than shrinking tap targets. */
.dl-pcol__actions {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.dl-pcol__actions .dl-pcol__details {
  flex: 1 1 38%;
  /* `.dl-btn--secondary` has no height rule of its own (button.css), so it fell back to
     `.dl-btn`'s base 36px next to a 44px primary action in the same row — the shorter of the
     card's two tap targets and, per tools/mobile_check.py, under the WCAG floor. Matched to the
     primary button beside it: same row, same height, no button in the pair reads as an
     afterthought. */
  height: var(--control-h-lg);
}

.dl-pcol__actions .dl-cta-block {
  flex: 1 1 55%;
  width: auto;
}

/* --- the price scale: where this figure sits among all tracked prices --------------------- */

/*
 * A 4px rule with one dot on it, and the two ends labelled. Not a chart and not trying to be:
 * the job is to give the 42px figure above it a scale, so a reader sees that $99 and $149 are
 * both near the cheap end of a range that runs to $305 rather than being "the range".
 *
 * The dot's position comes from `.dl-pos--NN` — 21 discrete classes in steps of 5, the same
 * technique score-panel.css uses for bar widths, because `test_no_inline_styles_or_handlers`
 * forbids `style="…"` project-wide.
 */
/* Pinned to the floor of the price zone.
 *
 * `margin-top: auto` in the zone's flex column, not a fixed margin. The subgrid aligns the four
 * zones across the three cards but says nothing about where an element sits *inside* one, and
 * the qualifier above this varies between one line and two — "what you are billed" against
 * "membership fee not published — the total may be higher". With a fixed top margin the three
 * scales landed on three different rows, which is exactly the raggedness the subgrid exists to
 * remove, reintroduced one level down. */
.dl-pscale {
  margin: var(--space-4) 0 0;
  margin-top: auto;
  padding-top: var(--space-4);
}

.dl-pscale__track {
  position: relative;
  display: block;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
}

.dl-pscale__dot {
  position: absolute;
  top: 50%;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--blue);
  /* Pulled back by its own half-width so the dot's centre lands on the value, not its left edge —
     otherwise every marker reads ~5px dearer than it is, and the one at 100% hangs off the end. */
  transform: translate(-50%, -50%);
}

.dl-pscale__cap {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: 7px;
  font-size: var(--step-micro);
  color: var(--text-2);
  font-variant-numeric: var(--tabular);
}

/* The middle label names what the axis is. Set quieter than the two figures: they are the data,
   it is the caption. */
.dl-pscale__what {
  color: var(--text-muted);
}

/* --- facts, catch, foot --------------------------------------------------- */

/* Two lines of consumer language, dot-separated. v4: "Short decision facts instead of
   sentences, and never as label/value rows — field names are schema, not copy." */
.dl-pcol__facts {
  margin: var(--space-4) 0 0;
  font-size: var(--type-small-size);
  line-height: 1.45;
  color: var(--text-2);
}

/* The catch: amber ink and a warning glyph, never a filled alert. One line, and only where
   there is something real to say. */
.dl-pcol__catch {
  display: flex;
  gap: 7px;
  margin: var(--space-3) 0 0;
  font-size: var(--type-small-size);
  line-height: 1.4;
  color: var(--amber);
}

/* **A gap in our record is muted, never amber** (2026-10-05). The doctrine's Law reserves amber
   for regulatory findings and provider status, and the price receipt already states the matching
   rule for its own unanswerable row: "Never amber — that is a hole in our research, not a
   regulatory finding." This line was amber for every branch, so "Our record does not identify
   the company behind this brand." was set exactly like "FDA warning letter, Feb 2026." on the
   next card along. */
.dl-pcol__catch--gap {
  color: var(--muted);
}

.dl-pcol__catch svg {
  flex: none;
  margin-top: 2px;
}


.dl-pcol__zone--act .dl-cta-block {
  width: 100%;
}

.dl-pcol__go,
.dl-pcol__more {
  margin-top: var(--space-3);
  font-size: var(--step-small);
  font-weight: var(--weight-medium);
  color: var(--blue);
}

.dl-pcol__more {
  font-size: var(--step-small);
  color: var(--text-2);
}

/* Evidence, quiet but present. */
.dl-pcol__evidence {
  margin: 0;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-regular);
  color: var(--text-2);
  font-variant-numeric: var(--tabular);
  white-space: nowrap;
}

@media (max-width: 56rem) {
  .dl-pcompare {
    grid-template-columns: 1fr;
    /* A row gap once they stack: the single-column tier gives each card the whole width, so there
       is no column gap left to separate them and the cards would butt against each other. Still
       zero inside a card, because the parent's three tracks are its own zones. */
    row-gap: var(--space-5);
  }

  /* **No border edits here** (Codex review of this phase, 2026-10-02). This block used to set
     `border-left: 0` and a `border-top` instead, which was right while `.dl-pcol` was a column of
     one divided frame and its left border WAS the divider. The card carries a border on all four
     sides now, so zeroing the left one left every stacked card with three sides drawn and a hole
     down its left edge. */

  /* Both foot links become 44px tall targets once the cards stack.
   *
   * They are text links, and at 22px and 21px they were the smallest of the tap-target misses
   * tools/mobile_check.py reports at 390 and 360. The other misses in that list are links inside
   * running prose, where a 44px box would put visible gaps between paragraph lines; these two are
   * the card's own actions sitting on their own row, so the height costs nothing.
   *
   * Keyed to the width, not to `pointer: coarse`. The pointer query is the more precise
   * expression of the intent, but our instrument measures in a desktop browser at a narrow
   * width, so a fix written that way would be invisible to the tool that found the problem — an
   * unverifiable improvement, which this project treats as no improvement. */
  .dl-pcol__go,
  .dl-pcol__more {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .dl-pcol__foot {
    padding-top: var(--space-2);
  }
}


/* Marker positions, 0-100 in steps of 1.
 *
 * Steps of 5 first, matching the bar-width classes in score-panel.css — and it was too coarse
 * here. $139 and $149 both rounded to 35%, so two different prices produced one marker position
 * and the graphic said they cost the same. Widths tolerate 5% because a bar's length is read
 * against its own track; a dot's *position* is read against its neighbours, and the dot is 11px
 * on a ~300px track, which is under 4%. The rounding has to be finer than the mark itself.
 *
 * 101 rules is about 2KB and no runtime. Written out because this project ships hand-authored
 * CSS with no build step beyond a concatenator. */
.dl-pos--0 { left: 0%; }
.dl-pos--1 { left: 1%; }
.dl-pos--2 { left: 2%; }
.dl-pos--3 { left: 3%; }
.dl-pos--4 { left: 4%; }
.dl-pos--5 { left: 5%; }
.dl-pos--6 { left: 6%; }
.dl-pos--7 { left: 7%; }
.dl-pos--8 { left: 8%; }
.dl-pos--9 { left: 9%; }
.dl-pos--10 { left: 10%; }
.dl-pos--11 { left: 11%; }
.dl-pos--12 { left: 12%; }
.dl-pos--13 { left: 13%; }
.dl-pos--14 { left: 14%; }
.dl-pos--15 { left: 15%; }
.dl-pos--16 { left: 16%; }
.dl-pos--17 { left: 17%; }
.dl-pos--18 { left: 18%; }
.dl-pos--19 { left: 19%; }
.dl-pos--20 { left: 20%; }
.dl-pos--21 { left: 21%; }
.dl-pos--22 { left: 22%; }
.dl-pos--23 { left: 23%; }
.dl-pos--24 { left: 24%; }
.dl-pos--25 { left: 25%; }
.dl-pos--26 { left: 26%; }
.dl-pos--27 { left: 27%; }
.dl-pos--28 { left: 28%; }
.dl-pos--29 { left: 29%; }
.dl-pos--30 { left: 30%; }
.dl-pos--31 { left: 31%; }
.dl-pos--32 { left: 32%; }
.dl-pos--33 { left: 33%; }
.dl-pos--34 { left: 34%; }
.dl-pos--35 { left: 35%; }
.dl-pos--36 { left: 36%; }
.dl-pos--37 { left: 37%; }
.dl-pos--38 { left: 38%; }
.dl-pos--39 { left: 39%; }
.dl-pos--40 { left: 40%; }
.dl-pos--41 { left: 41%; }
.dl-pos--42 { left: 42%; }
.dl-pos--43 { left: 43%; }
.dl-pos--44 { left: 44%; }
.dl-pos--45 { left: 45%; }
.dl-pos--46 { left: 46%; }
.dl-pos--47 { left: 47%; }
.dl-pos--48 { left: 48%; }
.dl-pos--49 { left: 49%; }
.dl-pos--50 { left: 50%; }
.dl-pos--51 { left: 51%; }
.dl-pos--52 { left: 52%; }
.dl-pos--53 { left: 53%; }
.dl-pos--54 { left: 54%; }
.dl-pos--55 { left: 55%; }
.dl-pos--56 { left: 56%; }
.dl-pos--57 { left: 57%; }
.dl-pos--58 { left: 58%; }
.dl-pos--59 { left: 59%; }
.dl-pos--60 { left: 60%; }
.dl-pos--61 { left: 61%; }
.dl-pos--62 { left: 62%; }
.dl-pos--63 { left: 63%; }
.dl-pos--64 { left: 64%; }
.dl-pos--65 { left: 65%; }
.dl-pos--66 { left: 66%; }
.dl-pos--67 { left: 67%; }
.dl-pos--68 { left: 68%; }
.dl-pos--69 { left: 69%; }
.dl-pos--70 { left: 70%; }
.dl-pos--71 { left: 71%; }
.dl-pos--72 { left: 72%; }
.dl-pos--73 { left: 73%; }
.dl-pos--74 { left: 74%; }
.dl-pos--75 { left: 75%; }
.dl-pos--76 { left: 76%; }
.dl-pos--77 { left: 77%; }
.dl-pos--78 { left: 78%; }
.dl-pos--79 { left: 79%; }
.dl-pos--80 { left: 80%; }
.dl-pos--81 { left: 81%; }
.dl-pos--82 { left: 82%; }
.dl-pos--83 { left: 83%; }
.dl-pos--84 { left: 84%; }
.dl-pos--85 { left: 85%; }
.dl-pos--86 { left: 86%; }
.dl-pos--87 { left: 87%; }
.dl-pos--88 { left: 88%; }
.dl-pos--89 { left: 89%; }
.dl-pos--90 { left: 90%; }
.dl-pos--91 { left: 91%; }
.dl-pos--92 { left: 92%; }
.dl-pos--93 { left: 93%; }
.dl-pos--94 { left: 94%; }
.dl-pos--95 { left: 95%; }
.dl-pos--96 { left: 96%; }
.dl-pos--97 { left: 97%; }
.dl-pos--98 { left: 98%; }
.dl-pos--99 { left: 99%; }
.dl-pos--100 { left: 100%; }

/* --- css/components/editorial.css --- */
/* ==========================================================================
   Editorial surfaces — verdict box, quick facts, pros/cons, the tipbox callout, the
   calculator, prose, the methodology strip, sources box. Per design-system.html
   §8-10 and the two page mockups.
   ========================================================================== */

/* --- record panel + coverage strip (provider index, state pages) ------------ */

/* Same follow-up note as the findings block below: not part of the v2 mockups,
   restyled on the v2 tokens only so the two page types that still render it
   (`provider_index_page.html`, `state_page.html`) are not left with a completely
   unstyled fact strip after layout.css's old carbon-blue band was retired. */
.dl-record-panel {
  background: var(--gray-bg);
  padding: var(--space-3) var(--space-5);
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-8);
  row-gap: var(--space-2);
  border-radius: var(--radius-lg);
}

.dl-record-panel__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 9rem;
}

.dl-record-panel__label {
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-record-panel__value {
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-coverage-strip {
  margin: var(--space-2) 0 0;
  font-size: var(--type-small-size);
  color: var(--text-body);
}

.dl-coverage-strip__sep {
  color: var(--text-muted);
  margin: 0 var(--space-2);
}

.dl-coverage-strip__note {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* --- numbered findings (provider index, state pages) ------------------------ */

/* Not part of the v2 mockups (which state a ranking conclusion in prose instead —
   see the homepage's tipbox and "Our pick" card), but `apps.providers.reporting`'s
   `Finding`/`dl_findings` is still real, tested backend logic that two page types
   this pass did not rebuild (`provider_index_page.html`, `state_page.html`) still
   render. Restyled on the v2 tokens so it reads as this site rather than an
   unstyled paragraph, rather than left to inherit nothing after layout.css's old
   `.dl-findings` callout was retired. Follow-up: fold this into each page's own v2
   rebuild and retire the visible "F-0x" reference in favour of the mockups' prose
   pattern.
   ========================================================================== */

.dl-findings {
  margin: var(--space-4) 0;
  padding: var(--space-4) var(--space-5);
  background: var(--gray-bg);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dl-findings__label {
  margin: 0;
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-finding {
  border-left: 3px solid var(--blue);
  padding-left: var(--space-4);
  max-width: 52rem;
}

.dl-finding__ref {
  font-weight: var(--weight-bold);
  color: var(--blue);
}

.dl-finding__text {
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-finding__qualifier {
  display: block;
  margin-top: 2px;
  font-size: var(--type-small-size);
  font-style: italic;
  color: var(--text-muted);
}

/* --- "Our pick" provider card (homepage) ------------------------------------ */

.dl-pcard {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
  /* 2026-08 depth pass — see .dl-qfacts's own comment just above in this file. No shadow: see
     that same comment for why elevation is scoped to only two surfaces in this pass. */
  background: var(--surface-elevated);
}

/* 2026-08 clickability fix — the whole card as a target, CSS-only.
   `.dl-plogo__link` (the provider's own mark + name, see provider-logo.css) is the card's one
   identity link. Its `::after` is stretched to the full card here, so a click or tap landing
   anywhere on the card that is not a *more specific* action (see below) follows that same link.
   This is not the nested-anchor trick a `<tr>` can't have — there is still exactly one real
   `<a>` in the DOM for the identity, one accessible name, one tab stop; the stretch is purely a
   hit-area expansion of that single anchor via `position: absolute; inset: 0`, the standard
   "block link" pattern (`position: relative` on the container plus one positioned `::after`),
   not a second link layered on top of the card's own CTA.
   `.dl-pcard-cta` is deliberately given its own stacking context above the stretched layer
   (`position: relative; z-index: 1`) so the primary CTA / secondary review button / outbound
   official-site button underneath it keep their own, different destinations — without this, a
   click on "Check your eligibility" would silently land on the review page instead of the offer,
   which is exactly the kind of link-that-lies-about-where-it-goes this project's compliance
   rules forbid. Nothing else in the card carries its own link (the tiles, the score, the "what
   we found" list are all plain text), so the rest of the card is safe to leave under the
   stretched layer. */
.dl-pcard .dl-plogo__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}

.dl-pcard-cta {
  position: relative;
  z-index: 1;
}

.dl-pcard-top {
  display: grid;
  grid-template-columns: 220px 1fr auto;
  gap: var(--space-6);
  align-items: start;
}

.dl-score-block {
  margin-top: var(--space-2);
  /* Column, not row. `_score.html` emits the rating and the "Best for ..." line as two spans,
     and `.dl-score__bestfor` carries `display: block` to put the second on its own line. A flex
     row makes both spans flex items, at which point `display: block` on the child stops creating
     a line break and the two ran together as "* 4.1 /5Best entry price" in the winner cards. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.dl-score__source {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* --- ranked-card rank badge (includes/_winner_card.html) -------------------- */

/* The rank sits beside the provider's own mark, not folded into a sentence — the eye lands on
   the number first, per the competitor pattern this pass is built from. `aria-hidden`: the
   card's real accessible name is the visually-hidden <h3> right beneath it ("#1 — Hims"), so a
   screen reader is not told the rank twice. */
.dl-winner-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Superseded by `.dl-rank-chip` (rank-chip.css) in the 2026-08 depth pass — a numbered circle
   in place of the plain "#1" glyph. Rule kept out of this file rather than left dead here. */

/* A grid, not a wrapping flex row.
   As `flex-wrap` this left an orphan whenever the items did not happen to fit: the winner cards
   carry three facts, so at any width where only two fit, the third sat alone with a card's worth of
   dead space beside it. It showed on card #2 at 1440px (its longer first-year note pushed
   membership down) and on every card at 500px. A grid with an explicit column count cannot produce
   that, because the count is chosen rather than discovered — and three items never go into two
   columns. `--factcols` lets each caller state how many facts it has. */
.dl-factcols {
  display: grid;
  grid-template-columns: repeat(var(--factcols, 3), minmax(0, 1fr));
  gap: var(--space-3) 36px;
  align-items: start;
}

/* A modifier rather than an inline `style="--factcols: 4"`: inline CSS is forbidden by
   .claude/rules/templates.md, and a named class is the thing a future reader can find. */
.dl-factcols--4 {
  --factcols: 4;
}

@media (max-width: 46rem) {
  /* One column, never two: two columns is precisely what orphans an odd third item. */
  .dl-factcols,
  .dl-factcols--4 {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2);
  }
}

@media (min-width: 46.0625rem) and (max-width: 64rem) {
  /* Four facts do not fit across a mid-width card, and 4-into-3 orphans one. Two columns is
     safe here because four items fill both rows exactly. */
  .dl-factcols--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.dl-fl {
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-fv {
  font-size: var(--step-lede);
  font-weight: var(--weight-bold);
  margin-top: 3px;
}

.dl-fv--sm {
  font-size: var(--step-body);
}

.dl-fv small {
  display: block;
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-pcard-cta {
  text-align: center;
}

/* The secondary "Read our X review" button under the primary CTA (2026-08 competitor-card
   pass) — spacing only; the button itself is the site's one standard `.dl-btn--secondary`. */
.dl-pcard-cta__secondary {
  margin-top: var(--space-3);
}

.dl-whybox {
  margin-top: var(--space-5);
  font-size: var(--step-body);
  max-width: 62rem;
}

/* "What we found" — the ranked card's own pros list, shown only when a published review backs
   it (includes/_winner_card.html). Reuses `.dl-marks--pro`'s tinted panel unchanged; this only
   sets its position and width within the card, narrower than the card's own full bleed so a
   short list of specifics doesn't stretch into a bar of mostly empty tint. */
.dl-whatfound {
  margin-top: var(--space-4);
  max-width: 34rem;
}

/* --- head-to-head links (homepage) ------------------------------------------- */

.dl-vslist {
  columns: 2;
  column-gap: 44px;
  font-size: var(--step-body);
}

.dl-vslist a {
  display: block;
  padding: 9px 0;
  border-bottom: var(--rule-hairline);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
  text-decoration: none;
  break-inside: avoid;
}

.dl-vslist a:hover {
  color: var(--blue);
  text-decoration: underline;
}

@media (max-width: 899px) {
  .dl-pcard-top {
    grid-template-columns: 1fr;
  }

  .dl-vslist {
    columns: 1;
  }
}

/* --- ranked card rebuild: 3 columns + full-width footer (2026-08) -----------------------
   Replaces `.dl-pcard-top`/`.dl-factcols`/`.dl-fl`/`.dl-fv`/`.dl-whatfound` *inside*
   includes/_winner_card.html only — those classes are untouched above because
   home_page.html's own single-provider "Our pick" card still renders with them exactly as
   before. The owner's reference card is a wider shape (identity / cost arithmetic / verdict,
   plus a full-width footer bar) that does not fit the old 220px-fixed three-column grid, so
   this is new markup and new classes rather than a fourth modifier on the old ones.
   `.dl-whybox` and `.dl-marks__heading` are reused unchanged from elsewhere in this file. */

.dl-rank-label {
  margin: 0 0 var(--space-4);
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  color: var(--text-2);
}

/* The 3-column grid, hairlines drawn the same way `.dl-tiles` draws them (metric-tile.css):
   a 1px grid gap filled with `--border`, each column's own background painted over it so only
   the gap shows through. One row, so the "gap" is a vertical rule at every width down to the
   breakpoint below, and — this is the reason for the technique rather than an explicit
   `border-left` on the 2nd/3rd column — a horizontal rule for free the moment the grid drops
   to a single column, with no separate mobile-only divider rule to maintain. */
.dl-pcard-cols {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 1px;
  background: var(--border);
}

.dl-pcard-col {
  background: var(--surface-elevated);
  padding: 0 var(--space-5);
  min-width: 0;
}

.dl-pcard-col--id {
  padding-left: 0;
}

.dl-pcard-col--verdict {
  padding-right: 0;
}

@media (max-width: 56rem) {
  .dl-pcard-cols {
    grid-template-columns: 1fr;
  }

  .dl-pcard-col {
    padding: var(--space-5) 0;
  }

  .dl-pcard-col--id {
    padding-top: 0;
  }

  .dl-pcard-col--verdict {
    padding-bottom: 0;
  }
}

/* --- left column: identity + the two score rows ------------------------------------ */

.dl-pcard__name {
  margin: var(--space-3) 0 0;
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--text-body);
}

.dl-pcard__desc {
  margin: 4px 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-pcard__label {
  margin: 0;
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

.dl-pcard__scores {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--rule-hairline);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.dl-pcard__scorerow {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* --- the user-rating row's own value line: deliberately not `.dl-score` ------------------
   No blue, no star: design-system.html reserves the single blue star for our own published
   rubric, and a crowd's app-store average is a different kind of number sitting one row below
   it. Plain, slightly smaller ink keeps the two from being read as one system. */
.dl-pcard__urating {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}

.dl-pcard__urating-num {
  font-size: var(--step-title);
  font-weight: var(--weight-black);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-pcard__urating-max {
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-pcard__urating-note {
  font-size: var(--type-micro-size);
  color: var(--text-muted);
}

/* The rating-drop callout. Neutral border, no tint: a crowd's mood is not a regulatory fact,
   so it gets none of the amber/red palette those are reserved for — the arrow and the stated
   percentage carry the direction, and both survive being read in greyscale. */
.dl-pcard__ratingdrop {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--type-small-size);
  line-height: 1.5;
  color: var(--text-body);
}

/* --- middle column: the cost arithmetic --------------------------------------------- */

.dl-pcard__costtotal {
  margin: var(--space-2) 0 0;
  font-size: 42px;
  font-weight: var(--weight-black);
  letter-spacing: -0.02em;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: var(--tabular);
}

.dl-pcard__costunit {
  font-size: var(--step-body);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  margin-left: 2px;
}

.dl-pcard__costnote {
  margin: 4px 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* The two addends, boxed, with a small "+" mark sitting on the hairline between them — see
   `.dl-tiles--2 .dl-tiles--nested` (metric-tile.css) for the boxes themselves, reused
   unchanged; this only adds the "+". */
.dl-pcard__sumwrap {
  position: relative;
  margin-top: var(--space-3);
}

.dl-pcard__sumop {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--surface-elevated);
  border: 1px solid var(--border);
  font-size: var(--step-small);
  font-weight: var(--weight-black);
  color: var(--text-muted);
}

.dl-pcard__insurance {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin: var(--space-3) 0 0;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: var(--type-small-size);
  font-weight: var(--weight-semibold);
  color: var(--text-body);
}

.dl-pcard__insurance--no {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

.dl-pcard__hr {
  border: 0;
  border-top: var(--rule-hairline);
  margin: var(--space-4) 0;
}

/* First-year total: the same `dl_cost_range` component every other caller renders, sized up
   for this column's own emphasis via a descendant selector rather than a new argument on that
   shared tag — see includes/_winner_card.html's own comment. */
.dl-pcard__yearvalue .dl-yearcost__value {
  font-size: var(--step-metric);
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

/* --- right column: findings, facts, rating (2026-08 density pass) ------------------ */

/* Retired: `.dl-checklist`/`.dl-checklist__*` (a met/unmet/informational list with a green
   check, a plain dash and a middle dot and no legend — the owner's own click-through review
   named it "noise"). Replaced below by `.dl-pcard__finding` (real, computed findings, as
   prose) and `.dl-pcard__facts` (a label/value grid reusing `.dl-factcols`/`.dl-fl`/`.dl-fv`,
   already defined earlier in this file). `.dl-pcard__quotes` (the old full-width quote block)
   is retired with it — the one quotation this card shows now sits inside
   `.dl-pcard__urating-block`. */

/* A finding that names this card's own provider, stated as prose — not a second copy of the
   boxed `.dl-findings`/`.dl-finding` callout (editorial.css, "numbered findings" section
   above): that component's own comment already marks it as pre-v3, legacy furniture for two
   page types the v3 pass has not yet rebuilt, carrying a visible "F-0x" reference this card has
   no use for (there is no table row here to cross-reference back to). A left rule still marks
   it as a sourced, checkable claim rather than an unstyled paragraph. */
.dl-pcard__finding {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-3);
  border-left: 2px solid var(--blue);
  font-size: var(--type-small-size);
  line-height: 1.5;
  color: var(--text-body);
}

.dl-pcard__finding-note {
  display: block;
  margin-top: 2px;
  font-style: italic;
  color: var(--text-muted);
}

/* Spacing only — `.dl-factcols` itself carries no margin, since its other three callers each
   sit inside their own layout. */
.dl-pcard__facts {
  margin-top: var(--space-3);
}

/* Separates the crowd's own rating from this column's own findings/facts/flag above it, the
   same "hairline + top spacing" treatment `.dl-pcard__scores` uses in the identity column. */
.dl-pcard__urating-block {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--rule-hairline);
}

.dl-pcard__urating-block .dl-quotes {
  margin-top: var(--space-3);
}

/* --- full-width footer bar ----------------------------------------------------------- */

.dl-pcard__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--rule-hairline);
  /* Above the card's stretched identity-link overlay (`.dl-pcard .dl-plogo__link::after`),
     the same reason `.dl-pcard-cta` already carries this pair of declarations. */
  position: relative;
  z-index: 1;
}

.dl-pcard__footer-meta {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-pcard__footer-meta .dl-icon {
  color: var(--green);
}

.dl-pcard__footer-actions {
  flex: 0 1 auto;
}

/* --- the review page's H1 row: status word beside the title ---------------- */

.dl-page-title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  row-gap: var(--space-1);
}

.dl-page-title h1 {
  min-width: 0;
}

/* --- verdict box: the one 2px-border container in the system ---------------- */

.dl-verdict {
  border: 2px solid var(--text);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: grid;
  grid-template-columns: 1fr 210px;
  gap: var(--space-6);
  align-items: start;
}

.dl-verdict__label {
  font-size: var(--step-small);
  font-weight: var(--weight-black);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
  display: block;
}

.dl-verdict__text {
  margin: 0;
  font-size: var(--step-body);
  line-height: 1.6;
  color: var(--text-body);
}

.dl-verdict__side {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

/* The "no sign-up link" note, in the verdict box's CTA slot for a dead/degraded
   provider — never a silent gap where the button would be. */
.dl-no-cta {
  font-size: var(--type-small-size);
  color: var(--text-muted);
  max-width: 22rem;
}

/* --- comparison page: a stacked verdict, plus a two-CTA row below it -------- */

/* ComparisonPage.verdict is one scenario-based field ("Choose A if X, choose B if
   Y"), not two — .dl-verdict's default two-column [text | one CTA] grid does not
   fit a page with two providers, so the comparison page stacks the text full width
   and follows it with .dl-cmp-ctas instead of a second .dl-verdict__side. */
.dl-verdict--stacked {
  display: block;
}

/* The ranked card's own "OUR VERDICT" panel (includes/_winner_card.html, 2026-08 rebuild):
   the same box, sized down for a card column roughly a third the width of the review page's
   own fold — the default `--space-6` padding and 15.5px body copy were sized for that much
   wider column and read oversized here. The 2px border and the "the one 2px-border container
   in the system" rule both stay: this is still the identical component, not a second verdict
   idiom for narrow spaces. */
.dl-verdict--card {
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.dl-verdict--card .dl-verdict__text {
  font-size: var(--type-small-size);
}

.dl-cmp-ctas,
.dl-cmp-glance {
  display: flex;
  gap: var(--space-6);
}

.dl-cmp-ctas {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--rule-hairline);
}

/* --- comparison page: the "at a glance" strip under the H1 ------------------ */

/* 2026-08 second pass. Same flex row `.dl-cmp-ctas` already is, no top rule: this sits
   directly under the H1's own facts line, not under a CTA row it needs to be visually
   separated from. `.dl-cmp-ctas__side` (below) is reused unchanged inside it, so a
   provider's logo and rating read exactly the same size and weight here as they do
   next to that provider's own CTA further down the page — one component, two places,
   not a second card idiom invented for this strip. */
.dl-cmp-glance {
  margin: var(--space-4) 0 var(--space-2);
}

/* The two provider columns are cards, not floating text.
   They previously reused `.dl-cmp-ctas__side` unchanged — a container built to hold a button, with
   no border, no padding and centred text. Two providers' figures then hung in white space with a
   `--space-6` gap between them and nothing holding either side together, which is exactly how the
   page read: a form print-out rather than a comparison. Every strong competitor vs-page puts each
   provider in a bordered block; this does the same, with the site's own hairline and radius. */
.dl-cmp-glance .dl-cmp-ctas__side {
  text-align: left;
  /* `align-items` too, not just `text-align`: the base component is a centred flex column, so the
     logo and the rating stayed centred over left-aligned figures until this was added. */
  align-items: flex-start;
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--surface-elevated);
}

/* **The two sides take their own brand surface** (v5, 2026-10-01).
 *
 * The pack's comparison opens with the two providers on sky and peach, which does the one thing a
 * white-on-white pair cannot: it tells a reader at a glance which column of the table below belongs
 * to which company. On a page whose whole promise is that both sides answer the same questions,
 * two identical white cards make the reader track position instead of identity.
 *
 * Position, not merit — the same rule as the three shortlist cards. The first side is always sky
 * and the second always peach, so swapping the pair swaps the colours: nothing a provider can earn
 * or buy. Both clear 12.2:1 and 11.1:1 against `--ink`. */
.dl-cmp-glance .dl-cmp-ctas__side:first-child {
  background: var(--sky);
  border-color: var(--sky);
}

.dl-cmp-glance .dl-cmp-ctas__side:last-child {
  background: var(--peach);
  border-color: var(--peach);
}

/* The score card inside a coloured side needs its own white ground back, or it dissolves into the
   surface it is sitting on. */
.dl-cmp-glance .dl-cmp-ctas__side .dl-plogo__tile {
  background: var(--white);
}

/* The one caveat this page owes a reader, on the pack's own explanatory surface. Sky, because
   `--sky` is what the system uses for "here is something you should know" — not caution, which
   would read as a warning about the two companies rather than about the comparison. */
.dl-cmp-caveat {
  margin: var(--space-4) 0 0;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--sky);
  color: var(--text-body);
  font-size: var(--step-small);
  line-height: 1.6;
  max-width: 70ch;
}

.dl-cmp-glance .dl-plogo {
  margin-bottom: var(--space-2);
}

.dl-cmp-glance .dl-score-block,
.dl-cmp-glance .dl-score {
  justify-content: flex-start;
}

@media (max-width: 46rem) {
  /* Stacked, not two cramped columns — a card each is worth more than two half-width ones. */
  .dl-cmp-glance {
    flex-direction: column;
    gap: var(--space-3);
  }
}

.dl-cmp-ctas__side {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

/* The glance strip's own two-fact row inside each side — narrower than the winner
   card's three-column default, so it needs its own column count rather than the
   `.dl-factcols--4` modifier already in this file. */
.dl-factcols--2 {
  --factcols: 2;
}

/* One fact, full width. The comparison glance dropped to a single fact when the price story
   took over the "From / month" cell it used to hold — see comparison_page.html. */
.dl-factcols--1 {
  --factcols: 1;
}

@media (max-width: 599px) {
  .dl-cmp-ctas,
  .dl-cmp-glance {
    flex-direction: column;
  }
}

/* --- the editorial note after the master table --------------------------- */

.dl-note {
  margin-top: 28px;
  border-left: 3px solid var(--text);
  padding-left: 18px;
  max-width: 40rem;
}

.dl-note p {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-note p + p {
  margin-top: var(--space-3);
}

/* --- pros and cons --------------------------------------------------------- */

.dl-procon {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
}

/* 600 at 14px. "Pros" and "Cons" label two mark lists; they are not page sections, and 800 below
   the 20px floor is what `tools/type_scale.py` measures. These were the last two it found. */
.dl-procon h4,
.dl-marks__heading {
  font-size: var(--step-small);
  font-weight: var(--weight-semibold);
  margin-bottom: var(--space-2);
  display: block;
}

.dl-marks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  font-size: var(--type-small-size);
}

/* 2026-08 depth pass: a tinted, bordered panel for pros/cons, not a bare list of glyphs — per
   the reference build's own component code (a tinted surface + a matching-hue border), copied
   with our own --green/--red rather than theirs. The list items' own padding (below) already
   leaves room for the checkmark/cross glyph inside this box. */
.dl-marks--pro,
.dl-marks--con {
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + 4px);
  border-radius: var(--radius-card);
}

.dl-marks--pro {
  background: var(--green-bg);
  border: 1px solid var(--green-border);
}

.dl-marks--con {
  background: var(--red-bg);
  border: 1px solid var(--red-border);
}

/* The verdict's own pros list — no h4 above it (the verdict paragraph already
   introduces it), so this is the one place `.dl-marks` needs its own top margin. */
.dl-verdict__text + .dl-marks,
.dl-verdict-lede + .dl-marks {
  margin-top: var(--space-3);
}

/* --- verdict as plain prose, no box — the review/comparison fold ------------- */

/* New in the 2026-08 owner-directed structural pass (docs/ai/depth_and_second_accent_2026-08-04.md):
   replaces the 2px-bordered `.dl-verdict` box on the review and comparison first screens. The
   reference build's own pattern is a plain paragraph with a bold inline lead-in, no section
   heading and no border — the verdict reads as the page's own voice rather than a quoted call-
   out. `.dl-verdict` itself is unchanged: `info_page.html` still reuses it deliberately for its
   own unrelated "lead" styling (see that template's own comment), just no longer for either
   page this pass rebuilt. */
.dl-verdict-lede {
  margin: 0 0 var(--space-2);
  font-size: var(--step-lede);
  line-height: 1.6;
  color: var(--text-body);
  max-width: 58ch;
}

/* 700 at 18px. The lead-in word ("Verdict:") is emphasis inside a paragraph, and 800 is the
   weight this system reserves for headings — at 18px it read as a heading that had lost its own
   line. Caught by `tools/type_scale.py`. */
.dl-verdict-lede strong {
  font-weight: var(--weight-bold);
}

.dl-marks > li {
  padding: 5px 0 5px 24px;
  position: relative;
  line-height: 1.5;
}

.dl-marks--pro > li::before {
  content: "\2713";
  position: absolute;
  left: 0;
  color: var(--green);
  font-weight: var(--weight-black);
}

.dl-marks--con > li::before {
  content: "\2715";
  position: absolute;
  left: 0;
  color: var(--red);
  font-weight: var(--weight-black);
}

/* --- prose (RichTextBlock output) ------------------------------------------- */

.dl-prose {
  max-width: 42rem;
}

.dl-prose > * + * {
  margin-top: var(--space-4);
}

.dl-prose h2 {
  font-size: var(--type-h3-size);
  margin-top: var(--space-8);
}

.dl-prose h3 {
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  margin-top: var(--space-6);
}

.dl-prose p {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-prose ul,
.dl-prose ol {
  padding-left: 1.15em;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.dl-prose li {
  line-height: 1.6;
}

.dl-prose a {
  color: var(--blue);
}

/* --- editorial heading inside a StreamField body --------------------------- */

.dl-body-heading {
  margin-top: var(--space-10);
  font-size: var(--type-h3-size);
}

/* --- SourcedClaimBlock ------------------------------------------------------ */

.dl-claim {
  border-left: 3px solid var(--text);
  padding-left: var(--space-4);
  max-width: 44rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.dl-claim__text {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-claim__source {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--step-micro);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

/* The "Primary source" link is the only interactive thing in a sourced claim's metadata row, and
   at 19px it was the smallest tap target on the provider review and the coverage detail page —
   under the 24px WCAG 2.5.8 AA floor, never mind the 44px platform guideline. Found on 2026-08-25
   by widening `tools/mobile_check.py` from five hand-listed pages to the whole tree.

   `min-height` rather than padding, matching what `.dl-reg__source` was already given for exactly
   this reason: it widens the box a source link sits in without touching the text's own size, which
   on a citation is deliberately small. `align-self` keeps it from stretching the flex row it is
   in. */
.dl-claim__source a {
  color: var(--blue);
  display: inline-flex;
  align-items: center;
  align-self: center;
  min-height: var(--control-h-lg);
  gap: var(--space-1);
}

/* --- "Before you compare" / "Expert tip" callout ---------------------------- */

.dl-tipbox {
  background: var(--tip);
  /* 2026-08-05: a hairline edge, added for one reason — measured, not eyeballed. `--tip`
     (#F3F8EC) sits at a 1.01:1 luminance ratio against `--page-bg` (#FAF7F2, added in the prior
     depth pass), and white/tip is only 1.08:1 to begin with: this fill was always a wash, not a
     contrast-graded region, and the warm page surface introduced since erased what little edge it
     had. `--tip`'s own hex is unchanged (an owner-supplied token value, not this pass's to
     revise); the fix is the same `1px solid var(--border)` edge every other card-like surface on
     this site already carries (`.dl-qfacts`, `.dl-pcard`, `.dl-table-wrap`), so the callout reads
     as a bounded block again regardless of how close its fill sits to the page around it. See
     docs/ai/costs_chart_and_homepage_anchor_2026-08-05.md. */
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  /* Padding and top margin trimmed one step down the spacing scale (space-6->space-5,
     space-8->space-6) from the mockup's own values, recovering ~16px toward the home
     page's fold budget — this box's own surrounding air, not its copy, the stat chips,
     the findings below it, the byline or the disclosure. See
     docs/ai/mobile_table_cards_and_trust_strip_2026-08-02.md. */
  padding: var(--space-5);
  margin: var(--space-6) 0 var(--space-2);
  display: flex;
  gap: var(--space-8);
  align-items: center;
  flex-wrap: wrap;
}

.dl-tipbox__text {
  flex: 1 1 280px;
  min-width: 280px;
}

.dl-tipbox__text h2 {
  font-size: var(--step-body);
  margin-bottom: var(--space-2);
}

.dl-tipbox__text p {
  font-size: var(--step-small);
  color: var(--text-body);
}

/* --- quick facts strip (review page) ---------------------------------------- */

.dl-qfacts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  margin: var(--space-6) 0 var(--space-2);
  /* 2026-08 depth pass: an explicit white fill, so the strip reads as a bordered card resting on
     --page-bg rather than a region of the same colour as the page underneath it. No shadow here
     — per tokens/shape.css's own comment, elevation in this pass is scoped to the score panel
     and the metric-tile grid only, not applied to every bordered surface. */
  background: var(--surface-elevated);
}

.dl-qf {
  padding: var(--space-4) 18px;
  border-right: 1px solid var(--border);
}

.dl-qf:last-child {
  border-right: 0;
}

/* Six cells wrap to a second row of two at the desktop 4-column width (see the
   2026-08 Quick facts addition of Regulatory record / Price history below) — zero the
   right border at the end of *every* row, not only the true last cell, or the fourth
   cell keeps a border-right that reads as a stray vertical rule at the row's own edge. */
.dl-qf:nth-child(4n) {
  border-right: 0;
}

/* --- 2026-08 second pass: the review page's "at a glance" strip (5 facts) and its
   trimmed-down "Quick facts" section (3 facts, once the glance strip took the other
   four) — see provider_page.html's file-level comment. Explicit fixed-column tiers,
   not `auto-fit`: this component's own border-right reset already had to be fixed once
   before (the comment above) for exactly the failure mode an unpredictable column
   count reintroduces — a stray vertical rule at a row's own right edge. */
.dl-qfacts--5 {
  grid-template-columns: repeat(5, 1fr);
}

.dl-qfacts--5 .dl-qf:nth-child(5n) {
  border-right: 0;
}

.dl-qfacts--3 {
  grid-template-columns: repeat(3, 1fr);
}

.dl-qfacts--3 .dl-qf:nth-child(3n) {
  border-right: 0;
}

/* Two facts (the cost guide's cheapest/most-expensive channel pair) in the base
   4-column grid left two empty tracks' worth of dead space inside the bordered box —
   a pre-existing gap this pass found while moving the no-clinical-review statement
   past it, not something the move introduced. */
.dl-qfacts--2 {
  grid-template-columns: repeat(2, 1fr);
}

.dl-qfacts--2 .dl-qf:nth-child(2n) {
  border-right: 0;
}

/* Between the mobile stack (899px, below) and full width, five columns run out of
   room before three or four do — drop to three so no cell holds a wrapping figure
   like "$948–$2,388" in under 150px. Two items trail in the third column's second row;
   that is an incomplete last row, not the single-orphan-with-a-full-row-of-dead-space
   pattern `.dl-factcols--4` guards against, so it needs no further special case. */
@media (min-width: 900px) and (max-width: 1099px) {
  .dl-qfacts--5 {
    grid-template-columns: repeat(3, 1fr);
  }

  .dl-qfacts--5 .dl-qf:nth-child(3n) {
    border-right: 0;
  }

  .dl-qfacts--5 .dl-qf:nth-child(5n) {
    border-right: 1px solid var(--border);
  }
}

.dl-qf__l {
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.dl-qf__v {
  font-size: var(--step-lede);
  font-weight: var(--weight-black);
  margin-top: 4px;
}

.dl-qf__v small {
  display: block;
  font-size: var(--type-small-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

.dl-qf__v--sm {
  font-size: var(--step-body);
}

/* The cost guide's cheapest-channel tile: that channel's own note and source, when it has one —
   see costs_page.html's own comment on why (surfaces a fact like the Medicare Bridge's programme
   end date, which otherwise only appeared in the channel table's Notes column, past this page's
   fold). Muted, small, same voice as every other secondary line in this component. */
.dl-qf__note {
  margin: 8px 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

.dl-qf__note a {
  color: var(--blue);
  font-weight: var(--weight-semibold);
}

/* --- sticky mobile CTA bar --------------------------------------------------- */

/* Ships `hidden` in the markup and stays that way until static/js/modules/sticky-cta.js removes
   the attribute — see includes/_sticky_cta.html. `display: none` on the bare class is the no-JS
   floor (this bar adds nothing a JS-free reader cannot already do from the in-page button); the
   media query below only ever fires once JS has cleared `[hidden]`, which is what makes "reveal
   on scroll past the primary CTA" true rather than "always on below 899px" as it read before. */
.dl-stickycta {
  display: none;
}

@media (max-width: 899px) {
  .dl-stickycta:not([hidden]) {
    display: flex;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--white);
    border-top: var(--rule-hairline);
    padding: 10px var(--space-4);
    /* iOS home-indicator clearance: additive, not `max()` — the bar needs its own 10px of
       breathing room *plus* whatever the device's own safe area reserves, not one or the other. */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    gap: 14px;
    align-items: center;
    z-index: 60;
  }

  /* Truncates rather than pushing the button off-screen. "Hims — $149/mo, checked 12 Aug
     2026" plus the button's own natural (nowrap) width added up to well over 390px with no
     `min-width: 0` on this flex item — a real overflow inside a `position: fixed` bar, which
     is exactly the one element class tools/mobile_check.py's own probe deliberately excludes
     (fixed-position elements are skipped, on the reasoning that they are usually already
     viewport-width), so a screenshot review is what actually caught it, not the tool. The
     button is the action a reader is being handed; it is never the thing that yields space. */
  .dl-stickycta__p {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: var(--weight-black);
    font-size: var(--type-small-size);
  }

  .dl-stickycta .dl-cta-block {
    flex: none;
    margin-left: auto;
  }

  .dl-stickycta .dl-oncopy {
    display: none;
  }
}

/* --- calculator band ---------------------------------------------------------- */

.dl-calcband {
  background: var(--band);
  padding: 44px 0;
  margin: var(--space-12) 0;
}

.dl-calcband__title {
  font-size: var(--step-title);
  text-align: center;
  margin-bottom: var(--space-6);
}

.dl-calccard {
  background: var(--white);
  border-radius: var(--radius-xl);
  padding: var(--space-8);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
  box-shadow: var(--shadow-card);
}

.dl-calccard select,
.dl-calccard input {
  width: 100%;
}

.dl-calcres {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--space-6);
  align-items: center;
  /* Was a hardcoded `#f4f9f4`, a pale green from the palette before this one. The calculator's
     result panel is an explanatory surface, which is the job `--sky` has in the new system. */
  background: var(--sky);
  border-radius: var(--radius-lg);
  padding: var(--space-5) 22px;
  flex-wrap: wrap;
}

.dl-calcres__big {
  font-size: var(--type-money-lg-size);
  font-weight: var(--weight-black);
  color: var(--green);
  letter-spacing: -0.01em;
}

.dl-calcres__label {
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
}

/* The reading date under the calculator's total (2026-10-05). Micro and muted, matching the
   price receipt's own verified line — it makes the same statement about the same kind of
   figure, and the figure above it is the one a reader screenshots. */
.dl-calcres__checked {
  margin: var(--space-2) 0 0;
  font-size: var(--step-micro);
  color: var(--text-muted);
}

.dl-calcres__checked time {
  font-variant-numeric: tabular-nums;
}

.dl-calcres__expl {
  font-size: var(--type-small-size);
  color: var(--text-muted);
  max-width: 26rem;
}

.dl-calc-note {
  text-align: center;
  font-size: var(--type-micro-size);
  color: var(--text-muted);
  margin-top: var(--space-4);
}

@media (max-width: 899px) {
  .dl-calccard {
    grid-template-columns: 1fr;
  }
}

/* --- sources box (review page) ----------------------------------------------- */

.dl-srcbox {
  background: var(--gray-bg);
  border-radius: var(--radius-lg);
  padding: 18px 22px;
  font-size: var(--type-small-size);
}

.dl-srcbox li {
  margin: 6px 0 6px 18px;
}

/* The visible link text here is the raw source URL, not a caption — one long unbroken string
   with no spaces to wrap at. It never overflowed at the review's own ~46rem desktop column, so
   this went unmeasured until the provider review page was added to tools/mobile_check.py's own
   page list: at 390px a bare FDA/CMS URL pushed the whole page 150px past its own viewport. */
.dl-srcbox a {
  overflow-wrap: anywhere;
}

/* --- methodology strip: three columns on a pale grey band -------------------- */

.dl-method {
  background: var(--gray-bg);
  border-top: var(--rule-hairline);
  padding: var(--space-8) 0;
  margin-top: var(--section-gap);
}

.dl-method__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
}

.dl-method h3 {
  font-size: var(--step-body);
  margin-bottom: var(--space-2);
}

.dl-method p {
  font-size: var(--step-small);
  color: var(--text-muted);
}

@media (max-width: 899px) {
  .dl-method__grid {
    grid-template-columns: 1fr;
  }

  .dl-qfacts {
    grid-template-columns: 1fr 1fr;
  }

  .dl-qf:nth-child(2n) {
    border-right: 0;
  }

  .dl-qf {
    border-bottom: 1px solid var(--border);
  }

  /* One column below the fold rather than two: two columns is exactly what orphans
     the fifth (or third) item on this card's own odd counts — see the desktop rule's
     comment on the same failure mode. */
  .dl-qfacts--5,
  .dl-qfacts--3 {
    grid-template-columns: 1fr;
  }

  .dl-qfacts--5 .dl-qf,
  .dl-qfacts--3 .dl-qf {
    border-right: 0;
  }

  .dl-qfacts--5 .dl-qf:last-child,
  .dl-qfacts--3 .dl-qf:last-child {
    border-bottom: 0;
  }

  .dl-verdict,
  .dl-procon,
  .dl-calccard {
    grid-template-columns: 1fr;
  }

  .dl-verdict__side {
    align-items: flex-start;
    text-align: left;
  }
}

/* --- dev-only contact-email placeholder banner ------------------------------ */

.dl-debug-banner {
  border: 2px dashed var(--text);
  background: var(--gray-bg);
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.dl-debug-banner__tag {
  font-size: var(--type-small-size);
  letter-spacing: 0.02em;
  font-weight: var(--weight-bold);
  color: var(--text-body);
}

.dl-debug-banner p {
  margin: 0;
  font-size: var(--type-body-size);
  line-height: 1.6;
}

.dl-debug-banner code {
  font-size: 0.92em;
  background: var(--white);
  padding: 1px 4px;
  border: 1px solid var(--border);
  border-radius: 3px;
}

/* --- StreamField body spacing ----------------------------------------------- */

.dl-streamfield > * + * {
  margin-top: var(--space-6);
}

.dl-streamfield > .dl-body-heading + * {
  margin-top: var(--space-4);
}

.dl-streamfield > .dl-body-heading:first-child {
  margin-top: 0;
}

/* Names the provider above its own button, in place of repeating the logo and rating that the
   at-a-glance cards higher up the page already carry. */
.dl-cmp-ctas__who {
  margin: 0 0 var(--space-2);
  font-size: var(--type-h3-size);
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  color: var(--text);
}

/* The provider profile's byline line. Same job and same weight as a review's byline strip, but it
   names the desk and the last read rather than an author and a review date, because a profile has
   neither. */
.dl-profile-byline {
  margin: var(--space-3) 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}

/* --- provider profile: the action block in the fold ------------------------------
   Added 2026-08-10. This page's only route to the provider used to be a sentence in a
   section below the terms, the regulatory record and the change log — a page about
   what a provider charges, with no way to act on it until the reader had scrolled past
   everything. The button sits with the numbers now. */
.dl-profile-act {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--rule-hairline);
}

.dl-profile-act__go {
  max-width: 22rem;
}

.dl-profile-act__note {
  margin: var(--space-3) 0 0;
  font-size: var(--type-small-size);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 34rem;
}

.dl-profile-act__note .dl-link-label {
  display: inline-block;
  margin-top: 4px;
}

/* `.dl-oncopy` centres itself under a table cell's button; wherever this page puts a
   button, its microcopy and the sentences below it share one left edge. Keyed to
   `__go` rather than the block, because the page's second button (the repeat at the
   end) has the `__go` wrapper without the surrounding block. */
.dl-profile-act__go .dl-oncopy {
  text-align: left;
}

/* --- comparison rows: the verification stamp beside a figure ---------------------
   `.dl-verified` was used by `comparison_page.html` and the comparator's data preview
   and defined nowhere, so "$99" and "Verified 6 Aug 2026" ran together as one line of
   body text — the date read as part of the price. Same shape as `.dl-price__verified`
   (price-cell.css): its own line, small, muted. */
.dl-verified {
  display: block;
  margin-top: 2px;
  font-size: var(--type-micro-size);
  font-weight: var(--weight-regular);
  color: var(--text-muted);
}

/* The differences table is a continuation of the two cards above it, not a new
   section pressed against them. */
.dl-cmp-glance + .dl-table,
.dl-cmp-glance + .dl-table-wrap {
  margin-top: var(--space-6);
}

/* Its columns line up with the cards: a narrow term column, then one column per
   provider of equal width, so the eye tracks straight down from each card. */
.dl-cmp-difftable {
  table-layout: fixed;
}

.dl-cmp-difftable thead th:first-child {
  width: 22%;
}

/* `.dl-cta-block--inline` carries a 24px top margin for the running-text context it was
   drawn for. Inside this flex column, which already spaces its children with `gap`, the
   two add up and the button floats 44px below the name it belongs to. The column owns
   its own spacing. */
.dl-cmp-ctas__side .dl-cta-block--inline {
  margin-top: 0;
}

/* --- the at-a-glance cards are click targets -------------------------------------
   2026-08-10, per the owner: the cards on a comparison page named a provider and only
   the name itself was clickable, on a 300px-tall card. Same stretched single-anchor
   pattern as `.dl-pcard` and the comparison index's own rows: the provider's identity
   link (`.dl-plogo__link`, the card's only anchor — the score, the price cell and the
   first-year figure are all plain text) covers the card. One anchor, one tab stop, one
   destination; nothing here to trap. */
.dl-cmp-glance .dl-cmp-ctas__side {
  position: relative;
}

.dl-cmp-glance .dl-cmp-ctas__side .dl-plogo__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.dl-cmp-glance .dl-cmp-ctas__side:hover {
  border-color: var(--border-2);
}

.dl-cmp-glance .dl-cmp-ctas__side:hover .dl-plogo__wordmark {
  text-decoration: underline;
}

/* --- two-column ranked card, and the fact strip that replaces the third column ------------
 *
 * 18 of the 22 providers we track have no written verdict, so the card's third column had
 * nothing to head it and measured 210px beside a 535px cost column — a third of the card white.
 * tools/card_balance.py measures exactly this and now guards it.
 *
 * The two remaining columns take the full width, and the facts run across the card underneath
 * instead of stacking in a narrow third of it. Nothing is hidden and no placeholder is invented:
 * the same tag renders the same fields in a shape that is dense without a verdict to lean on.
 */




/* Across, not down: on a strip the label/value pairs get as many columns as fit rather than the
   two the narrow verdict column allowed, so six facts occupy one band instead of three rows. */
@media (max-width: 56rem) {
  
}

/* The verdict's own "read the rest" link. Blue and underlined on hover like every other text
   link — never styled as a second button: design-system.md allows the site exactly one primary
   CTA, and this card already carries it in the footer. */
.dl-verdict__more {
  display: inline-block;
  margin-top: var(--space-2);
  font-size: var(--type-small-size);
  font-weight: var(--weight-bold);
  color: var(--blue);
}

/* The crowd block on the fact strip: score, direction and share on one line, since across the
   card there is room for them to sit together rather than stack. `.dl-pcard__ratingdrop` keeps
   its neutral border — a crowd's mood is not a regulatory fact and gets none of the amber/red
   those are reserved for — but drops to inline so it reads as a qualifier on the number beside
   it rather than a callout under it. */

/* --- marked conclusions in the third column ------------------------------------------------
 *
 * Four short lines where a label/value grid needed six rows of two cells. The symbol carries
 * polarity and the text carries the fact, so nothing needs a legend — the failure of the
 * checklist this replaced, which mixed a green tick, a dash and a middle dot with no key.
 *
 * Warn tone is amber INK on no fill: the amber/red backgrounds are reserved for regulatory
 * flags, and "cancellation terms not published" is a gap in what a company discloses, not an
 * action a regulator took against it. Keeping the fills apart is what keeps a real flag loud.
 */
.dl-pcard-col--verdict .dl-marks {
  gap: 4px;
  margin: var(--space-3) 0 0;
}

.dl-marks__item {
  display: grid;
  /* 28px for the icon chip, was 1rem for the glyph it replaced (2026-08-17). The column was
     16px wide and the chip is 28px, so it overflowed its track and printed on top of the axis
     label — visible immediately in a screenshot, invisible in the markup. */
  grid-template-columns: 28px 1fr;
  gap: 12px;
  /* `start`, not `baseline`: a circle has no baseline to share with text, and baseline alignment
     dropped the chip's centre onto the axis line so it sat a third of its height too low. */
  align-items: start;
  line-height: 1.35;
}

/* `.dl-marks__sym` was the glyph in front of each mark — a `✓`, `—` or `•` set at body size. It
   was replaced by an icon chip on 2026-08-17 (score-panel.css) and its rules are gone with it;
   the element no longer exists in any template. */

/* Two columns when no verdict is written. Not a fallback — the common case: a verdict exists
   for four of the twenty-two providers tracked. */
.dl-pcard-cols--2up {
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
}

.dl-pcard-col--id .dl-marks {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: var(--rule-hairline);
  gap: 4px;
}

@media (max-width: 56rem) {
  .dl-pcard-cols--2up {
    grid-template-columns: 1fr;
  }
}

/* Explicit placement. Auto-placement was putting the verdict column on a second grid row on
   cards that have one — measured with tools/card_balance.py: identity and cost at y=71, verdict
   at y=707, i.e. below both instead of beside them, which is what made a card with a verdict
   1,068px tall. Naming each column's track removes the ambiguity rather than relying on source
   order to resolve it. */
.dl-pcard-col--id {
  grid-column: 1;
  grid-row: 1;
}

.dl-pcard-col--cost {
  grid-column: 2;
  grid-row: 1;
}

.dl-pcard-col--verdict {
  grid-column: 3;
  grid-row: 1;
}

@media (max-width: 56rem) {
  .dl-pcard-col--id,
  .dl-pcard-col--cost,
  .dl-pcard-col--verdict {
    grid-column: 1;
    grid-row: auto;
  }
}

/* Card controls on a phone: the provider mark/name link and the "read the full review" link
 * measured 22-34px against the 44px minimum. Padding grows the hit area without moving the
 * text — tools/mobile_check.py holds both. */
@media (max-width: 56rem) {
  .dl-pcard .dl-plogo__link {
    padding-block: 8px;
  }

  .dl-verdict__more,
  .dl-pcard__footer a:not(.dl-btn) {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* What we know about the crowd when there is no publishable score. Body ink, not muted: it is a
   statement about the evidence — eleven reviews, or a rating the platform itself withholds — and
   a reader deciding between two providers needs to read it, not skim past it. */
.dl-pcard__urating-none {
  margin: 0;
  font-size: var(--type-small-size);
  line-height: 1.45;
  color: var(--text-body);
}

/* The corroboration line: how many separate days this record has been written on. Muted, because
   it is a statement about the completeness of our own work rather than about the provider. */
.dl-pcard__readings {
  margin: var(--space-3) 0 0;
  font-size: var(--type-small-size);
  color: var(--text-muted);
}


/* --- the provider price band (2026-08-25, given the homepage hero's own composition 2026-08-26)

 * Retired the 2026-08-15/19 card above (`.dl-review-price`, `.dl-pcard3__*`, `.dl-plan-incl__*` —
 * all deleted with this comment): Codex's composition read named that grid itself as the reason
 * four separate rounds of restyling the figure inside it never worked — three cells sharing one
 * padding and one border cannot make any one of them dominant, whatever size or colour the figure
 * takes (docs/ai/review_price_composition_codex_2026-08-25.md). `includes/_provider_price_band.html`
 * isolates the figure instead, on the one full-bleed dark surface tokens/colors.css already
 * reserved for it and no page had used ("--navy: the one dark full-bleed band").
 *
 * **One column was still not the fix.** The owner: "да главная более менее нормально, а вот это
 * ГОВНОООО" — the homepage hero, one screen away, is accepted; the one-column version of this band
 * was not. Diagnosis: `.dl-homehero` (home-hero.css) is two columns with a photograph behind it;
 * this was a label and a price story on a flat fill, which left roughly two thirds of a 430px-tall
 * rectangle empty at 1440px. Everything below borrows the hero's own three moves — a white card in
 * a second column, the same photograph behind both, hidden below the width its own grid collapses
 * at — rather than inventing a fourth composition for the same brand to hold. See
 * includes/_provider_price_band.html's own comment for the full account. */
/* The provider band, light (2026-10-02). `linear-gradient(115deg, var(--lavender), #f9e3da)` and
 * 40px of vertical padding are the pack's own `.provider-hero` values. Everything inside it that
 * was written for white-on-dark moves to ink below. */
.dl-priceband {
  position: relative;
  overflow: hidden;
  background: linear-gradient(115deg, var(--lavender), #f9e3da);
}

/* The photograph. Same file, same mechanism as `.dl-hero--home__art`/`__art img` (home-hero.css)
 * — masked in from the band's right edge, faded to transparent at its own left edge, luminosity
 * blend so it takes the band's own green rather than sitting on it as a picture. Anchored to the
 * full-bleed section, not `.dl-priceband__inner`, so it runs to the true viewport edge exactly as
 * the hero's does. */
.dl-priceband__art {
  position: absolute;
  inset: 0;
  left: auto;
  width: 42%;
  min-width: 280px;
  -webkit-mask-image: linear-gradient(to right, transparent, black 22%);
  mask-image: linear-gradient(to right, transparent, black 22%);
  pointer-events: none;
}

.dl-priceband__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  mix-blend-mode: luminosity;
  /* **0.14, not the hero's 0.28** (2026-08-26, measured against real renders, not assumed from the
   * hero's own value). Rendered this band at 1440px at both opacities and sampled the actual
   * composited pixels in a clean strip with no text and no card (x700-1040/y330-570 in the
   * screenshot), the same contrast formula `tools/dl_contrast_audit.py` uses:
   *
   *     0.28 (the hero's value)   brightest real pixel rgb(80,112,101)   white-on-that 5.46:1
   *     0.14 (shipped here)       brightest real pixel rgb(48,86,72)     white-on-that 8.23:1
   *
   * Both clear AA's 4.5:1. 5.46:1 is the thinnest margin this project accepts for the page's
   * single most important figure, on a band whose result sits at `--type-hero-size` (up to 64px,
   * larger than the hero's own fixed 52px claim) and closer to the art than the hero's fixed
   * 560px left column keeps its H1. Halved opacity for a real, measured margin instead. */
  opacity: 0.14;
}

/* Hidden below the width this band's own two-column grid collapses at (56rem, immediately below)
 * — not the hero's 1200px, which is where *that* grid collapses and has no reason to match this
 * one's. A narrower band means less room for the art regardless of the card; matching the
 * grid-collapse point is the same reasoning the hero itself uses, applied to this band's own
 * numbers rather than borrowed wholesale. */
@media (max-width: 56rem) {
  .dl-priceband__art {
    display: none;
  }
}

.dl-priceband__inner {
  position: relative;
  z-index: 1;
  /* 40px both ends, the pack's `.provider-hero`. The bottom was 48px, from the era when this band
     was dark and needed room to read as a section rather than a stripe. */
  padding-block: var(--space-10);
}

/* --single: no score and no crowd rating to put in a second column, so there is no second column
 * — "let the column collapse and tighten the band rather than leaving a hole for a card that has
 * nothing to say" (the owner's own framing of the fix). Tighter padding than the two-column case:
 * a lone price story needs less vertical room than a price story sitting beside a card tall enough
 * to hold a score and its methodology link. */
.dl-priceband--single .dl-priceband__inner {
  padding-block: var(--space-8) var(--space-10);
}

.dl-priceband__grid {
  display: grid;
  /* **The pack's own `1.4fr 1fr`** (2026-10-02, measured). The side was capped at 300px, so the
     price receipt rendered 270px wide and its rows wrapped: "Membership / month" broke across two
     lines next to "We could not establish one", which is the defect the owner's screenshot shows.
     The pack's `.provider-hero-inner` gives its receipt panel 513px at 1440 and every row sits on
     one line. A receipt is a two-column document; starving the column is what made it look like
     a list of fragments. */
  grid-template-columns: minmax(0, 1.4fr) minmax(300px, 1fr);
  gap: var(--space-12);
  /* **`start` again** (2026-10-02, measured) — the pack's own `.provider-hero-inner`.
   *
   * `stretch` was correct in August for the reason the note it replaces gives: the side column
   * was the shorter one, so letting it size itself left a card pinned to the top of a taller row.
   * The phase-3 note below already recorded that this inverted when the price receipt landed:
   * the side is now the taller column by 200px or more.
   *
   * Stretching the taller column changes nothing; what it did do was let the *price* column
   * stretch too, and `margin-top: auto` on the support strip then pushed "First year" and
   * "Cancellation" to the foot of a 600px row — 220px of empty band between the figure and the two
   * facts that qualify it. That is what the owner's screenshot shows. Both columns size to their
   * own content now and the facts sit under the figure they belong to. */
  align-items: start;
}

@media (max-width: 56rem) {
  .dl-priceband__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* **The row's height now comes from this column, and that inverted an assumption** (v5 phase 3).
 *
 * The note on the card below says "the row's height comes from the price column", which was true
 * for a year and stopped being true the day the price receipt landed above the card: the side went
 * to 506px against 267px of price, and the dead space the 2026-08-27 pass removed from the card
 * reappeared on the other side of the band.
 *
 * `align-content: start` keeps the two cards their own size and stacked at the top, rather than
 * spreading them down a track neither of them asked for. The price column handles its own half.
 */
.dl-priceband__side {
  display: grid;
  align-content: start;
  gap: var(--space-5);
}

/* The price column spans the row the side column now sets, so its content is placed rather than
 * piled: the figure at the top where a reader starts, its supporting facts at the foot. The
 * alternative is 240px of bare band under the price, which reads as something failing to load. */
.dl-priceband__main {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* **Nothing drops to the foot any more** (2026-10-02). `margin-top: auto` sent the support strip
 * to the bottom of the row, which was right while the price column was the taller of the two and
 * the alternative was bare band beneath it. The receipt inverted that: the row is 600px because
 * the *side* is, so the strip was being pushed 220px below the figure it qualifies. With both
 * columns sized to their own content (see `align-items: start` above) there is no foot to drop to
 * and "First year" sits where it belongs — directly under the monthly figure it should be read
 * against. */

/* The same card component the hero's `.dl-match` already is — white, bordered, `--radius-lg` —
 * not a second white-card recipe invented for one more context. Holds the VerifiedDose score
 * (`dl_score`, size="xl"), the second number a reader wants, which used to sit only in a
 * separate panel further down the page.
 *
 * **The card fits its content. It does not stretch** (2026-08-27).
 *
 * It was stretched to the price column's full height on 2026-08-26 and centred, on the reasoning
 * that leaving the content at the top would move the dead space rather than remove it. That was
 * true and it missed the point: centring emptiness is still emptiness, and the owner's second
 * screenshot was a large white rectangle holding a numeral and a link with a hand's width of
 * nothing around them. Stretching a box to a track it cannot fill is the same defect as the band
 * itself had before it was given a second column.
 *
 * A numeral, its label and one link are a small object. It is sized like one, aligned to the top
 * of the row where the price's own label begins, and the row's height comes from the price column
 * — which is the column that has something to fill it with. */
.dl-priceband__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-self: start;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

.dl-priceband__label {
  margin: 0 0 var(--space-4);
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* Not `--text-on-navy-muted`: that pair (tokens/colors.css) was measured for the old
     blue-family navy. `--priceband-ink-1` is this band's own, re-checked against the actual
     --navy hex — 8.29:1, well past AA even at this size. */
  color: var(--priceband-ink-1);
}

/* The same `_price_story.html` every ranked card, the comparison page and the master table's
 * "not published" branch already render — see includes/_provider_price_band.html's own comment.
 * Everything below is a colour and scale override for the on-navy context; none of it changes
 * what the component says, only how it reads against a dark ground. */
.dl-priceband .dl-pstory {
  margin-top: 0;
}

.dl-priceband .dl-pstory__eq {
  color: var(--priceband-ink-2);
}

.dl-priceband .dl-pstory__eq i {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-pstory__op {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-pstory__rule {
  background: rgba(255, 255, 255, 0.22);
}

/* The figure itself, at the display scale's own top step — "Hero", the same size and weight the
 * homepage's own H1 uses, per the doctrine's fixed ten-step type scale. Nothing here invents a
 * component-specific size: four earlier rounds tried that (42px to 148px) and all four were
 * rejected. Isolation is the change, not a new number. */
.dl-priceband .dl-pstory__result {
  font-size: var(--type-hero-size);
  line-height: var(--type-hero-lh);
  letter-spacing: var(--type-hero-ls);
  /* Ink. This was `--white`, which was the page's largest element and the reader's whole answer —
     and on the light band it rendered invisible (2026-10-02). The reference sets the same figure
     in ink on the same gradient. */
  color: var(--ink);
}

/* The medication-only figure keeps full ink (2026-08-26, owner review: "$99 цена плохо
 * читается... может их выделить цветом"). It used to dim to `--priceband-ink-2` here, which put
 * the page's single largest figure — a reversed, thin-stroked display serif at up to 64px — at
 * 6.68:1 instead of 8.29:1 on the darkest ground on the site, on TelosRx specifically: the
 * partial case *is* the provider the owner was looking at. Colour is not available as a fix
 * (rejected twice — a hue on a price reads as steering) and dimming was never the right
 * substitute for it: `.dl-pstory__result--partial` outside the band already keeps full ink for
 * this exact reason (provider-column.css: "ink alone is not a status signal... the label
 * beneath states the missing part in words"). The band had drifted from its own sibling rule
 * rather than from the doctrine. `.dl-pstory__total` two lines below already states "not
 * published" in words; the figure does not need to apologise for it a second time by going
 * pale. No rule needed here now — it inherits `.dl-priceband .dl-pstory__result`'s ink. */

.dl-priceband .dl-pstory--lg .dl-pstory__result--none {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-pstory__unit {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-pstory__total,
.dl-priceband .dl-pstory__qual,
.dl-priceband .dl-pstory__verified {
  color: var(--priceband-ink-2);
}

.dl-priceband .dl-pstory__total b {
  color: var(--priceband-ink-1);
}

/* A phone tightens regardless of whether there is a card: the stacked composition (price above,
 * card below, once the 56rem tier above has already collapsed the grid) needs less air than the
 * two-column desktop one either way. Same values `--single` uses above — not a second number, the
 * same "less air for less content" call applied on its own trigger. */
@media (max-width: 42rem) {
  .dl-priceband__inner {
    padding-block: var(--space-8) var(--space-10);
  }
}

/* --- the band's foot: the FTC disclosure and the fold's one action ---------------------------
 *
 * **It is inside the price column now, not full width** (2026-10-02) — the pack's own provider
 * hero puts them there. The note this replaces argued the opposite and was right at the time: the
 * price column was the taller of the two, so stacking the foot inside it would have made the
 * row-height mismatch worse. The receipt inverted that, and `align-items: start` on the grid
 * closed the gap it left instead.
 *
 * The `--single` branch still renders the foot outside the grid, which is correct there: with one
 * column the foot IS the column. The rules below apply to both. */

.dl-priceband__foot {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  /* `--border-on-navy`, which the band's own scoped block resolves to `--line` now that the band
     is light. The literal `rgba(255,255,255,.16)` was white-on-dark and rendered as nothing on a
     lavender ground — a rule that was there in the stylesheet and not on the page. The pack draws
     a `.divider` here, which is the same 1px of `--line`. */
  border-top: 1px solid var(--border-on-navy);
}

/* The facts line needs its own bottom margin now that `.dl-pricesupport`'s base rule carries no
 * spacing of its own (see that rule's own comment) — the disclosure below it renders with only
 * `var(--space-2)` of padding, built for sitting directly under a heading, not under a wrapped
 * three-item fact row. */
.dl-priceband__foot .dl-pricesupport {
  margin-bottom: var(--space-5);
}

/* Every plain-text link and focus ring in the foot needs its own ink: the site-wide defaults are
 * both `--blue`, which measures 1.3:1 against `--navy` (tools/dl_contrast_audit.py) — not merely
 * quiet, actually invisible. `--priceband-ink-1` rather than the footer's own
 * `--text-on-navy-muted` for the same reason the label and the arithmetic already use it
 * (colors.css's own header comment): that pair reads as a cool blue-grey next to this band's
 * green figure, and a "How we make money" link is close enough to the price to clash.
 *
 * Scoped to `.dl-priceband__foot`, not the whole band, and `:not(.dl-btn)` within it — the
 * band's other link, the score card's "How we score this" one, sits inside
 * `.dl-priceband__card`, which is white, and a `.dl-priceband a` rule reached it too: the first
 * render of this fix put pale mint text on a white card, under AA. `.dl-btn` (the CTA pair) is
 * excluded for the same reason — those already carry their own explicit colours
 * (button.css: solid `--blue` fill, or white fill with `--accent2` text) and this rule's higher
 * specificity (`.dl-priceband__foot a` beats `.dl-btn--accent2` alone) was overriding them to
 * the same pale mint, on their own white button surface, until it was excluded here. */
.dl-priceband__foot a:not(.dl-btn) {
  color: var(--priceband-ink-1);
  text-decoration: underline;
}

.dl-priceband__foot a:not(.dl-btn):hover,
.dl-priceband__foot a:not(.dl-btn):focus-visible {
  color: var(--ink);
}

.dl-priceband__foot :focus-visible {
  outline-color: var(--focus);
}

/* --- first-year range, cancellation, states served (includes/_provider_price_support.html) --- */

/* A single left-aligned fact line, not three more cards beside the price — the "not three equal
 * cards" the composition review named directly. Same `dl_cost_range` component the master table
 * and the ranked card use for the first-year figure, so it cannot disagree with the same figure
 * anywhere else on the site; cancellation and states served are the same fields
 * `includes/_provider_terms.html` and the review's own "How it works" list already state.
 *
 * Base rule stays the white-page ink it was built for — nothing else calls it on white today,
 * but the pattern this file already uses for `.dl-pstory` is "generic base, `.dl-priceband`
 * override for this ground", not "rewrite the base for its only current caller". */
.dl-pricesupport {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
}

.dl-pricesupport__item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 9rem;
}

.dl-pricesupport__label {
  margin: 0;
  font-size: var(--step-fine);
  font-weight: var(--weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.dl-pricesupport__value {
  margin: 0;
  font-size: var(--type-body-size);
  font-weight: var(--weight-bold);
  color: var(--text-body);
  font-variant-numeric: var(--tabular);
}

.dl-priceband .dl-pricesupport__label {
  color: var(--priceband-ink-2);
}

.dl-priceband .dl-pricesupport__value,
.dl-priceband .dl-yearcost__value {
  color: var(--ink);
}

.dl-priceband .dl-yearcost__note {
  color: var(--priceband-ink-2);
}

.dl-priceband .dl-price__unpublished {
  color: var(--priceband-ink-2);
}

.dl-priceband .dl-bar--range {
  background: rgba(255, 255, 255, 0.18);
}

.dl-priceband .dl-bar__fill--muted {
  background: var(--priceband-ink-1);
}

/* --- the FTC disclosure (includes/_affiliate_disclosure.html), on-navy ----------------------- */

.dl-priceband .dl-disclosure__text,
.dl-priceband .dl-disclosure__label {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-disclosure__none {
  color: var(--priceband-ink-2);
}

/* --- the fold's one action (includes/_provider_actions.html), on-navy ------------------------ */

/* `.dl-btn--primary` is a solid `--blue` fill and needs no override — it carries its own
 * background regardless of the page behind it. `.dl-btn--accent2` (the outlined secondary
 * button) is a solid white fill with coloured border/text, same reasoning. Only the plain-text
 * microcopy around them — none of it a button — inherits the invisible site-wide muted ink. */
.dl-priceband .dl-oncopy,
.dl-priceband .dl-idhead__actions-note,
.dl-priceband .dl-cta-note,
.dl-priceband .dl-no-cta {
  color: var(--priceband-ink-2);
}

/* The verdict band under the review fold (2026-08-18): full measure, a rule above, capped line
   length. Not a card — it is the page's argument, not another tile.
 *
 * `--band` suffix because `.dl-verdict` alone is the 2px-bordered box above, which info_page.html
 * still uses on purpose. This wrapper was briefly named `.dl-verdict` and inherited that frame,
 * putting the review's verdict in a heavy black box against this file's own stated intent. */
.dl-verdict-band {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.dl-verdict-band .dl-verdict-lede {
  max-width: 62rem;
}

/* The deck: the review's answer-first opening, rendered from 2026-08-24 after seventeen of them
   had been stored and never shown. It sits above the verdict in the same band, so it takes the
   band's measure and the body step rather than the lede's — the verdict is the emphatic line of
   the two and keeping it a step larger is what makes the order readable. Bottom margin only:
   `.dl-verdict-lede` already carries its own. */
.dl-verdict-band .dl-verdict-deck {
  margin: 0 0 var(--space-3);
  font-size: var(--step-body);
  line-height: 1.65;
  color: var(--text-2);
  max-width: 62rem;
}

/* --- the fold's actions, inverted for the band they now sit on --------------------------------
 *
 * `--blue` (#12503A) filled on `--navy` (#0F3D2C) measures **1.30 : 1**. The primary action —
 * the one thing on the page a reader is meant to press — had no visible shape at all; only its
 * white label showed, floating. The owner saw it before any check did: "кнопка блядь зеленая на
 * зеленом" (2026-08-27).
 *
 * It is the same 1.30 : 1 that was found and fixed for this band's link *text* a day earlier. The
 * button's *fill* was never measured, because `dl_contrast_audit.py` checks ink against surfaces
 * and a filled button is a surface — so the audit had nothing to say about a surface sitting on
 * another surface. That gap is worth more than this fix: it is why the defect survived a pass
 * whose whole subject was contrast on this band.
 *
 * Inverted, which is the only move available without a new token: white fill, navy ink, 12.18 : 1
 * both directions. The secondary then cannot also be white-filled or the two become one object,
 * so on the band it becomes the outlined ghost it already is everywhere else — white border,
 * white ink, transparent fill.
 *
 * Scoped to the band. On the white page below, both buttons keep exactly the styling they have
 * always had; nothing here reaches them. */
.dl-priceband__foot .dl-btn--primary,
/* The pack's own pair on a light band: a plum fill and an outlined control beside it. These were
   a white fill and a white outline, which is what a primary action looks like *on* ink — on the
   gradient the fill vanished into it and the outline drew nothing at all. */
.dl-priceband__foot .dl-btn--accent {
  background: var(--plum);
  border-color: var(--plum);
  color: var(--white);
}

.dl-priceband__foot .dl-btn--primary:hover,
.dl-priceband__foot .dl-btn--accent:hover {
  background: var(--band);
  border-color: var(--band);
  color: var(--navy);
}

.dl-priceband__foot .dl-btn--accent2 {
  background: transparent;
  border-color: var(--control);
  color: var(--ink);
}

.dl-priceband__foot .dl-btn--accent2:hover {
  background: var(--lavender);
  border-color: var(--white);
  color: var(--white);
}


/* --- UnpricedLead: the fold with no publishable all-in price -----------------------------
   Replaces a 92px "Not published" (2026-08-28, see _price_story_unpriced.html). Two rules govern
   every value below:

   1. **One step down from the anchor, never at it.** `.dl-pstory__result` is --step-price (42px)
      in a card and --step-hero (64px, 92px effective in the band) at --lg. None of these figures
      is an all-in total: an advertised figure has no stated billing term and a membership fee is
      not the total. So the figure here tops out at --step-price even in the band, and it uses the
      muted advertised ink the master table has used for the same class of figure since
      2026-08-21 — never the anchor's full-strength treatment.
   2. **The "none" shape carries no figure at all**, so it gets sentence type (--step-section),
      not a slot with a negation in it. An absence does not earn a figure's weight. */
.dl-unpriced {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* **The last two serif leaks from the old pair** (v5, 2026-10-01).
 *
 * These asked for `--font-serif` when that meant Instrument Serif, a display face with one weight,
 * and the whole unpriced fold was drawn in it to hold the same shape as the priced one. Phase 1
 * aliased `--font-serif` onto Fraunces — which ships italic 500 only and exists to set a single
 * accented word inside a headline. So this rendered every unpriced provider's figure as an italic
 * display serif at a weight the file does not contain, which the browser then synthesised.
 *
 * Ro's comparison card was the visible case: "$149/mo membership" in slanted serif beside Hims's
 * "$298" in the grotesk. Two providers' money in two different typefaces on one comparison, where
 * the entire point of the page is that both sides answer the same questions the same way.
 *
 * The grotesk at 600, which is the treatment every other uncomparable figure on the site already
 * has: present, legible, and deliberately not the anchor weight a verified all-in price gets. */
.dl-unpriced__figure {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-metric);
  line-height: 1.1;
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
  font-variant-numeric: var(--tabular);
  color: var(--text-2);
}

.dl-unpriced--lg .dl-unpriced__figure {
  font-size: var(--step-price);
}

.dl-unpriced__unit,
.dl-unpriced__tag {
  font-family: var(--font-sans);
  font-size: var(--step-small);
  font-weight: var(--weight-medium);
  color: var(--text-2);
}

/* The word that keeps the figure from being read as a price: it sits on the baseline beside it,
   not above it as a superscript, because a reader who skips it has misread the number. */
.dl-unpriced__tag {
  margin-left: var(--space-2);
  text-transform: lowercase;
  letter-spacing: 0.01em;
}

.dl-unpriced__sentence {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--step-title);
  line-height: 1.25;
  font-weight: var(--weight-black);
  letter-spacing: -0.01em;
  color: var(--text);
}

.dl-unpriced--lg .dl-unpriced__sentence {
  font-size: var(--step-section);
}

.dl-unpriced__what,
.dl-unpriced__fee {
  margin: 0;
  font-size: var(--step-small);
  color: var(--text);
}

.dl-unpriced__note,
.dl-unpriced__cond,
.dl-unpriced__more {
  margin: 0;
  font-size: var(--step-small);
  line-height: 1.45;
  color: var(--text-2);
}

.dl-unpriced__verified,
.dl-unpriced__date {
  margin: 0;
  font-size: var(--step-micro);
  font-weight: var(--weight-medium);
  color: var(--text-2);
}

.dl-unpriced__date {
  margin-left: var(--space-1);
}

/* On the dark band every one of these needs its on-navy counterpart: --text-2 measures 1.4:1 on
   --navy (tools/dl_contrast_audit.py), which is the same class of defect as the green-on-green
   button — legible nowhere. The band's own two inks are already measured for this surface
   (8.29:1 and 6.68:1), so they are reused rather than a third pair invented. */
.dl-priceband .dl-unpriced__figure,
.dl-priceband .dl-unpriced__sentence {
  color: var(--ink);
}

.dl-priceband .dl-unpriced__what,
.dl-priceband .dl-unpriced__fee {
  color: var(--priceband-ink-1);
}

.dl-priceband .dl-unpriced__unit,
.dl-priceband .dl-unpriced__tag,
.dl-priceband .dl-unpriced__note,
.dl-priceband .dl-unpriced__cond,
.dl-priceband .dl-unpriced__more,
.dl-priceband .dl-unpriced__verified,
.dl-priceband .dl-unpriced__date {
  color: var(--priceband-ink-2);
}


/* The foot with no fact strip above the buttons (2026-08-28). Same rule, less air: --space-6 was
   the space a three-column strip needed, and with the strip gone it read as a gap the layout had
   failed to fill — the same "unpainted background" defect the band itself was rebuilt to fix. */
.dl-priceband__foot--tight {
  padding-top: var(--space-4);
}

/* The 404 page (2026-10-05). Production served 179 bytes of unstyled "Not Found" from the first
   deploy; `templates/404.html` now extends the site, and this is the little it needs beyond what
   the shared chrome already provides. No component of its own: an error page that grows a design
   system is an error page somebody is spending time on. */
.dl-notfound {
  padding-block: var(--space-8) var(--space-9);
  max-width: 44rem;
}

.dl-notfound__lead {
  font-size: var(--step-lead);
  line-height: 1.5;
  color: var(--text-muted);
  margin-top: var(--space-4);
}

.dl-notfound__links {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dl-notfound__links a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  font-weight: var(--weight-semibold);
}

.dl-notfound__note {
  margin-top: var(--space-6);
  font-size: var(--step-small);
  color: var(--text-muted);
}

/* --- css/components/dose-pricing.css --- */
/* ==========================================================================
   DosePricing — includes/_dose_pricing.html, built by dl_dose_pricing(). "Pricing by dose": a
   horizontal bar per plan, scaled to the provider's own costliest one — the graphic the owner's
   Wellmedr screenshot named directly: "разные квадраты в которых много пустоты, надо добавить
   элемент выделенный цветом" (2026-08-18).

   Reuses the score panel's own `.dl-bar`/`.dl-bar__fill`/`.dl-bar--NN` mechanics (score-panel.css)
   rather than a second width scale — one "bar filled to a step-5 fraction" vocabulary site-wide.
   Only the fill colour is scoped here: this bar's subject is money, and --blue is this site's one
   colour for that, where the score panel's own bar is a rubric weight and keeps --accent2.
   ========================================================================== */

.dl-dosechart {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.dl-dosechart__label {
  margin: 0 0 var(--space-3);
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.dl-dosechart__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dl-dosechart__row {
  display: grid;
  grid-template-columns: minmax(6.5rem, auto) minmax(0, 1fr) 4.5rem;
  align-items: center;
  gap: var(--space-3);
}

.dl-dosechart__dose {
  font-size: var(--step-small);
  color: var(--text-body);
  min-width: 0;
  overflow-wrap: break-word;
}

.dl-dosechart__track {
  height: 10px;
  background: var(--gray-bg);
}

.dl-dosechart__fill {
  background: var(--blue);
}

.dl-dosechart__price {
  font-size: var(--step-small);
  font-weight: var(--weight-bold);
  font-variant-numeric: var(--tabular);
  text-align: right;
  color: var(--text-body);
}

.dl-dosechart__verified {
  margin: var(--space-3) 0 0;
  font-size: var(--type-micro-size);
  color: var(--verified-ink);
  font-variant-numeric: var(--tabular);
}

/* A dose label and a bar with its price share one line down to a narrow phone; below that the
   label's own text (a plan name plus its published dose range) started colliding with the bar,
   so the bar and its price move to their own row underneath instead of shrinking further. */
@media (max-width: 30rem) {
  .dl-dosechart__row {
    grid-template-columns: minmax(0, 1fr) 4rem;
    grid-template-areas:
      "dose price"
      "track track";
    row-gap: 4px;
  }

  .dl-dosechart__dose {
    grid-area: dose;
  }

  .dl-dosechart__price {
    grid-area: price;
  }

  .dl-dosechart__track {
    grid-area: track;
  }
}


/* The supply qualifier under the molecule: "Compounded" / "Brand name". Quiet, because the
   molecule is the row's subject and this is what distinguishes two rows that share it — the
   $59 compounded semaglutide from the $1,399 branded one. */
.dl-dosechart__supply {
  display: block;
  font-size: var(--step-micro);
  color: var(--text-muted);
}

/* --- css/components/steps.css --- */
/* ==========================================================================
   Steps — the numbered timeline on a switching guide, per
   docs/ui_refs_v3/switching-template.html's own `.steps` (navy circle, connecting
   line). Ported as pure CSS against a *bare* `<ol>`/`<li>` rather than styled
   directly on classed elements: `SwitchingStep.text` is one CharField ("one action
   the reader takes today"), not the mockup's own title+description pair, so each
   `<li>` in this codebase holds nothing but that one sentence — no nested heading,
   no wrapping span — and apps/content/tests/test_v3_page_types.py's
   `test_steps_render_in_order_as_an_ordered_list` checks exactly that shape (a plain
   `<ol>` of plain `<li>` elements, text only). The number and the connecting rule
   are therefore drawn with `::before`/`::after` against a wrapping `.dl-steps`
   container instead of a class on the list itself.
   ========================================================================== */

.dl-steps {
  max-width: 42rem;
  margin: var(--space-4) 0 var(--space-2);
}

.dl-steps ol {
  counter-reset: dl-step;
  list-style: none;
  margin: 0;
  padding: 0;
}

.dl-steps li {
  counter-increment: dl-step;
  position: relative;
  padding: 2px 0 var(--space-6) 52px;
  font-size: var(--type-body-size);
  line-height: 1.6;
  color: var(--text-body);
}

.dl-steps li::before {
  content: counter(dl-step);
  position: absolute;
  left: 0;
  top: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--navy);
  color: var(--white);
  font-weight: var(--weight-black);
  font-size: var(--step-body);
  display: flex;
  align-items: center;
  justify-content: center;
}

.dl-steps li::after {
  content: "";
  position: absolute;
  left: 16px;
  top: 38px;
  bottom: 2px;
  width: 2px;
  background: var(--border);
}

.dl-steps li:last-child {
  padding-bottom: 0;
}

.dl-steps li:last-child::after {
  display: none;
}

/* --- css/components/alert.css --- */
/* ==========================================================================
   Trigger alert — the switching guide's opening statement of the concrete event
   that sends a reader here (`SwitchingPage.trigger`).

   Same red, left-border language as `.dl-status-banner--dead` (status.css) rather
   than a new filled red box: this page's entire subject is a shutdown-class event,
   so it reuses that established colour role instead of inventing a second one.

   It never names a specific provider. `SwitchingPage.get_context` supplies only
   active providers on purpose — see the template's own comment — so this alert
   states the category of event the guide is for, not a claim about which company
   is affected today.
   ========================================================================== */

.dl-trigger-alert {
  border-left: 3px solid var(--red);
  padding: var(--space-3) 0 var(--space-3) var(--space-5);
  margin: var(--space-6) 0 0;
  max-width: 46rem;
}

.dl-trigger-alert__label {
  display: block;
  margin-bottom: 4px;
  font-size: var(--step-micro);
  font-weight: var(--weight-bold);
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.dl-trigger-alert__text {
  margin: 0;
  font-size: var(--step-lede);
  font-weight: var(--weight-bold);
  line-height: 1.4;
  color: var(--text-body);
}
