/* Stony Brook University visual system for the reblock project site.
   Loaded after fonts.css (see extra_css in mkdocs.yml).

   PALETTE POLICY. The SBU brand guide makes red and black primary and caps non-red secondary
   colour at roughly 20%. So: red is the ONLY chromatic colour in light mode -- everything else
   is white, near-black ink, or a neutral grey -- and Midnight Blue appears in exactly two
   places (the tail of the hero gradient and the footer band). Stony Brook Grey #828282 is
   deliberately NOT used for body text: it computes to 3.84:1 on white and fails WCAG AA. It
   survives only on non-text rules, where no ratio is required.

   DARK MODE. #990000 as *text* on a dark background is 2.15:1 -- unusable. Dark mode therefore
   splits the brand red into two tokens: --sbu-red-fill stays exactly #990000 wherever white
   text sits on top of it (8.92:1, unchanged in both schemes), and --sbu-red-ink is a lightness
   tint of the same hue used only for text and links on dark (6.37:1 on the page, 5.15:1 on the
   Midnight Blue surface). Every ratio in this file was computed, not estimated. */

/* ------------------------------------------------------------------ tokens: light (default) */

[data-md-color-scheme="default"] {
  /* brand */
  --sbu-red: #990000;             /* 8.92:1 on white  - AAA body */
  --sbu-red-fill: #990000;        /* white on it      - 8.92:1, AAA */
  --sbu-red-ink: #990000;         /* text/links       - 8.92:1, AAA */
  --sbu-red-deep: #7a0000;        /* hover            - 11.49:1, AAA */
  --sbu-midnight: #16243e;        /* secondary, supporting only */
  --sbu-grey: #828282;            /* 3.84:1 - NON-TEXT USE ONLY */
  --sbu-green-ink: #1a6630;       /* "fastest" -  7.03:1 on white, 6.70:1 on the card fill - AAA/AA */

  /* surfaces + ink */
  --sbu-bg: #ffffff;
  --sbu-surface: #fbf9f8;
  --sbu-ink: #111111;             /* 18.88:1 - AAA */
  --sbu-ink-muted: #5c5c5c;       /* 6.69:1  - AA   */
  --sbu-rule: #e4ded8;            /* decorative hairline; not a UI boundary */
  --sbu-rule-strong: #948a81;     /* 3.22:1 on the card fill, 3.38:1 on the page - clears 1.4.11 */

  /* Material overrides */
  --md-primary-fg-color: var(--sbu-red);
  --md-primary-fg-color--light: #b03030;
  --md-primary-fg-color--dark: var(--sbu-red-deep);
  --md-primary-bg-color: #ffffff;
  --md-accent-fg-color: var(--sbu-red-deep);
  --md-typeset-a-color: var(--sbu-red-ink);
  --md-default-fg-color: var(--sbu-ink);
  --md-default-bg-color: var(--sbu-bg);
  --md-code-bg-color: #f6f2ef;
  --md-footer-bg-color: var(--sbu-midnight);
  --md-footer-bg-color--dark: #0f1a2c;
}

/* --------------------------------------------------------------------- tokens: dark (slate) */

[data-md-color-scheme="slate"] {
  --sbu-red: #e87070;
  --sbu-red-fill: #990000;        /* unchanged: white on it is still 8.92:1 */
  --sbu-red-ink: #e87070;         /* 6.37:1 on --sbu-bg, 5.15:1 on --sbu-surface - AA */
  --sbu-red-deep: #f29a9a;        /* hover: lighter, not darker, on a dark ground */
  --sbu-midnight: #16243e;
  --sbu-grey: #828282;
  --sbu-green-ink: #6fcf8a;       /* "fastest" - 10.01:1 on --sbu-bg, 8.09:1 on --sbu-surface - AAA */

  --sbu-bg: #0f0f10;
  --sbu-surface: #16243e;         /* official Midnight Blue as the elevated surface */
  --sbu-ink: #e8e6e3;             /* 15.38:1 - AAA */
  --sbu-ink-muted: #a8a4a0;       /* 7.74:1  - AAA */
  --sbu-rule: #2c2b2a;
  --sbu-rule-strong: #78736c;     /* 3.29:1 on the card fill, 4.08:1 on the page - clears 1.4.11 */

  --md-primary-fg-color: var(--sbu-red-fill);
  --md-primary-fg-color--light: #b03030;
  --md-primary-fg-color--dark: #7a0000;
  --md-primary-bg-color: #ffffff;
  --md-accent-fg-color: var(--sbu-red-deep);
  --md-typeset-a-color: var(--sbu-red-ink);
  --md-default-fg-color: var(--sbu-ink);
  --md-default-bg-color: var(--sbu-bg);
  --md-code-bg-color: #1b1b1d;
  --md-footer-bg-color: #0b1421;
  --md-footer-bg-color--dark: #080f19;
}

/* Material derives several fg variants from --md-default-fg-color at fixed alphas; on a dark
   ground those land too faint for AA, so they are pinned to the computed tokens instead. */
[data-md-color-scheme="slate"] {
  --md-default-fg-color--light: var(--sbu-ink-muted);
  --md-default-fg-color--lighter: #6f6c69;
  --md-typeset-color: var(--sbu-ink);
}

/* --------------------------------------------------------------------------- type + measure */

:root {
  --sbu-display: 'Crimson Pro', Georgia, 'Times New Roman', serif;

  /* NOTE ON GREEK / MATH GLYPHS. Neither face covers Greek -- Atkinson Hyperlegible ships no
     Greek subset on Google Fonts at all (latin and latin-ext only), and Crimson Pro offers only
     latin, latin-ext and vietnamese. So a character like U+0394 (delta) falls through to the
     system sans below, verified by canvas metrics: Latin renders in Atkinson while Delta matches
     the fallback width exactly. The fallbacks here are therefore load-bearing, not decoration --
     they are what actually draws any Greek the pages ever carry. The generator currently spells
     it out in words ("delta k"), so nothing on the site depends on this today. */
  --sbu-body: 'Atkinson Hyperlegible', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
    sans-serif;

  /* Material sets 1rem = 20px, so 0.8rem = 16px. Scale is ~1.25 (major third). */
  --sbu-step--1: 0.7rem;   /* 14px - captions, table cells */
  --sbu-step-0: 0.8rem;    /* 16px - body */
  --sbu-step-1: 1rem;      /* 20px - h4 / lead */
  --sbu-step-2: 1.25rem;   /* 25px - h3 */
  --sbu-step-3: 1.5rem;    /* 30px - h2 */
  --sbu-step-4: 1.9rem;    /* 38px - h1 */

  --sbu-measure: 70ch;     /* inside the 65-75ch readable band */
}

