/* Avid Quant desk design system: the component layer.

   Every value here comes from tokens.css through var(). There is no colour
   literal, no font stack and no bare pixel size in this file that a token
   could have carried, and the guard in tests/test_design_system.py fails
   the build if one appears.

   Load order matters and is fixed:

     1. the vendored renderer's own inline <style>   (never edited)
     2. tokens.css                                   (retints 1 by cascade)
     3. components.css                               (this file)
     4. shell.css                                    (site chrome)

   Two kinds of rule live here. The first names an od- class and is the
   design system proper. The second reaches an unprefixed class that the
   vendored renderers already emit, and exists to bring that markup onto
   the system without editing the renderer. Those are grouped at the end
   under ADOPTED SELECTORS and each one says which service emits it.

   No script is required by anything in this file. The sibling Risk Desk
   service serves under script-src 'none'. */

/* ------------------------------------------------------------------ */
/* Page shell                                                          */
/* ------------------------------------------------------------------ */

.od-page {
  background: var(--od-ground);
  color: var(--od-text);
  font-family: var(--od-font-sans);
  font-size: var(--od-font-size-4);
  line-height: var(--od-line-body);
}

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

/* ------------------------------------------------------------------ */
/* Status and provenance strip                                         */
/*                                                                     */
/* One row that answers, without the reader hunting: what state is the  */
/* data in, how old is it, and where did it come from. It sits directly */
/* under the page title and above everything else, and it wraps rather  */
/* than truncating, because a provenance line that is cut off is worse  */
/* than one that takes a second line.                                   */
/* ------------------------------------------------------------------ */

.od-statusbar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--od-space-2) var(--od-space-5);
  padding: var(--od-space-3) var(--od-space-5);
  margin: 0 0 var(--od-space-5);
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-2);
  background: var(--od-surface);
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
}

.od-statusbar .od-status-item {
  display: inline-flex;
  align-items: baseline;
  gap: var(--od-space-2);
  white-space: nowrap;
}

/* A wide item is allowed to wrap and to run long: a source statement, a
   degraded reason or a digest must never be abbreviated. */
.od-statusbar .od-status-item.od-status-provenance {
  white-space: normal;
  flex: 1 1 24ch;
}

/* The key never wraps, whatever the item around it is doing. Without
   this the wide item's white-space: normal reaches the key as well, and
   a narrow column breaks the word RETENTION down the page one letter at
   a time. Seen on a rendered private report, not theorised. */
.od-status-key {
  flex: none;
  white-space: nowrap;
  font-size: var(--od-font-size-7);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-faint);
}

/* Only an identifier breaks mid-token. Prose must not: breaking a
   sentence anywhere it likes is how a source statement becomes
   unreadable. */
.od-status-value.od-mono {
  overflow-wrap: anywhere;
}

.od-status-value {
  color: var(--od-text);
  font-variant-numeric: tabular-nums;
}

.od-status-value.od-mono {
  font-family: var(--od-font-mono);
}

/* ------------------------------------------------------------------ */
/* State flag                                                          */
/*                                                                     */
/* A chip that names one fact about the data. The word is the primary   */
/* channel and is never omitted; colour is the second channel and the   */
/* border style is the third, so the chip still reads in monochrome.    */
/* ------------------------------------------------------------------ */

.od-flag,
.flag {
  display: inline-block;
  padding: var(--od-space-1) var(--od-space-4);
  border: var(--od-border-hair) solid var(--od-line-strong);
  border-radius: var(--od-radius-pill);
  font-size: var(--od-font-size-7);
  font-weight: var(--od-weight-strong);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-muted);
  white-space: nowrap;
}

.od-flag.od-flag-ok,
.flag.ok {
  color: var(--od-gain);
  border-color: var(--od-gain);
}

.od-flag.od-flag-stale {
  color: var(--od-stale);
  border-color: var(--od-stale);
  border-style: dashed;
}

