/* Maritime Attention Index — site styles.
   ────────────────────────────────────────────────────────────────────────
   Built on the Cardinal Response design system (see tokens.css) and on the
   "Maritime Attention Index Chart-Led" design (Claude Design project
   b65c1e4b). That design covers the home page only; everything it establishes
   — the sticky header and its live mini-readout, the tracked wordmark, the
   chamfered gold-wipe buttons, the flat hairline cards, the three-column
   footer — is applied here to every page of the site, so a reader moving from
   the Index to the methodology never crosses a seam.

   The design system's components are React, and the design file itself is a
   Claude Design component (inline styles, {{ }} bindings, a DCLogic class).
   This site is Python-generated static HTML with no build step, so both are
   re-expressed here as CSS classes plus one vanilla script (home.js). The
   rules they encode are what matter, and are followed strictly:

     · corners are 0 — --radius-pill is for genuinely circular things only
     · gold is one CTA per screen, plus the hero data series. Never decoration.
     · two border weights, no third
     · cards are flat — depth comes from the hairline and the whitespace
       around them, never from shadow
     · one corner-cut per view, on one key surface
     · commit to the type jump: one oversized Archivo statement, a small
       wide-tracked eyebrow, quiet Public Sans body — avoid mid sizes
     · sentence case everywhere except eyebrows and status chips
   ──────────────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
}

a { color: var(--text-link); text-decoration: underline; text-underline-offset: 3px; }
a:hover { color: var(--text-link-hover); }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

/* One focus treatment everywhere: squared, ink, offset. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--border-focus);
  outline-offset: var(--focus-ring-offset);
}

/* ── chamfered controls ─────────────────────────────────────────────────── */
/* The Cardinal corner-cut plus the design's wipe: a ground colour sweeps in
   from the left edge over 340ms and the label inverts against it. Three
   registers — ghost (header), gold (the one asking CTA), ink (everywhere
   else) — sharing one geometry so they read as one control. */

.btn {
  position: relative; overflow: hidden;
  display: inline-block; border: 0;
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-body); font-size: var(--text-base);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide);
  text-decoration: none; cursor: pointer;
  border-radius: var(--radius-none);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut-sm-y)),
                     calc(100% - var(--cut-sm-x)) 100%, 0 100%);
  transition: color var(--duration-base) var(--ease-emphasized),
              border-color var(--duration-slow) var(--ease-emphasized),
              transform var(--duration-fast) var(--ease-standard);
}
.btn > span { position: relative; z-index: 1; }
.btn::before {
  content: ""; position: absolute; inset: -1.5px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform 340ms var(--ease-emphasized);
  z-index: 0;
}
.btn:hover::before, .btn:focus-visible::before { transform: scaleX(1); }
.btn:active { transform: scale(0.985); }
.btn:focus-visible { outline-offset: 2px; }
.btn[disabled] { opacity: .6; cursor: default; }
.btn[disabled]::before { transform: scaleX(0); }

/* Ghost: header subscribe. Rule 3 allows one gold CTA per screen and the home
   page has three subscribe affordances — the gold belongs to the CTA band,
   which is the one asking for the action rather than merely offering it. */
.btn-ghost {
  background: transparent; color: var(--text-primary);
  border: var(--border-hairline) solid var(--border-default);
  padding: var(--space-2) var(--space-5);
  font-family: var(--font-display); font-size: var(--text-xs);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
}
/* Ink, not literal black: on the dark register the wipe has to arrive lighter
   than the header it crosses, or it fills invisibly. */
.btn-ghost::before { background: var(--text-primary); }
.btn-ghost:hover, .btn-ghost:focus-visible { color: var(--surface-card); border-color: var(--text-primary); }

/* Ink: the default. Fills gold on hover. */
.btn-ink { background: var(--text-primary); color: var(--surface-card); }
.btn-ink::before { background: var(--color-gold); }
.btn-ink:hover, .btn-ink:focus-visible { color: var(--color-black); }

/* Gold: exactly one per view, in the CTA band. Fills to ink. */
.btn-gold { background: var(--color-gold); color: var(--color-black); }
.btn-gold::before { background: var(--color-black); }
.btn-gold:hover, .btn-gold:focus-visible { color: var(--color-gold); }

/* ── header ─────────────────────────────────────────────────────────────── */
/* Sticky, so the live readout and the navigation stay reachable through a
   long history chart or a long methodology page. */

.site-header {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding: 13px clamp(var(--space-4), 4vw, var(--space-12));
  background: var(--surface-card);
  border-bottom: var(--border-hairline) solid var(--border-default);
}
.hdr-left { display: flex; align-items: center; gap: 22px; min-width: 0; }
.brand { display: flex; align-items: center; color: var(--text-primary); text-decoration: none; flex: none; }
.wordmark {
  font-family: var(--font-display); font-size: var(--text-md);
  font-weight: var(--weight-medium); letter-spacing: 0.14em; text-transform: uppercase;
}