body,
.md-typeset {
  font-family: var(--sbu-body);
}

/* Material caps its layout grid at 61rem, which after the left nav and right TOC leaves the
   content column around 690px. That is too narrow for this site's actual content: wide lens
   tables, side-by-side before/after renders, and a two-column hero. Widened to 76rem; prose is
   still held to --sbu-measure, so lines never get longer, only figures and tables get room. */
.md-grid {
  max-width: 76rem;
}

.md-typeset {
  font-size: var(--sbu-step-0);
  line-height: 1.65;       /* inside the 1.5-1.75 band */
}

.md-typeset h1,
.md-typeset h2,
.md-typeset h3,
.md-typeset h4 {
  font-family: var(--sbu-display);
  color: var(--sbu-ink);
  font-weight: 600;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.md-typeset h1 {
  font-size: var(--sbu-step-4);
  line-height: 1.15;
  margin-bottom: 0.6em;
}

/* More space above a heading than below it: headings bind to what follows, not what precedes. */
.md-typeset h2 {
  font-size: var(--sbu-step-3);
  line-height: 1.2;
  margin-top: 2.2em;
  margin-bottom: 0.7em;
  padding-bottom: 0.25em;
  border-bottom: 1px solid var(--sbu-rule);
}

.md-typeset h3 {
  font-size: var(--sbu-step-2);
  line-height: 1.3;
  margin-top: 1.8em;
  margin-bottom: 0.5em;
}

.md-typeset h4 {
  font-size: var(--sbu-step-1);
  margin-top: 1.5em;
}

/* A red rule under h2 would put red on every section break and swamp the 80:20 proportion; it
   is carried instead by a short leading mark, so red still signals hierarchy but sparingly. */
.md-typeset h2::before {
  content: "";
  display: block;
  width: 2.2rem;
  height: 3px;
  margin-bottom: 0.5rem;
  background: var(--sbu-red-fill);
}

[data-md-color-scheme="slate"] .md-typeset h2::before {
  background: var(--sbu-red-ink);
}

/* Measure applies to prose only. Tables, figures, and code carry data and stay full-width. */
.md-typeset p,
.md-typeset ul:not(.grid):not([class*="md-"]),
.md-typeset ol,
.md-typeset blockquote {
  max-width: var(--sbu-measure);
}

.md-typeset a {
  text-underline-offset: 0.15em;
  text-decoration-thickness: 1px;
}

.md-typeset a:hover {
  color: var(--sbu-red-deep);
}

/* Keyboard focus is a real UI boundary, so it uses the brand red at full strength: 8.92:1 in
   light, 6.37:1 in dark. Never removed, never replaced with a shadow-only cue. */
.md-typeset a:focus-visible,
.md-nav__link:focus-visible,
.md-header__button:focus-visible,
.md-footer a:focus-visible,
.sbu-card:focus-within {
  outline: 3px solid var(--sbu-red-ink);
  outline-offset: 2px;
  border-radius: 2px;
}

[data-md-color-scheme="slate"] .md-header__button:focus-visible {
  outline-color: #ffffff;
}

/* Inside the hero and the header the ground is brand red, where a red focus ring computes to
   1.29:1 against the gradient's dark stop -- an invisible focus indicator, and a real WCAG
   failure for anyone navigating by keyboard. On red, the ring is white: 11.49:1 on the dark
   stop, 8.92:1 on the light one. */
.sbu-hero a:focus-visible,
.md-header a:focus-visible,
.md-header__button:focus-visible {
  outline: 3px solid #ffffff;
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------------------------------------------------------------------------------- header */

.md-header {
  border-bottom: 3px solid var(--sbu-red-deep);
}

[data-md-color-scheme="slate"] .md-header {
  background-color: var(--sbu-red-fill);
}

.md-header__title {
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-1);
  font-weight: 600;
}

/* HEADER MARK. The horizontal lockup sits left of the title on a white plate. The plate is not
   decoration: the file is 78% transparent and its shield and the word "University" are #990000,
   so straight on the red header bar that half of the mark would disappear. There is no reversed
   variant in the pack and the mark must not be recoloured, so it gets a light ground instead.
   Padding is the clear space, ~29% of the mark's height on every side.

   Material hides this element below 76.234375em, so the plate is a wide-viewport affordance
   only -- which is why the hero keeps its own lockup for narrow screens. */
.md-header .md-header__button.md-logo {
  display: inline-flex;   /* shrink-wrap the plate to the mark; `flex` would let it stretch */
  align-items: center;
  flex: 0 0 auto;
  width: auto;
  background: #ffffff;
  border-radius: 3px;
  padding: 0.3rem 0.45rem;
  margin: 0.2rem 0.45rem 0.2rem 0.2rem;
  opacity: 1;
}

/* Specificity (0,3,1) to clear Material's own
   `.md-header__button.md-logo img{height:1.2rem;width:auto}` at (0,2,1). Height only, width
   auto: proportional, never stretched or skewed, never recoloured. */
.md-header .md-header__button.md-logo img,
.md-header .md-header__button.md-logo svg {
  display: block;
  height: 1.05rem;
  width: auto;
  max-width: none;
  object-fit: contain;
  fill: none;
}

/* The generic white focus ring would land on a white plate. A wider offset puts the ring out on
   the red bar where it is legible (white on #990000 is 8.92:1). */
.md-header .md-header__button.md-logo:focus-visible {
  outline: 3px solid #ffffff;
  outline-offset: 4px;
}

/* Restates Material's own breakpoint. Material hides the header logo below 76.234375em, but the
   rule above is (0,3,0) against their (0,2,0) and silently un-hid it -- which put a 142px plate
   next to the hamburger and truncated the site title to "reblock - REU Slu...". Below this
   width the institutional mark is carried by the hero lockup instead. */
@media screen and (max-width: 76.234375em) {
  .md-header .md-header__button.md-logo {
    display: none;
  }
}

/* ------------------------------------------------------------------------------------ hero */

.sbu-hero {
  margin: 0 0 3rem;
  padding: 2.6rem 1.8rem 2.2rem;
  border-radius: 3px;
  /* Red-to-red, per the brand guide's gradient rule. An earlier version ramped Stony Brook Red
     into Midnight Blue, whose midpoint interpolated through violet -- a hue in neither the
     primary nor the secondary palette. Navy is gone from the gradient entirely; Midnight Blue
     now appears only as the dark-scheme surface and the footer band.

     Linear only, never radial. 55deg in CSS is an axis 35 degrees above horizontal pointing up
     and to the right, inside the brand guide's 20-70 degree window. Both stops are values
     already established in this sheet (#7a0000 predates this redesign as --sbu-red-dark), so
     no new brand colour is invented here. */
  background: linear-gradient(55deg, #7a0000 0%, #990000 100%);
  color: #ffffff;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 2.4rem;
  /* Centred: the figure block is a little shorter than the text column, so centring splits the
     slack above and below it instead of pooling all of it under the caption. */
  align-items: center;
}

/* Every grid child needs an explicit min-width: 0. A grid track's default min-width is `auto`,
   so a replaced element's intrinsic width (the region render is ~1000px wide) overflows the
   track and scrolls the whole page sideways -- which is exactly what it did on a 414px viewport
   before this line existed. */
.sbu-hero > * {
  min-width: 0;
}

/* White on the gradient: 8.92:1 over the red stops, 15.49:1 over the Midnight Blue tail. */
.md-typeset .sbu-hero h1 {
  color: #ffffff;
  font-size: clamp(1.8rem, 3.6vw, 2.4rem);
  line-height: 1.12;
  margin: 0 0 0.6rem;
  max-width: 22ch;
  text-wrap: balance;
}

.md-typeset .sbu-hero p {
  color: #ffffff;
  max-width: 46ch;
}

/* Material's default code chip is a dark-grey fill meant for a white page; dropped on the red
   hero it reads as a rendering fault. Scoped to the hero only: a faint white wash that belongs
   to the gradient, with white text on it. */
.md-typeset .sbu-hero code {
  background-color: rgba(255, 255, 255, 0.18);
  color: #ffffff;
  border: 0;
  box-shadow: none;
  padding: 0.05em 0.35em;
}

.md-typeset .sbu-hero__thesis {
  font-size: var(--sbu-step-0);
  line-height: 1.6;
  margin: 0 0 1.1rem;
  max-width: 52ch;
}

/* The key result, stated up front. Emphasis comes from weight and a rule, not from a giant
   number floating over a label. */
.md-typeset .sbu-hero__result {
  font-size: var(--sbu-step-0);
  margin: 0 0 1.3rem;
  padding-left: 0.9rem;
  border-left: 1px solid rgba(255, 255, 255, 0.55);
}

.md-typeset .sbu-hero__result strong {
  font-weight: 700;
}

.sbu-hero__affil {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
  margin: 0;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.28);
}

/* The supplied lockup is a 2-colour mark: black wordmark plus a #990000 shield and "University".
   It cannot sit on the red hero directly -- the red half would disappear -- and there is no
   reversed variant to swap in, so it gets its own white card. The padding is the mark's clear
   space, held at ~25% of the mark's height on every side. */
.sbu-hero__lockup {
  display: inline-flex;
  flex: 0 0 auto;
  background: #ffffff;
  border-radius: 3px;
  padding: 0.8rem 1rem;
}

/* Selector is .md-typeset-qualified on purpose. Material ships
   `.md-typeset img,.md-typeset svg,.md-typeset video{height:auto;max-width:100%}` at specificity
   (0,1,1); a bare `.sbu-hero__logo` is (0,1,0) and loses, which rendered the mark at its full
   518x428 intrinsic size with Material's 1px image border still on it. Qualifying gets (0,2,0),
   which wins.

   Height only, width auto: the mark scales proportionally and is never stretched or skewed, and
   nothing here recolours it. */
.md-typeset .sbu-hero__logo {
  display: block;
  height: 3.2rem;
  width: auto;
  max-width: none;
  background: none;
  border: 0;
  border-radius: 0;
}

.md-typeset .sbu-hero__affil-text {
  margin: 0;
  font-size: var(--sbu-step--1);
  line-height: 1.45;
  color: #ffffff;
  max-width: 34ch;
}

.sbu-hero__figure {
  margin: 0;
  min-width: 0;
}

.md-typeset .sbu-hero__figure img {
  width: 100%;
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: none;
  display: block;
}

.md-typeset .sbu-hero__figure figcaption {
  color: #ffffff;
  font-size: var(--sbu-step--1);
  line-height: 1.45;
  margin-top: 0.6rem;
  text-align: left;
}

.md-typeset .sbu-hero__figure figcaption a {
  color: #ffffff;
  text-decoration-color: rgba(255, 255, 255, 0.6);
}

@media screen and (max-width: 76.1875em) {
  .sbu-hero {
    grid-template-columns: minmax(0, 1fr);   /* never a bare 1fr -- see the min-width note above */
    gap: 1.6rem;
    padding: 2rem 1.4rem 1.8rem;
  }

  .md-typeset .sbu-hero h1 {
    max-width: none;
  }

  .md-typeset .sbu-hero p {
    max-width: none;
  }
}

/* Belt-and-braces against sideways scroll: a wide data table scrolls inside its own box rather
   than dragging the page with it. `width: fit-content` matters -- display:block alone stretches
   the table's border to the full column while its cells stay narrow, leaving the frame floating
   far to the right of the data. */
.md-typeset table:not([class]) {
  display: block;
  overflow-x: auto;
  width: fit-content;
  max-width: 100%;
}

.md-typeset figure img,
.md-typeset .sbu-hero__figure img {
  max-width: 100%;
  height: auto;
}

/* ------------------------------------------------------------------------------ key figures */

/* Real measurements with their units and their basis -- read as a research figure's data row,
   not as marketing stat cards. Every value here is written by scripts/gen_site_pages.py from
   run artifacts. */
.sbu-keyfigures {
  display: grid;
  /* Material's root is 20px, so 1rem here is 20px -- 9rem is a 180px minimum track, which fits
     all four figures on one row in the widened content column. */
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0 2rem;
  margin: 0 0 2.5rem;
  padding: 1.1rem 0 0;
  border-top: 2px solid var(--sbu-red-fill);
  max-width: none;
}

[data-md-color-scheme="slate"] .sbu-keyfigures {
  border-top-color: var(--sbu-red-ink);
}

.md-typeset .sbu-keyfigures dt {
  font-size: var(--sbu-step--1);
  font-weight: 400;
  line-height: 1.4;
  color: var(--sbu-ink-muted);
  margin: 0;
  order: 2;
}

.md-typeset .sbu-keyfigures dd {
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-2);
  font-weight: 600;
  line-height: 1.15;
  color: var(--sbu-ink);
  margin: 0 0 0.15rem;
  order: 1;
  font-variant-numeric: tabular-nums;
}