.od-flag.od-flag-degraded,
.flag.degraded {
  color: var(--od-degraded);
  border-color: var(--od-degraded);
  border-width: var(--od-border-rule);
}

.od-flag.od-flag-synthetic {
  color: var(--od-synthetic);
  border-color: var(--od-synthetic);
  border-style: dotted;
}

/* ------------------------------------------------------------------ */
/* Banner                                                              */
/*                                                                     */
/* The full-width statement of what the reader is looking at. Its       */
/* colour is carried by the state, not by the brand: a synthetic page   */
/* is violet, a stale page amber, a degraded page orange and hatched.   */
/* ------------------------------------------------------------------ */

.od-banner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--od-space-3);
  padding: var(--od-space-4) var(--od-space-5);
  margin: var(--od-space-4) 0 var(--od-space-6);
  border: var(--od-border-hair) solid var(--od-line);
  border-left: var(--od-border-flag) solid var(--od-accent);
  border-radius: var(--od-radius-2);
  background: var(--od-surface);
  color: var(--od-text);
  font-size: var(--od-font-size-5);
}

/* The base variant is deliberate, not a fallback. It means: read this
   before the figures, but no claim is being made that the data is
   synthetic, stale or degraded. The private user-data view uses it,
   because that data is the reader's own and unverified is not a state
   the service is entitled to assert about it. */

.od-banner strong {
  font-weight: var(--od-weight-strong);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
}

.od-banner .detail {
  flex-basis: 100%;
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
  overflow-wrap: anywhere;
}

.od-banner .detail strong {
  color: var(--od-text);
  text-transform: none;
  letter-spacing: var(--od-tracking-none);
}

.od-banner.od-banner-synthetic {
  border-left-color: var(--od-synthetic);
  background: var(--od-synthetic-ground);
}

.od-banner.od-banner-synthetic strong {
  color: var(--od-synthetic);
}

.od-banner.od-banner-stale {
  border-left-color: var(--od-stale);
  border-left-style: dashed;
  background: var(--od-stale-ground);
}

.od-banner.od-banner-stale strong {
  color: var(--od-stale);
}

/* The hatch is a channel, not a wallpaper. It was painted across the
   whole block, behind running prose, which is illegible at any size.
   It now marks the left edge only: still a non-chromatic signal that a
   monochrome screen carries, with nothing behind the words. */
.od-banner.od-banner-degraded {
  border-left-color: var(--od-degraded);
  border-left-width: var(--od-space-4);
  border-left-style: solid;
  background-color: var(--od-degraded-ground);
  background-image: var(--od-degraded-hatch);
  background-repeat: no-repeat;
  background-size: var(--od-space-4) 100%;
  background-position: left top;
}

.od-banner.od-banner-degraded strong {
  color: var(--od-degraded);
}

/* A provenance strip inside a banner is part of the banner, not a second
   panel stacked on it: it drops its own border and ground and keeps only
   the rule that separates it from the warning above. */
.od-banner .od-statusbar {
  flex-basis: 100%;
  margin: var(--od-space-2) 0 0;
  padding: var(--od-space-3) 0 0;
  border: 0;
  border-top: var(--od-border-hair) solid var(--od-line);
  border-radius: 0;
  background: none;
}

.od-banner button {
  font: inherit;
  font-size: var(--od-font-size-5);
  font-weight: var(--od-weight-medium);
  cursor: pointer;
  padding: var(--od-space-2) var(--od-space-5);
  border: var(--od-border-hair) solid var(--od-line-strong);
  border-radius: var(--od-radius-2);
  background: var(--od-surface-raised);
  color: var(--od-text);
}

.od-banner button:hover,
.od-banner button:focus-visible {
  border-color: var(--od-accent);
  color: var(--od-accent);
}