/* The live number, riding along once the reader has scrolled past the real
   one. Purely a readout: never focusable, never a link. */
.hdr-mini {
  display: flex; align-items: baseline; gap: 7px;
  opacity: 0; transition: opacity 220ms var(--ease-standard); pointer-events: none;
}
.hdr-mini.is-on { opacity: 1; }
.hdr-mini-num {
  font-family: var(--font-display); font-size: var(--text-lg);
  font-weight: var(--weight-bold); font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}
.hdr-mini-dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--text-secondary); flex: none; }
.hdr-mini-dot.level-high { background: var(--color-gold); }
.hdr-mini-dot.level-elevated { background: var(--text-accent); }

.site-nav { display: flex; align-items: center; gap: clamp(var(--space-3), 2vw, 26px); }
.site-nav a:not(.btn) {
  color: var(--text-secondary); font-size: var(--text-sm);
  font-weight: var(--weight-medium); text-decoration: none;
}
.site-nav .btn { text-decoration: none; }
/* :not(.btn) — the Subscribe control is an anchor too, and this rule is
   specific enough to beat .btn-ghost:hover and leave its label the same
   colour as the ground wiping in behind it. */
.site-nav a:not(.btn):hover { color: var(--text-primary); }
.site-nav a.active { color: var(--text-primary); }

main { max-width: var(--container-wide); margin: 0 auto; padding: 0 clamp(var(--space-4), 4vw, var(--space-12)) var(--space-24); }
/* Scoped to main's own children: the staleness banner is a <section> too, and
   it sits outside main, flush under the header with no gap of its own. */
main > section { margin-top: clamp(var(--space-12), 7vw, var(--space-24)); }
main > section:first-child { margin-top: clamp(var(--space-6), 3vw, 44px); }
.muted { color: var(--text-secondary); font-size: var(--text-sm); }

/* ── home: the reading ──────────────────────────────────────────────────── */
/* Chart-led. The number sits on the page ground rather than in a black band —
   the type jump alone carries it, and the chart immediately below is the
   thing the page is actually for. */

.mai-eyebrow { display: flex; align-items: center; gap: var(--space-3); margin: 0 0 clamp(14px, 1.8vw, 22px); animation: mai-fade 600ms both; }
.mai-eyebrow p {
  margin: 0; font-family: var(--font-mono); color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: var(--tracking-widest);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
}
.mai-eyebrow strong { color: var(--text-primary); font-weight: var(--weight-semibold); }
/* A live signal, not decoration: it marks the one number on the site that
   changes on its own. */
.pulse { position: relative; width: 7px; height: 7px; flex: none; }
.pulse::before, .pulse::after {
  content: ""; position: absolute; inset: 0;
  border-radius: var(--radius-pill); background: var(--color-gold);
}
.pulse::after { animation: mai-pulse 2600ms var(--ease-emphasized) infinite; }

.topline {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: clamp(18px, 3vw, var(--space-12)); flex-wrap: wrap;
  margin-bottom: clamp(14px, 2vw, 22px);
  animation: mai-rise 700ms var(--ease-emphasized) both;
}
.topline-read { display: flex; align-items: flex-end; gap: clamp(14px, 2vw, 26px); flex-wrap: wrap; }
.topline-num { display: flex; align-items: baseline; gap: 7px; }

/* The type jump. Nothing on the page competes with this number. */
.hero-num {
  font-family: var(--font-display);
  font-size: clamp(54px, 6vw, var(--text-6xl));
  font-weight: var(--weight-bold);
  line-height: 0.82; letter-spacing: var(--tracking-tightest);
  font-variant-numeric: tabular-nums; color: var(--text-primary);
}
.hero-unit { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-tertiary); letter-spacing: var(--tracking-wide); }
.topline-chips { display: flex; gap: var(--space-2); flex-wrap: wrap; padding-bottom: var(--space-1); }

/* ── width locks ────────────────────────────────────────────────────────── */
/* Dragging across the chart retargets the score, the level, the day-on-day
   change and the trend continuously. Each of them changes width as it changes
   value, and the topline is a flex row — so a wider chip pushes the range
   switch onto a second line and a narrower one pulls it back, and the whole
   page jumps under the reader's hand while they are trying to read it.

   Every live readout therefore reserves the widest value it can ever hold and
   never resizes. The reservation is a hidden copy of that widest string,
   stacked in the same grid cell as the live one: `build.py` measures it from
   the published series, so it is exactly wide enough by construction rather
   than by a guessed character count, and it stays right as the history grows.
   visibility:hidden keeps the box and drops it from the accessibility tree. */
.lock, .chip.lock { display: inline-grid; }
.lock > * { grid-area: 1 / 1; min-width: 0; }
.lock-max { visibility: hidden; }

