/* Newspaper report layout — /teg-reports (webapp/templates/teg_reports.html).
 *
 * Ported from webapp/report_layout_prototypes/composite.html (desktop) and
 * mobile.html (mobile pattern A) — the settled design, see that folder's
 * README.md. Only the chosen pieces are here: E1/E2 desktop arrangements
 * (E3/G1-G3 were rejected) and mobile pattern A (B/C were not shipped).
 *
 * `.np-page` is a SCOPING wrapper (2026-09-11) — fonts/colour variables and
 * a CSS reset for its contents — not a full-bleed page background. The site
 * nav and page chrome (`base.html`) stay live around it; the newspaper
 * "paper" card (`.np-paper`, below) is the thing that carries the cream
 * colour and grain texture.
 *
 * On desktop the default theme (clean-page.css) wraps every page's content
 * in a white card (`.main-content`: background, border-radius, box-shadow,
 * padding) floating on the grey page background. `.np-paper` is already a
 * card in its own right, so under that theme the report was a card inside a
 * card. `body.np-host` (set by `reports.py` via the `newspaper_page` context
 * flag) neutralises `.main-content` down to a plain layout box so `.np-paper`
 * reads as THE page's card, not a second one nested inside it — see the
 * NP-HOST block below. Mobile doesn't need this: `mobile.css`'s M1 pass
 * already makes `.main-content` transparent/edge-to-edge for every page.
 *
 * The prototypes switch look via buttons; this page switches on viewport
 * width instead — the breakpoint mobile.html itself uses for "this is a real
 * phone, drop the simulator frame" (`max-width:700px`).
 */
.np-page *{ box-sizing:border-box; }

/* ==========================================================================
   NP-HOST — desktop only (mobile's .main-content is already transparent,
   see the file-header comment). Same specificity trick already used by
   clean-page.css's own `body.ts-e2 .main-content` override (0,2,0), and this
   stylesheet loads after every theme stylesheet in `base.html`'s <head>, so
   it wins on source order too without needing `!important`. Covers light and
   dark mode variants of clean-page.css / dark.css's `.main-content` rules.
   ========================================================================= */
@media (min-width:701px){
  body.np-host .main-content{
    background:transparent; box-shadow:none; border-radius:0;
    margin-top:0; margin-bottom:0; padding-left:0; padding-right:0;
    /* Tighten the top of the page (2026-09-11): main-content's own py-8
       (Tailwind, 2rem) was adding to the gap already built into
       .page-title-area and .np-desktop below — three paddings stacking
       into one big block of empty space above the masthead. */
    padding-top:0.5rem;
  }
  html[data-mode="dark"] body.np-host .main-content{ background:transparent; }
  body.np-host .page-title-area{ padding-top:1rem; padding-bottom:0.5rem; }
}

.np-page{
  /* Palette/type variables live on the wrapper (not :root) so this scope
   * never leaks into the rest of the page. T1 Broadsheet (elements.html
   * .pal-a), the settled choice. Accent re-hued to a muted forest green
   * (2026-09-13, was #7A2420 brick) to tie the report into the site's
   * forestgreen accent (clean.css) instead of an unrelated colour — kept
   * muted rather than the site's literal forestgreen so it still sits
   * quietly on this warm cream paper. */
  --paper:#F6F3EA; --ink:#211D17; --ink-soft:#66604F; --ink-contrast:#63615C; --accent:#256B3D; --accent2:#256B3D;
  --rule:#C9C2AC; --tint:#EDE8D9;
  --font-display:'Fraunces',Georgia,'Times New Roman',serif;
  --font-body:'Source Serif 4',Georgia,'Times New Roman',serif;
  --font-mono:'IBM Plex Mono','Courier New',monospace;
  --font-contrast:'Libre Franklin',Arial,sans-serif;
  color:var(--ink); font-family:var(--font-body);
}

/* ==========================================================================
   PALETTE — T1 Broadsheet (elements.html .pal-a), settled 2026-09-11.
   `.pal-a` on <body> restates the values already on `.np-page` above so the
   two other candidate palettes (b/c/d, and the sf-roman/sf-edge/sf-italic
   standfirst treatments and rail-s1 layout that were compared against them)
   could be dropped once chosen — see git history / elements.html for the
   rejected options, not kept here as dead CSS.
   ========================================================================= */