/* ------------------------------------------------------------------ */
/* Sign convention callout                                             */
/*                                                                     */
/* Its own component because a sign convention is not a caveat. It is   */
/* the key to reading every number on the page, and on a trading floor  */
/* a misread sign is the expensive mistake. It is never collapsed, and  */
/* it renders before the figures it governs.                            */
/* ------------------------------------------------------------------ */

.od-convention,
.convention {
  margin: 0 0 var(--od-space-5);
  padding: var(--od-space-4) var(--od-space-5);
  border: var(--od-border-hair) solid var(--od-line);
  border-left: var(--od-border-flag) solid var(--od-accent);
  border-radius: var(--od-radius-2);
  background: var(--od-surface);
  color: var(--od-text);
  font-size: var(--od-font-size-5);
  line-height: var(--od-line-loose);
  max-width: var(--od-measure);
}

.od-convention > strong,
.convention > strong {
  display: block;
  margin-bottom: var(--od-space-2);
  font-size: var(--od-font-size-7);
  font-weight: var(--od-weight-strong);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-accent);
}

.od-convention dl,
.convention dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--od-space-2) var(--od-space-5);
  margin: var(--od-space-3) 0 0;
}

.od-convention dt,
.convention dt {
  font-variant-numeric: tabular-nums;
  font-weight: var(--od-weight-medium);
  color: var(--od-text-muted);
  white-space: nowrap;
}

.od-convention dd,
.convention dd {
  margin: 0;
}

/* ------------------------------------------------------------------ */
/* Card                                                                */
/*                                                                     */
/* Flat. A hairline and a surface step, no shadow. A shadow on a data   */
/* card costs vertical room and buys nothing a border does not.         */
/* ------------------------------------------------------------------ */

.od-card {
  padding: var(--od-space-5) var(--od-space-6) var(--od-space-4);
  margin: 0 0 var(--od-space-5);
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-3);
  background: var(--od-surface);
  box-shadow: var(--od-elevation-0);
}

.od-card > h3,
.od-card-title {
  margin: 0 0 var(--od-space-1);
  font-size: var(--od-font-size-3);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-tight);
  color: var(--od-text);
}

.od-card-hint {
  margin: 0 0 var(--od-space-5);
  max-width: var(--od-measure);
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
}

/* ------------------------------------------------------------------ */
/* Dense data table                                                    */
/*                                                                     */
/* The measured shape of this domain: the widest table served today is  */
/* 20 columns by 14 rows. So the row identity column stays put while    */
/* the numbers scroll under the reader, the column head stays put while */
/* the rows scroll, and every figure is a tabular numeral so digits in  */
/* a column line up on the decimal point.                               */
/* ------------------------------------------------------------------ */

.od-tablewrap {
  overflow: auto;
  max-height: 60vh;
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-2);
  background: var(--od-surface);
}

.od-table,
table {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  font-size: var(--od-font-size-5);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.od-table th,
.od-table td,
th,
td {
  padding: var(--od-cell-pad-y) var(--od-cell-pad-x);
  border-bottom: var(--od-border-hair) solid var(--od-line);
  text-align: right;
  white-space: nowrap;
}

/* The first column names the row. It is a label, so it reads left, and
   it holds its position while the figures scroll sideways. */
.od-table th:first-child,
.od-table td:first-child,
th:first-child,
td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  text-align: left;
  background: var(--od-surface);
  border-right: var(--od-border-hair) solid var(--od-line);
}

/* This renderer writes <table><tr><th> with no <thead>, so the
   browser parses those cells into an implicit <tbody> and a
   thead-only selector never matches them. Measured on the served
   page: the column heads were rendering as plain body cells. Every
   th here is in the first row, so that row is named directly. */
.od-table thead th,
table tr:first-child th {
  position: sticky;
  top: 0;
  z-index: 2;
  font-size: var(--od-font-size-7);
  font-weight: var(--od-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-muted);
  background: var(--od-surface-raised);
  border-bottom: var(--od-border-rule) solid var(--od-line-strong);
  box-shadow: var(--od-elevation-1);
}