/* Status chip. Squared — a pill here would break rule 1. */
.chip {
  display: inline-block;
  padding: var(--space-1) 10px;
  font-family: var(--font-mono); font-size: var(--text-xs);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wider);
  text-transform: uppercase; border-radius: var(--radius-none);
  border: var(--border-hairline) solid var(--border-default);
  color: var(--text-secondary);
}
.chip.level-high     { border-color: var(--color-gold); color: var(--color-gold); }
.chip.level-elevated { border-color: rgba(232,168,38,.55); color: var(--text-accent); }

/* Range switch. One control, five segments, hairline shared between them. */
.ranges { display: flex; border: var(--border-hairline) solid var(--border-default); }
.ranges button {
  padding: 7px var(--space-4); border: 0;
  border-left: var(--border-hairline) solid var(--border-default);
  background: transparent; color: var(--text-secondary);
  font-family: var(--font-mono); font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide); cursor: pointer;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.ranges button:first-child { border-left: 0; }
.ranges button:hover { color: var(--text-primary); }
.ranges button[aria-pressed="true"] { background: var(--text-primary); color: var(--surface-card); }

/* Says what the number is, immediately under it. Reads a step louder than the
   timing note below — body size, secondary rather than tertiary — because it
   is the definition, not a footnote. The two are a pair: this one is the
   subject, that one is the schedule, so they share a left edge and a measure
   and only differ in weight. */
.topline-what {
  margin: calc(var(--space-2) * -1) 0 var(--space-3);
  max-width: 88ch;
  font-size: var(--text-base); line-height: var(--leading-normal);
  color: var(--text-secondary); text-wrap: pretty;
}
.topline-what strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* Sits directly under the topline and answers "why does today say yesterday?".
   Deliberately quiet — small, secondary, sentence case — so it reads as a
   footnote to the number rather than as a caveat about it. Not uppercase mono
   like .chart-note below: that treatment is for live readouts, and this is
   fixed prose that must never look like a status. */
.topline-timing {
  margin: 0 0 var(--space-5);
  max-width: 88ch;
  font-size: var(--text-sm); line-height: 1.5;
  color: var(--text-tertiary);
}
.topline-timing strong { color: var(--text-secondary); font-weight: var(--weight-semibold); }
.topline-timing a { color: var(--text-secondary); }

.chart-note {
  margin: 0 0 var(--space-4); font-family: var(--font-mono);
  font-size: var(--text-xs); color: var(--text-secondary);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
}
/* No default state to restate any more (see the comment in build.py) — so
   when there is nothing to say, the line and its margin both disappear
   rather than holding the chart down for an empty caption. */
.chart-note:empty { display: none; }

/* Two hairlines and nothing else — the chart is framed by rules, not a box. */
.chart-frame {
  max-width: 1180px; margin: 0 auto;
  border-top: var(--border-hairline) solid var(--border-default);
  border-bottom: var(--border-hairline) solid var(--border-default);
  padding: clamp(18px, 2.4vw, 28px) 0;
  animation: mai-fade 700ms 120ms both;
}
.chart-row { display: flex; gap: 28px; align-items: stretch; }
.chart-wrap { position: relative; flex: 1; min-width: 0; }
.chart-wrap svg { width: 100%; display: block; cursor: crosshair; touch-action: none; }
.chart-aside { width: 170px; flex: none; position: relative; }
/* Pinned to the baseline's own height in the plot, so the sentence explaining
   50 sits level with the 50 line rather than floating near it. */
.chart-aside p {
  position: absolute; left: 0; right: 0; top: 50%; margin: 0;
  transform: translateY(-50%);
  color: var(--text-secondary); font-size: var(--text-sm);
  line-height: var(--leading-normal); text-wrap: pretty;
}
.chart-aside strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* Hover readout. The one place a shadow is allowed: it floats over the plot,
   so it needs to detach from it. */
.mai-tip {
  position: absolute; opacity: 0; pointer-events: none; width: 250px; z-index: 5;
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--border-strong);
  box-shadow: var(--shadow-md);
  padding: var(--space-3) 13px;
  transition: opacity 140ms var(--ease-standard);
}
.mai-tip.is-on { opacity: 1; }
.tip-date { margin: 0 0 var(--space-2); font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-secondary); letter-spacing: var(--tracking-wide); }
.tip-head { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: 10px; }
.tip-score { font-family: var(--font-display); font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); font-variant-numeric: tabular-nums; }
.tip-unit { font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-tertiary); }
.tip-delta { margin-left: auto; font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-secondary); }
/* Contribution bar: each signal's share of that day's score, in weight order. */
.tip-bar { display: flex; height: 7px; width: 100%; gap: 1px; margin-bottom: 10px; }
.tip-bar span { display: block; }
.tip-grid { display: grid; grid-template-columns: 9px 1fr auto auto; gap: 5px var(--space-2); align-items: center; }
.tip-grid .dot { width: 7px; height: 7px; border-radius: var(--radius-pill); }
.tip-grid .lbl { font-size: var(--text-xs); color: var(--text-secondary); }
.tip-grid .lbl span { color: var(--text-tertiary); }
/* Mono, so ch is an exact character width: six covers a four-digit signal
   value with its decimal. Fixed here rather than `auto`, or the grid's columns
   would resize under the pointer along with the numbers in them. */
