/* ============================================================================
   lr-shell.css — v4 shared shell chrome (P3-A, ARCHITECTURE-v4.md §2.2/§11).

   Layout ONLY: the header/nav/toolbar strips, the footer, the main content
   grid, and the dialog host.

   2026-09-09, Unit 3: assets/css/lr-blocks.css (v4's own generic block
   stylesheet) is RETIRED and deleted. Every block now renders in one of the
   previous version's brand.css components, and the handful of rules with no
   brand.css component of their own moved into §8 below. Every colour,
   radius, spacing and type family in this file is a brand.css token; this
   file introduces no new literal.

   Mounts render EMPTY until lr-site.js (P3-B) writes into them, so most
   rules here reserve sane chrome height (so first paint doesn't jump once
   that content lands) plus the a11y/utility pieces the shell needs on day
   one: skiplink, live region, dialog host, main grid.

   .site-header / .site-footer / .container / .brand-logo keep the v3 look
   (colours, padding, logo sizing) already defined in brand.css — reused
   verbatim so the v4 shell matches the v3 site; this file adds only what
   brand.css doesn't already cover for the v4 mount contract.
   ========================================================================== */

/* -----------------------------------------------------------------------
   0. Page frame
   -------------------------------------------------------------------- */
html, body.lr-site { min-height: 100%; }

body.lr-site {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: linear-gradient(180deg, var(--bg-grad-1) 0%, var(--bg) 40%, var(--bg-grad-2) 100%) no-repeat var(--bg);
  color: var(--text);
  font-family: var(--font-body);
}

/* -----------------------------------------------------------------------
   1. Skip link — visually hidden until keyboard-focused
   -------------------------------------------------------------------- */
.skiplink {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: 10px 16px;
  background: var(--navy-700);
  color: var(--text);
  border-radius: 0 0 var(--radius-sm) 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  text-decoration: none;
}
.skiplink:focus { left: 0; }

/* -----------------------------------------------------------------------
   2. (retired) Nav / toolbar strips
   2026-09-09, Unit 2: both mounts are gone — the previous version's floating
   command card (brand.css's .commandcard-wrap / .commandcard / .cc- family)
   replaces the tab strip AND the edition-line toolbar, and it needs nothing
   from this file. The [data-lr-mount="header"] reservation rule retired with
   Unit 1 for the same reason (brand.css's own .site-header covers it).
   -------------------------------------------------------------------- */

/* -----------------------------------------------------------------------
   3. Main content grid
   -------------------------------------------------------------------- */
/* 2026-09-09, Unit 3: #lr-main is a plain block with NO horizontal padding
   and no flex gap. Page layout moved onto brand.css's own .section (vertical
   rhythm) + .container (max-width and side gutter) pattern, which lr-site.js
   renders inside the page-header and blocks mounts — exactly the frame the
   previous version's pages sat in. Main keeping its own padding on top of
   that would double the gutter and push the content off the hero's measure. */
#lr-main {
  flex: 1 0 auto;
  width: 100%;
  display: block;
}

[data-lr-mount="page-header"]:empty,
[data-lr-mount="page-footer"]:empty { display: none; }

/* The pair of <section>s the two content mounts render meet in the middle:
   .section's own clamp padding stays around the OUTSIDE of the page, and
   nothing is added between the title and the body. */
.lr-section--head { padding-top: clamp(30px, 4vw, 52px); padding-bottom: 0; }
.lr-section--body { padding-top: 0; }

/* -----------------------------------------------------------------------
   4. Dialog host + live region
   -------------------------------------------------------------------- */
[data-lr-mount="dialogs"] {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
[data-lr-mount="dialogs"]:empty { display: none; }
[data-lr-mount="dialogs"] > * { pointer-events: auto; }

.lr-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* -----------------------------------------------------------------------
   5. Tenth shell (D5): tablet.html's deck-style presentation over the same
   mount contract. This is the only selector in this file keyed off
   data-view — tablet.html is still generated from the one shell template
   and differs from the other nine only by this attribute (§2.2), plus the
   top bar's own "Full experience" exit link (tools/scaffold-shells.mjs).
   The deck itself (slides, paging UI, swipe) is lr-site.js's (renderTabletDeck/
   wireDeck); its styles are §9 below. This section clears every piece of
   chrome the deck replaces with its own — the hero (the deck's cover slide
   IS the hero), the command card, the pager and the footer — and makes the
   shell fill the viewport instead of scrolling, exactly the previous
   version's `.tabletmode` (Unit 8).

   2026-09-09, Unit 2: lr-site.js's renderChrome() already renders neither
   the command card nor the pager on the deck; these two rules are the belt
   to that braces, so a deck that boots from a stale cache still can't grow
   a command card halfway down a swipeable surface. */
body[data-view="tablet"] {
  height: 100dvh;
  overflow: hidden;
}
body[data-view="tablet"] [data-lr-mount="hero"],
body[data-view="tablet"] [data-lr-mount="commandbar"],
body[data-view="tablet"] [data-lr-mount="page-footer"],
body[data-view="tablet"] .site-footer {
  display: none;
}
body[data-view="tablet"] #lr-main {
  max-width: none;
  padding: 0;
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  display: flex;
}
/* min-width: 0 on both flex items above: without it, a flex item's
   automatic minimum size is its content's min-content width — and the
   deck's own track (11+ slides, each flex: 0 0 100%, laid out nowrap side
   by side) has a huge min-content width, since flex-basis is ignored by
   min-content sizing. Left at the default `min-width: auto`, that
   min-content size wins over `flex: 1 1 auto`'s "fill the viewport" and
   the whole deck balloons to slide-count × viewport-width instead of
   staying one viewport wide. */
body[data-view="tablet"] [data-lr-mount="blocks"] {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  display: flex;
}
/* The top bar's own exit link (tablet.html only — every other shell has the
   fab below instead). Sits after the language select, far right. */
