/* ═══════════════════════════════════════════════════════════════════════════
   DISPATCH — structure MOSAIC
   Phase 23c.13 skeleton: THE CHROME ONLY.

   One 12-column grid holds the page. THERE IS NO MAIN COLUMN AND NO SIDEBAR —
   sections are grid BANDS of tiles whose spans vary from 12 down to 2,
   separated by 1px navy rules rather than stacked as blocks. That absence of a
   main column is what separates dispatch from magazine, which keeps a central
   column with full-width breakouts.

   Site mark .......... A SPLIT BOXED PAIR: the first initial reversed out of a
                        solid navy field, the second on white in an outlined box
                        flush against it, sharing the edge. One tile, half
                        filled and half drawn. No gap, no rounded corners
                        (§7.1/§7.1.1/§4.3.6, decided by Khalaf before the build).
   Newsreader headlines + Public Sans body.
   Paper #FFFFFF / ink #101418 / navy #1B3A6B. Class prefix: dp-.

   HOW THIS FILE IS WRITTEN — the lessons the five built themes paid for:
   - Shared-partial class names are read from the partials themselves, not
     assumed to be BEM (§3.2): .news-ticker__list, .newsletter-form,
     .search-overlay__input, .nav-drawer__link.
   - State classes are COMPOUND selectors (§5.1): .search-overlay.is-hidden and
     .nav-drawer.is-hidden. A bare .is-hidden ties on specificity with the
     element's own display declaration and loses on source order — that is what
     left ledger's overlay painted open on load.
   - MOBILE-FIRST media queries (clarion's lesson): the narrow layout is the
     base and breakpoints add to it. Writing the wide layout first forces an
     order/undo reset at narrow widths, and undoing a rule is what creates the
     pressure to reach for !important.
   - THE MARK'S CUSTOM PROPERTIES ARE DECLARED BESIDE EACH MARK SIZE, NOT ON
     :root. Register declared --rg-cap on :root, where it resolved once against
     the root's mark size, and the footer silently inherited header-sized
     line-height and insets while both the CSS and the spec claimed one calc
     chain. Each mark rule here declares --dp-mark-size and --dp-cap together.
   - ONE rule set: no rule written then overridden later, no patch blocks, and
     no forced-priority declarations.
   - `picture { display: contents }` is REQUIRED by
     themes/_shared/partials/responsive_image.html.jinja so the wrapper does not
     disturb the <img> layout.
   - No dark mode. Dispatch is a light theme; §4.3 #34 specifies a light footer.

   Colours are custom properties on :root so the §15 per-site override
   mechanism can reach them without editing this file.

   This file covers the CHROME ONLY. The tile bands, the module overrides, the
   article variants and the category page are later prompts.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────────────────
   1. Design tokens
   ─────────────────────────────────────────────────────────────────────────── */

:root {
    /* Ink */
    --ink: #101418;
    --ink-soft: #37424D;
    --ink-mute: #6B7683;

    /* Paper */
    --paper: #FFFFFF;
    --paper-soft: #F7F9FB;
    --paper-deep: #EDF1F5;

    /* Accent — navy */
    --accent: #1B3A6B;
    --accent-soft: #3E639C;
    --accent-deep: #12284A;
    --accent-tint: rgba(27, 58, 107, 0.07);

    /* Rules */
    --rule: #D4DCE5;
    --rule-soft: #E6EBF1;

    /* Type */
    --font-headline: 'Newsreader', Georgia, 'Times New Roman', serif;
    --font-body: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    --font-ui: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    --font-mono: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

    /* Scale (§4.3.4) */
    --size-display: clamp(34px, 4.4vw, 58px);
    --size-h1: clamp(34px, 4vw, 54px);
    --size-h2: 12px;
    --size-h3: 18px;
    --size-body: 16px;
    --size-meta: 12px;
    --lh-tight: 1.05;
    --lh-head: 1.2;
    --lh-body: 1.55;

    /* Layout (§4.3.1) — the grid IS the page */
    --max-width: 1440px;
    --measure: 70ch;
    --gutter: 16px;
    --columns: 12;

    /* Vertical rhythm */
    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 16px;
    --space-lg: 28px;
    --space-xl: 44px;

    /* THE MARK'S ONE PINNED CONSTANT.
       The outline weight is pinned, never scaled: clarion's rule was specified
       at 0.035 L and produced a 0.63px hairline at footer size, and an outlined
       half whose border thins stops reading as "drawn" at all.

       --dp-cap is deliberately NOT declared here. See the header note: register
       put its equivalent on :root, it resolved once against the root's mark
       size, and the footer inherited header-sized values. Each mark rule below
       declares --dp-mark-size and --dp-cap TOGETHER so every scope derives its
       own. */
    --dp-mark-rule: 2px;

    /* Newsreader's OWN cap-height in em, measured by rendering the vendored
       Newsreader[opsz,wght].ttf at weight 700 — 0.680 at 100px, 0.670 at 200px.
       Not chosen by eye. One source for the ratio; the mark rules do the rest. */
    --dp-cap-ratio: 0.680;
}

/* ───────────────────────────────────────────────────────────────────────────
   2. Reset and base
   ─────────────────────────────────────────────────────────────────────────── */

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

html {
    -webkit-text-size-adjust: 100%;
}

body.dp-body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: var(--size-body);
    line-height: var(--lh-body);
    -webkit-font-smoothing: antialiased;
}

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

/* Required by _shared/partials/responsive_image.html.jinja: the <picture>
   wrapper must not become a layout box of its own. */
picture {
    display: contents;
}

a {
    color: inherit;
}