.tip-grid .val { font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium); text-align: right; min-width: 6ch; }
.tip-grid .pts { font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-tertiary); text-align: right; min-width: 52px; }
.tip-event { margin: 9px 0 0; font-size: var(--text-2xs); color: var(--text-tertiary); line-height: 1.4; }

/* ── dials ──────────────────────────────────────────────────────────────── */

.dials-section { display: grid; grid-template-columns: 230px 1fr; gap: 28px; align-items: start; }
.dials-intro h2 { margin: 0 0 var(--space-2); font-size: var(--text-2xl); }
.dials-date { display: block; margin-bottom: var(--space-3); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-tertiary); letter-spacing: var(--tracking-wide); text-transform: uppercase; }
.dials-intro p { margin: 0; color: var(--text-secondary); font-size: var(--text-sm); text-wrap: pretty; }
.dials-intro strong { color: var(--text-primary); font-weight: var(--weight-semibold); }
.dials-intro .key-gold { color: var(--text-accent); font-weight: var(--weight-semibold); }

.gauge-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }

/* Flat by default. Hairline and whitespace, never shadow. */
.card {
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-none);
  padding: var(--space-6);
}
.gauge-card { padding: 22px; }
.gauge-card h3 {
  margin: 0 0 var(--space-4); font-family: var(--font-body);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--text-secondary);
}
.gauge-card .weight { font-family: var(--font-mono); color: var(--text-tertiary); font-size: var(--text-2xs); margin-left: var(--space-1); white-space: nowrap; letter-spacing: 0; }
.gauge-dial { position: relative; width: 100%; max-width: 170px; }
.gauge-dial svg { width: 100%; display: block; }
.gauge-val {
  position: absolute; left: 0; right: 0; bottom: 13%; margin: 0; text-align: center;
  font-family: var(--font-display); font-size: clamp(22px, 2vw, 28px);
  font-weight: var(--weight-semibold); line-height: 1;
  letter-spacing: var(--tracking-tight); font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}
.gauge-note { color: var(--text-secondary); font-size: var(--text-sm); margin: var(--space-3) 0 0; line-height: var(--leading-normal); }

/* ── split: CTA + methodology ───────────────────────────────────────────── */

.split { display: grid; grid-template-columns: 1.15fr 1fr; gap: clamp(var(--space-6), 4vw, 56px); }

/* The one corner-cut surface on this view, and the one gold CTA. */
.cta-band {
  background: var(--surface-inverse); color: var(--text-inverse);
  border-radius: var(--radius-none);
  padding: clamp(var(--space-8), 4vw, var(--space-12));
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--cut-lg-y)),
                     calc(100% - var(--cut-lg-x)) 100%, 0 100%);
}
.cta-band h2 { margin: 0 0 var(--space-3); font-size: var(--text-2xl); color: var(--text-inverse); max-width: 26ch; text-wrap: pretty; }
.cta-band p { margin: 0; color: var(--text-inverse); opacity: .72; max-width: 58ch; font-size: var(--text-base); }
.cta-band .sub-note { opacity: .55; margin-top: 10px; }
.cta-band .sub-form { max-width: 560px; margin-top: 22px; }
.cta-band .sub-form input { background: var(--text-inverse); color: var(--surface-inverse); border-color: transparent; }

.method-teaser { display: flex; flex-direction: column; justify-content: center; }
.method-teaser h2 { margin: 0 0 14px; font-size: var(--text-2xl); }
.method-teaser p { margin: 0; color: var(--text-secondary); font-size: var(--text-base); max-width: 68ch; text-wrap: pretty; }
.method-teaser strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* ── the citation block ─────────────────────────────────────────────────── */
/* Deliberately quiet: hairlines and a rule, no fill, no corner cut. It sits
   between the dials and the gold CTA, and the design allows one emphatic
   surface per view — which the CTA has already spent. */

.cite-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.cite-head h2 { margin: 0 0 var(--space-4); font-size: var(--text-2xl); }
.cite-head p { margin: 0 0 var(--space-4); color: var(--text-tertiary); font-size: var(--text-sm); }

.cite-quote {
  margin: 0;
  border-left: 3px solid var(--color-gold);
  padding: var(--space-4) 0 var(--space-4) var(--space-5);
  background: var(--surface-sunken, transparent);
}
/* Body copy size, same as the working prose elsewhere on the page (e.g.
   .dials-intro p) — it's a sentence to copy-paste, not a pull quote, and a
   measure that keeps it to two or three lines. `text-wrap: pretty` matters
   more than usual: it is quoted text, and a one-word last line looks like a
   mistake in the citation rather than in the layout. */
.cite-quote p {
  margin: 0;
  font-size: var(--text-sm); line-height: 1.55;
  color: var(--text-primary); max-width: 78ch; text-wrap: pretty;
  /* It exists to be selected. */
  -webkit-user-select: text; user-select: text;
}