.od-table thead th:first-child,
table tr:first-child th:first-child {
  z-index: 3;
  background: var(--od-surface-raised);
}

.od-table tbody tr:last-child th,
.od-table tbody tr:last-child td {
  border-bottom: 0;
}

/* A unit belongs with its column head, not repeated in every cell. */
.od-table thead th .od-unit {
  display: block;
  font-size: var(--od-font-size-7);
  font-weight: var(--od-weight-normal);
  text-transform: none;
  letter-spacing: var(--od-tracking-none);
  color: var(--od-text-faint);
}

/* ------------------------------------------------------------------ */
/* Signed figure                                                       */
/*                                                                     */
/* Three channels, so no single failure hides the direction:            */
/*   1. the sign character, always rendered, never dropped on a gain    */
/*   2. the hue                                                        */
/*   3. a leading glyph, which survives monochrome and colour blindness */
/* ------------------------------------------------------------------ */

.od-num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.od-num-gain {
  color: var(--od-gain);
}

.od-num-loss {
  color: var(--od-loss);
}

.od-num-gain::before {
  content: var(--od-gain-glyph) " ";
  font-size: 0.8em;
}

.od-num-loss::before {
  content: var(--od-loss-glyph) " ";
  font-size: 0.8em;
}

/* A row whose direction matters carries it on its edge as well. The edge
   differs in border STYLE, not only in colour: solid for a gain, dashed
   for a loss. Two rows of opposite sign stay distinguishable on a
   monochrome screen and in a printout. */
.od-table tr.od-row-gain td:first-child {
  border-left: var(--od-border-flag) var(--od-gain-border-style)
               var(--od-gain);
  background: var(--od-gain-ground);
}

.od-table tr.od-row-loss td:first-child {
  border-left: var(--od-border-flag) var(--od-loss-border-style)
               var(--od-loss);
  background: var(--od-loss-ground);
}

/* ------------------------------------------------------------------ */
/* Chart frame                                                         */
/*                                                                     */
/* The existing charts are kept exactly as they are: the Option Desk    */
/* dashboard draws with its own bundled script into a div, and the Risk */
/* Desk report emits inline SVG from the standard library. This frames  */
/* both and rewrites neither. The frame owns the title, the units, the  */
/* provenance note and the border; the chart owns the pixels inside.    */
/* ------------------------------------------------------------------ */

.od-chartframe {
  margin: 0 0 var(--od-space-5);
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-3);
  background: var(--od-surface);
}

.od-chartframe-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--od-space-2) var(--od-space-5);
  padding: var(--od-space-4) var(--od-space-5);
  border-bottom: var(--od-border-hair) solid var(--od-line);
}

.od-chartframe-title {
  margin: 0;
  font-size: var(--od-font-size-3);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-tight);
  color: var(--od-text);
}

.od-chartframe-unit {
  font-size: var(--od-font-size-7);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-faint);
}

.od-chartframe-body {
  padding: var(--od-space-4) var(--od-space-5);
}

/* An inline SVG chart fills the frame and keeps its aspect ratio. */
.od-chartframe-body > svg {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}