h1, h2, h3, h4 {
    margin: 0;
    font-family: var(--font-headline);
    font-weight: 700;
    line-height: var(--lh-head);
}

ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Shared partials emit .visually-hidden for screen-reader-only text. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ───────────────────────────────────────────────────────────────────────────
   3. THE GRID — declared once, reused by every strip and band (§4.3.1)
   MOBILE-FIRST: one column at the base, 12 columns from 861px. Nothing below
   undoes this; the breakpoint only adds columns.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--gutter);
    width: 100%;
    max-width: var(--max-width);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

/* Span utilities. At the base every tile is full width; the 861px breakpoint
   turns them into real column spans. They are declared here so the markup can
   name its spans exactly as §4.3.2 does. */
.dp-span-2,
.dp-span-3,
.dp-span-4,
.dp-span-5,
.dp-span-6,
.dp-span-7,
.dp-span-8,
.dp-span-9,
.dp-span-10,
.dp-span-12 {
    grid-column: 1 / -1;
    min-width: 0;
}

/* ───────────────────────────────────────────────────────────────────────────
   4. Skip link (7A #1)
   ─────────────────────────────────────────────────────────────────────────── */

.dp-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: var(--space-sm) var(--space-md);
    background: var(--accent);
    color: var(--paper);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    font-weight: 700;
    text-decoration: none;
}

.dp-skip-link:focus {
    left: 0;
}

/* ───────────────────────────────────────────────────────────────────────────
   5. Top bar (7A #2–#6, #11, #12) — §4.3.2 #1
   A GRID ROW, not a flex strip: meta spans 7, actions span 5.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-topbar {
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
}

.dp-topbar__inner {
    align-items: center;
    padding-top: var(--space-xs);
    padding-bottom: var(--space-xs);
}

.dp-topbar__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-sm);
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.dp-topbar__rule {
    width: 1px;
    height: 11px;
    background: var(--rule);
}

.dp-topbar__edition,
.dp-topbar__kicker {
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.dp-topbar__kicker {
    color: var(--accent);
}

.dp-topbar__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-md);
}

.dp-topbar__links {
    display: flex;
    align-items: center;
    gap: var(--space-md);
}

.dp-topbar__link {
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    text-decoration: none;
}

.dp-topbar__link:hover,
.dp-topbar__link:focus {
    color: var(--accent);
}

.dp-topbar__social {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.dp-topbar__social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    color: var(--ink-soft);
    text-decoration: none;
}

.dp-topbar__social-link:hover,
.dp-topbar__social-link:focus {
    color: var(--accent);
}

/* ───────────────────────────────────────────────────────────────────────────
   6. Masthead (7A #7–#10) — §4.3.2 #2
   Left-aligned Newsreader wordmark with a FULL-WIDTH NAVY RULE under it.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-mast {
    background: var(--paper);
    border-bottom: 3px solid var(--accent);
}

.dp-mast__inner {
    align-items: end;
    padding-top: var(--space-lg);
    padding-bottom: var(--space-md);
}

.dp-mast__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-md);
    text-decoration: none;
}

/* ── THE SPLIT BOXED PAIR, header size ───────────────────────────────────
   ONE TILE, HALF FILLED AND HALF DRAWN. The two halves are the same size and
   sit flush — `gap: 0` is the point of the form, so it is stated rather than
   left to default. No radius: the corners are square because a tile's are.

   --dp-mark-size and --dp-cap are declared TOGETHER, here, in the same rule.
   A --dp-cap on :root would resolve there against the root's mark size and the
   footer would inherit header-sized values — which is exactly what shipped in
   register before it was caught from computed styles.

   Each half is sized from the measured cap-height: a square whose side is the
   cap plus symmetric padding, so the pair is twice as wide as it is tall and
   reads as two tiles of equal span. */
.dp-mast__mark {
    --dp-mark-size: 30px;
    --dp-cap: calc(var(--dp-mark-size) * var(--dp-cap-ratio));
    display: inline-flex;
    align-items: stretch;
    gap: 0;
    font-family: var(--font-headline);
    font-weight: 700;
    font-size: var(--dp-mark-size);
    line-height: 1;
}

/* BOTH HALVES ARE A FIXED WIDTH, NOT A MIN-WIDTH, and that is a correction
   French caught.

   The first draft sized each half with `min-width` plus horizontal padding, so
   the box grew to fit whatever glyph sat in it. English initials "AC" are of
   similar width and both halves measured 43.9px, which looked correct. French
   initials "IM" are not: the narrow "I" sat at the floor while the wide "M"
   pushed its half to 52.3px, so the mark was 43.9 + 52.3 — visibly lopsided,
   and the spec's claim that the halves are "the same size" was false in French.

   A fixed width derived from the cap-height makes the pair identical for any
   letters, and the glyph centres inside it. box-sizing is border-box globally,
   so the declared width is the final box width, borders included.

   The filled half carries a border in ITS OWN navy — invisible against its
   fill — so both halves share one box model and come out the same height. That
   also keeps the ONE-LETTER mark the same height as a two-letter one, which the
   first draft did not: the lone fill measured 55.6px against the pair's 59.6px,
   because only the outlined half contributed border height. */
.dp-mark__fill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(var(--dp-cap) * 1.85);
    padding: calc(var(--dp-cap) * 0.34) 0;
    border: var(--dp-mark-rule) solid var(--accent);
    background: var(--accent);
    color: var(--paper);
    border-radius: 0;
}

/* The DRAWN half — second initial on white inside an outlined box, flush
   against the filled half and SHARING ITS EDGE. The left border is omitted
   precisely because the filled block supplies that edge: drawing both would
   put a navy line on top of a navy block and thicken the join. */