.cite-actions {
  display: flex; align-items: center; gap: var(--space-5);
  flex-wrap: wrap; margin-top: var(--space-5);
}
.cite-actions .btn { flex: 0 0 auto; }
.cite-note {
  margin: 0; flex: 1 1 32ch;
  color: var(--text-tertiary); font-size: var(--text-sm); max-width: 62ch;
}
.cite-note a { color: var(--text-secondary); }

@media (max-width: 620px) {
  .cite-actions { gap: var(--space-4); }
}

/* ── sections ───────────────────────────────────────────────────────────── */

.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.section-head h2 { margin: 0 0 var(--space-5); font-size: var(--text-2xl); }

/* ── forms ──────────────────────────────────────────────────────────────── */

.sub-form { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-5); }
.sub-form input[type="email"] {
  flex: 1 1 220px; min-width: 0;
  background: var(--surface-card); color: var(--text-primary);
  border: var(--border-control) solid var(--border-default);
  border-radius: var(--radius-none);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-body); font-size: var(--text-base);
}
.sub-form input[type="email"]:focus-visible { outline: var(--focus-ring-width) solid var(--accent); outline-offset: 0; border-color: transparent; }
.sub-form-lg { max-width: 560px; }
.sub-form-lg input[type="email"], .sub-form-lg .btn { padding-block: var(--space-3); }

/* Honeypot: off-screen rather than display:none, which bots skip. Never
   focusable or announced, so it costs real readers nothing. */
.sub-form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* Turnstile is interaction-only — a zero-height box until a reader actually
   has to prove they're human. */
.sub-form .cf-turnstile:not(:empty) { flex: 1 1 100%; margin-top: var(--space-1); }

.sub-note { color: var(--text-secondary); font-size: var(--text-sm); margin-top: var(--space-2); }
.sub-note.ok { color: var(--color-good); }
.sub-note.err { color: var(--color-danger); }
.cta-band .sub-note.ok { color: var(--color-gold); opacity: 1; }
.cta-band .sub-note.err { color: var(--color-danger-border); opacity: 1; }

/* ── prose ──────────────────────────────────────────────────────────────── */

.prose { max-width: 72ch; }
.prose h1 { font-size: clamp(var(--text-3xl), 5vw, var(--text-4xl)); margin-bottom: var(--space-3); }
.prose h2 {
  font-size: var(--text-xl); margin-top: var(--space-12); margin-bottom: var(--space-4);
  padding-top: var(--space-6); border-top: var(--border-hairline) solid var(--border-default);
}
.prose h3 { font-size: var(--text-md); margin-top: var(--space-8); margin-bottom: var(--space-2); }
.prose p, .prose li { color: var(--text-primary); }
.prose li { margin-bottom: var(--space-2); }
.prose strong { color: var(--text-primary); font-weight: var(--weight-semibold); }
.prose code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-sunken); padding: 1px 5px; }

.badge {
  display: inline-block; vertical-align: middle; white-space: nowrap;
  font-family: var(--font-mono); font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-secondary);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-none);
  padding: var(--space-1) var(--space-2); margin-left: var(--space-3);
}

table, table.weights { border-collapse: collapse; width: 100%; margin: var(--space-6) 0; font-size: var(--text-base); }
th, td, table.weights th, table.weights td {
  text-align: left; padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-hairline) solid var(--border-default); vertical-align: top;
}
th, table.weights th {
  font-size: var(--text-xs); text-transform: uppercase;
  letter-spacing: var(--tracking-wider); color: var(--text-secondary);
  font-weight: var(--weight-semibold);
}
table.weights td:last-child { color: var(--text-secondary); }
.table-scroll { overflow-x: auto; }

/* ── frames ─────────────────────────────────────────────────────────────── */

.frame-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.frame-card h3 { margin: 0 0 var(--space-2); font-size: var(--text-md); }
.frame-card p { color: var(--text-secondary); font-size: var(--text-sm); margin: 0; }

/* The five families. Each is an anchor the fortnightly email links straight
   into, so they need scroll-margin — the header is sticky, and without it an
   arriving reader lands with the heading tucked underneath it. */
.bucket, #agency { scroll-margin-top: var(--space-20); }
.bucket-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.bucket-head h2 { margin: 0; font-size: var(--text-2xl); }
.bucket-blurb {
  font-family: var(--font-mono); font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-secondary);
}
.bucket-def {
  max-width: 72ch; color: var(--text-secondary);
  margin: var(--space-3) 0 var(--space-6);
}

.agency-scale { display: grid; gap: 0; border-top: var(--border-hairline) solid var(--border-default); }
.agency-band {
  display: grid; grid-template-columns: 210px 1fr; gap: var(--space-6);
  padding: var(--space-4) 0;
  border-bottom: var(--border-hairline) solid var(--border-default);
}
.agency-band-name {
  font-family: var(--font-mono); font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--text-primary);
}
.agency-band p { margin: 0; color: var(--text-secondary); font-size: var(--text-base); max-width: 68ch; }