.sbu-keyfigures > div {
  display: flex;
  flex-direction: column;
  padding-bottom: 1rem;
}

/* --------------------------------------------------------------------------- Material cards */

.md-typeset .grid.cards > ul > li,
.md-typeset .grid > .sbu-card {
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 3px;
  padding: 1.1rem 1.2rem;
  background: var(--sbu-surface);
  box-shadow: none;
  transition: border-color 200ms ease, transform 200ms ease;
}

.md-typeset .grid.cards > ul > li:hover {
  border-color: var(--sbu-red-ink);
  transform: translateY(-2px);
}

.md-typeset .grid.cards > ul > li > :first-child {
  margin-top: 0;
}

.md-typeset .grid.cards > ul > li > :last-child {
  margin-bottom: 0;
}

.md-typeset .grid.cards > ul > li p {
  max-width: none;
  font-size: var(--sbu-step--1);
  line-height: 1.55;
  color: var(--sbu-ink-muted);
}

.md-typeset .grid.cards > ul > li p:first-child strong,
.md-typeset .grid.cards > ul > li h3 {
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-1);
  font-weight: 600;
  color: var(--sbu-ink);
}

.md-typeset .grid.cards > ul > li p:first-child a {
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-1);
  font-weight: 600;
}

/* --------------------------------------------------------------------------------- tables */