.dp-mark__outline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(var(--dp-cap) * 1.85);
    padding: calc(var(--dp-cap) * 0.34) 0;
    border: var(--dp-mark-rule) solid var(--accent);
    border-left: 0;
    background: var(--paper);
    color: var(--accent);
    border-radius: 0;
}

.dp-mast__wordmark {
    font-family: var(--font-headline);
    font-weight: 700;
    font-size: clamp(30px, 4.2vw, 50px);
    line-height: var(--lh-tight);
    letter-spacing: -0.015em;
}

.dp-mast__tagline {
    margin: 0;
    color: var(--ink-mute);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* ───────────────────────────────────────────────────────────────────────────
   7. Nav (7A #13–#16) — §4.3.2 #3
   A sticky row of CATEGORY CHIPS: 1px navy border, navy text. A chip is a tile.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-nav {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
}

.dp-nav__inner {
    align-items: center;
    padding-top: var(--space-sm);
    padding-bottom: var(--space-sm);
}

.dp-nav__toggle {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 34px;
    height: 34px;
    padding: 0 6px;
    border: 1px solid var(--accent);
    background: transparent;
    cursor: pointer;
}

.dp-nav__toggle span {
    display: block;
    height: 2px;
    background: var(--accent);
}

/* Mobile-first: the chips are the drawer's job below the breakpoint. */
.dp-nav__chips {
    display: none;
}

.dp-chip {
    display: inline-block;
    padding: var(--space-xs) var(--space-sm);
    border: 1px solid var(--accent);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
}

.dp-chip:hover,
.dp-chip:focus {
    background: var(--accent-tint);
}

.dp-chip.is-active {
    background: var(--accent);
    color: var(--paper);
    font-weight: 700;
}

.dp-nav__search {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    padding: var(--space-xs) var(--space-sm);
    border: 1px solid var(--accent);
    background: transparent;
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 13px;
    cursor: pointer;
}

.dp-nav__search:hover,
.dp-nav__search:focus {
    background: var(--accent-tint);
}

.dp-nav__search-icon {
    font-size: 16px;
    line-height: 1;
}

/* ───────────────────────────────────────────────────────────────────────────
   8. Bands (7A #17, #18 and the newsletter) — §4.3.2 #5, #6, #33
   A band is a grid row whose tile spans 12. Bands are separated by rules, not
   by tint changes — that is courier's mechanism.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-band {
    border-bottom: 1px solid var(--rule);
}

.dp-band__inner {
    padding-top: var(--space-sm);
    padding-bottom: var(--space-sm);
}

.dp-band--stocks {
    background: var(--paper-soft);
}

.dp-band--newsletter {
    background: var(--accent-tint);
    border-top: 3px solid var(--accent);
}

.dp-band--newsletter .dp-band__inner {
    padding-top: var(--space-xl);
    padding-bottom: var(--space-xl);
}

/* Breaking news ticker — §3.2 exact names from _shared/partials/ticker.html.jinja */
.news-ticker {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    width: 100%;
    overflow: hidden;
}

.news-ticker__label {
    flex: 0 0 auto;
    padding: 3px var(--space-sm);
    background: var(--accent);
    color: var(--paper);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.news-ticker__track {
    flex: 1 1 auto;
    overflow: hidden;
}

.news-ticker__list {
    display: flex;
    align-items: center;
    gap: var(--space-xl);
    white-space: nowrap;
}

.news-ticker__item {
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.news-ticker__link {
    color: var(--ink-soft);
    text-decoration: none;
}

.news-ticker__link:hover,
.news-ticker__link:focus {
    color: var(--accent);
}

/* Stock ticker — §3.2 exact names from _shared/partials/stock_ticker.html.jinja */
.stock-ticker {
    /* The mount is empty in the HTML: stock_ticker.js injects the track and items at
       runtime, and it deliberately renders the twelve items twice so the marquee can
       wrap seamlessly. That only reads as one line if this box clips it. */
    --ticker-up: #12694A;
    --ticker-down: #A62A20;
    position: relative;
    overflow: hidden;
    width: 100%;
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    font-variant-numeric: tabular-nums;
}

.stock-ticker-track {
    display: inline-flex;
    flex-wrap: nowrap;
    white-space: nowrap;
    animation: stock-ticker-scroll 40s linear infinite;
}

.stock-ticker:hover .stock-ticker-track {
    animation-play-state: paused;
}

.stock-ticker-item {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-xs);
    padding: 0 var(--space-lg);
    color: var(--ink-soft);
}

.stock-ticker-name {
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink);
}

.stock-ticker-price {
    font-weight: 500;
}

.stock-ticker-change.up {
    color: var(--ticker-up);
    font-weight: 600;
}

.stock-ticker-change.down {
    color: var(--ticker-down);
    font-weight: 600;
}

.stock-ticker-updated {
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    height: 100%;
    padding: 0 var(--space-sm);
    background: var(--paper-soft, #F7F9FB);
    border-left: 1px solid var(--rule);
    color: var(--ink-mute);
    font-size: 10px;
    pointer-events: none;
}

@keyframes stock-ticker-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
    /* No scrolling, so the row has to be reachable by hand instead: the track becomes a
       normal flex row the reader can swipe, rather than a clipped smear. */
    .stock-ticker {
        overflow-x: auto;
    }

    .stock-ticker-track {
        animation: none;
    }

    .stock-ticker-updated {
        position: static;
        height: auto;
        border-left: 0;
    }
}
/* ───────────────────────────────────────────────────────────────────────────
   9. Main
   ─────────────────────────────────────────────────────────────────────────── */

.dp-main {
    display: block;
    padding-bottom: var(--space-xl);
}

/* ───────────────────────────────────────────────────────────────────────────
   10. Newsletter (7B #1) — §3.2 exact names: the partial emits .newsletter-form,
   NOT .newsletter__form. Herald lost ~200 lines of CSS to that assumption.
   ─────────────────────────────────────────────────────────────────────────── */

.newsletter-inner {
    max-width: 620px;
    margin: 0 auto;
    text-align: center;
}

.newsletter-heading {
    font-family: var(--font-headline);
    font-size: 30px;
    font-weight: 700;
    line-height: var(--lh-head);
}

.newsletter-subline {
    margin: var(--space-sm) 0 var(--space-md);
    color: var(--ink-soft);
    font-size: 15px;
}

.newsletter-form {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-sm);
}

.newsletter-label {
    flex: 0 0 100%;
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.newsletter-input {
    flex: 1 1 260px;
    min-width: 0;
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--accent);
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 15px;
}

.newsletter-input:focus {
    border-color: var(--accent-deep);
    outline: none;
}

.newsletter-button {
    flex: 0 0 auto;
    padding: var(--space-sm) var(--space-lg);
    border: 0;
    background: var(--accent);
    color: var(--paper);
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.04em;
    cursor: pointer;
}

.newsletter-button:hover,
.newsletter-button:focus {
    background: var(--accent-deep);
}

.newsletter-status {
    flex: 0 0 100%;
    color: var(--ink-soft);
    font-size: var(--size-meta);
}

/* ───────────────────────────────────────────────────────────────────────────
   11. Footer (7B #2–#11) — §4.3.2 #34
   A 12-column grid of 4 column groups on a pale grey-blue tint, navy rules.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-footer {
    background: var(--paper-deep);
    border-top: 3px solid var(--accent);
}

.dp-footer__inner {
    padding-top: var(--space-lg);
    padding-bottom: var(--space-lg);
}

.dp-footer__banner {
    margin-bottom: var(--space-md);
}

.dp-footer__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    text-decoration: none;
}

/* ── THE SPLIT BOXED PAIR, footer size ───────────────────────────────────
   THE FORM IS KEPT, not changed. A filled block beside an outlined block is
   legible small, so unlike courier — whose dropped initial needs two lines to
   drop into — there is no reason to change treatment here.

   --dp-mark-size and --dp-cap are declared TOGETHER again, in this rule, so the
   footer derives its own values. That is the whole point of not putting
   --dp-cap on :root. The OUTLINE STAYS PINNED at --dp-mark-rule: only the
   letters and the padding scale. */
.dp-footer__mark {
    --dp-mark-size: 19px;
    --dp-cap: calc(var(--dp-mark-size) * var(--dp-cap-ratio));
    display: inline-flex;
    align-items: stretch;
    gap: 0;
    font-family: var(--font-headline);
    font-weight: 700;
    font-size: var(--dp-mark-size);
    line-height: 1;
}

.dp-footer__wordmark {
    font-family: var(--font-headline);
    font-size: 20px;
    font-weight: 700;
    line-height: var(--lh-tight);
}

.dp-footer__tagline {
    margin: var(--space-sm) 0 0;
    color: var(--ink-mute);
    font-size: var(--size-meta);
}

.dp-footer__heading {
    margin-bottom: var(--space-sm);
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--accent);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: var(--size-h2);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.dp-footer__heading--spaced {
    margin-top: var(--space-md);
}

.dp-footer__links li {
    margin-bottom: var(--space-xs);
}

.dp-footer__links a,
.dp-footer__social-link {
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 14px;
    text-decoration: none;
}

.dp-footer__links a:hover,
.dp-footer__links a:focus,
.dp-footer__social-link:hover,
.dp-footer__social-link:focus {
    color: var(--accent);
}

.dp-footer__bottom {
    margin-top: var(--space-lg);
    padding-top: var(--space-md);
    border-top: 1px solid var(--accent);
    color: var(--ink-mute);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.dp-footer__copy,
.dp-footer__meta {
    margin: 0 0 var(--space-xs);
}

/* ───────────────────────────────────────────────────────────────────────────
   12. Ad slots — §3.2 exact names from _shared/partials/ad_slot.html.jinja
   ─────────────────────────────────────────────────────────────────────────── */

.ad-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    min-height: 90px;
    padding: var(--space-md);
    background: var(--paper-soft);
    border: 1px solid var(--rule);
    text-align: center;
}

.ad-slot__label {
    color: var(--ink-mute);
    font-family: var(--font-ui);
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.ad-slot__video {
    max-width: 100%;
}

.ad-slot--house {
    background: var(--accent-tint);
    border-color: var(--accent-soft);
}

.house-ad {
    font-family: var(--font-ui);
    font-size: 14px;
}

/* ───────────────────────────────────────────────────────────────────────────
   13. Search overlay and nav drawer
   §5.1 — THE HIDDEN STATE IS A COMPOUND SELECTOR, from the first line.
   A bare `.is-hidden { display: none }` has specificity (0,1,0), exactly the
   same as `.search-overlay { display: flex }`, and loses on source order. That
   defect shipped in ledger and left the overlay painted open on load.
   §3.2 exact names read from the partials themselves.
   ─────────────────────────────────────────────────────────────────────────── */

.search-overlay {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 12vh var(--gutter) 0;
    background: rgba(16, 20, 24, 0.94);
}

.search-overlay.is-hidden {
    display: none;
}

.search-overlay__inner {
    width: 100%;
    max-width: 620px;
}

.search-overlay__form {
    display: flex;
    gap: var(--space-sm);
}

.search-overlay__label {
    display: block;
    margin-bottom: var(--space-sm);
    color: var(--paper);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.search-overlay__input {
    flex: 1 1 auto;
    min-width: 0;
    padding: var(--space-md);
    border: 0;
    border-bottom: 2px solid var(--accent-soft);
    background: transparent;
    color: var(--paper);
    font-family: var(--font-headline);
    font-size: 24px;
}

.search-overlay__input:focus {
    border-bottom-color: var(--paper);
    outline: none;
}

.search-overlay__submit,
.search-overlay__close {
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--paper);
    background: transparent;
    color: var(--paper);
    font-family: var(--font-ui);
    font-size: 14px;
    cursor: pointer;
}

.search-overlay__submit:hover,
.search-overlay__submit:focus,
.search-overlay__close:hover,
.search-overlay__close:focus {
    background: var(--accent);
    border-color: var(--accent);
}

.search-overlay__close {
    margin-top: var(--space-md);
}

.nav-drawer {
    position: fixed;
    inset: 0;
    z-index: 95;
    display: flex;
    background: rgba(16, 20, 24, 0.94);
}

.nav-drawer.is-hidden {
    display: none;
}

.nav-drawer__inner {
    width: min(86vw, 360px);
    height: 100%;
    padding: var(--space-lg) var(--space-md);
    background: var(--paper);
    overflow-y: auto;
}

.nav-drawer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    margin-bottom: var(--space-lg);
    padding-bottom: var(--space-sm);
    border-bottom: 2px solid var(--accent);
}

.nav-drawer__brand {
    font-family: var(--font-headline);
    font-size: 20px;
    font-weight: 700;
    text-decoration: none;
}

.nav-drawer__close {
    padding: var(--space-xs) var(--space-sm);
    border: 1px solid var(--accent);
    background: transparent;
    color: var(--accent);
    font-size: 16px;
    cursor: pointer;
}

.nav-drawer__item {
    border-bottom: 1px solid var(--rule-soft);
}

.nav-drawer__link,
.nav-drawer__search-trigger {
    display: block;
    width: 100%;
    padding: var(--space-md) 0;
    border: 0;
    background: transparent;
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 16px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.nav-drawer__link:hover,
.nav-drawer__link:focus,
.nav-drawer__search-trigger:hover,
.nav-drawer__search-trigger:focus {
    color: var(--accent);
}

/* ───────────────────────────────────────────────────────────────────────────
   14. Breakpoints — MOBILE-FIRST
   The 12-column grid arrives here. Nothing above is undone; this block only
   adds columns and turns the span utilities into real spans.
   ─────────────────────────────────────────────────────────────────────────── */

@media (min-width: 861px) {
    .dp-grid {
        grid-template-columns: repeat(var(--columns), 1fr);
    }

    .dp-span-2  { grid-column: span 2; }
    .dp-span-3  { grid-column: span 3; }
    .dp-span-4  { grid-column: span 4; }
    .dp-span-5  { grid-column: span 5; }
    .dp-span-7  { grid-column: span 7; }
    .dp-span-8  { grid-column: span 8; }
    .dp-span-10 { grid-column: span 10; }
    .dp-span-12 { grid-column: span 12; }

    .dp-topbar__actions {
        justify-content: flex-end;
    }

    .dp-nav__toggle {
        display: none;
    }

    .dp-nav__chips {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-sm);
    }

    .dp-mast__tagline {
        text-align: right;
    }

    /* The mark grows with the masthead at desktop width. Only --dp-mark-size
       changes: --dp-cap is re-derived from it IN THIS RULE, and the outline
       stays pinned. */
    .dp-mast__mark {
        --dp-mark-size: 38px;
        --dp-cap: calc(var(--dp-mark-size) * var(--dp-cap-ratio));
    }
}

/* ───────────────────────────────────────────────────────────────────────────
   15. BANDS AND TILES — the mosaic proper (§4.3.1)
   A band is a row group of the page grid. It is NOT a container with a sidebar:
   nothing nests a column inside another column.
   ─────────────────────────────────────────────────────────────────────────── */

/* `.dp-band` itself is declared once, in §8 with the ticker bands — the rule is
   the same for every band on the page, so it is not restated here. §15 only adds
   what the homepage bands need on top of it. */

.dp-band__grid {
    padding-top: var(--space-lg);
    padding-bottom: var(--space-lg);
    align-items: start;
}

.dp-band--ad .dp-band__grid {
    padding-top: var(--space-md);
    padding-bottom: var(--space-md);
}

.dp-band--investigation {
    background: var(--accent-tint);
}

/* ── NO EMPTY CELLS, BY CONSTRUCTION (answer to short data) ──────────────
   Every uniform run of tiles lives in a subgrid using
   `repeat(auto-fit, minmax(floor, 1fr))`. auto-fit COLLAPSES tracks that hold
   no item, so the remaining tiles stretch to fill the row: two tiles in a
   four-up band become two wide tiles, never two tiles and two holes.

   auto-FILL would keep the empty tracks and produce exactly the void register
   taught us to design out. The distinction is the whole mechanism, so it is
   stated here rather than left to the reader of the shorthand.

   The floors differ per band, which is what gives each band its own column
   rhythm — and different rhythms per band are what stop any single x-range
   repeating down the page as a main column. */
.dp-subgrid {
    display: grid;
    gap: var(--gutter);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
}

.dp-subgrid--halves {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}

.dp-subgrid--thirds {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

.dp-subgrid--quarters {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
}

/* THE FRONT BAND's one structural guard: if Hero 2 is silent, Hero 1 takes the
   full width rather than leaving four columns empty beside it. */
.dp-front__tile:only-child {
    grid-column: 1 / -1;
}

/* ───────────────────────────────────────────────────────────────────────────
   16. Tiles
   ─────────────────────────────────────────────────────────────────────────── */

.dp-tile {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    min-width: 0;
}

.dp-tile--label {
    gap: var(--space-xs);
}

/* A HAIRLINE, NOT A BAR (polish audit D1, shortlist item 4). At 2px in full
   navy this rule was the heaviest line on the page and it repeated under every
   band label — fourteen of them on one homepage, which read as scaffolding
   rather than as labelling. At 1px it still underlines the label and stops
   competing with the tiles it introduces. */
.dp-band__label {
    margin: 0;
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--accent);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: var(--size-h2);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.dp-band__more {
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
    text-decoration: none;
}

.dp-tile--framed {
    padding: var(--space-md);
    border: 1px solid var(--rule);
}

.dp-tile--ad,
.dp-tile--quote {
    justify-content: flex-start;
}

/* ── ONE IMAGE RATIO AT EVERY SPAN (answer to image treatment) ───────────
   16:9 on every tile image, wide or narrow, with object-fit: cover. A wide tile
   and a narrow tile therefore crop the SAME WAY; only the box size changes.
   Varying the ratio by span is what makes a grid look accidental, and it would
   also mean two tiles of the same article cropping differently in different
   bands. The Hero 1 tile is taller than its neighbours by ROW SPAN, not by a
   different ratio. */
.dp-tile__media {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    position: relative;
}

.dp-tile__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.dp-tile__image--empty {
    display: block;
    width: 100%;
    height: 100%;
    background: var(--paper-deep);
}

.dp-tile__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    min-width: 0;
}

.dp-tile__title {
    margin: 0;
    font-family: var(--font-headline);
    font-size: var(--size-h3);
    font-weight: 700;
    line-height: var(--lh-head);
    overflow-wrap: break-word;
}

.dp-tile__title--lead {
    font-size: 26px;
}

.dp-tile__title--quote {
    font-style: italic;
}

.dp-tile__title a {
    text-decoration: none;
}

.dp-tile__title a:hover,
.dp-tile__title a:focus {
    color: var(--accent);
}

.dp-tile__deck {
    margin: 0;
    color: var(--ink-soft);
    font-size: 15px;
    line-height: var(--lh-body);
}

.dp-meta {
    display: inline-block;
    color: var(--ink-mute);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.dp-kicker {
    display: inline-block;
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.dp-kicker--reverse {
    color: var(--paper);
}

.dp-empty {
    margin: 0;
    color: var(--ink-mute);
    font-size: 15px;
}

/* ───────────────────────────────────────────────────────────────────────────
   17. Hero tiles and the carousel
   ─────────────────────────────────────────────────────────────────────────── */

.dp-tile--hero .hero-carousel__slide,
.dp-tile--hero2 .hero-carousel__slide {
    display: none;
    flex-direction: column;
    gap: var(--space-sm);
}

.dp-tile--hero .hero-carousel__slide.is-active,
.dp-tile--hero2 .hero-carousel__slide.is-active {
    display: flex;
}

.dp-hero__title {
    margin: 0;
    font-family: var(--font-headline);
    font-size: var(--size-display);
    font-weight: 700;
    line-height: var(--lh-tight);
    overflow-wrap: break-word;
}

.dp-hero__title a {
    text-decoration: none;
}

.dp-hero__title a:hover,
.dp-hero__title a:focus {
    color: var(--accent);
}

.dp-carousel__controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    margin-top: var(--space-sm);
}

.dp-carousel__nav {
    display: flex;
    gap: var(--space-xs);
}

.dp-carousel__prev,
.dp-carousel__next {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--accent);
    background: var(--paper);
    color: var(--accent);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
}

.dp-carousel__prev:hover,
.dp-carousel__prev:focus,
.dp-carousel__next:hover,
.dp-carousel__next:focus {
    background: var(--accent);
    color: var(--paper);
}

.dp-carousel__dots {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.dp-carousel__dot {
    width: 9px;
    height: 9px;
    padding: 0;
    border: 1px solid var(--accent);
    background: transparent;
    cursor: pointer;
}

.dp-carousel__dot.is-active {
    background: var(--accent);
}

/* ───────────────────────────────────────────────────────────────────────────
   18. Numbered and ranked lists (#17, #18)
   ─────────────────────────────────────────────────────────────────────────── */

.dp-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

.dp-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    min-width: 0;
    padding-bottom: var(--space-sm);
    border-bottom: 1px solid var(--rule-soft);
}

.dp-row__numeral,
.dp-row__rank {
    flex: 0 0 auto;
    min-width: 24px;
    color: var(--accent);
    font-family: var(--font-headline);
    font-size: 18px;
    font-weight: 700;
    line-height: 1.3;
}

.dp-row__thumb-link {
    display: block;
    flex: 0 0 64px;
    width: 64px;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

.dp-thumb {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.dp-thumb--square {
    aspect-ratio: 1 / 1;
}

.dp-row__title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-family: var(--font-headline);
    font-size: 15px;
    font-weight: 500;
    line-height: var(--lh-head);
    overflow-wrap: break-word;
}

.dp-row__title a {
    text-decoration: none;
}

.dp-row__title a:hover,
.dp-row__title a:focus {
    color: var(--accent);
}

/* ───────────────────────────────────────────────────────────────────────────
   19. Photo essay, video, opinion, investigation, long read
   ─────────────────────────────────────────────────────────────────────────── */

.dp-tile--photo {
    margin: 0;
}

.dp-photo__link {
    display: block;
    position: relative;
    width: 100%;
    aspect-ratio: 21 / 9;
    overflow: hidden;
    text-decoration: none;
}

.dp-photo__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A light theme's overlay: a restrained ink scrim for contrast, not a mood. */
.dp-photo__caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: var(--space-md);
    background: rgba(16, 20, 24, 0.68);
    color: var(--paper);
}

.dp-photo__title {
    margin: var(--space-xs) 0 0;
    font-family: var(--font-headline);
    font-size: 26px;
    font-weight: 700;
    line-height: var(--lh-head);
}

.dp-photo__deck {
    margin: var(--space-xs) 0 0;
    font-size: 14px;
}

.dp-tile__media--video {
    position: relative;
}

.dp-video__glyph {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(27, 58, 107, 0.9);
    color: var(--paper);
    font-size: 15px;
}

.dp-opinion__byline {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.dp-headshot {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--paper-deep);
    border: 1px solid var(--rule);
}

.dp-opinion__author {
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 700;
}

.dp-tile--investigation {
    padding: var(--space-lg) var(--space-md);
    border-top: 4px solid var(--accent);
}

.dp-investigation__label {
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.dp-investigation__title {
    margin: var(--space-xs) 0 0;
    font-family: var(--font-headline);
    font-size: 30px;
    font-weight: 700;
    line-height: var(--lh-head);
}

.dp-investigation__body {
    margin: var(--space-sm) 0 0;
    max-width: var(--measure);
    color: var(--ink-soft);
    line-height: var(--lh-body);
}

.dp-longread__title {
    margin: 0;
    font-family: var(--font-headline);
    font-size: 28px;
    font-weight: 700;
    line-height: var(--lh-head);
}

.dp-longread__title a {
    text-decoration: none;
}

.dp-quote {
    margin: 0;
    padding-left: var(--space-md);
    border-left: 4px solid var(--accent);
    font-family: var(--font-headline);
    font-size: 20px;
    font-style: italic;
    line-height: 1.35;
}

/* ───────────────────────────────────────────────────────────────────────────
   20. Category page
   ─────────────────────────────────────────────────────────────────────────── */

.dp-cat__heading {
    margin: 0;
    font-family: var(--font-headline);
    font-size: var(--size-h1);
    font-weight: 700;
    line-height: var(--lh-tight);
}

/* ───────────────────────────────────────────────────────────────────────────
   21. Article variants (§8 unified geometry, dispatch voice)
   ─────────────────────────────────────────────────────────────────────────── */

.dp-article {
    display: block;
    padding-bottom: var(--space-xl);
}

.dp-article__grid {
    padding-top: var(--space-lg);
}

.dp-article__hero,
.dp-article-v3__hero {
    margin: 0;
    width: 100%;
    max-height: 60vh;
    overflow: hidden;
}

.dp-article__hero-image,
.dp-article-v3__hero-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.dp-article__header {
    max-width: var(--measure);
}

.dp-article__category {
    display: inline-block;
    margin-bottom: var(--space-sm);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    text-decoration: none;
}

.dp-article__title {
    margin: 0;
    font-family: var(--font-headline);
    font-size: var(--size-h1);
    font-weight: 700;
    line-height: 1.08;
    overflow-wrap: break-word;
}

.dp-article__deck {
    margin: var(--space-sm) 0 0;
    color: var(--ink-soft);
    font-size: 19px;
    line-height: var(--lh-body);
}

.dp-article__byline {
    margin-top: var(--space-md);
    padding-top: var(--space-sm);
    border-top: 1px solid var(--accent);
}

.dp-article__main,
.dp-article__column {
    min-width: 0;
    max-width: var(--measure);
}

.dp-article__body {
    font-size: var(--size-body);
    line-height: var(--lh-body);
}

.dp-article__body p {
    margin: 0 0 var(--space-md);
}

.dp-article__body h2,
.dp-article__body h3 {
    margin: var(--space-lg) 0 var(--space-sm);
    font-family: var(--font-headline);
}

.dp-article__body img {
    display: block;
    width: 100%;
    height: auto;
    margin: var(--space-md) 0;
}

.dp-article__body a {
    color: var(--accent);
}

.dp-article__body blockquote {
    margin: var(--space-lg) 0;
    padding-left: var(--space-md);
    border-left: 4px solid var(--accent);
    font-family: var(--font-headline);
    font-style: italic;
}

/* The rail is NOT sticky — same rule as the grid, for the Phase 7c reason. */
.dp-article__rail {
    min-width: 0;
}

.dp-article-v2__hero {
    margin: var(--space-md) 0 0;
    max-width: var(--measure);
}

.dp-article-v2__hero-image {
    display: block;
    width: 100%;
    height: auto;
}

/* ───────────────────────────────────────────────────────────────────────────
   22. Shared partial classes on article and listing pages — §3.2 EXACT names,
   read from the partials themselves rather than assumed to be BEM.
   ─────────────────────────────────────────────────────────────────────────── */

.breadcrumbs {
    margin: 0 0 var(--space-sm);
    color: var(--ink-mute);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.breadcrumbs-list,
.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
}

.breadcrumbs-item,
.breadcrumbs__item {
    display: inline;
}

.article-byline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs);
    font-family: var(--font-ui);
    font-size: var(--size-meta);
}