.od-chartframe-note {
  margin: 0;
  padding: var(--od-space-3) var(--od-space-5) var(--od-space-4);
  border-top: var(--od-border-hair) solid var(--od-line);
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------ */
/* Focus                                                               */
/*                                                                     */
/* The brand signal earns its keep here. A focus ring must be the most  */
/* visible thing on the page and must not be confusable with any data   */
/* state, which is exactly why the signal colour is reserved for it.    */
/* ------------------------------------------------------------------ */

:where(a, button, input, select, summary, [tabindex]):focus-visible {
  outline: var(--od-border-rule) solid var(--od-signal);
  outline-offset: var(--od-space-1);
}

/* ================================================================== */
/* ADOPTED SELECTORS                                                   */
/*                                                                     */
/* Up to riskdesk 0.1 the vendored renderer carried literal colours,  */
/* so every one of its rules is overridden below by name, from a sheet */
/* that loads after it. From 0.2 it paints with the same --od- tokens, */
/* which tokens.css redefines; these overrides remain because they     */
/* also carry this service's sizes. The line numbers name 0.1.          */
/*                                                                     */
/* The list is deliberately exhaustive and ordered to match the        */
/* renderer's own stylesheet, so the two can be read side by side.     */
/* ================================================================== */

/* report.py:331-333 */
body {
  font-family: var(--od-font-sans);
  color: var(--od-text);
  background: var(--od-ground);
  line-height: var(--od-line-body);
  padding: var(--od-space-7);
}

/* report.py:334-336. The renderer's 22 / 17 / 14 become the shared
   scale's 20 / 17 / 14, so a heading here is the same size as the same
   heading on the sibling service. */
h1 {
  font-size: var(--od-font-size-1);
  font-weight: var(--od-weight-strong);
  letter-spacing: var(--od-tracking-tight);
  color: var(--od-text);
  line-height: var(--od-line-tight);
}

h2 {
  font-size: var(--od-font-size-2);
  font-weight: var(--od-weight-medium);
  letter-spacing: var(--od-tracking-tight);
  color: var(--od-text);
  line-height: var(--od-line-tight);
}

h3 {
  font-size: var(--od-font-size-3);
  font-weight: var(--od-weight-medium);
  color: var(--od-text);
  line-height: var(--od-line-tight);
}

/* report.py:337 */
p,
li {
  font-size: var(--od-font-size-4);
  max-width: var(--od-measure);
}

/* report.py:338. The renderer uses .meta for the as-of and provenance
   lines, which is the same job as the status strip. It is given the
   strip's type and colour so the two read alike even though only the
   chrome can emit the strip's own markup. */
.meta {
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
  overflow-wrap: anywhere;
}

/* The renderer styles no anchor and no code span, so both would fall to
   a browser default that was chosen for a white page. */
a {
  color: var(--od-accent);
}

code {
  font-family: var(--od-font-mono);
  font-size: var(--od-font-size-6);
  padding: 0 var(--od-space-2);
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-1);
  background: var(--od-surface-sunken);
  color: var(--od-text);
}

/* report.py:348. The charts are kept exactly as the renderer draws them.
   From riskdesk 0.2 the renderer wraps each one in its own .chart-frame,
   which carries the border, ground and padding, so the SVG here is only
   sized; framing it as well drew a second border inside the first. */
svg.chart {
  box-sizing: border-box;
  display: block;
  /* These SVGs carry a viewBox and no width attribute, so they stretch
     to whatever box they are given and the whole drawing scales with
     them, the renderer's 11px labels included. On a 1502px viewport the
     live service renders them at 1438px, a scale of 1.75 to 1.89, which
     puts those labels on screen at 19.3 to 20.8px. That is the
     renderer's existing behaviour and not something the design system
     introduced; it was measured on the deployment that predates it.
     What the frame can do is stop giving them an unlimited box. The cap
     below holds the scale near 1, so a chart label is a chart label
     again rather than the largest text on the page. */
  width: auto;
  max-width: min(100%, var(--od-column));
  height: auto;
}

/* report.py:349 */
.chart .axis {
  stroke: var(--od-axis);
}

/* ONE COLOUR SYSTEM.

   The renderer paints long and gain with one literal and short and loss
   with another, so on a page carrying both kinds of chart the same hue
   means two different things. Rendered and counted: in the stress charts
   brent_front_future_short is a GAIN and draws green, while in the
   exposure chart above it the same leg is SHORT and draws red. A reader
   scanning for red is scanning for two unrelated facts.

   So the hues are separated by meaning rather than by the renderer's
   class pairing:

     green, red    profit and loss, and nothing else
     blue          exposure and magnitude: a position, a share of gross,
                   a histogram bin. Never a direction.
     violet        synthetic data
     amber, orange stale and degraded state

   Direction is not lost. The exposure chart is signed around a zero line
   and the renderer prints the words "short" and "long" on either side of
   it, so position and label carry direction without spending a hue that
   already means something else. */