@media (max-width: 620px) {
  .agency-band { grid-template-columns: 1fr; gap: var(--space-2); }
}

.empty-state {
  border: var(--border-hairline) dashed var(--border-strong);
  border-radius: var(--radius-none);
  padding: var(--space-10); text-align: center; color: var(--text-secondary);
}

/* ── footer ─────────────────────────────────────────────────────────────── */

.site-footer {
  border-top: var(--border-hairline) solid var(--border-default);
  background: var(--surface-card);
  padding: clamp(var(--space-10), 5vw, var(--space-16)) clamp(var(--space-4), 4vw, var(--space-12));
}
.foot-grid { max-width: var(--container-wide); margin: 0 auto; display: grid; gap: var(--space-8); grid-template-columns: repeat(3, 1fr); }
.foot-brand { margin: 0; font-family: var(--font-display); font-size: var(--text-md); font-weight: var(--weight-medium); letter-spacing: 0.14em; text-transform: uppercase; }
.foot-tag { margin: var(--space-2) 0 0; color: var(--text-secondary); font-size: var(--text-sm); }
.foot-links { display: flex; flex-direction: column; gap: var(--space-2); }
.foot-links a { color: var(--text-secondary); font-size: var(--text-sm); text-decoration: none; }
.foot-links a:first-child { font-family: var(--font-mono); }
.foot-links a:hover { color: var(--text-primary); }
.foot-sub p:first-child { margin-top: 0; font-size: var(--text-sm); }
.foot-sub .sub-form { margin-top: 14px; }
.foot-legal {
  max-width: var(--container-wide); margin: var(--space-8) auto 0;
  color: var(--text-tertiary); font-family: var(--font-mono); font-size: var(--text-xs);
  border-top: var(--border-hairline) solid var(--border-default);
  padding-top: var(--space-5);
}

/* ── staleness banner ───────────────────────────────────────────────────── */
/* Sits above the reading so a reader meets the caveat before the number
   rather than after. Two tones: a neutral note for the normal multi-week
   freeze lag, and a stronger one past 45 days, where the likeliest
   explanation stops being "Trends hasn't frozen the month" and starts being
   "the pipeline stopped". Colours are stated in absolutes, not tokens: this
   band must read the same in either theme. */
/* Full-bleed by sitting outside <main> rather than by escaping it with
   negative viewport-width margins. `calc(50% - 50vw)` measures 50% against the
   containing block, which excludes the scrollbar, and 50vw against the
   viewport, which includes it — so on any page tall enough to scroll it
   overhangs by half a scrollbar on each side and the page scrolls sideways by
   a few pixels. The old stylesheet hid that with `overflow-x: hidden` on
   <html>; that is no longer available, because it makes the sticky header's
   containing block scroll and can strand it mid-page. Being a sibling of
   <main> needs neither trick.

   The inner wrapper then repeats <main>'s own max-width and padding, so the
   text lands on the page's content line at every width instead of being
   approximated by a percentage that drifts once the container stops growing. */
.staleness {
  padding-block: var(--space-4);
  border-bottom: var(--border-hairline) solid var(--color-gold);
  background: var(--color-gold-light); color: var(--color-black);
}
.staleness-inner {
  max-width: var(--container-wide); margin: 0 auto;
  padding-inline: clamp(var(--space-4), 4vw, var(--space-12));
}
.staleness p { margin: 0; font-size: var(--text-sm); line-height: var(--leading-normal); max-width: 68ch; }
.staleness a { color: inherit; }
.staleness-warn { background: var(--color-black); color: var(--color-page); }
.staleness-warn a { color: var(--color-gold); }

/* ── image export ───────────────────────────────────────────────────────── */
/* The range switch and this button are one cluster: both answer "what am I
   looking at, and how do I take it with me". */
/* Its own row immediately above the chart, sharing the chart frame's measure
   and pushed to the right so the cluster ends on the frame's right-hand rule.
   justify-content, not margin-left:auto — it is no longer a flex item in the
   topline row having to fend for itself. */
.topline-ranges {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--space-3); flex-wrap: wrap;
  max-width: 1180px; margin: 0 auto var(--space-3);
}
.share-open {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px var(--space-3);
  background: transparent; color: var(--text-secondary);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-none);
  font-family: var(--font-mono); font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide); cursor: pointer;
  transition: color var(--duration-fast), border-color var(--duration-fast);
}
.share-open:hover, .share-open:focus-visible {
  color: var(--text-primary); border-color: var(--border-focus);
}
.share-open svg { flex: none; }