.byline-author-name {
    font-weight: 700;
}

.byline-sep,
.byline-date {
    color: var(--ink-mute);
}

.article-tldr,
.article-key-takeaways,
.article-faq,
.article-qa,
.article-entities,
.article-topics,
.article-tags {
    margin: var(--space-lg) 0;
    padding: var(--space-md);
    background: var(--paper-soft);
    border-left: 4px solid var(--accent);
}

.tldr-heading,
.kt-heading,
.faq-heading,
.qa-heading,
.entities-heading,
.entities-group__heading,
.related-heading,
.article-rail__heading,
.article-v2-rail__entity-group-heading,
.article-tags__heading,
.article-v3-related__heading,
.social-share__label {
    margin: 0 0 var(--space-sm);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: var(--size-h2);
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.tldr-body {
    margin: 0;
    line-height: var(--lh-body);
}

.kt-list,
.faq-list,
.qa-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.kt-list li {
    padding-left: var(--space-md);
    border-left: 2px solid var(--rule);
}

.faq-question,
.qa-question {
    font-family: var(--font-headline);
    font-weight: 700;
}

.faq-answer,
.qa-answer {
    margin: var(--space-xs) 0 0;
    color: var(--ink-soft);
}

.entities-list,
.article-tags__list,
.article-rail__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
}