.pal-a{
  --paper:#F6F3EA; --ink:#211D17; --ink-soft:#66604F; --ink-contrast:#63615C; --accent:#256B3D; --accent2:#256B3D;
  --rule:#C9C2AC; --tint:#EDE8D9;
  --font-display:'Fraunces',Georgia,'Times New Roman',serif;
  --font-body:'Source Serif 4',Georgia,'Times New Roman',serif;
  --font-mono:'IBM Plex Mono','Courier New',monospace;
  --font-contrast:'Libre Franklin',Arial,sans-serif;
}

/* ==========================================================================
   STANDFIRST — contrast treatment (elements.html H4-adjacent), settled
   2026-09-11: the standfirst sets in `--font-contrast`, the opposite type
   family from the headline's `--font-display`, upright, in a neutral grey
   (`--ink-contrast`) rather than the warmer `--ink-soft` used elsewhere on
   the page. A sans reads bigger than a serif at the same px, so the sizes
   below are nudged down so the standfirst doesn't out-weigh the headline.
   ========================================================================= */
.sf-contrast .lead-standfirst,.sf-contrast .sub-standfirst,.sf-contrast .m-sf{
  /* Italic since 2026-09-15 (Jon, after an A/B of five treatments): upright
   * Libre Franklin read as a label sitting under the headline; the italic
   * reads as a voice introducing the story. Covers all three standfirsts
   * (lead, sub-article, mobile) so they stay consistent. `.topbar .tb-title`
   * also uses --font-contrast but sets no font-style, so it stays upright —
   * which is why the upright faces are still loaded and bundled. */
  font-family:var(--font-contrast); font-style:italic; font-weight:500; color:var(--ink-contrast);
  padding-left:0; border-left:none; letter-spacing:0.003em;
}
.sf-contrast .lead-standfirst{ font-size:0.92rem; }
.sf-contrast .sub-standfirst{ font-size:0.79rem; }
.sf-contrast .idx-lead .m-sf{ font-size:0.85rem; }
.sf-contrast .article .m-sf{ font-size:0.83rem; }
/* :where() contributes zero specificity, so this reset still beats UA
 * defaults but loses to every single-class component rule below (0,1,0).
 * The previous version repeated the selector list as plain `h1,h2,...`,
 * which is specificity (0,1,1) — it silently beat every component rule
 * on this page (`.r5-title` padding, `.lead-headline` margin-top, etc,
 * see README.md's CSS-reset note) and had to be worked around per-rule
 * instead of fixed once. Do not "simplify" this back to a bare selector. */
.np-page :where(h1,h2,h3,p,ul,table){ margin:0; padding:0; }
.np-page ul{ list-style:none; }
.np-page table{ border-collapse:collapse; }
.np-page :focus-visible{ outline:2.5px solid var(--accent); outline-offset:2px; }

/* Pre-TEG-8 caption, printed above the paper card in the site's own type
 * (not the newspaper scope) — it's a note about the report, not part of it. */
.np-caption{
  font-size:0.85rem; font-style:italic; opacity:0.65; margin:0 0 1rem;
}

/* Back-link to /results or /latest-round, in the site's own type (not the
 * newspaper scope) — it's chrome, not content. Only rendered when reports.py
 * could resolve a specific teg/round to deep-link to. */