/* report.py:350, 358. Profit and loss. */
.chart .bar.loss,
.chart .mark.var {
  fill: var(--od-loss);
  stroke: var(--od-loss);
}

/* report.py:351 */
.chart .bar.gain {
  fill: var(--od-gain);
}

/* report.py:352, 353. Position direction is exposure, not profit. */
.chart .bar.long,
.chart .bar.short {
  fill: var(--od-accent);
}

/* report.py:354 */
.chart .bar.flat {
  fill: var(--od-text-faint);
}

/* report.py:355-356. Share of gross and the histogram bins are neither a
   gain nor a loss: gross counts absolute values, so a share can never be
   negative. They take the neutral accent, never the direction pair. */
.chart .bar.share,
.chart .bin {
  fill: var(--od-accent);
}

/* report.py:359. Expected Shortfall keeps a dash pattern of its own from
   the renderer, which is the non-colour channel that separates it from
   the VaR mark beside it. */
.chart .mark.es {
  stroke: var(--od-synthetic);
}

/* report.py:360-362 */
.chart .label,
.chart .value,
.chart .tick,
.chart .marklabel {
  fill: var(--od-text);
  font-family: var(--od-font-sans);
  font-size: var(--od-font-size-6);
}

.chart .tick {
  fill: var(--od-text-muted);
}

/* report.py:364 */
footer {
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
  border-top: var(--od-border-hair) solid var(--od-line);
}

/* ================================================================== */
/* COMPOSITION                                                         */
/*                                                                     */
/* Rules about where things sit and how big they are, rather than what */
/* colour they are. Written after rendering the page at 1280 and at    */
/* 390 and looking at it, which is the only way these were visible.    */
/* ================================================================== */

/* The banner's own parts. The lead is one line of consequence, the
   reason is the sentence a reader must not miss, and the long prose is
   a disclosure that costs nothing until it is opened. */
.od-banner-lead {
  margin: 0;
  max-width: none;
  font-size: var(--od-font-size-4);
}

.od-banner-lead strong {
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
}

.od-banner-reason {
  margin: 0;
  max-width: none;
  padding: var(--od-space-3) var(--od-space-4);
  border-radius: var(--od-radius-1);
  background: var(--od-ground);
  font-size: var(--od-font-size-4);
}

.od-banner-reason strong {
  color: var(--od-degraded);
}

.od-banner-more {
  font-size: var(--od-font-size-6);
  color: var(--od-text-muted);
}

.od-banner-more summary {
  cursor: pointer;
  font-size: var(--od-font-size-7);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-muted);
}

.od-banner-more summary:hover,
.od-banner-more summary:focus-visible {
  color: var(--od-accent);
}

.od-banner-more p {
  margin: var(--od-space-3) 0 0;
  max-width: var(--od-measure);
}

/* The field row inside the banner sits flat, with no second border and
   no second ground: it is part of the banner, not a panel stacked on it. */
.od-banner .od-statusbar {
  margin: 0;
  padding: var(--od-space-3) 0 0;
  border: 0;
  border-top: var(--od-border-hair) solid var(--od-line);
  border-radius: 0;
  background: none;
}

.od-banner .private-controls {
  margin: 0;
}

/* THE READING COLUMN.
   Prose stops at a measure. Tables and charts do not: a table that spans
   1280px puts its label at one end of the screen and its number at the
   other, and the eye cannot connect them. Both are capped so label and
   value stay within a glance of each other, and the cap is generous
   enough that a wide table still has room. */
main > p,
main > ul,
main > ol,
main > details {
  max-width: var(--od-measure);
}