.share-dlg {
  width: min(1000px, calc(100vw - 2 * var(--space-6)));
  max-height: calc(100vh - 2 * var(--space-8));
  padding: 0; border: var(--border-hairline) solid var(--border-strong);
  border-radius: var(--radius-none);
  background: var(--surface-card); color: var(--text-primary);
  box-shadow: var(--shadow-lg);
  overflow: auto;
}
.share-dlg::backdrop { background: var(--overlay-ink); }
/* The non-modal fallback, for a browser without showModal(): the dialog is
   opened with the `open` attribute instead and has to place itself. */
.share-dlg[open]:not(:modal) {
  position: fixed; inset-block-start: var(--space-8); inset-inline: 0;
  margin-inline: auto; z-index: 60;
}

.share-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6) var(--space-4);
  border-bottom: var(--border-hairline) solid var(--border-default);
}
.share-head h2 { margin: 0; font-size: var(--text-lg); }
.share-span {
  margin: 6px 0 0; font-family: var(--font-mono); font-size: var(--text-xs);
  color: var(--text-secondary); letter-spacing: var(--tracking-wide);
}
.share-x {
  flex: none; display: grid; place-items: center;
  width: 30px; height: 30px; padding: 0;
  background: transparent; border: 0; cursor: pointer; color: var(--text-secondary);
}
.share-x:hover { color: var(--text-primary); }

.share-body {
  display: grid; grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--space-6); padding: var(--space-6);
}

/* Sunken, so a light card and a page-coloured card both read as an image
   sitting on a surface rather than bleeding into the dialog. */
.share-preview {
  display: grid; place-items: center; min-width: 0;
  background: var(--surface-sunken);
  border: var(--border-hairline) solid var(--border-default);
  padding: var(--space-4);
}
/* The canvas is rendered at full output size (2× the platform's pixels) and
   shown at whatever fits. Both constraints are needed: max-width alone lets a
   9:16 story run off the bottom of the screen. */
.share-preview canvas {
  display: block; max-width: 100%; max-height: 52vh;
  width: auto; height: auto;
  box-shadow: var(--shadow-sm);
}

.share-opts { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
.share-field { margin: 0; padding: 0; border: 0; }
.share-field legend {
  padding: 0 0 var(--space-3);
  font-family: var(--font-mono); font-size: var(--text-2xs);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-widest);
  text-transform: uppercase; color: var(--text-tertiary);
}

.share-ars { display: flex; flex-direction: column; }
.share-ar { display: block; cursor: pointer; }
.share-ar input {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); border: 0;
}
.share-ar-face {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 9px var(--space-3);
  border: var(--border-hairline) solid var(--border-default);
  border-top: 0;
  transition: background var(--duration-fast), border-color var(--duration-fast);
}
.share-ar.is-first .share-ar-face { border-top: var(--border-hairline) solid var(--border-default); }
.share-ar:hover .share-ar-face { background: var(--surface-sunken); }
.share-ar input:checked + .share-ar-face { background: var(--text-primary); }
.share-ar input:focus-visible + .share-ar-face { outline: var(--focus-ring-width) solid var(--border-focus); outline-offset: 2px; }

/* A scale drawing of what you are choosing. The slot is a constant envelope so
   every row is the same height and the swatches share a centre line; build.py
   sizes the box inside it from the format's real dimensions. */
.share-ar-slot { flex: none; display: grid; place-items: center; width: 34px; height: 28px; }
.share-ar-box { display: block; border: var(--border-control) solid var(--text-tertiary); }
.share-ar input:checked + .share-ar-face .share-ar-box { border-color: var(--color-gold); }
.share-ar-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-size: var(--text-xs); color: var(--text-secondary); }
.share-ar-txt strong {
  font-family: var(--font-mono); font-size: var(--text-sm);
  font-weight: var(--weight-semibold); color: var(--text-primary);
  letter-spacing: var(--tracking-wide);
}
.share-ar input:checked + .share-ar-face .share-ar-txt { color: rgba(251, 251, 252, 0.72); }
.share-ar input:checked + .share-ar-face .share-ar-txt strong { color: var(--color-page); }

.share-check { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; }
.share-check input { margin: 2px 0 0; accent-color: var(--color-black); flex: none; }
.share-check span { font-size: var(--text-sm); color: var(--text-primary); }
.share-check em {
  display: block; margin-top: 3px; font-style: normal;
  font-size: var(--text-xs); color: var(--text-tertiary); line-height: var(--leading-snug);
}

.share-hint {
  margin: 0; font-family: var(--font-mono); font-size: var(--text-2xs);
  color: var(--text-secondary); letter-spacing: var(--tracking-wide);
  line-height: var(--leading-snug); min-height: 2.4em;
}
.share-opts .btn { width: 100%; justify-content: center; text-align: center; }
.share-note { margin: 0; font-size: var(--text-xs); color: var(--text-tertiary); line-height: var(--leading-normal); }
.share-note strong { color: var(--text-secondary); font-weight: var(--weight-semibold); }