.np-back-link{
  font-size:0.85rem; font-weight:500; text-decoration:none;
  color:var(--text-muted, #666); white-space:nowrap;
}
.np-back-link:hover{ color:var(--accent, #256B3D); }

/* The two right-hand chrome links ("Download PDF", "Go to Full Results").
 * Grouped so they stay together when .section-controls wraps on a narrow
 * screen — left as two separate flex children they split across the wrap,
 * stranding one of them under the round pills. */
.np-chrome-links{
  display:flex; align-items:center; gap:1rem; flex-wrap:wrap;
}

/* Deliberately the same quiet text treatment as .np-back-link rather than a
 * .pill or a filled button (2026-09-15, Jon: "discreet"). The report is the
 * thing on this page; the download is an affordance, not a call to action.
 * Shown on desktop and on the mobile index alike — no width gating, the only
 * thing that hides it is the .np-reading rule that hides the whole control
 * row while a story is open on a phone. */
.np-pdf-link{
  font-size:0.85rem; font-weight:500; text-decoration:none;
  color:var(--text-muted, #666); white-space:nowrap;
}
.np-pdf-link:hover{ color:var(--accent, #256B3D); }

@media (max-width:700px){
  /* Comfortable tap target on a phone without making the link look like a
     button — vertical padding only, so the text stays flush with the round
     pills above it instead of being indented by its own inline padding. */
  .np-pdf-link, .np-back-link{
    padding:0.35rem 0; min-height:2.2rem; display:inline-flex; align-items:center;
  }
  .np-chrome-links{ gap:1.1rem; }
}

/* TEG select, sized/weighted to match the .pill buttons it sits next to
 * (2026-09-12, Jon: font/colour didn't match) — .theme-select's sitewide
 * defaults (400 weight, 12px) read as a visibly different, thinner control
 * next to .pill's 500/600-weight, 13px labels. Scoped to this page rather
 * than changed globally: .theme-select is shared by ~20 other pages. */
.reports-teg-select{
  font-weight:500; font-size:0.8125rem; color:var(--text-secondary, #555);
}

/* ==========================================================================
   DESKTOP — lead + rail, sub-articles packed into full-width rows by
   `plan_rows` (teg_analysis/reporting/newspaper_edition.py). No named
   arrangements any more — see that module's docstring for why.
   ========================================================================= */
.np-desktop{ display:none; }

@media (min-width:701px){
  .np-desktop{ display:block; padding:0.75rem 1.5rem 3rem; }
  .np-mobile{ display:none; }

  .np-paper{
    width:100%; max-width:1180px; margin:0 auto;
    --pad: clamp(1.1rem, 4vw, 3.2rem);
    background:var(--paper);
    border:1px solid #D3D0C6;
    box-shadow:0 1px 2px rgba(0,0,0,0.08), 0 18px 40px -20px rgba(0,0,0,0.35);
  }
  .np-paper > *, .np-paper .main-split > *, .np-paper .subs-row > *{ min-width:0; }

  .masthead{ padding:1.5rem var(--pad) 0; }
  .mh-row{ display:flex; align-items:baseline; justify-content:space-between; gap:1rem; flex-wrap:wrap; }
  .wordmark{ font-family:var(--font-display); font-weight:600; font-size:1.6rem; letter-spacing:0.02em; }
  .dateline{ font-family:var(--font-body); font-weight:500; font-size:0.73rem; letter-spacing:0.07em;
    text-transform:uppercase; color:var(--ink-soft); white-space:nowrap; }
  .mh-rule{ border-top:3px solid var(--ink); border-bottom:1px solid var(--ink); height:5px; margin-top:0.85rem; }

  .kicker{ font-family:var(--font-mono); font-weight:700; font-size:0.71rem; letter-spacing:0.13em;
    text-transform:uppercase; color:var(--accent); }
  .lead-headline{ font-family:var(--font-display); font-weight:700; line-height:1.04; color:var(--ink);
    text-wrap:balance; font-size:clamp(2.05rem,4vw,3rem); margin-top:0.45rem; }
  .lead-standfirst{ font-family:var(--font-body); font-size:1.02rem; line-height:1.42;
    margin-top:0.6rem; text-wrap:balance; }

  .lead-body{ margin-top:1.15rem; columns:2; column-gap:2.1rem; column-rule:1px solid var(--rule); }
  .lead-body p{ font-family:var(--font-body); font-size:0.98rem; line-height:1.56; color:var(--ink); }
  .lead-body p + p{ margin-top:0.85em; }
  .lead-body p:first-of-type::first-letter{
    float:left; font-family:var(--font-display); font-weight:700; font-size:3.6rem; line-height:0.78;
    padding:0.12rem 0.5rem 0 0; color:var(--ink);
  }

  .rail{ align-self:start; }
  .r5{ border:1px solid var(--ink); }
  .r5-title{ background:var(--ink); color:var(--paper); font-family:var(--font-body); font-size:0.72rem;
    font-weight:700; letter-spacing:0.12em; text-transform:uppercase; padding:0.38rem 0.75rem; }
  /* Gap plus a hairline between rows (the "ruled scorecard" the box border
   * already implies) — three results need to read as three entries, not one
   * run-on list; see README.md item 2. */
  .r-list{ display:flex; flex-direction:column; padding:0.6rem 0.8rem 0.7rem; gap:0.9rem; }
  .r-item{ display:flex; flex-direction:column; align-items:flex-start; gap:0.1rem; }
  .r-item + .r-item{ border-top:1px solid var(--rule); padding-top:0.75rem; }
  .r-vals{ display:flex; flex-direction:column; align-items:flex-start; gap:0.05rem; }
  .r-runner{ font-family:var(--font-body); font-size:0.71rem; letter-spacing:0.01em; color:var(--ink-soft); }
  .r-label{ font-family:var(--font-body); font-size:0.76rem; letter-spacing:0.03em; text-transform:uppercase; color:var(--ink-soft); }
  .r-value{ font-family:var(--font-body); font-size:0.9rem; font-variant-numeric:tabular-nums; font-weight:700; }
  /* The text after the name (a short "(3rd Trophy)" or a long "— 169 pts, by
   * 8 from John Patterson") de-emphasised so the name reads first — task 4. */
  .r-qual{ font-weight:400; font-size:0.76rem; color:var(--ink-soft); }
  /* The trophy row carries no shading in either rail variant (2026-09-08) —
   * bold weight is the only emphasis it gets. `.r5 .r-lead` used to set
   * padding and a negative margin purely so its tint bled into the list
   * padding; with the tint gone those would only push the row out of line with
   * its siblings, so the rule went with it. Left ink rather than accent
   * (2026-09-13, when the accent moved to green) — the player's name isn't
   * "the honour", the trophy label above it already says that, so colouring
   * the name read as if green meant something about that specific player. */
  .r-lead .r-value{ color:var(--ink); font-weight:700; }
  .rail-standings{ margin-top:1.1rem; padding-top:1rem; border-top:1px solid var(--rule); }
  .sb-lab{ font-family:var(--font-body); font-size:0.68rem; font-weight:700; letter-spacing:0.09em;
    text-transform:uppercase; color:var(--accent); }
  .sb-row{ font-family:var(--font-body); font-size:0.82rem; font-variant-numeric:tabular-nums; padding:0.18rem 0 0; }
  .sb-lab + .sb-row{ margin-bottom:0.5rem; }

  /* S1 — full-width results strip in place of the rail (elements.html's
   * `.r3`: "ruled strip — full width, three cells"; its winner tint was
   * dropped here, as in S2). */
  .r3{ border-top:2px solid var(--ink); border-bottom:1px solid var(--ink); margin:1.15rem var(--pad) 0; }
  .r3 .r-list{ flex-direction:row; padding:0; gap:0; }
  .r3 .r-item{ flex:1; padding:0.8rem 1.1rem; border-right:1px solid var(--rule); }
  .r3 .r-item:last-child{ border-right:none; }
  .r3 .r-lead .r-value{ font-size:1.08rem; }
  .r3 .r-value{ font-size:0.98rem; }

  .lead-head{ padding:1.35rem var(--pad) 0; }
  .main-split{ display:grid; grid-template-columns:2fr 1fr; gap:2.2rem; padding:1.15rem var(--pad) 0; }
  .deck-rule{ margin:1.9rem var(--pad) 0; border-top:3px solid var(--ink); }

  .sub-headline{ font-family:var(--font-display); font-weight:700; font-size:1.24rem; line-height:1.15;
    color:var(--ink); margin-top:0.35rem; text-wrap:balance; }
  .sub-standfirst{ font-family:var(--font-body); font-size:0.85rem; line-height:1.4; margin-top:0.4rem; }
  .sub-body{ margin-top:0.7rem; }
  .sub-body p{ font-family:var(--font-body); font-size:0.895rem; line-height:1.53; color:var(--ink); }
  .sub-body p + p{ margin-top:0.75em; }

  /* Appendix: kicker + headline + standfirst like any other section on the
     page (2026-09-11 — was a lone eyebrow label over a rigid 2-up grid,
     which read as an afterthought). Categories flow through ruled columns
     instead of a fixed split, so a light TEG and a heavy one both settle
     naturally instead of forcing the same two columns. */
  .appendix{ border-top:1px solid var(--rule); margin-top:2rem; padding:1.7rem var(--pad) 2.3rem; }
  .apx-hl{ font-family:var(--font-display); font-weight:700; font-size:1.4rem; line-height:1.15;
    color:var(--ink); margin:0 0 1.3rem; text-wrap:balance; }
  .apx-flow{ columns:2; column-gap:2.2rem; column-rule:1px solid var(--rule); }
  .apx-grp{ break-inside:avoid; margin-bottom:1.1rem; }
  .apx-cat{ font-family:var(--font-mono); font-weight:600; font-size:0.68rem; letter-spacing:0.09em;
    text-transform:uppercase; color:var(--ink-soft); margin:0 0 0.4rem; padding-bottom:0.3rem;
    border-bottom:1px solid var(--rule); }
  .apx-list{ list-style:none; margin:0; padding:0; }
  .apx-list li{ font-family:var(--font-body); font-size:0.87rem; line-height:1.5; color:var(--ink);
    position:relative; padding-left:0.9rem; margin-bottom:0.5rem; }
  .apx-list li::before{ content:"—"; position:absolute; left:0; color:var(--accent); }

  /* Row packing: `plan_rows` (newspaper_edition.py) chunks sub-articles into
     rows of 1, 2 or 3 so a grid row is always exactly as wide as its items —
     never a partial row with empty trailing cells. Each row is its own grid
     container, so `:first-child`/`:last-child` on `.sub-card` always target
     correctly, unlike the old shared-grid arrangement system. */
  .subs-row{ display:grid; padding:1.5rem var(--pad) 0; }
  .subs-row.cols-3{ grid-template-columns:repeat(3,1fr); }
  .subs-row.cols-2{ grid-template-columns:repeat(2,1fr); }
  .subs-row.cols-1{ grid-template-columns:1fr; }
  .subs-row .sub-card{ padding:0 1.55rem; border-left:1px solid var(--rule); }
  .subs-row .sub-card:first-child{ padding-left:0; border-left:none; }
  .subs-row .sub-card:last-child{ padding-right:0; }
  /* cols-1 is the promoted "second lead" row — full width, no side padding,
     body split into columns so it doesn't read as one long single column. */
  .subs-row.cols-1 .sub-card{ padding:0; border-left:none; }
  .subs-row.cols-1 .sub-body{ columns:3; column-gap:1.9rem; column-rule:1px solid var(--rule); }
  .subs-row.cols-1 .sub-standfirst{ max-width:110ch; }
  .subs-row + .thin-rule{ margin:1.7rem var(--pad) 0; border-top:1px solid var(--rule); }

  /* S1 — no rail: results become the full-width `.r3` strip above (in
     _main_html) and the lead body needs a third column so the measure stays
     readable at the full 1180px page width instead of a 2-column rail width. */
  .rail-s1 .main-split{ grid-template-columns:1fr; gap:0; }
  .rail-s1 .lead-body{ columns:3; column-gap:1.9rem; }
}

/* ==========================================================================
   MOBILE — mobile.html, pattern A (index first) only.
   ========================================================================= */
@media (max-width:700px){
  /* Same Tailwind-collision problem as the desktop NP-HOST block above, at
     phone width instead (2026-09-12): mobile.css's `.main-content{
     padding-left:0 }` only fires at its own ≤640px breakpoint and is a
     single-class selector, so between 641-700px — this page's own
     breakpoint — nothing zeroes `.main-content`'s Tailwind `px-6`, and even
     inside 640px the CDN Tailwind runtime injects its stylesheet at
     runtime in a way that can still out-order a same-specificity rule.
     Diagnosed from a real-phone screenshot showing the report inset ~24px
     further than intended. body.np-host raises specificity enough to win
     regardless, scoped to just this page like its desktop counterpart.
     padding-block is zeroed too (2026-09-12 follow-up) — Tailwind's `py-8`
     was never touched by the horizontal fix, and was the actual source of
     the dead space above the article topbar Jon flagged from a live
     screenshot; every component below already supplies its own vertical
     spacing (.m-masthead, .article, .tabbar-spacer), so main-content
     doesn't need to contribute any of its own on this page. */
  body.np-host .main-content{ padding-inline:0; padding-block:0; }

  /* .section-controls (TEG select + Tournament/round pills + "Go to Full
     Results") is shared with /results and /leaderboard and has never had
     its own horizontal inset — it always relied on .main-content's
     Tailwind px-6. Zeroing main-content above (for the edge-to-edge
     article/index panels) left it with nothing, so it went flush to the
     screen edge. Give it the same inset as the rest of this page's mobile
     content rather than main-content's. .page-title-area sits outside
     .main-content entirely (a page_title block sibling in base.html) and
     is dropped altogether on mobile instead of repadded — see the
     .page-title-area rule below. */
  body.np-host .section-controls{ padding-inline:0.9rem; }

  /* "HISTORY / TEG Reports" dropped on mobile only (2026-09-12, Jon: "for a
     more seamless look") — the bottom tab bar already highlights History as
     the active section, and the masthead a few lines below already names
     the TEG, so the label was pure repeated weight at the top of the
     screen, not information. */
  body.np-host .page-title-area{ display:none; }

  .np-mobile{
    display:block;
    /* The site page-level gutter is gone (.page-container, mobile.css,
       2026-09-11) — every mobile page is edge-to-edge now, insetting
       itself. --pad is this report's own inset, the only one it gets:
       sized for a comfortable reading margin, not a sliver assuming
       another gutter underneath it. */
    --pad:0.9rem;
    color:var(--ink);
  }
  .np-desktop{ display:none; }

  /* An open article is its own full screen (Pattern A) — the page's TEG
     select/round pills above .np-mobile belong to the index screen only
     (.page-title-area is dropped unconditionally above, not just while
     reading). newspaper_preview.js toggles this on <body> when a story is
     open, so the article's own topbar sits right under the site nav
     instead of below a second, redundant control row. */
  body.np-reading .section-controls,
  body.np-reading .np-caption{ display:none; }

  .m-masthead{ padding:calc(0.95rem + env(safe-area-inset-top)) var(--pad) 0; }
  .m-mh-row{ display:flex; align-items:baseline; justify-content:space-between; gap:0.6rem; }
  .m-wordmark{ font-family:var(--font-display); font-weight:600; font-size:1.18rem; letter-spacing:0.02em; }
  .m-dateline{ font-family:var(--font-body); font-weight:500; font-size:0.58rem; letter-spacing:0.06em;
    text-transform:uppercase; color:var(--ink-soft); }
  .m-mh-rule{ border-top:3px solid var(--ink); border-bottom:1px solid var(--ink); height:4px; margin-top:0.6rem; }

  .kicker{ font-family:var(--font-mono); font-weight:700; font-size:0.63rem; letter-spacing:0.13em;
    text-transform:uppercase; color:var(--accent); }
  .m-hl{ font-family:var(--font-display); font-weight:700; line-height:1.09; color:var(--ink); text-wrap:balance; }
  .m-sf{ font-family:var(--font-body); line-height:1.4; }
  .m-body p{ font-family:var(--font-body); font-size:1rem; line-height:1.6; color:var(--ink); }
  .m-body p + p{ margin-top:0.8em; }
  .m-meta{ font-family:var(--font-mono); font-size:0.6rem; letter-spacing:0.09em; text-transform:uppercase; color:var(--ink-soft); }

  /* "At a glance" results — slim ruleless strip (2026-09-12), replacing the
     boxed dark-title-bar panel: a small-caps label does the job the border
     and reversed title bar used to, item separation comes from spacing
     rather than dividers, and the block sits AFTER the lead headline
     (Jon: results shouldn't outrank the actual story) ending on whitespace
     rather than another rule before "Also in this edition". Runner-up is
     deliberately not rendered — tried in prototype, judged one line too many
     for this compact a block. */
  .m-strip{ margin:1.35rem 0 1.5rem; }
  .m-strip-h{ font-family:var(--font-mono); font-size:0.6rem; font-weight:700; letter-spacing:0.16em;
    text-transform:uppercase; color:var(--ink-soft); padding:0 var(--pad) 0.55rem; }
  .m-strip-list{ display:flex; flex-direction:column; gap:0.65rem; padding:0 var(--pad); }
  .m-strip-item{ display:flex; justify-content:space-between; align-items:baseline; gap:0.6rem; }
  .m-strip-label{ font-family:var(--font-mono); font-size:0.62rem; font-weight:700; letter-spacing:0.09em;
    text-transform:uppercase; color:var(--ink-soft); flex:none; }
  .m-strip-value{ font-family:var(--font-body); font-size:0.84rem; text-align:right; color:var(--ink); }
  .m-strip-value strong{ font-weight:700; }
  .m-strip-value .m-strip-qual{ color:var(--ink-soft); font-size:0.78rem; font-weight:400; }

  /* A — index first */
  .idx-lead{
    display:block; width:100%; text-align:left; background:none; border:none;
    padding:1.1rem var(--pad) 1.2rem; cursor:pointer; border-bottom:3px solid var(--ink);
  }
  .idx-lead:active{ background:var(--tint); }
  .idx-lead .m-hl{ font-size:1.72rem; margin-top:0.3rem; }
  .idx-lead .m-sf{ font-size:0.94rem; margin-top:0.5rem; }
  .idx-lead .lead-cta{ display:flex; justify-content:space-between; align-items:baseline; gap:0.6rem; margin-top:0.7rem; }
  .idx-list{ margin-top:0; border-top:none; }
  .idx-h{ font-family:var(--font-mono); font-size:0.6rem; font-weight:700; letter-spacing:0.16em;
    text-transform:uppercase; color:var(--ink-soft); padding:0.6rem var(--pad) 0.2rem; }
  .idx-item{
    display:block; width:100%; text-align:left; background:none; border:none;
    border-top:1px solid var(--rule); padding:0.85rem var(--pad); cursor:pointer;
  }
  .idx-item:first-of-type{ border-top:none; }
  .idx-item:active{ background:var(--tint); }
  .idx-item .m-hl{ font-size:1.14rem; margin-top:0.25rem; }
  .idx-item .idx-foot{ display:flex; justify-content:space-between; align-items:baseline; gap:0.6rem; margin-top:0.45rem; }
  .chev{ font-family:var(--font-mono); font-size:0.72rem; font-weight:600; letter-spacing:0.04em; color:var(--accent); }

  /* Article header — minimal text, no card (2026-09-12, Jon: chosen over a
     boxed "← Front page" panel and an icon-only chevron). Spelled out in
     full as "Report front page" rather than a bare "Back": the phone's own
     Back gesture already exists, so this control's job is specifically
     distinguishing "back to this report's index" from the site's own Home —
     that needs words, not an icon. */
  .topbar{
    /* Static, not sticky (2026-09-12 fix) — .nav is ALSO sticky (JS-driven,
       base.html: pinned at top:0 until 300px of scroll, then slides away),
       and with .topbar sticky too at a lower z-index, .nav painted over it
       for that whole first 300-350px, hiding "Report front page" until .nav
       cleared. A static top offset here can't track .nav's scroll-position-
       driven height without duplicating that JS, and the "quick way back"
       job is already covered without it: the phone's own back gesture, plus
       the "Report front page" row already in the .artnav footer below. No
       background needed either now nothing can scroll in behind it. */
    display:flex; align-items:center;
    /* No safe-area-inset-top here any more — that was for clearing the
       notch/dynamic island while pinned to the physical top of the screen;
       static, it always sits below .nav instead, so it's dead weight now. */
    padding:0.6rem var(--pad) 0.55rem;
    border-bottom:1px solid var(--rule);
  }
  .topbar button{ background:none; border:none; cursor:pointer; padding:0; min-height:44px;
    display:flex; align-items:center; gap:0.35rem; font-family:var(--font-body); font-size:0.86rem;
    font-weight:500; color:var(--ink); -webkit-tap-highlight-color:transparent; }
  .topbar button svg{ width:16px; height:16px; flex:none; }
  .topbar .tb-title{ margin-left:auto; font-family:var(--font-contrast); font-size:0.72rem; color:var(--ink-soft); }
  .kicker-line{ padding:0.9rem var(--pad) 0; }
  .article{ padding:0.5rem var(--pad) 1.6rem; }
  .article .m-hl{ font-size:1.55rem; margin-top:0.3rem; }
  .article .m-sf{ font-size:0.92rem; margin-top:0.5rem; }
  .article .m-body{ margin-top:1rem; }

  /* Prev/next/front-page footer — clean single-line links (2026-09-12,
     replacing a boxed 2-up grid Jon called "awful"). A small-caps direction
     label sits inline before the headline, truncated with an ellipsis
     rather than wrapping, separated by hairlines only — no backgrounds, no
     vertical dividers. Previous is omitted entirely on the lead article
     rather than shown as a disabled ghost row. "Report front page" sits
     last and reads quieter (ink-soft, not accent) since it's a return
     action, not a "read more" one. */
  .artnav{ border-top:1px solid var(--rule); margin-top:1.6rem; padding-bottom:env(safe-area-inset-bottom); }
  .an-line{ display:flex; align-items:baseline; gap:0.5rem; width:100%; background:none; border:none;
    border-top:1px solid var(--rule); padding:0.8rem var(--pad); text-align:left; cursor:pointer;
    min-height:44px; font-family:var(--font-body); -webkit-tap-highlight-color:transparent; }
  .an-line:first-child{ border-top:none; }
  .an-dir{ flex:none; font-family:var(--font-mono); font-size:0.6rem; font-weight:700; letter-spacing:0.1em;
    text-transform:uppercase; color:var(--ink-soft); }
  .an-hl{ flex:1; min-width:0; font-size:0.92rem; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .an-front{ color:var(--ink-soft); font-size:0.82rem; }
  .an-front .an-dir{ color:var(--ink-soft); }

  /* appendix accordion, shared between index and article screens */
  .m-apx{ border-top:3px solid var(--ink); }
  .m-apx-btn{ display:flex; width:100%; text-align:left; background:none; border:none; cursor:pointer;
    padding:0.85rem var(--pad); justify-content:space-between; align-items:center; gap:0.6rem;
    min-height:44px; -webkit-tap-highlight-color:transparent; }
  .m-apx-btn:active{ background:var(--tint); }
  .m-apx-h{ font-family:var(--font-mono); font-size:0.63rem; font-weight:700; letter-spacing:0.12em;
    text-transform:uppercase; color:var(--accent); }
  .acc-sign{ font-family:var(--font-mono); font-size:0.8rem; font-weight:700; color:var(--accent); }
  .m-apx-body{ padding:0 var(--pad) 1.5rem; }
  .m-apx-hl{ font-family:var(--font-display); font-weight:700; font-size:1.05rem; line-height:1.2;
    color:var(--ink); margin:0 0 0.9rem; text-wrap:balance; }
  .m-apx-sec + .m-apx-sec{ margin-top:1.2rem; }
  .recs-cat{ font-family:var(--font-body); font-weight:700; font-size:0.8rem; margin-bottom:0.3rem; }
  .recs li{ font-family:var(--font-body); font-size:0.86rem; color:var(--ink-soft); position:relative;
    padding-left:0.9rem; line-height:1.45; }
  .recs li + li{ margin-top:0.3rem; }
  .recs li::before{ content:"—"; position:absolute; left:0; color:var(--accent); }
  .recs-group + .recs-group{ margin-top:0.9rem; }

  .np-mobile [tabindex="-1"]:focus{ outline:none; }
  .np-mobile :focus-visible{ outline:2.5px solid var(--accent); outline-offset:-2px; }
}

@media (prefers-reduced-motion: reduce){
  .np-page *{ scroll-behavior:auto !important; }
}