/* A table takes the room its own content needs and no more, so a two
   column table does not stretch its label to one edge of a 1280px screen
   and its number to the other. The cap keeps a wide table inside the
   page's single content column, which the chart frame shares, so table
   and chart line up on the same right edge. */
table {
  width: max-content;
  min-width: min(100%, 30rem);
  max-width: min(100%, var(--od-column));
}

/* Bring the number back to its label. The first column carries the row
   name and should take only the room the name needs. */
th:first-child,
td:first-child {
  width: 1%;
  padding-right: var(--od-space-7);
}

/* HIERARCHY.
   The renderer gives every section the same weight. A section heading is
   a signpost and should read as one, so it takes a rule above it and a
   quieter, wider-tracked label treatment than the page title. */
h2 {
  margin-top: var(--od-space-8);
  padding-top: var(--od-space-4);
  border-top: var(--od-border-hair) solid var(--od-line);
}

h3 {
  margin-top: var(--od-space-6);
  color: var(--od-text-muted);
  font-size: var(--od-font-size-6);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
}

/* The provenance and method lines are support, not content. They stay
   legible but stop competing with the figures. */
.meta {
  max-width: var(--od-measure);
}

/* RESPONSIVE.
   At a phone width the banner must not be the whole first screen, the
   fields stack into two readable columns rather than one long ladder,
   and a wide table scrolls sideways inside its own box instead of
   forcing the page to. */
@media (max-width: 40rem) {
  body {
    padding: var(--od-space-5) var(--od-space-4) var(--od-space-7);
  }

  .od-banner {
    padding: var(--od-space-4);
  }

  .od-statusbar {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--od-space-3) var(--od-space-4);
  }

  .od-statusbar .od-status-item {
    display: flex;
    flex-direction: column;
    gap: 0;
    white-space: normal;
  }

  h2 {
    margin-top: var(--od-space-7);
  }

  /* A table wider than the phone scrolls in its own box. Without this
     the whole page scrolls sideways and the header goes with it. */
  table {
    display: block;
    width: 100%;
    min-width: 0;
    overflow-x: auto;
  }

  th:first-child,
  td:first-child {
    padding-right: var(--od-space-5);
  }
}

/* ------------------------------------------------------------------ */
/* Headline figures                                                    */
/*                                                                     */
/* The answer, before the explanation. Measured before this existed:   */
/* the first number on the demo report sat 671px down at 1280 wide.    */
/* ------------------------------------------------------------------ */

.od-kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
  gap: var(--od-border-hair);
  margin: 0 0 var(--od-space-6);
  border: var(--od-border-hair) solid var(--od-line);
  border-radius: var(--od-radius-3);
  background: var(--od-line);
  overflow: hidden;
}

.od-kpi-tile {
  display: flex;
  flex-direction: column;
  gap: var(--od-space-1);
  padding: var(--od-space-5) var(--od-space-5) var(--od-space-4);
  background: var(--od-surface);
}

.od-kpi-label {
  display: flex;
  align-items: baseline;
  gap: var(--od-space-2);
  font-size: var(--od-font-size-7);
  font-weight: var(--od-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--od-tracking-label);
  color: var(--od-text-muted);
}

/* The currency sits with the label, stated once, rather than repeated in
   the value where it cost the number its room. */
.od-kpi-label abbr {
  font-weight: var(--od-weight-normal);
  color: var(--od-text-faint);
  text-decoration: none;
}

/* The one place on the page with a genuinely large numeral. Tabular
   figures so a column of these aligns, and a tight negative tracking so
   a long amount still fits its tile. */
.od-kpi-value {
  /* Fluid, because a nine-figure amount in a two-column grid on a 390px
     screen overflowed its tile and clipped the number. It never grows
     past the token step and never shrinks below the page title. */
  font-size: clamp(var(--od-font-size-1), 4.2vw, var(--od-font-size-kpi));
  font-weight: var(--od-weight-strong);
  line-height: var(--od-line-tight);
  letter-spacing: var(--od-tracking-tight);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--od-text);
}