.lr-deck-exit {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  margin-left: 14px;
}
.lr-deck-exit:hover { color: #fff; }

/* -----------------------------------------------------------------------
   5b. The "Tablet view" fab (Unit 8) — every report shell but tablet.html.
   Ported from the previous version's .fabcluster/.tablet-fab (brand.css);
   this is the one action that survived that cluster, so it is a single
   round-cornered pill rather than a whole expand/collapse menu.
   -------------------------------------------------------------------- */
/* Label ink on the blue ramp is #04193b, never #fff -- both gradient stops are
   light, so a white label has no AA-passing area. Full reasoning and the list
   of which selectors are deliberately left white: brand.css, above
   .btn-primary. The five sites in this file are .lr-tablet-fab,
   .cc-nav a.is-active, .cc-explore.is-active, .explore-picker__row.is-active
   and .explore-dd__btn[data-count]. */
.lr-tablet-fab {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 80;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13.5px;
  color: #04193b;
  background: linear-gradient(135deg, var(--blue), var(--blue-300));
  border-radius: 999px;
  padding: 13px 20px 13px 16px;
  box-shadow: 0 16px 38px -14px rgba(7, 167, 232, .65);
  transition: transform .15s ease;
}
.lr-tablet-fab:hover { transform: scale(1.04); color: #04193b; }
.lr-tablet-fab svg { width: 18px; height: 18px; flex: none; }
@media (max-width: 700px) { .lr-tablet-fab { display: none; } }
@media (prefers-reduced-motion: reduce) { .lr-tablet-fab:hover { transform: none; } }

/* -----------------------------------------------------------------------
   6. Narrow viewport (390px-class handsets)
   2026-09-09, Unit 3: brand.css's .container already floors its side gutter
   at 18px, and #lr-main no longer pads at all, so the old override here
   (#lr-main { padding-inline: 14px }) had nothing left to override.
   -------------------------------------------------------------------- */

/* -----------------------------------------------------------------------
   7. Unit 2 (2026-09-09) additions.

   The command card, the "Set your scenario" modal and every control inside
   them are the previous version's components, and brand.css already owns
   every one of their classes — .commandcard-wrap / .commandcard / the .cc-
   family / .viewtoggle / .scenario-trigger / .modal-overlay / .modal /
   .filters--modal / .scfield / .profsw / .lr-lang. So the whole v4-specific
   Customize-dialog stylesheet that used to live here (the two-tier
   fieldsets, .lr-switch, .lr-customize__* and the .lr-toolbar__* strip)
   retired with the markup it styled.

   What is left below is only what genuinely has no rule anywhere: the
   Section menu's <a> items (brand.css styles .cc-nav button — this menu
   navigates between pages, so its items are real links), the one-sentence
   note under each scenario row's head, the language block's position in the
   top bar, and the Previous/Next pager.
   -------------------------------------------------------------------- */

/* The Section menu's items are <a href> links, not buttons. Same
   declarations brand.css gives .cc-nav button, plus text-decoration:none —
   kept here so the previous version's component library stays untouched. */
.cc-nav a {
  cursor: pointer;
  text-align: left;
  color: var(--text-soft);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 14px;
  padding: 9px 12px;
  border-radius: 9px;
  text-decoration: none;
}
.cc-nav a:hover { color: #fff; background: var(--surface-2); }
/* Eight real page titles need more room than brand.css's 240px default (the
   previous version's menu held five short section names).
   2026-09-11, B2: the Explorer is no longer a ninth entry here — it has its
   own command-card button (.cc-explore, below) — so this is eight, not nine. */
#lrCardNav { min-width: min(340px, calc(100vw - 48px)); }
.cc-nav a.is-active { color: #04193b; background: linear-gradient(135deg, var(--blue), var(--blue-300)); }

/* A set scenario can be long ("EMEA · Financial Services · +2 filters"), and
   at ≤1080px the right-hand CTAs share one row, equally — so the label
   truncates rather than pushing a sibling CTA off the card. */
.scenario-trigger { min-width: 0; }
.scenario-trigger [data-lr-scenario-label] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 2026-09-11, B2: "Explore Data" — the command card's own link to
   explore.html, between the View toggle and Customize Scenario. Plain .btn
   chrome (brand.css) reads as a secondary action beside the two tinted CTAs
   either side of it; marked current with the same active look the Section
   menu gives its own current entry, so a reader already on the Explorer
   sees it highlighted rather than just another link. */
.cc-explore.is-active {
  background: linear-gradient(135deg, var(--blue), var(--blue-300));
  border-color: transparent;
  color: #04193b;
}

/* Each scenario row's own one-sentence note, between the row head and its
   fields. (.scrow__help, brand.css, is the same type at the BOTTOM of the
   modal; this is the same look with the margin the other way round.) */
.scrow__note {
  margin: -4px 0 12px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
/* The base line hides rather than collapsing to an empty paragraph when the
   engine is absent (methodology.html omits the dataset by design). */
.scrow__base[hidden] { display: none; }
.scenario-copy-status { margin-right: auto; font-size: 12.5px; color: var(--muted); }

/* The language block sits at the far right of the top bar, vertically
   centred. brand.css's .lr-lang is already `display:flex; align-items:
   center`; these add only the gap to the flag/state spans it now hosts and
   keep the top bar's flex row from stretching it. */
.lr-lang[data-lr-mount="lang"] { flex: none; gap: 8px; }
/* 2026-09-10, Unit 7: the rule that hid `.lr-lang__disclaimer` here is gone
   with the element. A disclaimer rendered and then hidden is not a
   disclaimer; D2's one sentence now has one home, `.site-footer__disclaimer`
   below, and the pill carries it as a title tooltip. */

/* lr-translate.js's two hooks used to live in the retired toolbar strip. */
.lr-toolbar__ai-flag {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border-2);
  background: var(--surface-2);
  color: var(--text-soft);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  white-space: nowrap;
}
.lr-toolbar__ai-flag[hidden] { display: none; }
.lr-toolbar__translate-state { font-size: 12px; color: var(--muted); white-space: nowrap; }
.lr-toolbar__translate-state[hidden] { display: none; }
/* 2026-09-10: in ONE state the status line is a control — "Select the
   language again to translate this page", where the click itself is the user
   gesture Chrome needs before it will download an on-device model. It reads
   as a control only in that state. */
.lr-toolbar__translate-state[data-lr-translate-retry] {
  color: var(--text);
  cursor: pointer;
  text-decoration: underline;
}

/* Previous / Next, in the page-footer mount. */
/* 2026-09-09, Unit 3: #lr-main gave up its own max-width and side padding
   (page layout is .section + .container now), so the pager's own .container
   is what lines it up with the prose above it — the rule that used to zero
   its padding (to stop a double gutter under the old #lr-main) would now
   push it flush to the viewport edge. */
.lr-pager { margin-top: 34px; padding-bottom: clamp(24px, 4vw, 44px); }
.lr-pager__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.lr-pager__link { max-width: 46%; }
.lr-pager__spacer { flex: 0 0 auto; }
@media (max-width: 600px) {
  .lr-pager__row { flex-direction: column; align-items: stretch; }
  .lr-pager__link { max-width: none; justify-content: center; }
}

/* 2026-09-09 Unit 2 — sticky command bar.
   position:sticky is constrained by the element's PARENT box. The card is
   rendered inside the <div data-lr-mount="commandbar"> mount, so the mount
   (whose parent is <main>, i.e. the whole page) is what must be sticky, not
   brand.css's .commandcard-wrap inside it. It sticks just below the top bar
   (which is itself sticky, 66px tall incl. border) with a lower z-index so the
   language select in the top bar stays clickable. */
[data-lr-mount="commandbar"] {
  position: sticky; top: 66px; z-index: 55;
  /* 2026-09-09, Unit 3: #lr-main no longer pads, so the negative-margin hack
     that escaped that padding is gone. What is left is the previous
     version's own -26px pull up over the hero's bottom edge. */
  margin-top: -26px;
  margin-bottom: 0;
}
[data-lr-mount="commandbar"] .commandcard-wrap { position: static; margin: 0; }
/* 2026-09-11: with the Explore Data button (B2) the five controls measured
   1,139px against a 1,128px card interior at a 1280px viewport, so the card
   wrapped to two rows in English. Tighter gaps keep it on one row down to
   ~1,250px; below that it wraps as designed. */
[data-lr-mount="commandbar"] .commandcard { gap: 12px 16px; padding: 12px 14px; }
[data-lr-mount="commandbar"] .cc-group { gap: 16px; }
[data-lr-mount="commandbar"] .cc-group--right { gap: 10px; }
@media (max-width: 700px) { [data-lr-mount="commandbar"] { position: static; margin-top: 14px; } }

/* 2026-09-09 (Ben): "Current view: …" — the active scenario, in italics, under
   the command card and above the page header. Only present in the
   personalized view; scrolls away with the page like any other content. */
.lr-current-view { margin: 14px 0 4px; color: var(--muted); font-size: 14px; }
.lr-current-view em { font-style: italic; }

/* =========================================================================
   8. Unit 3 (2026-09-09) — lr-blocks.css retired.

   assets/css/lr-blocks.css was v4's own generic block stylesheet. Every
   component it styled now renders in one of the previous version's own
   brand.css components instead (.case-feature/.cfstat, .vcar, .taki,
   .quotebox/.qmodal, .card.chart-card, .report-callout, .wtd, .profstrip),
   so the file is deleted and its <link> is gone from the shell template.

   What is left of it lands here, and only this: the handful of rules with
   no brand.css component of their own (a page notice, a plain list, the
   generic callout, the tablet deck), plus the small additions the new
   markup needs. Nothing below invents a colour, radius or spacing — every
   value is a brand.css token.
   ====================================================================== */

/* ---- ONE body type, site-wide (2026-09-10, polish 1, Ben) --------------
   Every page's body text is the Executive Summary's body text. The values
   are brand.css's `.case-feature__prose p` — 19px / 1.78 / #e2eaf7, the type
   Ben signed off on for the feature block — lifted into four custom
   properties so every other prose selector can POINT at them rather than
   carry its own copy of the numbers. Change the type here and the whole site
   moves together; nothing below this block hard-codes a body size or colour.

   The measure is brand.css's own --measure-body (72ch), unchanged.
   Deliberate exception: `.quotebox--analysis`'s pulled quote keeps its own
   19.5px italic (Ben), and headings, kickers, eyebrows, notes, chart chrome
   and the .cfstat number are not body text and are untouched. */
:root {
  --body-size: 19px;
  --body-lh: 1.78;
  --body-color: #ffffff;
  --body-measure: var(--measure-body, 72ch);
}

/* ---- METRIC CALLOUT type (2026-09-10, polish 3, Ben) ------------------
   A metric callout's supporting lines — the label under the number, the
   analyst's delta sentence and §4.6's compare row — are NOT body text. They
   are a caption on a big number, in both metric components (.cfstat in the
   feature block's Key Metrics column, .taki__metric anywhere else), so they
   get their own two tokens rather than 19px/1.78 body type or a hard-coded
   number at each selector. Change the pair here and every metric callout on
   the site moves together.

   --metric-accent is the light blue the metric VALUE is drawn in
   (brand.css's .cfstat__n gradient runs #77CFF2 -> var(--blue-300), which
   are the same colour, and .taki__metric .n is flat var(--blue-300)) — so
   this token IS --blue-300, named for the job it does here. Ben's rule:
   every metric subtext that is not a literal rise (green) or a fall/warning
   (red) is drawn in the value's own light blue, never in white/body colour.
   The recommendations list's bold lead sentence (§ .wtd-item__lead) reads
   the same token: one page accent, one definition. */
:root {
  --metric-size: 16px;
  --metric-lh: 1.3;
  --metric-accent: var(--blue-300);
}

/* ---- CHART LABEL type (2026-09-10, polish 4, Ben) ---------------------
   ONE size and ONE colour for every piece of text a chart DRAWS: axis tick
   labels, category labels, value labels, series labels and legend items, in
   every chart type. Before this the engine carried a per-renderer scatter of
   10px / 10.5px / 11px / 11.5px / 12px / 12.5px / 13px literals, so an axis
   tick was a different size on a line chart, a column chart and a box plot,
   and the doughnut's legend was a different size again from the bar legend.

   --chart-label-size is read by the SVG renderers as well as by CSS: SVG
   presentation attributes cannot understand var(), so fcr-chart-core.js and
   fcr-chart-core-ext.js resolve this property once per render through
   getComputedStyle (labelSize(), the same trick resolveColor() has always
   used for colours) and write the number into `font-size`. Since polish 2
   every renderer sizes its viewBox to the card's real pixel width, so 13 in
   the viewBox IS 13px on screen at every card width.

   --chart-label-color is the card's own footer-note colour (--text-soft), so
   a label, a base line and a subtitle are one family. Two deliberate
   exceptions keep their own ink, because they are not "a label on the card
   background": a value label drawn INSIDE a coloured segment (contrast ink
   picked per segment colour — a muted grey on a green bar is unreadable),
   and the D4 reader-highlight treatments, which are an emphasis state.
   Only the SIZE is universal for those two. */
:root {
  --chart-label-size: 13px;
  --chart-label-color: var(--text-soft);
}

/* ---- page-level notices (renderEditionLine / the empty-page notice) ---- */
.lr-empty-notice,
.lr-edition-line {
  font-size: 13px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin: 0 0 18px;
}

/* ---- prose runs outside the feature block -----------------------------
   brand.css's .report-prose supplies the paragraph type; it has no measure
   of its own, and unbounded 16px lines at 1280px are unreadable. */
.report-prose { max-width: 82ch; }
.report-prose > p:last-child { margin-bottom: 0; }
.report-prose + .report-prose { margin-top: 16px; }
.report-prose a,
.case-feature__prose a,
.taki__body a { color: var(--blue-300); text-decoration: underline; text-underline-offset: 2px; }

/* ---- a subheader and a list rendered through the generic block list ----
   (the tablet deck, and any page shape the composer hands straight to
   renderBlockList — a section page's own subheaders become .taki rows) */
.lr-sub { font-size: 20px; color: var(--text); margin-top: 36px; }
.lr-list {
  margin: 0 0 20px;
  padding-left: 22px;
  color: var(--text-body);
  font-size: 15px;
  line-height: 1.6;
}
.lr-list li { margin-bottom: 8px; }
.lr-list li:last-child { margin-bottom: 0; }

/* ---- {{callout}} -> the analyst's aside (2026-09-10, Unit 4A) ----------
   brand.css has no callout component, but it HAS this box: `.wtd`, the
   previous version's teal-rule "what to do" callout. The analyst callout is
   that box with no label — Futurum speaking in its own voice inside the
   analysis, not a labelled instruction — so it reuses .wtd's own values
   (3px blue left rule, the same padding, the same tint and corner) rather
   than inventing a second highlighted box. Full width, unlike .wtd: a
   callout in the report flow is the section's own aside, not a note inside
   a card. Replaces v4's amber `.lr-callout`. */
.analyst-callout {
  margin: 22px 0;
  padding: 14px 16px 14px 18px;
  border-left: 3px solid var(--blue);
  background: rgba(7, 167, 232, .07);
  border-radius: 0 10px 10px 0;
}
.analyst-callout p {
  margin: 0 0 12px;
  font-size: var(--body-size); line-height: var(--body-lh); color: var(--body-color);
}
.analyst-callout > :last-child { margin-bottom: 0; }
.analysis .analyst-callout { margin: 16px 0 0; }

/* ---- the feature block's side column ---------------------------------
   brand.css's .cfstat is the previous version's own two-line stat (number +
   label). A metric carries two more reader-facing lines it has to keep:
   §4.6's comparison row (the normalized whole-sample figure personalization
   bumped off the headline) and the analyst's own delta sentence.

   2026-09-10, polish 1 (Ben): both lines are body size and italic, one line
   each, and they take their colour from the delta's `dir` word — green for a
   literal rise, red for a fall or a `warn`, and plain body colour for a
   `note` and for no icon at all. The benchmark rows ("Leaders 74%") are gone:
   `bench` stays in metrics.yaml as analyst data and is rendered nowhere, so
   .cfstat__bench / .cfstat__bench-row no longer exist. */
/* 2026-09-10, polish 3 (Ben): all three lines are 16px/1.3 through the
   --metric-* tokens above (not body type), and the colour rule is now
   closed: green for `up`, red for `down`/`warn`, and the metric value's own
   light blue for EVERY other case — `note`, no icon at all, and the compare
   row. 2026-09-10 (Ben): the LABEL stays in the white body colour; only
   the subtext lines take the light blue. Value blue / label white /
   subtext blue. */
.cfstat__l,
.cfstat__delta,
.cfstat__cmp {
  font-size: var(--metric-size); line-height: var(--metric-lh);
  color: var(--metric-accent);
}
.cfstat__l { margin-top: 6px; color: var(--body-color); }
.cfstat__delta,
.cfstat__cmp {
  font-style: italic;
  margin-top: 6px;
}
.cfstat__delta--up { color: var(--green-300); }
.cfstat__delta--down,
.cfstat__delta--warn { color: var(--red); }
/* --note and --none keep the light blue set above: they are interesting,
   not good or bad. */

/* The dir icon, inline before the delta text: it takes the line's own
   colour and rides the text's own size. */
.mico { width: .78em; height: .78em; flex: none; vertical-align: -.03em; }

/* The authored words drawn INSIDE the big value ("Only 2.9%").
   2026-09-10, polish 3 (Ben): the affix is drawn at the SAME size, weight
   and fill as the number itself — the earlier 0.72em step is gone. `1em`
   rather than no declaration at all, so the rule is explicit and a later
   stylesheet cannot re-shrink it by accident; the colour/gradient needs no
   declaration (brand.css clips .cfstat__n's gradient to the text of the
   whole element, and -webkit-text-fill-color inherits). */
.cfstat__n,
.taki__metric .n { text-wrap: balance; }
.cfstat__n .n__pre, .cfstat__n .n__suf,
.taki__metric .n .n__pre, .taki__metric .n .n__suf {
  font-size: 1em;
  font-weight: inherit;
  color: inherit;
}

/* Ben: "the hover effect used to be a little smoother." The v3 markup took
   its transform transition from brand.css's `.reveal` rule (transform .2s
   ease) — which the .cfstat markup carries again as of polish 1 — and this
   softens the grow into a longer eased curve, leaving the reveal's own
   opacity fade to brand.css. */
.case-feature__side .cfstat {
  transition: opacity .45s ease, transform .34s cubic-bezier(.22, .7, .25, 1);
}
@media (prefers-reduced-motion: reduce) {
  .case-feature__side .cfstat { transition: none; }
}

/* ---- metric tiles ----------------------------------------------------
   brand.css's .taki__metrics is built for the previous version's three
   tiles; a metric row is 1 or 2 (2026-09-10, polish 1: the exec-summary rows
   are exactly two, and a modal carries no metric at all).
   The delta line gets the same treatment as the .cfstat one — body size,
   italic, coloured by `dir`. */
.taki__metrics--1 { grid-template-columns: minmax(0, 1fr); }
.taki__metrics--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.taki__metrics--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 2026-09-10, polish 3 (Ben): the tile's three supporting lines read the
   same --metric-* tokens and the same closed colour rule as the .cfstat
   ones — the two components can never disagree about metric caption type.
   `.src` is §4.6's compare row (renderMetricTile's `.src.src--cmp`). */
.taki__metric .l,
.taki__metric .delta,
.taki__metric .src {
  font-size: var(--metric-size); line-height: var(--metric-lh);
  color: var(--metric-accent);
}
/* 2026-09-10 (Ben): label white, subtext blue — same rule as .cfstat__l. */
.taki__metric .l { margin-top: 6px; color: var(--body-color); }
.taki__metric .delta {
  font-style: italic;
  margin-top: 6px;
}
.taki__metric .src { font-style: italic; margin-top: 5px; }
.taki__metric .delta--up { color: var(--green-300); }
.taki__metric .delta--down,
.taki__metric .delta--warn { color: var(--red); }
@media (max-width: 640px) {
  .taki__metrics, .taki__metrics--2, .taki__metrics--4 { grid-template-columns: minmax(0, 1fr); }
}

/* ---- Key Insights rows: the kicker and title are links ----------------
   Ben: the underline appears on hover/focus only, so the row still reads as
   a heading rather than as a link list. */
/* 2026-09-10, polish 1 (Ben): the body text INSIDE a Key Insights row is the
   same body type as everywhere else. brand.css's `.taki__body` carries the
   tokens, but renderTakiRow() emits the subsection's paragraphs through the
   ordinary paraHtml() (they keep their own tids), so the row's text column
   needs the same three tokens by selector. */
.taki__text p {
  font-size: var(--body-size);
  line-height: var(--body-lh);
  color: var(--body-color);
  max-width: var(--body-measure);
}
.taki__text p:last-child { margin-bottom: 0; }

.taki__kicker a,
.taki__heading a { color: inherit; text-decoration: none; }
.taki__kicker a:hover, .taki__kicker a:focus-visible,
.taki__heading a:hover, .taki__heading a:focus-visible { text-decoration: underline; }
.taki__kicker a { display: inline-flex; align-items: center; gap: 2px; }
.taki__kicker-arrow { width: 13px; height: 13px; flex: none; }
.taki__side > .quotebox { margin-top: 0; }
.taki__metrics + .quotebox { margin-top: 4px; }
.quotebox:focus-visible { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(7,167,232,.35); }

/* ---- What To Do Next: a plain list (2026-09-10, polish 1, Ben) ---------
   The coloured `.wtd` boxes are gone. Each recommendation is one list item:
   a small themed icon on the left, aligned to the first line, the first
   sentence bold, the rest ordinary body text, comfortable air between items,
   no background and no border. The `wtd` prefix is kept so the component
   keeps the name the renderer and the tests already use. */
.wtd-list {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
}
.wtd-item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
}
.wtd-item + .wtd-item { margin-top: 26px; }
.wtd-item__ico {
  width: 18px;
  height: 18px;
  flex: none;
  color: var(--blue);
  /* Centred on the first line's cap height rather than its box top, so the
     glyph sits with the text at any body size. */
  margin-top: calc((var(--body-size) * var(--body-lh) - 18px) / 2);
}
.wtd-item__t {
  margin: 0;
  font-size: var(--body-size); line-height: var(--body-lh); color: var(--body-color);
  max-width: 88ch;
}
/* 2026-09-10, polish 3 (Ben): the bold FIRST SENTENCE is drawn in the page's
   light blue accent — the same --metric-accent the metric values use — and
   stays bold. The rest of the item keeps the body colour set above. The
   renderer gives the <b> a class of its own (.wtd-item__lead) so the accent
   can never leak onto an authored **bold** phrase later in the sentence. */
.wtd-item__t b { color: var(--text); font-weight: 700; }
.wtd-item__t b.wtd-item__lead { color: var(--metric-accent); font-weight: 700; }

/* ---- chart cards ------------------------------------------------------
   brand.css styles .card.chart-card and .grid-2; these two are what the
   pairing needs on top: cards top-aligned in the row (a tall card must not
   stretch a short one) and the row's own vertical rhythm. */
.lr-chart-grid { align-items: start; margin-top: 14px; }
/* The card is a <figure> (its head is a real <figcaption>, so the figure has
   an accessible name) where the previous version used a plain <div>. A
   <figure> carries the UA's own `margin: 1em 40px`, which would inset every
   chart 40px inside its own column; the previous version's spacing is the
   margin-top below and nothing else. */
figure.chart-card { margin: 14px 0 0; }
.lr-chart-grid > figure.chart-card { margin-top: 0; }
.chart-card__title { margin: 0; }
/* 2026-09-16 (a11y fix, nested-interactive): the figure itself is no longer
   role="button"/tabindex (a clickable figure around real <button> children
   is a nested-interactive control axe flags) — the title is a real <button>
   instead, reset to read exactly like the plain text it replaces. Its click
   still bubbles to the card's own lightbox-open listener, so nothing about
   the mouse behaviour or the look changes; a keyboard user now lands on a
   focusable control with the same aria-label the card used to carry. */
.chart-card__title-btn {
  appearance: none; -webkit-appearance: none; display: inline; border: 0; background: none;
  margin: 0; padding: 0; font: inherit; color: inherit; text-align: inherit; cursor: pointer;
}
.chart-card .card__head { align-items: flex-start; margin-bottom: 4px; }
.chart-card .chart-png { flex: none; white-space: nowrap; }
@media (max-width: 720px) {
  .lr-chart-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- the INLINE chart card (2026-09-10, polish 4, Ben) ----------------
   `{{chart:<id> width=inline}}` in a page's lead zone draws the card inside
   the feature block's LEFT column, directly under the lead paragraphs
   (assets/js/lr-site.js, renderFeatureBlock's `inlineCharts`). Section 1's
   Figure 1 is the worked example.

   Three rules, and no new component: the card is the same `.chart-card` a
   full-width one is.

   1. It FILLS the prose column. The column is a grid track (1.55fr), so the
      card only needs its own box not to opt out of it — `width: 100%` and
      no max-width of its own, with min-width: 0 on the track so a wide SVG
      cannot push the track past its share (brand.css does the same for the
      methodology tables).
   2. The row height is SHARED. `.case-feature` keeps `align-items: start`,
      so the metrics still sit at the top of their column; what changes is
      the aside itself, which stretches to the full row height — otherwise
      its left-hand rule would stop where the last metric ends and leave the
      chart beside a blank half-column. `--haschart` is on the block only
      when a chart is actually inside it, so a feature block with no inline
      chart is untouched.
   3. Its vertical rhythm is the paragraph above it: the last lead paragraph
      keeps its own bottom margin (it is no longer `:last-child`), so the
      card adds only a small top margin on top of that. */
.case-feature--haschart .case-feature__side { align-self: stretch; }
.case-feature__prose { min-width: 0; }
.case-feature__prose > figure.chart-card--inline {
  width: 100%;
  max-width: none;
  margin: 10px 0 0;
}
@media (max-width: 900px) {
  /* One column: the aside's rule is a top border down here (brand.css), so
     there is no row height left to share. */
  .case-feature--haschart .case-feature__side { align-self: auto; }
}

/* ---- the quote modal --------------------------------------------------
   Same widening the previous version gave its own quote modal
   (`#lrQuotesModal .modal { width: min(900px,100%) }`): two columns of
   quotes need the room. .qmodal__item is a <figure>, so its UA
   `margin-inline: 40px` is reset — brand.css's .qmodal__list already owns
   the 44px column gap, and the previous version's own rule shows the
   columns were meant to run full width. A modal holding ONE quote drops to
   a single column rather than leaving half the sheet empty. */
[data-lr-modal-dialog] .modal { width: min(900px, 100%); }
figure.qmodal__item { margin: 0; }
.qmodal__list--1 { grid-template-columns: minmax(0, 1fr); }
/* 2026-09-10, polish 1 (Ben): a From-the-interviews modal is quotes and
   nothing else, so the list is the last thing in the dialog and brand.css's
   per-item hairline would leave the modal ending on a rule. The metric row
   that used to follow the list is gone with the metrics themselves. */
.modal-overlay .qmodal__item:last-child { border-bottom: 0; padding-bottom: 4px; }

/* ---- the inline profile strip ----------------------------------------- */
.profstrip { margin-top: 22px; }

/* ---- report sections + Detailed Analysis pairs (2026-09-10, Unit 4A) ---
   brand.css owns every component here (.report-sec, .report-sec__h,
   .summary-prose.report-prose, .analysis*, .grid.grid-2). These are the
   three things it does not have, because the previous version had them as
   inline styles on the row (`margin-top:34px;align-items:center`) or did
   not put charts inside a .report-sec at all:

   1. .report-sec is capped at 82ch for READING. A subsection that also
      carries a chart or an analysis pair has to span the column, with only
      its prose keeping the measure.
   2. the pair's own vertical rhythm and centre alignment.
   3. the .analysis column's first child must not carry a top margin — the
      row centres the two sides, so a leading margin tips the text up. */
.report-sec--flow { max-width: none; }
.report-sec--flow > * { max-width: 82ch; }
.report-sec--flow > .analysis-row,
.report-sec--flow > .lr-chart-grid,
.report-sec--flow > .profstrip,
.report-sec--flow > figure.chart-card { max-width: none; }
.analysis-row { margin-top: 34px; align-items: center; }
.analysis-row > .analysis > :first-child { margin-top: 0; }
.analysis-row .analysis p { margin: 0 0 14px; }
.analysis-row .analysis p:last-of-type { margin-bottom: 0; }
.analysis-row .taki__metrics { margin-top: 18px; }
.report-sec--flow .analysis-row > figure.chart-card { margin-top: 0; }
/* brand.css collapses .grid-2 at 720px, which is right for two text cards
   and too late for a chart beside prose: at 900px each column is ~400px and
   the chart's category labels start to crowd. The pair stacks earlier, chart
   first, and stops centring once it is one column. */
@media (max-width: 900px) {
  .analysis-row { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .analysis-row > .analysis { margin-top: 18px; }
  .analysis-row[data-lr-analysis-row="text-first"] > .analysis { order: 2; margin-top: 18px; }
}

/* ---- the quotes-only analysis column (2026-09-10, Unit 4B) -------------
   A pair whose text half is nothing but quotes renders the previous
   version's `.quotebox` card instead of the hairline `.analysis-quote`.
   brand.css already owns the card itself (.quotebox, .quotebox__head,
   .quotebox__expand, .quotebox__current, .feed-tag.pov) and the carousel
   slides (.vcar__item, .vcar__track, .vcar__nav) — these rules only:

   1. widen the row's gutter from .grid's 18px to a real 32px, so the card
      does not hug the chart card's edge;
   2. centre the card in its column and cap the quote at ~38ch, so the
      lines read like a pull quote instead of spanning the page;
   3. re-skin the ported .vcar slides COMPACT: one slide fully visible
      rather than 64% with faded neighbours, left-aligned, no 54px opening
      glyph (the text carries its own curly quotes), smaller nav circles,
      and a dot row between them. */
.analysis-row--quotes { gap: 32px; }
.analysis--quotes { display: flex; justify-content: center; }
.analysis-row > .analysis--quotes { align-self: center; }

.quotebox--analysis {
  padding: 28px;
  max-width: 460px;
  width: 100%;
  border-radius: var(--radius);
}
.quotebox--analysis .quotebox__head { margin-bottom: 14px; }
.quotebox--analysis .quotebox__current {
  font-size: 19.5px;
  line-height: 1.62;
  font-style: italic;
  color: #fff;
  text-align: left;
  max-width: 38ch;
  margin: 0;
  min-height: 0;
}
.quotebox--analysis .quotebox__who {
  display: block;
  margin-top: 14px;
  color: var(--blue-300);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  font-style: normal;
  text-align: left;
}
/* A single quote is not a carousel and not a link to anywhere: kill the
   pointer, the hover lift and the hover border. */
.quotebox--static { cursor: default; }
.quotebox--static:hover { border-color: var(--border-2); box-shadow: none; transform: none; }

.qbcar__view { position: relative; overflow: hidden; }
.quotebox--analysis .vcar__track { display: flex; gap: 28px; will-change: transform; }
.quotebox--analysis .vcar__item.qbcar__item {
  flex: 0 0 100%;
  /* <figure>'s UA margin is `1em 40px`, which brand.css never had to reset
     because a 64%-wide .vcar slide had room for it. A 100%-wide slide does
     not: the 40px pushes the text past the view's overflow edge. */
  margin: 0;
  min-width: 0;
  padding: 0;
  text-align: left;
  opacity: 1;
}
.qbcar__item blockquote::before { content: none; display: none; }
.qbcar__item figcaption { color: var(--muted); font-size: 13px; }

.qbcar__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 18px;
}
.vcar__nav.qbcar__nav { width: 32px; height: 32px; font-size: 19px; }
.qbcar__dots { display: flex; align-items: center; gap: 8px; }
.qbcar__dot {
  appearance: none;
  width: 8px;
  height: 8px;
  padding: 0;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  background: var(--border-2);
  transition: background .2s ease, transform .2s ease;
}
.qbcar__dot.is-active { background: var(--blue-300); transform: scale(1.35); }
.qbcar__dot:hover { background: var(--blue); }

@media (max-width: 900px) {
  .analysis-row--quotes { gap: 18px; }
  .analysis--quotes { justify-content: flex-start; }
  .quotebox--analysis { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .quotebox--analysis .vcar__track { transition: none !important; }
  .qbcar__dot { transition: none; }
}

/* ---- the doughnut's own size (2026-09-10, Unit 4B) --------------------
   brand.css's chart-embed rule stretches every core SVG to `width:100%`,
   which is right for a bar/line plot (they fill the card) and wrong for a
   ring: on a FULL-width card the 460×200 donut viewBox scaled to ~1100px
   and the ring became a 400px wheel. It draws at its own natural width
   instead, shrinking only when the card is narrower than that — which
   gives the previous version's ~150px ring at both card widths, with the
   legend list beside it on a wide card and wrapped under it on a half. */
.corechart .donut-card svg { width: 460px; max-width: 100%; height: auto; }

/* ---- the chart card's "Figure n" eyebrow (2026-09-10, Unit 4B) --------
   The same type treatment as brand.css's .taki__kicker (display face, 800,
   uppercase, wide tracking, --blue-300) one size step down, written out here
   rather than borrowing the class: .taki__kicker is the KEY-INSIGHTS row's
   eyebrow, and several places (tests included) count it to answer "does
   this page use taki rows?". A chart card is not a taki row. */
.chart-card__fig {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 11.5px;
  color: var(--blue-300);
  margin-bottom: 5px;
}

/* ---- vertical rhythm between composed page pieces ---------------------
   The composer emits siblings, not a flex column with one gap (that gap is
   what #lr-main gave up above), so the pieces space themselves the way the
   previous version's own page did. */
.case-feature + .vcar,
.case-feature + .report-prose { margin-top: 26px; }
.report-prose + .vcar { margin-top: 24px; }

/* =========================================================================
   9. Tablet deck (Unit 8; redesigned 2026-09-10, Unit 8B) — a full-screen,
   swipeable presentation over the SAME composed content the report pages
   read (composePage's helpers, reused, not forked — see renderTabletDeck()'s
   own header in lr-site.js). Ported from the previous version's tablet.css
   look (huge display type, dark gradient slides, round prev/next + dots)
   onto the current component set: renderMetricTile() draws the metric tiles,
   renderChartFigure() the chart cards, renderWtdItem() the "What To Do Next"
   items — every one of them the SAME class the report page uses.

   THE RULE THIS SECTION KEEPS (Unit 8B): a deck is the report's own
   components at DECK TYPE SIZE, so everything here either lays a slide out
   or re-scales type through §9.0's tokens. NOTHING here restyles the inside
   of a chart. The previous pass broke exactly that rule — it gave the plot
   wrap `display: flex` and the SVG a CSS height, which (a) laid the DOM-row
   chart types out side by side instead of stacked, and (b) sized SVG charts
   by height against a viewBox the engine computes from WIDTH. Both are
   gone: the chart draws through the identical path it draws on a page, and
   its own ResizeObserver (assets/js/charts.js observeWidth) measures the
   real pixel width of the column it lands in.
   ====================================================================== */

/* ---- 9.0 DECK TYPE TOKENS -----------------------------------------------
   The one place the deck's scale is set. A deck is read from across a table,
   not at 45cm, so every size here is far larger than the page's: body text
   from 20px, a chart's own labels from 16px, a metric value from 44px.

   --chart-label-size is deliberately (re)defined on the ROOT element and
   not only on `body[data-view="tablet"]`: the SVG renderers cannot read a
   var() out of a presentation attribute, so they resolve this property once
   per render with getComputedStyle(document.documentElement) (see
   fcr-chart-core.js's labelSize()). Setting it on <body> alone would scale
   the CSS-drawn chart text (the DOM-row types) and leave every SVG chart at
   the page's 13px. `:root:has(body[data-view="tablet"])` reaches the root
   from the body attribute the shell actually carries; the body copy below
   stays as the fallback for an engine without :has(), where the CSS-drawn
   labels still scale and the SVG ones simply keep the page size. */
:root:has(body[data-view="tablet"]) { --chart-label-size: 16px; }
@media (min-width: 1100px) { :root:has(body[data-view="tablet"]) { --chart-label-size: 17px; } }
body[data-view="tablet"] {
  --deck-body: clamp(20px, 2.1vmin, 23px);
  --deck-lead: clamp(20px, 2.2vmin, 25px);
  --deck-title: clamp(28px, 3.8vmin, 42px);
  --deck-kicker: clamp(16px, 2vmin, 20px);
  --deck-metric-n: clamp(44px, 4.8vmin, 54px);
  --deck-metric-l: clamp(16px, 1.8vmin, 18px);
  --deck-metric-sub: clamp(14px, 1.5vmin, 16px);
  --deck-gap: clamp(16px, 2.4vmin, 30px);
  --chart-label-size: 16px;
  font-size: var(--deck-body);
}
@media (min-width: 1100px) { body[data-view="tablet"] { --chart-label-size: 17px; } }

/* ---- 9.1 the slide track ------------------------------------------------
   One full-viewport slide at a time, laid out side by side (never
   display:none) so a chart's canvas measures its real width the instant it
   binds — off-screen is a `translateX`, not a collapse.

   EVERY slide is exactly the space between the top bar and the bottom
   controls (the flex chain already gives it that — #lr-main/
   [data-lr-mount="blocks"]/.lr-deck are all flex:1 1 auto normal-flow
   siblings of the header and, inside .lr-deck, of .lr-deck__controls, so no
   slide ever needs its own calc(100vh - ...)). A slide does not SCROLL:
   `overflow: hidden` clips, and the content itself is short enough to fit
   at 1024x768, 1180x820 and 1366x1024 (§9.5's 3-per-slide first-sentence
   Key Insights, §9.7's same treatment for What To Do Next, §9.6's
   two-column section slide), so clipping is a safety net, not the fit. */
.lr-deck { display: flex; flex-direction: column; height: 100%; width: 100%; min-width: 0; overflow: hidden; }
.lr-deck__viewport { flex: 1 1 auto; min-height: 0; overflow: hidden; }
.lr-deck__track {
  display: flex;
  height: 100%;
  transition: transform .45s cubic-bezier(.22, .7, .25, 1);
  touch-action: pan-y;
}
@media (prefers-reduced-motion: reduce) { .lr-deck__track { transition: none; } }
.lr-deck__slide {
  flex: 0 0 100%;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 2.4vh, 32px) clamp(20px, 4vw, 56px);
  box-sizing: border-box;
}
.lr-deck__slide-inner { width: min(94vw, 1320px); max-height: 100%; text-align: center; }
/* The two slides whose content is naturally as tall as the space available
   (a section slide's chart column; the Explorer's own panel) stretch to it
   and lay themselves out as a column, instead of sizing to their content
   and floating in the middle. */
.lr-deck__slide--section,
.lr-deck__slide--explore { align-items: stretch; }
.lr-deck__slide--section .lr-deck__slide-inner,
.lr-deck__slide--explore .lr-deck__slide-inner {
  height: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
  text-align: left;
}

/* ---- 9.2 shared slide type ----------------------------------------------
   The kicker over every non-cover slide (Key Metrics / Key Insights /
   "Section n" / "Explore the data") — the same treatment as brand.css's
   .taki__kicker, at deck scale. */
.lr-deck__kicker {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: var(--deck-kicker);
  color: var(--blue-300);
  margin-bottom: clamp(12px, 2.4vmin, 26px);
  flex: none;
}
.lr-deck__slide--section .lr-deck__kicker { margin-bottom: clamp(6px, 1vmin, 10px); }

/* ---- 9.3 the cover slide ------------------------------------------------
   study.eyebrow/title/subtitle/analyst — the same fields and the same
   heroBylineHtml() markup renderHero() draws, at deck scale. */
.lr-deck__eyebrow { justify-content: center; font-size: clamp(14px, 2vmin, 19px); }
.lr-deck__cover-title {
  font-size: clamp(34px, 6.2vmin, 68px);
  line-height: 1.1;
  color: #fff;
  margin: 2.6vmin 0 3vmin;
  max-width: 22ch;
  margin-inline: auto;
}
.lr-deck__cover-sub {
  font-size: clamp(19px, 2.6vmin, 26px);
  line-height: 1.5;
  color: var(--text-soft);
  max-width: 60ch;
  margin: 0 auto;
}
.lr-deck__cover-sub em { display: block; margin-top: 12px; font-style: italic; color: var(--muted); }
.lr-deck__byline { justify-content: center; margin-top: clamp(20px, 3vmin, 30px); font-size: clamp(15px, 2vmin, 18px); }
.lr-deck__byline .hero__byline-authors { justify-content: center; gap: clamp(14px, 2.4vmin, 26px) clamp(24px, 4vmin, 44px); }
.lr-deck__byline .hero__author-photo { width: clamp(40px, 4.4vmin, 56px); height: clamp(40px, 4.4vmin, 56px); }
/* No "Data as of" pill: see deckCoverHtml()'s own comment in lr-site.js —
   there is no registry-sourced date to draw one from. */

/* ---- 9.4 the metric tiles (both slides that carry them) -----------------
   Value blue / label white / subtext blue is already .taki__metric's rule
   (§ above); this only re-scales the four tokens for a deck read from
   across a table. The Key Metrics slide runs one row of up to four tiles
   wide (wrapping on a narrow/portrait screen) with a bigger value still,
   since it is the only thing on that slide; a section slide stacks two or
   three of the same tiles in its right-hand column (§9.6). */
.lr-deck .taki__metric { padding: clamp(14px, 2.1vmin, 24px); text-align: left; }
.lr-deck__sec-metrics .taki__metric { padding: clamp(11px, 1.5vmin, 17px) clamp(13px, 1.8vmin, 19px); }
.lr-deck__sec-metrics .taki__metric .n { line-height: 1.04; }
.lr-deck__sec-metrics .taki__metric .l { margin-top: 2px; line-height: 1.24; }
.lr-deck .taki__metric .n { font-size: var(--deck-metric-n); }
.lr-deck .taki__metric .l { font-size: var(--deck-metric-l); color: #fff; }
.lr-deck .taki__metric .delta,
.lr-deck .taki__metric .src { font-size: var(--deck-metric-sub); }
.lr-deck__metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--deck-gap);
}
.lr-deck__metrics .taki__metric { padding: clamp(18px, 2.6vmin, 28px); }
.lr-deck__metrics .taki__metric .n { font-size: clamp(44px, 7.4vmin, 78px); }

/* ---- 9.5 Key Insights — numbered 01..N, headline only -------------------
   3 items per slide and title + FIRST SENTENCE only (deckInsightItemHtml's
   own firstSentence() call) — short enough, at this size, to read across a
   room and to fit one slide with no scroll. */
.lr-deck__insights { display: flex; flex-direction: column; gap: clamp(16px, 3vmin, 30px); text-align: left; justify-content: center; height: 100%; }
.lr-deck__insight {
  display: flex;
  gap: clamp(16px, 2.6vmin, 26px);
  align-items: flex-start;
  padding-bottom: clamp(14px, 2.6vmin, 24px);
  border-bottom: 1px solid var(--border);
}
.lr-deck__insight:last-child { border-bottom: 0; padding-bottom: 0; }
.lr-deck__insight-num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(26px, 4.2vmin, 40px);
  color: rgba(7, 167, 232, .65);
  line-height: 1.2;
  flex: none;
}
.lr-deck__insight-title { font-size: clamp(23px, 3.3vmin, 32px); color: #fff; margin: 0 0 .6vmin; }
.lr-deck__insight-body p { font-size: var(--deck-body); line-height: 1.45; color: #ffffff; margin: 0; }

/* ---- 9.6 the SECTION slide (Unit 8B, Ben) -------------------------------
   "A slide should not be complex, but it can have the title, a sentence or
   two or three, a chart, and two or three key metrics, all fitting a tablet
   screen." Kicker and title across the top, the section's opening sentences
   under them full width (they read as the slide's own standfirst, and a
   line of 90-odd characters at 21px+ is far more readable across the slide
   than squeezed into 39% of it), then a two-column body: the chart card at
   ~61% on the left, the metric tiles stacked at ~39% on the right.

   The chart card is CONTENT-HEIGHT and top-aligned in its column. That is
   the whole fix for the "small chart in a big box": nothing stretches the
   card to the slide's height any more, so there is no leftover space inside
   it to leave blank. The card fills the column's WIDTH, the chart engine
   measures that width (charts.js's ResizeObserver) and draws its viewBox to
   it, and the drawing's height follows from its own geometry — which is the
   one dimension this file must not touch.

   The PNG/Slide buttons, "Explore this figure" and the whole-card "Enlarge"
   affordance are switched off in §13 — a slide is not a place to open a
   lightbox or export a file the deck's own script set doesn't carry. */
.lr-deck__sec-title {
  font-size: var(--deck-title);
  line-height: 1.13;
  color: #fff;
  margin: 0 0 clamp(8px, 1.4vmin, 14px);
  flex: none;
}
.lr-deck__sec-lead {
  font-size: var(--deck-lead);
  line-height: 1.42;
  color: #ffffff;
  margin: 0 0 clamp(14px, 2.2vmin, 26px);
  max-width: 96ch;
  text-wrap: pretty;
  flex: none;
}
.lr-deck__sec-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 62fr) minmax(0, 38fr);
  gap: var(--deck-gap);
  align-items: center;
}
/* A section with no metric row of its own (or no chart at all) gets the one
   piece it does have, full width. */
.lr-deck__sec-body--one { grid-template-columns: minmax(0, 1fr); }
/* …and a section with NEITHER — this study's section 6, whose own content is
   the recommendation list the next two slides carry — is a statement slide:
   there is nothing to lay out beside the text, so the text centers in the
   slide at statement size instead of sitting above a 400px hole. */
.lr-deck__sec-body:empty { flex: 0 0 auto; }
.lr-deck__slide--section .lr-deck__slide-inner:has(> .lr-deck__sec-body:empty) { justify-content: center; }
.lr-deck__slide-inner:has(> .lr-deck__sec-body:empty) .lr-deck__sec-title { font-size: clamp(31px, 4.8vmin, 52px); }
.lr-deck__slide-inner:has(> .lr-deck__sec-body:empty) .lr-deck__sec-lead {
  font-size: clamp(21px, 2.7vmin, 29px);
  max-width: 64ch;
  margin-bottom: 0;
}
.lr-deck__sec-metrics {
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.2vmin, 14px);
  min-width: 0;
  /* Fittable for the same reason the chart column is (deckFitCharts): three
     tiles whose labels run to three lines can want more height than a
     768px-high slide has, and a clipped third metric is worse than three
     drawn a hair smaller. In practice this is 1.0 on all but one slide at
     one size. */
  transform-origin: top center;
}
/* FIT-TO-SLIDE (see deckFitCharts() in lr-site.js). A chart card is exactly
   as tall as the figure it holds, and a six-row figure with a three-row
   legend is taller than a 768px-high slide can give it. The card is
   therefore scaled AS A WHOLE BOX when, and only when, it does not fit —
   `transform` is invisible to the chart engine (which measures clientWidth,
   a layout value), so the figure still draws through the identical path at
   its natural size and simply presents smaller. The inline `transform` and
   the negative bottom margin that pulls the layout box back to the painted
   height are set by that function; this is only the origin it scales from. */
.lr-deck__chart-wrap { min-width: 0; text-align: left; transform-origin: top center; }
.lr-deck__chart-wrap .chart-card { margin: 0; padding: clamp(14px, 2vmin, 22px); }
.lr-deck__chart-wrap .chart-card__title { font-size: clamp(19px, 2.4vmin, 25px); }
.lr-deck__chart-wrap .chart-card__sub { font-size: clamp(15px, 1.8vmin, 18px); }
.lr-deck__chart-wrap .chart-card__fig { font-size: clamp(12.5px, 1.5vmin, 15px); }
.lr-deck__chart-wrap .chart-card__note { font-size: clamp(13px, 1.6vmin, 16px); margin-top: clamp(6px, 1.2vmin, 12px); }

/* ---- 9.7 the recommendations slide(s) — bold headline only --------------
   deckRecommendationsHtml() draws its own kicker + `.wtd-list` (max 4
   items/slide, each item's bold lead only) instead of the full
   renderRecommendations() component — there is no intro paragraph and no
   continuation sentence on a slide. */
.lr-deck__wtd-list { text-align: left; max-width: 1000px; margin: 0 auto; display: flex; flex-direction: column; gap: clamp(14px, 2.4vmin, 24px); }
.lr-deck__wtd-list .wtd-item { padding-bottom: clamp(12px, 2vmin, 20px); border-bottom: 1px solid var(--border); }
.lr-deck__wtd-list .wtd-item:last-child { border-bottom: 0; padding-bottom: 0; }
.lr-deck .wtd-item__t { font-size: clamp(20px, 2.7vmin, 26px); line-height: 1.35; }
.lr-deck .wtd-item__lead { color: #fff; }

/* ---- 9.8 the Explore slide (Unit 8B) ------------------------------------
   FCR.lrExplore.mountCompact() owns everything inside [data-deck-explore]
   (assets/js/lr-explore.js); this only hands it the slide's leftover height
   and a left-aligned text flow to build in. Where that module is absent the
   slide falls back to its heading plus one centered link. */
.lr-deck__explore { flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden; text-align: left; }
.lr-deck__slide--explore .lr-deck__slide-inner:has(.lr-deck__explore-fallback) {
  text-align: center;
  justify-content: center;
}
.lr-deck__explore-fallback { margin: clamp(10px, 2vmin, 20px) 0 0; }

/* ---- 9.9 the closing slide ----------------------------------------------
   Study title, then the two logos side by side and centered (the same image
   sources the top bar carries), then the one action off the deck. */
.lr-deck__closing-title { font-size: clamp(28px, 4.4vmin, 48px); color: #fff; max-width: 24ch; margin: 0 auto clamp(24px, 4vmin, 44px); }
.lr-deck__logos {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(26px, 4.6vmin, 56px);
  margin: 0 auto clamp(26px, 4.4vmin, 48px);
}
.lr-deck__logo { height: clamp(30px, 4.6vmin, 52px); width: auto; display: block; }
.lr-deck__logo--client { height: clamp(34px, 5.2vmin, 58px); }
.lr-deck__cta { font-size: clamp(16px, 2vmin, 19px); }

/* ---- 9.10 the bottom bar: round prev/next + dots ------------------------- */
.lr-deck__controls {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 12px 0 clamp(12px, 2.2vh, 20px);
}
.lr-deck__nav {
  appearance: none;
  cursor: pointer;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-2);
  background: var(--surface-2);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  display: grid;
  place-items: center;
}
.lr-deck__nav:hover { background: var(--surface-3); }
.lr-deck__dots { display: flex; gap: 10px; }
.lr-deck__dot {
  appearance: none;
  cursor: pointer;
  width: 10px;
  height: 10px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .22);
}
.lr-deck__dot.is-active { background: var(--blue); transform: scale(1.25); }

/* ---- 9.11 narrow / portrait ---------------------------------------------
   Below 900px the section slide's two columns stop being two columns: a
   chart at 55% of a 780px-wide slide is not a chart any more. The metric
   tiles run underneath as a row that wraps. */
/* A 1024-wide tablet: an even-er split. The metric labels are the long
   strings on a section slide, and at 38% of 1024 they wrap to three lines
   each — three tiles then want more height than the slide has. 43% takes
   them back to two lines, which is worth more to the slide than the 60px
   it costs the chart column (the chart is being fitted by scale at this
   size anyway — see deckFitCharts()). */
@media (max-width: 1100px) {
  .lr-deck__sec-body { grid-template-columns: minmax(0, 57fr) minmax(0, 43fr); }
}
@media (max-width: 900px) {
  .lr-deck__sec-body { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .lr-deck__sec-metrics { flex-direction: row; flex-wrap: wrap; }
  .lr-deck__sec-metrics .taki__metric { flex: 1 1 190px; }
}
@media (max-width: 700px) {
  .lr-deck__slide { padding: 18px 20px; }
  .lr-deck__metrics { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

/* =========================================================================
   10. Polish 3 (2026-09-10, Ben) — site-wide scroll reveal, the chart
   lightbox, the clickable chart card and the chart mark hover.

   All four are SITE DEFAULTS: nothing below names a page, a section or a
   chart id, and every value is a brand.css token or a plain number that is
   the same on every page.
   ====================================================================== */

/* ---- 10.1 scroll reveal for every top-level content block -------------
   The composer marks each top-level wrapper it emits with `class="reveal"`
   and `data-reveal` (renderFeatureBlock, renderReportSection, renderTakiRow,
   renderChartFigure / the chart row, renderCarousel, renderRecommendations,
   pagerHtml). The observer in lr-site.js (wireReveals) is generic — it reads
   `[data-reveal]` and nothing else — so a new block type gets the reveal by
   carrying the attribute, with no CSS or JS change.

   THE HIDDEN STATE IS GATED on `.lr-reveal-on`, which wireReveals() puts on
   <html> in the SAME synchronous block that marks everything above the fold
   `.is-revealed`. Before that block runs the page is simply visible, so a
   reader never sees a flash of hidden content and a browser with JS off (or
   a failed script) never gets a blank page.

   `translate` rather than `transform`: the card's own hover lift (10.3) is a
   transform, and the two properties COMPOSE — putting the rise on
   `transform` too would mean the reveal and the hover fighting over one
   property on the same element. */
.lr-reveal-on [data-reveal] {
  opacity: 0;
  translate: 0 12px;
  transition: opacity .45s ease, translate .45s cubic-bezier(.22, .7, .25, 1);
}
.lr-reveal-on [data-reveal].is-revealed { opacity: 1; translate: 0 0; }
@media (prefers-reduced-motion: reduce) {
  .lr-reveal-on [data-reveal],
  .lr-reveal-on [data-reveal].is-revealed {
    opacity: 1 !important;
    translate: none !important;
    transition: none;
  }
}

/* ---- 10.2 the chart lightbox -----------------------------------------
   ONE element (`#lr-chart-lightbox`), reused by every chart card on the
   page, built by renderChartLightbox() and driven by the SAME modal helpers
   every other dialog uses (openModalEl / closeModal / wireModalDialog), so
   Escape, the focus trap, the backdrop click and restore-focus are not a
   second implementation. The sheet is brand.css's `.modal` widened to the
   viewport; the chart inside re-renders through the normal adapter at the
   modal's own width, which is what keeps its type at px-constant sizes. */
.modal--chart {
  /* 92vw of the VIEWPORT, but never wider than the overlay's own padded
     box: `.modal-overlay` is a centred grid with 24px of padding, and 92vw
     plus those gutters ran off a phone screen. A percentage max-width could
     not fix it — the grid track is sized BY the item, so the percentage
     resolves against the width it is meant to cap — hence the explicit
     calc against the overlay's padding. */
  width: min(92vw, calc(100vw - 48px));
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  padding: 26px 28px 22px;
  /* 2026-09-10: the lightbox sheet is OPAQUE.
     `--surface` / `--surface-2` (and therefore brand.css's `.card`
     background) are white OVERLAY tokens — rgba(255,255,255,.045) and
     rgba(255,255,255,.07) — which read as a card only because the page's own
     --bg is painted behind them. Inside a dialog the thing behind is the
     DIMMED PAGE, so the same token let the report's own cards and charts show
     straight through the sheet: the title, the subtitle and the donut legend
     were all sitting on top of a second chart. The sheet therefore paints the
     COMPOSITE of --surface-2 over --bg as a solid colour — #144164 is exactly
     rgba(255,255,255,.07) over #023358 — so it still reads as the same
     slightly-raised report card, with no alpha at all. Deliberately NO
     backdrop-filter here: the blur belongs to the overlay behind the sheet
     (brand.css `.modal-overlay`), never to the surface being read. */
  background: #144164;
  overflow: auto;
}
/* The page behind must not compete with the sheet, so the lightbox's own
   overlay dims harder than the shared .modal-overlay's .62 (2026-09-10).
   Scoped to the lightbox by id: the scenario dialog is a small sheet over a
   page the reader is still orienting against and keeps the shared dim. */
#lr-chart-lightbox.modal-overlay { background: rgba(2, 10, 28, .78); }
.modal--chart .card__head { align-items: flex-start; margin-bottom: 4px; }
.modal--chart .chart-card__title { margin: 0; }
.modal--chart .chart-wrap { flex: 1 1 auto; min-height: 340px; height: auto; }
.modal--chart .chart-card__note { margin-top: 12px; }
/* The ring is the one shape whose site default is a NATURAL width (§ the
   .donut-card rule above), which on a 92vw sheet left the chart a small
   disc in a wide empty box — the opposite of "enlarge". It may grow here
   because the donut's SVG contains NO TEXT at all (its labels and values
   are the HTML `.donut__legend` rows beside it, at CSS px), so scaling the
   viewBox scales only geometry: nothing about px-constant type is at risk.
   Every other chart type sizes itself from the wrap's own pixel width
   through the adapter and needs nothing here. */
.modal--chart .corechart .donut-card { justify-content: center; }
.modal--chart .corechart .donut-card svg { width: 720px; }
/* Room for the × in the corner, which sits over the head row. */
.modal--chart .card__head { padding-right: 34px; }
@media (max-width: 720px) {
  .modal--chart { padding: 22px 16px 18px; }
}

/* ---- 10.3 the chart card is clickable --------------------------------
   Every card opens the lightbox (renderChartFigure gives it role="button",
   tabindex="0" and an aria-label from ui-strings' `chart.enlarge`), so it
   says so: `zoom-in` over the whole figure, and the default cursor back over
   the one child that is NOT the lightbox — the PNG button.

   The lift overrides brand.css's own -3px/.18s chart-card hover with Ben's
   -2px, and adds the same treatment on keyboard focus so the affordance is
   not mouse-only. */
figure.chart-card { cursor: zoom-in; }
figure.chart-card .chart-png { cursor: pointer; }
figure.chart-card,
.modal--chart { transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease; }
figure.chart-card:hover,
figure.chart-card:focus-visible {
  transform: translateY(-2px);
  border-color: rgba(7, 167, 232, .5);
  box-shadow: 0 18px 40px -22px rgba(7, 167, 232, .6);
}
figure.chart-card:focus-visible { outline: none; }
@media (prefers-reduced-motion: reduce) {
  figure.chart-card:hover,
  figure.chart-card:focus-visible { transform: none; }
}

/* ---- 10.4 every data mark lightens on hover --------------------------
   The core renderers give EVERY data mark the class `.mark` (bar and hbar
   fills, stacked segments, pie/doughnut slices, line and area points,
   column bars, the box body) alongside the `.tipnode` hit hook they already
   carried. One pair of rules therefore covers every chart type the site
   draws, present and future: a new renderer inherits the hover by emitting
   `.mark`.

   Only the hovered mark changes. The rest of the series is NOT dimmed —
   dimming re-colours data the legend has already named, which is the one
   thing a chart must not do on a mouse move. */
.corechart .mark {
  transition: filter .16s ease, opacity .16s ease;
}
.corechart .mark:hover,
.corechart .mark:focus-visible {
  filter: brightness(1.18);
}
/* `.bar__fill` is an HTML div, not an SVG shape: the hover has to be driven
   from the row the pointer is actually over (the fill is inside a track that
   spans the full row), so the bar's own wrapper is the hover target. */
.corechart .bar:hover > .bar__track > .mark { filter: brightness(1.18); }
@media (prefers-reduced-motion: reduce) {
  .corechart .mark { transition: none; }
}

/* ---- 10.5 the footer, trimmed (Ben) ----------------------------------
   The prototype/confidential disclaimer paragraph is gone from the shell
   template (tools/scaffold-shells.mjs); the footer is one row — the Futurum
   logo and the copyright line — so it no longer needs brand.css's 46px of
   vertical air for a two-line block. */
.site-footer { padding-block: 22px; margin-top: 44px; }

/* 2026-09-10, Unit 7 (D2): the ONE site-wide machine-translation
   disclaimer, as a slim muted line under the base line. Hidden — and
   therefore taking no space at all — on every English page, which is why it
   can live in the shell unconditionally. Deliberately quieter and smaller
   than the copyright line above it: it is a standing note about the page,
   not a warning banner. */
.site-footer__disclaimer {
  margin: 8px 0 0;
  max-width: 78ch;
  font-size: 11.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, .58);
}
.site-footer__disclaimer[hidden] { display: none; }

/* =========================================================================
   § 11. THE EXPLORER (2026-09-10, Unit 5) — explore.html only
   =========================================================================
   Everything on this page reuses the v3 component vocabulary that is
   already in brand.css: the picker's rows are the command card's own
   `.cc-nav button` list-row look (2026-09-10; they were `.chip.explore-chip`
   before the two-column picker), the
   "Show as" toggle is `.viewtoggle`, the dimension dropdowns are the
   command card's own `.cc-dropdown` / `.cc-nav` popover, the two SHOW
   switches are `.profsw`, and the chart card is the report's own
   `.card.chart-card`. What is added here is only what those components
   cannot know: the panel's three-row grid, the checklist inside a
   dropdown, and the paused (greyed) state of the FILTER row.
   ====================================================================== */

/* ---- 11.1 picker: ONE card, two columns ------------------------------
   2026-09-10. WAS a stack of chip rows, one row per group under a small
   kicker: twenty-eight wrapping chips that pushed the chart itself off the
   first screen. It is now a two-column selector inside a single `.card` —
   LEFT the groups (Executive Summary, Section 1…6, More questions), RIGHT
   the selected group's questions — so the whole picker is about a screen
   third at 1280 and the chart is visible with it.

   The rows are the v3 list-row look, borrowed from the command card's own
   `.cc-nav button` (brand.css): display type, 9px radius, --surface-2 on
   hover, the blue→blue-300 gradient when current. They are BUTTONS with
   `role="option"` inside a `role="listbox"` column, so aria-selected is the
   one selection signal and Enter/Space select with no keyboard code of ours
   (lr-explore.js's onKeydown adds only arrow/Home/End movement within a
   column). Both lists cap their height and scroll rather than growing the
   card: the group column has eight rows and never needs it, but a long
   question group would otherwise reintroduce the tall picker. */
.explore-picker {
  display: grid;
  grid-template-columns: minmax(0, 32fr) minmax(0, 68fr);
  gap: 0 20px;
  align-items: start;
  padding: 15px 18px 14px;
  margin: 4px 0 22px;
}
.explore-picker__col { min-width: 0; }
.explore-picker__col--groups {
  border-right: 1px solid var(--border);
  padding-right: 20px;
}
.explore-picker__kicker {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 11px;
  color: var(--blue-300);
  margin: 0 0 7px 10px;   /* 10px aligns the kicker with the rows' own text */
}
.explore-picker__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* The GROUP list is short and fixed — one row per report section that has
   explore figures, plus "More questions" — so it is NEVER capped: hiding
   half the report's sections behind a scrollbar is exactly what the reader
   came to this column for. It is the QUESTION list that is capped, because
   that one is as long as the biggest group and a re-authored pack could make
   it much longer. The cap is set to comfortably clear the group list at
   1280, so the card's height is the group list's and nothing jumps as the
   reader moves between groups. */
.explore-picker__col--groups .explore-picker__list { max-height: none; }
.explore-picker__col--questions .explore-picker__list {
  max-height: 252px;
  overflow-y: auto;
}
.explore-picker__row {
  appearance: none;
  cursor: pointer;
  text-align: left;
  border: 0;
  background: transparent;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 9px;
  color: var(--text-soft);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  line-height: 1.28;
}
.explore-picker__rowlab { flex: 1 1 auto; min-width: 0; }
.explore-picker__count {
  flex: none;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.explore-picker__row:hover { color: #fff; background: var(--surface-2); }
.explore-picker__row.is-active {
  color: #04193b;
  background: linear-gradient(135deg, var(--blue), var(--blue-300));
}
.explore-picker__row.is-active .explore-picker__count { color: rgba(4, 25, 59, .9); }
.explore-picker__row:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(7, 167, 232, .35); }
/* One column under the other on a phone, divided by a rule instead of a
   border between them. The group list stays uncapped here too — on a phone
   the page already scrolls, and a nested scroll region inside a page scroll
   is worse than a slightly taller card — so only the question list tightens,
   just enough that the group above it is not pushed off screen. */
@media (max-width: 720px) {
  .explore-picker { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .explore-picker__col--groups {
    border-right: 0;
    padding-right: 0;
    padding-bottom: 13px;
    border-bottom: 1px solid var(--border);
  }
  .explore-picker__col--questions .explore-picker__list { max-height: 208px; }
}

/* ---- 11.2 the controls panel: three labelled rows -------------------- */
.explore-panel { padding: 16px 18px 14px; margin-bottom: 22px; }
.explore-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 8px 0;
  flex-wrap: wrap;
}
.explore-row + .explore-row { border-top: 1px solid var(--border); }
.explore-row .explore-ctl-label {
  flex: none;
  width: 74px;
  margin: 8px 0 0;
  font-size: 11px;
  letter-spacing: .12em;
  color: var(--blue-300);
}
.explore-ctl-label--inline { width: auto; margin: 0 8px 0 0; display: inline-block; }
.explore-row__ctls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: 1 1 auto; }
/* Compare mode pauses filters: the whole row greys out and every control
   in it is disabled in the markup too, so "paused" is not a look alone. */
.explore-row.is-paused { opacity: .45; }
.explore-row.is-paused .explore-dd__btn { cursor: not-allowed; }
.explore-paused-note {
  margin: 2px 0 6px;
  font-size: 13px;
  color: var(--amber);
  border-left: 2px solid var(--amber);
  padding-left: 10px;
}
.explore-base { margin-left: auto; font-size: 13px; white-space: nowrap; align-self: center; }

/* ---- 11.3 a dimension dropdown with a checklist inside --------------- */
.explore-dd { display: inline-block; }
.explore-dd__btn {
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--border-2);
  background: transparent;
  color: var(--text-soft);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 13px;
  padding: 7px 13px;
  border-radius: 999px;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.explore-dd__btn:hover:not([disabled]) { color: #fff; background: rgba(255, 255, 255, .08); border-color: var(--blue-300); }
.explore-dd__btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(7, 167, 232, .35); }
.explore-dd__btn[data-count] { color: #04193b; border-color: transparent;
  background: linear-gradient(135deg, var(--blue), var(--blue-300)); }
.explore-dd__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: rgba(255, 255, 255, .38); font-size: 11px; font-weight: 800;
}
/* The panel is the command card's own popover (.cc-nav does the surface,
   the blur, the shadow and the open/close transition); these rules only
   size it and lay the checklist out inside. */
.explore-dd__panel { min-width: 232px; max-height: 320px; overflow-y: auto; padding: 6px; }
.explore-dd__opts { display: flex; flex-direction: column; gap: 1px; }
.explore-dd__opt {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 8px; cursor: pointer;
  font-size: 13.5px; font-weight: 600; color: var(--text-soft);
}
.explore-dd__opt:hover { color: #fff; background: var(--surface-2); }
.explore-dd__opt input { accent-color: var(--blue); width: 15px; height: 15px; flex: none; }
.explore-dd__bulk { display: flex; gap: 6px; padding: 4px 5px 7px; }
.explore-dd__bulk button,
.explore-dd__foot button {
  appearance: none; cursor: pointer; border: 1px solid var(--border-2);
  background: transparent; color: var(--text-soft);
  font-family: var(--font-display); font-weight: 800; font-size: 12px;
  padding: 5px 11px; border-radius: 999px;
}
.explore-dd__bulk button:hover,
.explore-dd__foot button:hover { color: #fff; border-color: var(--blue-300); background: rgba(255, 255, 255, .08); }
.explore-dd__foot { padding: 3px 5px 7px; border-bottom: 1px solid var(--border); margin-bottom: 5px; }

/* ---- 11.4 the two SHOW switches -------------------------------------- */
.explore-sw { display: inline-flex; align-items: center; gap: 8px; margin-right: 6px; }
.explore-sw__label { font-size: 13.5px; font-weight: 700; color: var(--text-soft); cursor: pointer; }
.explore-sw.is-off .explore-sw__label { color: var(--muted); }
.explore-sw.is-disabled { opacity: .55; }
.explore-sw.is-disabled .explore-sw__label { cursor: default; }
/* 2026-09-16 (a11y fix, color-contrast): this hint only ever renders inside
   an `.is-disabled` switch (opacity: .55, above), where the shared --muted
   token blends down to ~2.99:1 against the panel background — under the
   4.5:1 minimum. A color of its own (not a change to --muted, which every
   other muted label on the site still uses) that clears 4.5:1 once blended
   through that same .55 opacity — same source order as `.muted`, so this
   wins over the element's own `.muted` class without touching it. */
.explore-sw__hint { font-size: 12px; color: #dfe9f6; }
.explore-showas { display: inline-flex; align-items: center; }

/* ---- 11.5 the card, and the welcome prose above it ------------------- */
.lr-explore__welcome { max-width: 82ch; margin-bottom: 8px; }
.lr-explore .chart-card--full { width: 100%; }
/* No height override: the Explorer's card is the report's card, and every
   renderer on it sizes to its own content (a fixed height left a 4-row bar
   chart sitting in 300px of empty card). A shape that DOES need a box —
   a ring, a line — gets one from brand.css / fcr-chart-core.css exactly as
   it does on a report page. */

/* ---- 11.6 "Explore this figure →" (Unit 5, D) ------------------------
   Every report chart card whose registry entry is `explore: true` carries
   this link in its footer: right-aligned, muted, underlined on hover.
   Quiet by design — it is an offer, not a call to action, and it must not
   compete with the card's own title. */
.chart-card__explore { margin: 6px 0 0; text-align: right; }
.chart-card__explore a {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
}
.chart-card__explore a:hover { color: var(--blue-300); text-decoration: underline; }
.chart-card__explore a:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(7, 167, 232, .35); border-radius: 6px; }

/* ---- 11.7 narrow screens --------------------------------------------
   Below the tablet break the three rows stack their label above their
   controls, and the base readout drops onto its own line rather than
   squeezing the toggle. */
@media (max-width: 860px) {
  .explore-row { flex-direction: column; gap: 8px; }
  .explore-row .explore-ctl-label { width: auto; margin: 0; }
  .explore-base { margin-left: 0; white-space: normal; }
}

/* ---- 11.8 THE COMPACT EXPLORER (2026-09-11, Unit 9) ------------------
   FCR.lrExplore.mountCompact() — the tablet deck's own one-question,
   one-chart slide (assets/js/lr-explore.js's own file banner has the full
   brief). `.lr-explore--compact` is the root the module stamps on whatever
   element it is given; NOTHING here assumes that element is explore.html's
   `.lr-explore` — a deck slide hands it a bare, otherwise unstyled div, so
   this block is a complete, self-contained layout rather than a set of
   overrides on §11.1–§11.4 above.

   THE HEIGHT. A deck slide is `flex: 1 1 auto; min-height: 0; overflow:
   hidden` with no page scroll of its own (ARCHITECTURE-v4.md's own
   tablet-deck rule) — this surface must fit whatever height it is handed,
   never assume more, and never let the page scroll to find the rest of it.
   So the root is itself a flex column filling 100% of that height, its
   controls row sized to its own content (`flex: none`), and the card given
   everything left over (`flex: 1 1 auto; min-height: 0`) — the same
   "leftover space" contract the deck's own chart-only slides use (§9.6),
   minus that section's forced SVG height: the chart engine sizes its
   viewBox from the card's measured WIDTH alone (fcr-chart-core.js), so
   nothing here touches the svg/canvas itself. `sizeCompactCard()`
   (lr-explore.js) additionally measures the real leftover pixels and pins
   `.lrxc-chartwrap`'s min-height to them, live, on resize — this CSS is
   what that measurement is a refinement OF, not a replacement for: with no
   JS measurement at all (a static preview, a slow first paint) the flex
   column alone already gives the card the room. `.lrxc-chartwrap` itself
   still scrolls (`overflow-y: auto`) as the one safety valve if a chart's
   natural height ever exceeds what was measured — the ONE place on this
   surface that may scroll, never the root and never the page. */
.lr-explore--compact {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  gap: 10px;
  font-size: 15px;
}
.lrxc-controls { flex: none; display: flex; flex-direction: column; gap: 8px; }
.lrxc-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; }
.lrxc-label {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: 13px;
  color: var(--blue-300);
}
/* THE TYPE FLOOR. Every compact control reads at 18px or larger — a tablet
   used across a table in conversation, not a desktop the reader leans into
   — which is also why the select is a native one (a platform picker a
   reader can drive with a fingertip) rather than the full picker's two
   custom listbox columns; there is no room for those on a slide, and a
   native control needs no keyboard wiring of its own. */
.lrxc-select {
  appearance: none;
  cursor: pointer;
  width: 100%;
  max-width: 640px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  line-height: 1.3;
  color: #fff;
  background: var(--surface-2);
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm, 10px);
  padding: 11px 16px;
}
.lrxc-select:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(7, 167, 232, .35); }
.lrxc-showas { display: inline-flex; align-items: center; gap: 10px; }
.lrxc-showas .viewtoggle__btn { font-size: 18px; padding: 9px 18px; }
.lrxc-overlays { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
.lrxc-overlays .explore-sw__label { font-size: 18px; }
.lrxc-overlays .explore-sw__hint { font-size: 15px; }
.lrxc-overlays .profsw { width: 40px; height: 22px; }
.lrxc-overlays .profsw__knob { width: 16px; height: 16px; }
.lrxc-overlays .profsw[aria-checked="true"] .profsw__knob { transform: translateX(18px); }

/* THE CARD. `.lrxc-card` (the mount `mountCompact` writes the frame into)
   is itself the flex item that gets the root column's LEFTOVER height —
   `flex: 1 1 auto; min-height: 0`, exactly like every other "give this one
   the rest of the space" child on the site (§9.6's own `.lr-deck__chart-
   wrap`). `.lrxc-cardframe` then fills it completely and is a flex column
   of its own: head (title+sub) fixed, the chart flexing to fill what is
   left, the note fixed at the bottom — so a long legend or a tall table of
   option rows never pushes the source line off the slide; it scrolls
   inside `.lrxc-chartwrap` instead (see the banner above). No width cap: a
   compact instance fills whatever width its host gives it, full-bleed,
   because it IS the slide's one visual, not a card sharing space with
   prose. */
.lrxc-card { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.lrxc-cardframe {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
}
.lrxc-cardframe__head { flex: none; }
.lrxc-title {
  font-size: clamp(20px, 2.6vmin, 28px);
  line-height: 1.25;
}
.lrxc-sub { font-size: clamp(15px, 1.8vmin, 18px); }
.lrxc-chartwrap {
  flex: 1 1 auto;
  min-height: 120px;
  height: auto;
  overflow-y: auto;
  margin-top: 10px;
}
/* --chart-label-size is the HOST's to set (the deck's own root token, read
   by fcr-chart-core.js off document.documentElement — see that file's own
   banner): this surface never redeclares it, so a mounted instance always
   draws at whatever size the page around it has chosen. */
.lrxc-note {
  flex: none;
  font-size: clamp(13px, 1.5vmin, 15px);
  margin-top: 8px;
}
@media (max-width: 720px) {
  .lrxc-select { max-width: 100%; }
  .lrxc-row { gap: 10px 14px; }
}

/* ======================================================================
   12. THE DOWNLOADS MODAL (2026-09-10, Unit 6; rebuilt 2026-09-11, B2)
   ======================================================================
   brand.css already carries every part of `#exportModal`'s SHELL
   (.modal-overlay / .modal / .modal__close / .modal__title / .modal__sub);
   what is added here is the Content x Format choice groups that replaced
   the five-card grid + dark/light chart-style control (both fully retired
   — see tools/scaffold-shells.mjs's exportModal() for the markup and
   assets/js/lr-site.js's wireExportModal() for the click-to-kind wiring). */

/* ---- 12.1 the two choice groups (Content, Format) -------------------- */
.export-choices {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin: 4px 0 18px;
}
@media (max-width: 700px) {
  .export-choices { grid-template-columns: 1fr; }
}
.export-group {
  margin: 0;
  padding: 10px 14px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,.045);
}
.export-group__legend {
  padding: 0 0 8px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--blue-300);
}
.export-choice {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 4px;
  border-radius: 8px;
  cursor: pointer;
}
.export-choice:hover { background: rgba(255,255,255,.06); }
.export-choice input { flex: none; width: 15px; height: 15px; margin-top: 3px; accent-color: var(--blue); cursor: pointer; }
.export-choice__body { display: flex; flex-direction: column; gap: 2px; }
.export-choice__name { font-size: 13.5px; font-weight: 700; color: #fff; }
.export-choice__meta { font-size: 12px; line-height: 1.4; color: var(--text-soft); }

/* ---- 12.2 the one Download action ------------------------------------- */
.export-download { margin-top: 2px; }

/* ---- 12.3 the second per-figure button (⬇ Slide) ---------------------
   .head-actions (brand.css) is already a flex row with a 10px gap, so the
   pair needs no layout of its own — only permission to wrap on a narrow
   card, so a long chart title never squeezes the two buttons into a column
   of one-character lines. */
.card__head .head-actions { flex-wrap: wrap; justify-content: flex-end; }

/* ======================================================================
   13. Deck fix pass (2026-09-10) — no export or explore controls on a slide
   ======================================================================
   tablet.html never loads assets/js/lr-exports.js (§2.4's own script list),
   so a ⬇ PNG click there has no handler — the button would be dead chrome.
   "Explore this figure" and the whole-card "Enlarge" lightbox affordance
   are live (lr-site.js is loaded), but neither belongs on a presentation
   slide, so all three are switched off here, CSS-only, scoped to the deck. */
body[data-view="tablet"] .chart-card .head-actions,
body[data-view="tablet"] .chart-card__explore {
  display: none;
}
/* The whole figure is otherwise `role="button" tabindex="0"` (opens the
   lightbox) with a `cursor: zoom-in` + hover-lift cue (§10.2/brand.css) —
   neither belongs on a slide, so both are neutralized rather than hidden
   (the figure itself still renders, only its "this opens something" look
   goes away). */
body[data-view="tablet"] figure.chart-card { cursor: default; }
body[data-view="tablet"] .chart-card:hover {
  border-color: var(--border);
  box-shadow: none;
  transform: none;
}

/* =========================================================================
   13. Phone layout

   The site was desktop + tablet deck. Everything below targets phone widths
   (≤480px) where the existing breakpoints at 560/600/700/720/900 leave gaps.
   ====================================================================== */

/* ---- 13.1 carousel and card sizing at phone width -------------------- */
@media (max-width: 480px) {
  .vcar__item { flex-basis: 100%; padding: 8px 12px; }
  .vcar__item blockquote { font-size: 16px; }
  .vcar__nav { width: 36px; height: 36px; font-size: 20px; }
}

/* ---- 13.2 chart card header ------------------------------------------
   At phone width the figure title and the PNG/Slide action buttons crowd
   each other. Wrap the flex row so buttons drop below the title. */
@media (max-width: 480px) {
  .chart-card .card__head { flex-wrap: wrap; }
  .chart-card .head-actions { width: 100%; justify-content: flex-start; gap: 8px; margin-top: 4px; }
}

/* ---- 13.3 modal / lightbox / dialog ----------------------------------
   Tighten padding so content uses the available width on narrow screens. */
@media (max-width: 480px) {
  .modal-overlay { padding: 12px; }
  .modal { padding: 20px 16px; }
  .modal__title { font-size: 20px; }
  .modal--chart { padding: 16px 12px 14px; }
  .modal--chart .chart-wrap { min-height: 260px; }
}

/* ---- 13.4 header / nav -----------------------------------------------
   Tighter gap so the two logos and language selector fit without overflow. */
@media (max-width: 480px) {
  .site-header__row { gap: 12px; padding-block: 10px; }
  .site-header__row img { max-height: 28px; }
}

/* ---- 13.5 command card -----------------------------------------------
   Already static at ≤700px. At phone width, stack the controls vertically
   and let each button take full width. Prefixed with the mount selector
   to match the specificity of the existing rules at lr-shell.css:366-369. */
@media (max-width: 480px) {
  [data-lr-mount="commandbar"] .commandcard { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px; }
  [data-lr-mount="commandbar"] .cc-group { flex-direction: column; gap: 8px; }
  [data-lr-mount="commandbar"] .cc-group--right { flex-direction: column; gap: 8px; }
  [data-lr-mount="commandbar"] .cc-group select { width: 100%; min-width: 0; }
}

/* ---- 13.6 hero -------------------------------------------------------
   Tighten the hero at phone width — smaller title, less vertical padding. */
@media (max-width: 480px) {
  .hero { padding-block: 24px; }
  .hero h1 { font-size: 28px; }
  .hero__byline { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ---- 13.7 Explorer controls ------------------------------------------
   The question picker and filter rows already wrap, but at phone width the
   section list and control labels need tighter spacing. */
@media (max-width: 480px) {
  [data-lr-explore-controls] { gap: 8px; }
  [data-lr-explore-controls] select { min-width: 0; width: 100%; }
}

/* ---- 13.8 body text --------------------------------------------------
   19px body copy is comfortable on desktop but large on a phone screen. */
@media (max-width: 480px) {
  :root {
    --body-size: 16px;
    --body-lh: 1.65;
  }
}

/* ---- 13.9 footer ------------------------------------------------------
   Stack the footer on phone width. */
@media (max-width: 480px) {
  .site-footer .row { flex-direction: column; gap: 8px; text-align: center; }
}