.md-typeset table:not([class]) {
  font-size: var(--sbu-step--1);
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 2px;
}

.md-typeset table:not([class]) th {
  background-color: var(--sbu-surface);
  color: var(--sbu-ink);
  font-family: var(--sbu-body);
  font-weight: 700;
  border-bottom: 2px solid var(--sbu-red-fill);
  white-space: nowrap;
}

[data-md-color-scheme="slate"] .md-typeset table:not([class]) th {
  border-bottom-color: var(--sbu-red-ink);
}

.md-typeset table:not([class]) td {
  font-variant-numeric: tabular-nums;
  border-top: 1px solid var(--sbu-rule);
}

.md-typeset table:not([class]) tr:hover td {
  background-color: var(--sbu-surface);
}

/* Figure-comparison tables (the generator's before/after image rows) are frames, not data. */
.md-typeset table:not([class]) td:has(> img:only-child) {
  padding: 0.4rem;
  vertical-align: top;
}

/* ------------------------------------------------------------------------ figures + images */

.md-typeset img {
  background: var(--sbu-bg);
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 3px;
}

.md-typeset figure {
  margin: 2rem 0;
  max-width: none;
}

.md-typeset figure img {
  display: block;
  margin: 0 auto;
  max-width: 100%;
}

.md-typeset figcaption {
  font-size: var(--sbu-step--1);
  line-height: 1.5;
  color: var(--sbu-ink-muted);
  margin-top: 0.65rem;
  padding-left: 0.85rem;
  border-left: 1px solid var(--sbu-red-fill);
  max-width: var(--sbu-measure);
  text-align: left;
}

[data-md-color-scheme="slate"] .md-typeset figcaption {
  border-left-color: var(--sbu-red-ink);
}

/* Dark mode: renders are light-background scientific figures. They are not inverted (that would
   falsify the colour scales the captions describe), only softened at the frame. */
[data-md-color-scheme="slate"] .md-typeset img {
  background: #f3f1ee;
}

/* Before/after figure pairs that share one width norm or one node `vmax` (e.g. Permeability's
   egress-graph panels) need to sit side by side for the comparison that shared scale exists to
   make -- stacked with a multi-megabyte image between each pair, the comparison cannot be made.
   Two columns; the caller emits its figures row-major, so grid auto-flow lands layer-as-row x
   before/after-as-column.

   Every grid child needs an explicit min-width: 0, same reason as .sbu-hero above -- a grid
   track's default min-width is `auto`, so a wide image's intrinsic width overflows the track and
   scrolls the whole page sideways. That bug has already happened once in this sheet, at a 414px
   viewport (see the .sbu-hero > * rule). */
.sbu-figure-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1rem 2rem;
  margin: 2rem 0;
}

.md-typeset .sbu-figure-grid > figure {
  min-width: 0;
  margin: 0;
}

@media screen and (max-width: 76.1875em) {
  .sbu-figure-grid {
    grid-template-columns: minmax(0, 1fr);   /* never a bare 1fr -- see the min-width note above */
  }
}

/* ------------------------------------------------------------------- the Explore stage rail */

/* The stage strip at the top of Explore. Deliberately STATIC: mkdocs.yml already enables
   `toc.follow`, so the right-hand table of contents already scroll-follows the reader through these
   same five headings. A second IntersectionObserver here would duplicate behaviour the theme ships
   and add a per-page observer to maintain.

   The numbers come from `counter()` on the <ol>, never typed into the markup: hand-written "1."
   text renumbers wrongly the moment a stage is added or reordered, and `counter()` cannot. What
   that does NOT buy is silence in assistive tech -- Chrome and Firefox expose `::before` content to
   the accessibility tree, on top of whatever list position the <ol> itself carries. Typed digits
   would be announced the same way AND go stale, so the counter is the better of the two, not a free
   one.

   `flex-wrap` rather than `.sbu-figure-grid`'s `min-width: 0`, because the failure mode is the
   other one: these children are short text, not images with a large intrinsic width, so nothing
   here overflows a track. What can overflow is the ROW -- a flex item's automatic minimum size is
   its min-content width, so five labels plus their counters and gaps stop shrinking well before
   they stop being wider than a phone's content column, and a non-wrapping row would then scroll
   the whole page sideways (the bug this sheet has already hit once -- see the .sbu-hero rule).
   Which viewport width that starts at is NOT measured: this repo has no browser. Wrapping makes
   the question moot rather than answering it. */
.md-typeset .sbu-stage-rail ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  margin: 0 0 1.6rem;
  padding: 0.55rem 0;
  list-style: none;
  counter-reset: sbu-stage;
  border-top: 1px solid var(--sbu-rule);
  border-bottom: 1px solid var(--sbu-rule);
}

.md-typeset .sbu-stage-rail li {
  margin: 0;
  counter-increment: sbu-stage;
}

.md-typeset .sbu-stage-rail li::before {
  content: counter(sbu-stage);
  display: inline-block;
  min-width: 1.3em;
  color: var(--sbu-ink-muted);
  font-variant-numeric: tabular-nums;
}