.od-kpi-note {
  font-size: var(--od-font-size-7);
  color: var(--od-text-faint);
}

/* Direction on a headline figure carries three channels, as everywhere
   else: the sign character the formatter already prints, the hue, and a
   rule along the tile's top edge whose STYLE differs, solid for a gain
   and dashed for a loss, so the pair survives a monochrome screen. */
.od-kpi-tile.od-kpi-exposure {
  box-shadow: inset 0 var(--od-border-rule) 0 var(--od-accent);
}

.od-kpi-tile.od-kpi-exposure .od-kpi-value {
  color: var(--od-accent);
}

/* A figure the runtime has declared degraded. It keeps its place and its
   value, because hiding it would be the opposite of this project's
   argument, but it stops being the largest and most confident thing on
   the page. Three channels say so and none of them is colour alone: the
   value drops to body size, the tile is edged in a DASHED rule where a
   sound tile is solid, and the note names the reason. */
.od-kpi-tile.od-kpi-degraded {
  border-top: var(--od-border-rule) dashed var(--od-degraded);
  background-image: var(--od-degraded-hatch);
  background-repeat: no-repeat;
  background-size: 100% var(--od-space-3);
  background-position: left top;
}

.od-kpi-tile.od-kpi-degraded .od-kpi-value {
  font-size: var(--od-font-size-2);
  color: var(--od-degraded);
}

.od-kpi-tile.od-kpi-degraded .od-kpi-note {
  color: var(--od-degraded);
}

.od-kpi-tile.od-kpi-gain {
  box-shadow: inset 0 var(--od-border-rule) 0 var(--od-gain);
}

.od-kpi-tile.od-kpi-loss {
  box-shadow: inset 0 var(--od-border-rule) 0 var(--od-loss);
}

.od-kpi-tile.od-kpi-gain .od-kpi-value {
  color: var(--od-gain);
}

.od-kpi-tile.od-kpi-loss .od-kpi-value {
  color: var(--od-loss);
}

/* ------------------------------------------------------------------ */
/* Reading order                                                       */
/*                                                                     */
/* The renderer emits its children in the order it wants to explain     */
/* them: title, flag, a four-line input-source paragraph, a bare        */
/* SHA-256, a six-line sign-convention block, and only then the first   */
/* figure. That order is the author's, not the reader's.                */
/*                                                                     */
/* Nothing is deleted and no markup is touched. The body becomes a      */
/* flex column and three groups of children are given an order: the     */
/* chrome and the figures first, the report's own sections next, and    */
/* the provenance prose and the full sign-convention text last, where   */
/* they become the provenance section the page never had. Every word    */
/* is still on the page and still in the document, so a screen reader   */
/* and a text-only client see the author's original order untouched.    */
/* ------------------------------------------------------------------ */

body {
  display: flex;
  flex-direction: column;
}

body > * {
  order: 2;
}

body > .skip-link,
body > .site-header {
  order: 0;
}

body > .warn,
body > .od-kpi {
  order: 1;
}

/* h1 and the as-of line stay with the figures rather than above the
   banner: the page says what it is, then what the data is, then what
   the numbers are. */
body > h1,
body > p:first-of-type,
body > p:has(.flag) {
  order: 1;
}

/* The provenance heading the chrome adds, then the prose itself. Two
   steps because a tie in order falls back to document order, and the
   heading is appended to the document after the paragraphs it labels. */
body > .od-provenance-heading {
  order: 3;
}

body > .convention,
body > p.meta:not(:first-of-type) {
  order: 4;
}

body > footer {
  order: 5;
}

/* Moved to the foot, the sign-convention block is reference rather than
   preamble, so it stops taking a reader's first screen and keeps every
   word. */
body > .convention {
  max-width: var(--od-measure);
}