@media (max-width: 820px) {
  /* One column, and the preview pins itself to the top of the scroll. Stacked
     without that, changing the shape would update a picture the reader had
     already scrolled past — the choice and its result have to stay on screen
     together, which the two-column desktop layout gets for free. */
  .share-body { grid-template-columns: 1fr; gap: var(--space-5); padding-top: 0; }
  .share-preview {
    position: sticky; top: 0; z-index: 1;
    margin-inline: calc(-1 * var(--space-6));
    padding: var(--space-4) var(--space-6);
    border-inline: 0; border-top: 0;
  }
  .share-preview canvas { max-height: 30vh; }
}

/* ── responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 1020px) {
  /* The side note loses its anchor once the plot narrows; it reads fine as a
     caption underneath. */
  .chart-row { flex-direction: column; gap: var(--space-4); }
  .chart-aside { width: auto; }
  .chart-aside p { position: static; transform: none; max-width: 60ch; }
  .dials-section { grid-template-columns: 1fr; }
  .gauge-grid { grid-template-columns: repeat(2, 1fr); }
  .split { grid-template-columns: 1fr; }
}

@media (max-width: 1020px) {
  /* The design hides the nav entirely below this width. A live site cannot —
     the sub-pages are only reachable through it — so it wraps under the
     wordmark instead. Two stacked rows are too much furniture to pin to the
     top of a phone screen, so the header stops being sticky here and the
     mini-readout (a scroll affordance) goes with it.
     Threshold is wider than it looks like it needs to be: below it, "Live
     index" — the longest nav label — no longer fits its own flex item and
     wraps onto two lines before the row as a whole would wrap. Moving to the
     stacked arrangement here pre-empts that. */
  .site-header { position: static; flex-wrap: wrap; row-gap: var(--space-3); }
  .hdr-mini { display: none; }
  .site-nav { width: 100%; flex-wrap: wrap; justify-content: flex-start; gap: var(--space-2) var(--space-4); }
  .site-nav a { font-size: var(--text-xs); white-space: nowrap; }
  .site-nav .btn-ghost { padding: 6px var(--space-3); }
  /* No pointer to follow, and no room to put it if there were. */
  .mai-tip { display: none; }
}

@media (max-width: 620px) {
  .wordmark { font-size: 15px; letter-spacing: 0.1em; }
}

@media (max-width: 620px) {
  .gauge-grid { grid-template-columns: 1fr; }
  .foot-grid { grid-template-columns: 1fr; }
  .topline { gap: var(--space-4); }
  /* The status line is one line wide enough, two lines narrow enough, and it
     swaps text on every touch-drag across the chart — which would walk the
     chart up and down the page. Hold two lines' height here. */
  .chart-note { min-height: 3.3em; }

  /* On a phone everything in the lead stacks one item per row, so the two
     paragraphs land between the number and the chart as eight or nine lines
     of prose — enough to push the chart, which is the reason to be here, off
     the first screen entirely. They read just as well as a closing note, so
     below the chart they go, still ahead of "What's driving it".

     Done with flex order rather than by moving the markup, because on a wide
     screen the order in the source is the order that should be read: define
     the number, then show its history. Source order is also what a screen
     reader and a stylesheet-less render follow, and this is the one layout
     where the visual order should differ from it. */
  .home-lead { display: flex; flex-direction: column; }
  .mai-eyebrow    { order: 0; }
  .topline        { order: 1; }
  .topline-ranges { order: 2; }
  .chart-note     { order: 3; }
  .chart-frame    { order: 4; }
  .topline-what   { order: 5; margin: var(--space-5) 0 var(--space-3); }
  .topline-timing { order: 6; margin: 0; }
}

/* ── degraded-day notice ────────────────────────────────────────────────────
   Shown only when a source has not published and the figure is built from
   fewer than four signals. Deliberately NOT an alarm: nothing is broken and
   nothing is wrong with the number — it is a narrower measurement, and the
   reader is being told which part is missing. Styled as a quiet aside, in the
   page's own palette, rather than as an error state that would misdescribe it. */
.notice {
  margin: var(--space-4) 0 0;
  padding: var(--space-4) var(--space-5);
  background: var(--surface-sunken);
  border: var(--border-hairline) solid var(--border-default);
  border-left: 3px solid var(--text-accent);
  border-radius: var(--radius-none);
}
.notice p {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--text-secondary);
}
.notice strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* A dial whose source has not reported. The track and baseline tick still
   draw, so it reads as an instrument awaiting a reading rather than as a
   missing card — and the em dash is the value, never a zero. */
.gauge-pending .gauge-val { color: var(--text-tertiary); }
.gauge-pending .gauge-note-pending { font-style: italic; }

/* The card carries both captions and shows one. home.js drops .gauge-pending
   while the reader hovers a historical day — that day's value is real, so the
   dial becomes ordinary and its caption has to become the ordinary one with
   it. Default state (no .gauge-pending on the card) hides the pending line. */
.gauge-note-pending { display: none; }
.gauge-pending .gauge-note-pending { display: block; }
.gauge-pending .gauge-note-normal { display: none; }