.entity-chip,
.entity-person,
.entity-org,
.entity-tag,
.article-tags__link,
.article-rail__chip {
    display: inline-block;
    padding: 3px var(--space-sm);
    border: 1px solid var(--accent);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 12px;
    text-decoration: none;
}

.article-rail,
.article-v2-rail {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
    min-width: 0;
}

.article-rail__section,
.article-v2-rail__section {
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--rule);
}

.social-share__list,
.article-rail__share-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
}

.social-share__link,
.article-rail__share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-xs) var(--space-sm);
    border: 1px solid var(--rule);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 12px;
    text-decoration: none;
}

.social-share__link:hover,
.social-share__link:focus,
.article-rail__share-btn:hover,
.article-rail__share-btn:focus {
    border-color: var(--accent);
    color: var(--accent);
}

/* Related articles come back as a TILE BAND (4.3.5), so the reader lands back
   in the grid rather than in a list. */
.article-related,
.article-v3-related {
    margin-top: var(--space-xl);
    padding-top: var(--space-lg);
    border-top: 3px solid var(--accent);
}

.related-grid {
    display: grid;
    gap: var(--gutter);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}

.article-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    min-width: 0;
}

.article-card__image-link {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

.article-card__image-link img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.article-card__category {
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.article-card__title {
    margin: 0;
    font-family: var(--font-headline);
    font-size: 17px;
    font-weight: 700;
    line-height: var(--lh-head);
}

.article-card__title a {
    text-decoration: none;
}

.article-card__deck,
.article-card__byline {
    margin: 0;
    color: var(--ink-soft);
    font-size: 14px;
}

.article-v3-related__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.article-v3-related__item {
    display: flex;
    gap: var(--space-sm);
    padding-bottom: var(--space-sm);
    border-bottom: 1px solid var(--rule-soft);
}

.article-v3-related__num {
    color: var(--accent);
    font-family: var(--font-headline);
    font-weight: 700;
}

.article-v3-related__link {
    text-decoration: none;
}

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px solid var(--accent);
    font-family: var(--font-ui);
    font-size: 14px;
}

.pagination-prev,
.pagination-next {
    padding: var(--space-xs) var(--space-md);
    border: 1px solid var(--accent);
    color: var(--accent);
    text-decoration: none;
}

.pagination-disabled {
    color: var(--ink-mute);
}

.sponsored-badge {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-xs);
    padding: var(--space-xs) var(--space-sm);
    background: var(--accent-tint);
    font-family: var(--font-ui);
    font-size: 12px;
}