.md-typeset .sbu-stage-rail a {
  font-weight: 600;
}

/* --------------------------------------------------------------------------- status pills */

/* Colour never carries the meaning alone: each pill states its status in words, and the two
   states differ in border weight and fill as well as hue. */
.pill {
  display: inline-block;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
  white-space: nowrap;
}

.pill-done {
  color: var(--sbu-ink);
  border: 1px solid var(--sbu-rule-strong);
  background: var(--sbu-surface);
}

.pill-progress {
  color: #ffffff;
  border: 1px solid var(--sbu-red-fill);
  background: var(--sbu-red-fill);
}

/* --------------------------------------------------------------------------------- footer */

.md-footer-meta {
  background-color: var(--md-footer-bg-color--dark);
}

.sbu-affiliation {
  font-size: var(--sbu-step--1);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.9);
  margin: 0.3rem 0 0;
  max-width: 74ch;
}

/* ---------------------------------------------------------------------------------- motion */

/* One authored moment on the page: the hero settles in on load. Nothing else moves on scroll.
   Timing follows the Scroll Reveal (Subtle) preset -- 350ms, 12px, exponential ease-out -- and
   the element's default state is already visible, so a failed animation degrades to "shown". */
@keyframes sbu-settle {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

.sbu-hero {
  animation: sbu-settle 350ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .sbu-hero {
    animation: none;
  }

  .md-typeset .grid.cards > ul > li:hover {
    transform: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------------------------- lightbox */

/* glightbox is configured (mkdocs.yml) to opt out of any image carrying .skip-lightbox: the
   header logo, the hero logo, and the hero figure, none of which are data worth zooming. */
.md-header__button.md-logo img,
.sbu-hero__logo {
  cursor: default;
}

.glightbox-clean .gslide-description {
  font-family: var(--sbu-body);
}

/* ------------------------------------------------------------------------- widget controls */

/* Material 9.7.7 ships `.md-typeset figure{text-align:center}`, and both interactive widgets insert
   their controls INTO the <figure> that carries their mount point -- so sliders, legend buttons, the
   aria-live summary and Frontier's eight full-sentence verdicts all inherit centring. One centred
   slider row reads as a caption; eight centred sentences read as a mistake. Scoped by
   `figure[data-widget]`, so it reaches whatever a widget inserts (both widgets, and any later one)
   without needing a class the TypeScript has to remember to set, and without touching the ordinary
   figures on the site whose centring is deliberate.

   The <figcaption> keeps its own left-aligned rule further up this file; this covers everything the
   widget itself builds. */
.md-typeset figure[data-widget] > * {
  text-align: left;
}

/* The one call to action on the home page. The interactive page is what most readers actually
   want, and it sat seventh in a card grid; this puts it directly under the thesis. A tinted
   panel with a left rule rather than a button: the whole heading is the link, so the target is
   the full line rather than a word inside a sentence, and it stays a heading in the outline for
   anyone navigating by structure. Colour is never the only signal -- the rule, the fill and the
   arrow all carry it, so it survives a monochrome print and a colour-blind reader. */
.md-typeset .sbu-cta {
  background: var(--sbu-surface);
  border-left: 3px solid var(--sbu-red);
  border-radius: 0 4px 4px 0;
  padding: 1.15rem 1.35rem 0.9rem;
  margin: 1.8rem 0 2.2rem;
}

.md-typeset .sbu-cta h2 {
  margin: 0 0 0.55rem;
  font-size: var(--sbu-step-1);
  border-bottom: none;      /* the panel is the boundary; a second rule reads as a section break */
}

.md-typeset .sbu-cta h2 a {
  color: var(--sbu-red-ink);
  text-decoration: none;
}

.md-typeset .sbu-cta h2 a:hover,
.md-typeset .sbu-cta h2 a:focus-visible {
  color: var(--sbu-red-deep);
  text-decoration: underline;
}

.md-typeset .sbu-cta p {
  margin: 0;
  max-width: 68ch;
  color: var(--sbu-ink);
}

/* ------------------------------------------------------------------------------ the Demo page */

/* A sentence the build-out adds when a budget shares an earlier budget's network. */
.md-typeset .sbu-buildout__same {
  margin: 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

/* The block's one-line identity under the H1: a fact line, so it reads as metadata, not lede. */
.md-typeset .sbu-demo-place {
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

/* The fires near the block: a dated list, the date in the display face beside each report. */
.md-typeset .sbu-fires {
  list-style: none;
  margin: 1.2rem 0 2rem;
  padding: 0;
  max-width: none;
}

.md-typeset .sbu-fires > li {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 0.2rem 1.4rem;
  margin: 0;
  padding: 0.9rem 0 0.5rem;
  border-top: 1px solid var(--sbu-rule);
}

.md-typeset .sbu-fires > li:last-child {
  border-bottom: 1px solid var(--sbu-rule);
}

.md-typeset .sbu-fires__when {
  margin: 0;
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-1);
  font-weight: 600;
  line-height: 1.3;
}

.md-typeset .sbu-fires__what p {
  margin: 0 0 0.45rem;
}

.md-typeset .sbu-fires__where,
.md-typeset .sbu-fires__src {
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

@media screen and (max-width: 44.9375em) {
  .md-typeset .sbu-fires > li {
    grid-template-columns: minmax(0, 1fr);
  }
}

.md-typeset .sbu-footnote {
  font-size: var(--sbu-step--1);
  line-height: 1.55;
  color: var(--sbu-ink-muted);
}

/* ----------------------------------------------------------------- the build-out widget */

/* Material sizes a <figure> to fit its content, and a widget's content is all `width: 100%` --
   so the figure shrinks to its caption's measure (--sbu-measure) and the plate and plots with it.
   The Demo page's build-out and peel figures take the column instead; the Demo page hides its
   table of contents to make that column wide enough for the plate and plots side by side. */
.md-typeset figure[data-widget="build-out"],
.md-typeset figure[data-widget="build-out-compare"],
.md-typeset figure.sbu-peel {
  width: 100%;
}

/* Their captions keep the measure but sit at the column's left edge, under the figure they
   describe, rather than centred in a column that is now wider than the measure. */
.md-typeset figure[data-widget="build-out"] > figcaption,
.md-typeset figure[data-widget="build-out-compare"] > figcaption,
.md-typeset figure.sbu-peel > figcaption {
  margin-left: 0;
}

/* Everything below is built by web/src/widgets/build-out.ts inside its <figure>. Its layout answers
   to the FIGURE's width, not the viewport's -- a container query -- because the content column's
   width depends on which of Material's sidebars are showing, and the plate and its plots need the
   column, not the window, to be wide enough to sit side by side. */
.sbu-buildout {
  container-type: inline-size;
}

/* The controls stay in reach while the plate scrolls under them, but only within the figure: sticky
   inside its own container, just below Material's header (2.4rem in every layout). */
.sbu-buildout__bar {
  position: sticky;
  top: 2.4rem;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  padding: 0.55rem 0;
  background: var(--md-default-bg-color);
  border-top: 1px solid var(--sbu-rule);
  border-bottom: 1px solid var(--sbu-rule);
}

.sbu-buildout__budget {
  display: flex;
  flex: 1 1 14rem;
  align-items: center;
  gap: 0.7rem;
  font-size: var(--sbu-step--1);
  font-weight: 700;
  color: var(--sbu-ink-muted);
}

.sbu-buildout__budget input {
  flex: 1 1 auto;
  min-width: 0;
}

.sbu-buildout__budget output {
  min-width: 3.5ch;
  font-size: var(--sbu-step-0);
  color: var(--sbu-ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The compact readout: shown only where the plots drop under the plate (below). */
.md-typeset .sbu-buildout__mini {
  display: none;
  flex-basis: 100%;
  margin: 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
  font-variant-numeric: tabular-nums;
}

.sbu-buildout__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.2rem 1.8rem;
  margin-top: 1rem;
  align-items: start;
}

@container (min-width: 40rem) {
  .sbu-buildout__stage {
    grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  }
}

@container (max-width: 39.99rem) {
  .md-typeset .sbu-buildout__mini {
    display: block;
  }
}

/* The plate: the frame is a window onto one sprite sheet, moved by `transform`. The plates are
   drawn on white in both schemes, like every render on this site (see the dark-mode note above). */
.sbu-buildout__plate {
  position: relative;
  overflow: hidden;
  background: #ffffff;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 3px;
}

.md-typeset .sbu-buildout__plate img {
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

.md-typeset .sbu-buildout__read {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0.6rem 0;
  border-top: 1px solid var(--sbu-rule);
  border-bottom: 1px solid var(--sbu-rule);
}

.md-typeset .sbu-buildout__read > div {
  padding: 0.35rem 0.5rem 0.35rem 0;
  min-width: 0;
}

.md-typeset .sbu-buildout__read dt {
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

.md-typeset .sbu-buildout__read dd {
  margin: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@container (max-width: 30rem) {
  .md-typeset .sbu-buildout__read {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.sbu-buildout__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.3rem;
  margin: 0.4rem 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

.sbu-buildout__key {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.sbu-buildout__swatch {
  display: inline-block;
  flex: none;
  width: 1.5rem;
  height: 0.7rem;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 2px;
}

/* Damage's fade, beside the road's swatch: a short ramp and what "destroyed" means. */
.sbu-buildout__damage {
  width: 100%;
  max-width: 14rem;
}

.md-typeset .sbu-buildout__damage-note {
  margin: 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

.sbu-buildout__ramp {
  width: 100%;
  max-width: 24rem;
}

.sbu-buildout__ramp-bar {
  height: 0.7rem;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 2px;
}

.sbu-buildout__ramp-ticks {
  display: flex;
  justify-content: space-between;
  font-variant-numeric: tabular-nums;
}

.sbu-buildout__plots {
  display: grid;
  gap: 0.6rem;
  min-width: 0;
}

.md-typeset .sbu-buildout__plot-title {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 0.8rem;
  margin: 0;
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-0);
  font-weight: 600;
}

.md-typeset .sbu-buildout__plot-title span + span {
  font-family: var(--sbu-body);
  font-size: var(--sbu-step--1);
  font-weight: 400;
  color: var(--sbu-ink-muted);
  font-variant-numeric: tabular-nums;
}

/* The axes, gridlines and labels paint in `currentColor` (render/svg.ts): the muted ink here, so
   the curve and the dot over them carry the weight. */
.md-typeset .sbu-buildout__chart svg {
  display: block;
  overflow: visible;
  font-size: 0.55rem;
  color: var(--sbu-ink-muted);
}

.sbu-buildout__chart .sbu-curve {
  opacity: 0.6;
}

.sbu-buildout__chart .sbu-cursor {
  fill: var(--sbu-ink);
  stroke: var(--md-default-bg-color);
  stroke-width: 2.5px;
}

/* Several methods on one slider (the comparison): the plates side by side at every width -- the
   point is to see them at once -- with the plots beneath, laid across by the widget when the
   figure is wide enough (it sets the plots' columns, since it also sizes each chart to them). */
.sbu-buildout--multi .sbu-buildout__stage {
  grid-template-columns: minmax(0, 1fr);
}

.sbu-buildout__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

.sbu-buildout--multi .sbu-buildout__cols {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.8rem 1.4rem;
}

.sbu-buildout__col {
  min-width: 0;
}

/* Side by side, each plate at two-thirds of its column's width -- so two-thirds of the height it
   would have, the aspect kept -- centred over its readouts (owner, 2026-10-29). Both plates share
   one frame, so the two columns' readouts and plots still line up. Not on a phone: there each
   column is already a third of the screen, and two-thirds of it is a 110 px plate. */
@media screen and (min-width: 45em) {
  .sbu-buildout--multi .sbu-buildout__plate {
    width: 66.667%;
    margin-inline: auto;
  }
}

.md-typeset .sbu-buildout__head {
  margin: 0;
  font-size: var(--sbu-step--1);
  font-weight: 700;
  color: var(--sbu-ink-muted);
}

.md-typeset .sbu-buildout__time {
  margin: 0;
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-2);
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

/* Two lines' height whichever wraps, so the two plates under them start level. */
.md-typeset .sbu-buildout__sub {
  min-height: 2.7em;
  margin: 0.1rem 0 0.5rem;
  font-size: var(--sbu-step--1);
  line-height: 1.35;
  color: var(--sbu-ink-muted);
}

.md-typeset .sbu-buildout--multi .sbu-buildout__read {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.md-typeset .sbu-buildout--multi .sbu-buildout__read dd {
  white-space: normal;
}

/* Each method's colour on the plots: ours the brand red, the earlier method the ink. Curves, their
   points and the moving dot all paint in `currentColor`. */
.sbu-buildout .sbu-series-0 {
  color: var(--sbu-red-ink);
}

.sbu-buildout .sbu-series-1 {
  color: var(--sbu-ink);
}

/* Laid across, each plot's heading may wrap to a different height: the charts sit level at the
   bottom of equal boxes. */
.sbu-buildout--multi .sbu-buildout__plot {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 0;
}

.sbu-buildout--multi .sbu-buildout__chart .sbu-curve {
  opacity: 0.85;
}

.sbu-buildout--multi .sbu-buildout__chart .sbu-cursor {
  fill: currentColor;
}

.sbu-buildout__series {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.3rem;
  grid-column: 1 / -1;
  font-size: var(--sbu-step--1);
  font-weight: 700;
}

.sbu-buildout__serie::before {
  content: "";
  display: inline-block;
  width: 1.4rem;
  height: 3px;
  margin-right: 0.45rem;
  vertical-align: middle;
  background: currentColor;
}

/* ---------------------------------------------------------------- the Demo's peel figure */

/* The flagship run's depth figure in one zoomable frame (web/src/widgets/peel-zoom.ts): the
   image fills the frame and is moved by one transform. On white, like every render on this site.
   As on a map, one finger is always the browser's (it scrolls the page) and two are the
   widget's: `pan-x pan-y` leaves the page's pinch-zoom to the widget too. No transition on the
   image: a gesture follows the fingers. */
/* Two-thirds of the column's width, centred: two-thirds of the height a full-width square would
   take (owner, 2026-10-29). The zoom cap is the image's native pixels over this width, so the
   narrower frame zooms further. */
.sbu-peel__frame {
  position: relative;
  overflow: hidden;
  width: 66.667%;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
  background: #ffffff;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 3px;
  touch-action: pan-x pan-y;
  cursor: zoom-in;
}

.sbu-peel__frame[data-zoomed="yes"] {
  cursor: grab;
}

/* "Use two fingers to move the image": shown over the frame on a one-finger drag, then faded --
   at once, with no fade, for a reader who asked for reduced motion. */
.sbu-peel__notice {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.55);
  color: #ffffff;
  font-size: var(--sbu-step-0);
  font-weight: 700;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.sbu-peel__notice[data-shown="yes"] {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .sbu-peel__notice {
    transition: none;
  }
}

.md-typeset .sbu-peel__frame img {
  display: block;
  width: 100%;
  max-width: none;
  height: auto;
  margin: 0;
  transform-origin: 0 0;
  user-select: none;
  -webkit-user-drag: none;
}

.sbu-peel__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.5rem;
  margin: 0 0 0.5rem;
}

.md-typeset .sbu-peel__hint {
  margin: 0 0 0 0.4rem;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

.sbu-peel__legend {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0 0.6rem;
  align-items: center;
  max-width: 30rem;
  margin: 0.6rem 0 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink-muted);
}

.sbu-peel__ramp {
  grid-column: 1 / -1;
  height: 0.7rem;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 2px;
}

.sbu-peel__legend span:nth-child(3) {
  grid-column: 3;
}

/* ------------------------------------------------------- the Demo's methods comparison */

/* The comparison's one-line finding, read from the build-out: the sentence the figure argues. */
.md-typeset .sbu-compare__takeaway {
  font-weight: 700;
  max-width: var(--sbu-measure);
}

/* The comparison's run times: a block per row, a method per column. Each cell says how its time
   was had; the fastest method's cells are green AND say "fastest", so the colour is never the only
   cue. */
/* Fixed layout at the column's width: four columns share it and their words wrap, so a phone
   never scrolls sideways. */
.md-typeset .sbu-runtimes {
  display: table;
  table-layout: fixed;
  width: 100%;
  margin: 1rem 0 1.4rem;
  font-size: var(--sbu-step--1);
}

.md-typeset .sbu-runtimes th,
.md-typeset .sbu-runtimes td {
  vertical-align: top;
  padding: 0.5rem 0.7rem;
}

/* Headers as the site's other tables have them (`table:not([class]) th`, above): body font at the
   cells' size, bold, ink, on the card fill over a red rule -- and aligned with the cells, left. */
.md-typeset .sbu-runtimes th {
  font-family: var(--sbu-body);
  font-size: var(--sbu-step--1);
  font-weight: 700;
  color: var(--sbu-ink);
  text-align: left;
}

.md-typeset .sbu-runtimes thead th {
  background-color: var(--sbu-surface);
  border-bottom: 2px solid var(--sbu-red-fill);
}

[data-md-color-scheme="slate"] .md-typeset .sbu-runtimes thead th {
  border-bottom-color: var(--sbu-red-ink);
}

.md-typeset .sbu-runtimes tbody th,
.md-typeset .sbu-runtimes td {
  border-top: 1px solid var(--sbu-rule);
}

.md-typeset .sbu-runtimes td strong {
  display: block;
  font-family: var(--sbu-display);
  font-size: var(--sbu-step-1);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* On a phone five columns leave a word per line, so each block's row stacks: the block on a line
   of its own, then a line per method, its name (the cell's `data-label`, the column's header) to
   the left of its time. The header row stays for screen readers, off screen. */
@media screen and (max-width: 44.9375em) {
  .md-typeset .sbu-runtimes,
  .md-typeset .sbu-runtimes tbody,
  .md-typeset .sbu-runtimes tr {
    display: block;
  }

  .md-typeset .sbu-runtimes caption {
    display: block;
  }

  .md-typeset .sbu-runtimes thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .md-typeset .sbu-runtimes tbody th {
    display: block;
    padding: 0.6rem 0.3rem 0.2rem;
    border-top: 2px solid var(--sbu-red-fill);
  }

  [data-md-color-scheme="slate"] .md-typeset .sbu-runtimes tbody th {
    border-top-color: var(--sbu-red-ink);
  }

  .md-typeset .sbu-runtimes tbody th span {
    display: inline;
    margin-left: 0.5rem;
  }

  .md-typeset .sbu-runtimes td {
    display: grid;
    grid-template-columns: 9rem 1fr;
    column-gap: 0.6rem;
    padding: 0.4rem 0.3rem;
  }

  .md-typeset .sbu-runtimes td::before {
    content: attr(data-label);
    grid-row: span 2;
    font-weight: 700;
    color: var(--sbu-ink);
  }

  .md-typeset .sbu-runtimes td strong {
    font-size: var(--sbu-step-0);
  }
}

.md-typeset .sbu-runtimes td span,
.md-typeset .sbu-runtimes th span {
  display: block;
  font-weight: 400;
  color: var(--sbu-ink-muted);
}

.md-typeset .sbu-runtimes td.sbu-runtimes__fastest,
.md-typeset .sbu-runtimes td.sbu-runtimes__fastest span {
  color: var(--sbu-green-ink);
}

/* ------------------------------------------------------------------- the page's QR code */

/* "Open this view on your phone" (web/src/qr/qr.ts): at the foot of the navigation sidebar, only
   where that sidebar stays on screen -- Material's 76.25em breakpoint, below which it becomes the
   drawer -- and never in print. Sticky, so it stays in view however long the nav. */
.sbu-qr {
  display: none;
}

@media screen and (min-width: 76.25em) {
  .md-sidebar--primary .sbu-qr {
    display: block;
    position: sticky;
    bottom: 0;
    padding: 0.6rem 0.6rem 0.8rem;
    background: var(--md-default-bg-color);
  }
}

.md-typeset .sbu-qr__label,
.sbu-qr__label {
  margin: 0 0 0.35rem;
  font-size: 0.64rem;
  color: var(--sbu-ink-muted);
}

/* The code on white, with its own quiet zone, in both schemes: scanners want dark on light. */
.sbu-qr__open {
  display: block;
  padding: 0;
  border: 0;
  background: #ffffff;
  cursor: zoom-in;
}

.sbu-qr__open:focus-visible,
.sbu-qr__close:focus-visible {
  outline: 3px solid var(--sbu-red-ink);
  outline-offset: 2px;
}

.sbu-qr__code {
  display: block;
  width: 120px;
  height: 120px;
}

.sbu-qr__dialog {
  padding: 1rem;
  border: 0;
  border-radius: 4px;
  background: #ffffff;
  text-align: center;
}

.sbu-qr__dialog::backdrop {
  background: rgba(0, 0, 0, 0.75);
}

.sbu-qr__big {
  display: block;
  width: min(80vmin, 40rem);
  height: auto;
}

.sbu-qr__close {
  margin-top: 0.6rem;
  padding: 0.3rem 0.9rem;
  border: 1px solid #5c5c5c;
  border-radius: 3px;
  background: #ffffff;
  color: #111111;
  font: inherit;
  cursor: pointer;
}

/* The Demo's screen formula, in display math (docs/_partials/demo.md): its under-brace labels in
   full where the column holds them, one word each on a phone, so the page never scrolls sideways.
   One of the two is in the page at a time; the other is display:none, so it is neither drawn nor
   read aloud. */
.sbu-formula--narrow {
  display: none;
}

/* Large: the formula is the section's point, not a footnote to it. */
.sbu-formula {
  font-size: 140%;
}

@media screen and (max-width: 44.9375em) {
  .sbu-formula {
    font-size: 125%;
  }
}

/* The full labels need about 760 px at the formula's size; below a 56em column they would touch
   its edge, so the one-word ones take over there. */
@media screen and (max-width: 55.9375em) {
  .sbu-formula--wide {
    display: none;
  }

  .sbu-formula--narrow {
    display: block;
  }
}

/* ------------------------------------------------------------------- shared controls */

/* Every widget's controls (web/src/dom/controls.ts makes them): the native elements, in the
   site's look rather than the browser's -- the brand red where the browser would draw blue, a
   bordered select and button on the page's own ground, a visible keyboard focus -- in both colour
   schemes. A widget adds layout classes of its own; colour, type and focus come from here. */
.sbu-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.4rem;
  margin: 0.8rem 0 0;
}

.sbu-control,
.sbu-control-group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  font-size: var(--sbu-step--1);
  font-weight: 700;
  color: var(--sbu-ink-muted);
}

.sbu-range,
.sbu-check {
  accent-color: var(--sbu-red-fill);
}

[data-md-color-scheme="slate"] .sbu-range,
[data-md-color-scheme="slate"] .sbu-check {
  accent-color: var(--sbu-red-ink);
}

.sbu-range {
  min-width: 8rem;
  flex: 1 1 10rem;
}

.sbu-check {
  width: 1rem;
  height: 1rem;
  margin: 0;
}

.sbu-select,
.sbu-button {
  min-width: 2.2rem;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--sbu-rule-strong);
  border-radius: 3px;
  background: var(--md-default-bg-color);
  color: var(--sbu-ink);
  font: inherit;
  font-size: var(--sbu-step--1);
  font-weight: 400;
}

.sbu-button {
  cursor: pointer;
}

.sbu-button:hover:not(:disabled) {
  border-color: var(--sbu-red-ink);
}

.sbu-button:disabled {
  opacity: 0.45;
  cursor: default;
}

.sbu-range:focus-visible,
.sbu-check:focus-visible,
.sbu-select:focus-visible,
.sbu-button:focus-visible {
  outline: 3px solid var(--sbu-red-ink);
  outline-offset: 2px;
}

.md-typeset .sbu-readout {
  flex-basis: 100%;
  margin: 0.2rem 0 0;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink);
  font-variant-numeric: tabular-nums;
}

/* The Demo's screen map at half its width, so half its height, centred (owner, 2026-10-29): the
   city is context there, not the subject. Demo-only, by the wrapper the Demo's partial puts around
   the shared figure, which Screening and Explore show full size. The canvas's width is set inline
   by the widget (screen-map.ts: it must beat Material's `img` rule), hence !important; the
   controls and readout under it keep the column. */
.sbu-demo-screenmap figure[data-widget="screen-map"] > canvas,
.sbu-demo-screenmap figure[data-widget="screen-map"] > a > img,
.sbu-demo-screenmap figure[data-widget="screen-map"] > img {
  display: block;
  width: 50% !important;
  margin-inline: auto;
}

/* The screen map's score view legend (screen-map.ts `scoreLegend`): the ramp, framed in the map's
   dark ground by the widget, its three ticks under it (the lowest score, the floor at the middle,
   the highest), and a line on what a shade means. Hidden in the selection view. */
.md-typeset .sbu-screenmap__legend {
  margin: 0.6rem auto 0;
  max-width: 28rem;
  font-size: var(--sbu-step--1);
  color: var(--sbu-ink);
}
.md-typeset .sbu-screenmap__legend[hidden] {
  display: none;
}
.md-typeset .sbu-screenmap__ramp {
  height: 0.9rem;
  border-radius: 3px;
}
.md-typeset .sbu-screenmap__ticks {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.15rem;
  font-variant-numeric: tabular-nums;
}
.md-typeset .sbu-screenmap__note {
  display: block;
  margin-top: 0.3rem;
}