/* ───────────────────────────────────────────────────────────────────────────
   23. THE SINGLE :empty GUARD (lesson 4, proved on ledger)
   responsive_image.html.jinja rejects whitespace-only, bare "/" and blob: URLs
   INSIDE the macro, which a `{% if art.image_url %}` truthiness test does not
   catch — so the link renders with nothing in it. The link owns the dimensions,
   so one rule paints the frame and no tile collapses.
   ─────────────────────────────────────────────────────────────────────────── */

.dp-tile__media:empty,
.dp-row__thumb-link:empty,
.dp-photo__link:empty,
.article-card__image-link:empty {
    background: var(--paper-deep);
    border: 1px solid var(--rule);
}

/* ───────────────────────────────────────────────────────────────────────────
   24. Stage 2 breakpoints — MOBILE-FIRST
   Base is one column. 561px gives the uniform bands two-up. 861px brings the
   12-column grid (declared in §14) and, with it, the explicit spans and the
   Hero 1 row span. Nothing here undoes a rule written above.
   ─────────────────────────────────────────────────────────────────────────── */

@media (min-width: 561px) {
    .dp-subgrid {
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
    }
}

@media (min-width: 861px) {
    .dp-span-6 { grid-column: span 6; }

    /* span 9 exists for ONE case: a topic cluster whose followers and ad are
       both absent, where the lead takes the span the subgrid would have used.
       It was briefly deleted as "unused" and then reintroduced in the markup
       without its rule, so the lead matched no selector, fell back to
       auto-placement at content width, and left 965px trailing. Declared here
       deliberately, with the reason, so it is not tidied away again. */
    .dp-span-9 { grid-column: span 9; }

    /* THE HERO IS TALLER BY ROW SPAN, not by a different image ratio — the
       tile takes two grid rows while its picture stays 16:9 like every other. */
    .dp-tile--hero {
        grid-row: span 2;
    }

    .dp-tile--hero .dp-tile__media {
        aspect-ratio: 3 / 2;
    }

    .dp-tile--label-inline {
        justify-content: center;
    }

    .dp-article__grid {
        column-gap: var(--space-xl);
    }
}
