/* =================================================================================================
   site.css - the whole visual system. Every page loads this file and no other.

   THE TOKENS ARE THE CONTRACT. Palette, type scale and spacing rhythm are custom properties on
   :root, and nothing below hard-codes a colour or a size that a token already names. That is what
   makes a Case Study written six issues from now look like it belongs to the same site as the
   homepage without anyone comparing them side by side.

   NOTHING IS HIDDEN EXCEPT UNDER .js-motion. The scroll reveals need their elements to start
   invisible, and an element that starts invisible in a plain stylesheet stays invisible forever
   when the script is blocked, fails, or simply has not arrived yet. So the hiding rule is scoped
   to a class that motion.js adds to <html> as its first act. No script, no class, no hiding, and
   the page is just a page. suites/run.py fails the run on any opacity:0 outside that scope.
   (The Spine's table of contents is not hidden in this sense below 64rem: it is a second route to
   headings the page already shows, dropped where there is no column beside the prose for it.)
   ============================================================================================== */

:root {
  /* ---- palette -------------------------------------------------------------------------------
     The spec's colours and no others. Ink on white; electric blue for what a reader can act on,
     for the Exhibit label and for stat units; navy bands for the figures strip and the footer, and
     a blue band for the contact call to action. Every neutral below is a mix of those, so a new
     grey cannot arrive without somebody typing a new hex value where a reviewer will see it. */
  --ink: #0a1b33;
  --paper: #ffffff;
  --blue: #2b5cff;
  --blue-deep: #1d44d6;
  --navy-deep: #0c1e3a;

  /* Pass and fail, and nothing else. Always paired with an icon and a label, never colour alone. */
  --good: #1b8a5a;
  --bad: #b3261e;
  /* A verdict's ground and its words: one pair for pass and one for fail, shared by every Exhibit
     that states one (the triage's extracted and refused, the Gate's cleared and disqualified). */
  --good-wash: color-mix(in srgb, var(--good) 13%, var(--paper));
  --good-ink: color-mix(in srgb, var(--good) 72%, var(--ink));
  --bad-wash: color-mix(in srgb, var(--bad) 11%, var(--paper));
  --bad-ink: color-mix(in srgb, var(--bad) 80%, var(--ink));

  /* The categorical data palette, in this fixed order, validated for colour-vision separation.
     Its contrast warning is answered by direct labels and the data table, not by the colours. */
  --data-1: #2b5cff;
  --data-2: #10a5b5;
  --data-3: #d9971a;
  --data-4: #c7405a;

  --ink-soft: color-mix(in srgb, var(--ink) 76%, var(--paper));
  --ink-mute: color-mix(in srgb, var(--ink) 66%, var(--paper));
  --ink-faint: color-mix(in srgb, var(--ink) 60%, var(--paper));   /* the faintest text: 4.6:1 */
  --line: color-mix(in srgb, var(--ink) 15%, var(--paper));
  --line-soft: color-mix(in srgb, var(--ink) 9%, var(--paper));
  --mist: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
  --mist-deep: color-mix(in srgb, var(--ink) 7%, var(--paper));
  --hatch: color-mix(in srgb, var(--ink) 5%, transparent);
  --blue-wash: color-mix(in srgb, var(--blue) 10%, var(--paper));
  /* The red categorical colour as words and as a fill: a trouble chip, a broken grade, a stale
     notice. Always with its word, never colour alone. */
  --trouble-ink: color-mix(in srgb, var(--data-4) 70%, var(--ink));
  --trouble-wash: color-mix(in srgb, var(--data-4) 13%, var(--paper));

  /* Text and rules ON a navy band, and on the blue one. */
  --on-dark: color-mix(in srgb, var(--paper) 84%, var(--navy-deep));
  --on-dark-mute: color-mix(in srgb, var(--paper) 64%, var(--navy-deep));
  --blue-on-dark: color-mix(in srgb, var(--blue) 45%, var(--paper));
  --rule-on-dark: color-mix(in srgb, var(--paper) 16%, transparent);
  --rule-on-blue: color-mix(in srgb, var(--paper) 50%, transparent);

  /* ---- type ----------------------------------------------------------------------------------
     Serif for display only: headlines, titles, stat figures, pull quotes, the drop cap. Sans for
     everything a reader reads through: prose, decks, captions, controls, tables. The display steps
     are fluid so a headline is large on a monitor without overflowing a 320px phone. */
  --font-serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --font-sans: "Source Sans 3", "Helvetica Neue", Arial, sans-serif;

  --text-xs: 0.8125rem;
  --text-compact: 0.875rem;
  --text-sm: 0.9375rem;
  --text-base: 1.0625rem;
  --text-read: 1.125rem;
  --text-md: 1.25rem;
  --text-brand: 1.3rem;
  --text-name: 1.5rem;
  --text-lg: clamp(1.4rem, 1.2rem + 0.8vw, 1.85rem);
  --text-xl: clamp(1.9rem, 1.4rem + 2vw, 2.9rem);
  --text-2xl: clamp(2.3rem, 1.5rem + 3.4vw, 4.1rem);
  --text-3xl: clamp(2.5rem, 1.6rem + 4vw, 4.75rem);
  --text-figure: clamp(2.6rem, 2rem + 2.5vw, 3.75rem);

  --tracking-display: -0.022em;
  --tracking-hero: -0.028em;

  /* ---- spacing rhythm ------------------------------------------------------------------------ */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 2.25rem;
  --space-xl: 3.5rem;
  --space-2xl: 5rem;
  --space-3xl: 6.5rem;

  --wrap: 76rem;
  --measure: 46rem;
  --gutter: clamp(1rem, 4vw, 3rem);
  --radius: 10px;
  /* The shared slider's track inset: half its thumb, so the thumb's centre reaches both ends. A mark
     placed along a track (the age-serial floor) measures from the same inset. */
  --slider-inset: 0.625rem;
  --radius-photo: 12px;
  --radius-ring: 2px;
  --pill: 999px;
  --chip-pad: 0.1rem 0.45rem;

  /* The sticky chrome, and what has to clear it: the header sits at one layer, the progress rule
     drawn along its lower edge at the next, and anything else sticky stops this far from the top. */
  --z-chrome: 40;
  --z-over-chrome: 41;
  --chrome-clearance: 6rem;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --settle: 300ms;   /* a state change inside an Exhibit: a grade, a ring, a break */
  --speed: 800ms;

  accent-color: var(--blue);
}

/* =================================================================================================
   RESET - the small, deliberate part of one
   ============================================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The chrome is sticky, so an in-page anchor has to land below it rather than under it. */
  scroll-padding-top: 6.5rem;
  scrollbar-color: var(--line) var(--paper);
}

@media (min-width: 40rem) {
  html {
    scroll-padding-top: 5.5rem;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.6;
  font-feature-settings: "kern", "liga";
  /* A long unbroken token - a URL in body copy - is the usual cause of overflow at 320px. */
  overflow-wrap: break-word;
}

::selection {
  background: var(--blue);
  color: var(--paper);
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-serif);
  font-weight: 600;
  font-optical-sizing: auto;
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin: 0;
  text-wrap: balance;
}

/* HEADLINE EMPHASIS. One phrase per headline may carry the finding, and this is what it looks
   like. Body text never uses it, and suites/run.py refuses a second one in a headline and any in a
   Spine heading or an Exhibit title. On a navy or blue band the phrase keeps the band's colour:
   blue on blue is invisible, and blue on navy is a link that goes nowhere. */
h1 em,
h2 em,
h3 em,
h4 em,
h5 em,
h6 em {
  font-style: italic;
  color: var(--emphasis, var(--blue-deep));
}

/* A coloured band declares that it is one, and every headline on it follows. */
.stats,
.contact,
.site-footer {
  --emphasis: currentColor;
}

p {
  margin: 0 0 var(--space-sm);
  text-wrap: pretty;
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
}

a:hover {
  color: var(--blue-deep);
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

button {
  font: inherit;
}

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: var(--radius-ring);
}

/* =================================================================================================
   UTILITIES - site-wide helpers any component may use
   ============================================================================================== */
/* Read by a screen reader, not drawn: words that a visual layout already says some other way. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* =================================================================================================
   LAYOUT
   ============================================================================================== */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(var(--space-xl), 8vw, var(--space-3xl));
}

.section--shade {
  background: var(--mist);
}

/* THE LABEL TREATMENT - one rule, every small label on the site. Sentence case, and never above a
   heading: the eyebrow was v1's, and a label above a headline competes with the headline, so none
   ships on any page. `.eyebrow` survives only as the look of the numbers row's own heading. Each
   label still owns its own colour and spacing; the typography is what makes them one site. */
.eyebrow,
.about__facts dt,
.case-head__flag,
.pending-result__flag,
.withheld-result__flag,
.track__standing dt,
.omissions dt,
.channels dt {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
}

.eyebrow {
  color: var(--ink-mute);
  margin: 0 0 var(--space-xs);
}

.section__title {
  font-size: var(--text-xl);
  font-weight: 500;
  letter-spacing: var(--tracking-display);
  max-width: 22ch;
}

/* A heading INSIDE a section, under its title. The step down in size is the whole of the
   difference between them. */
.section__subtitle {
  font-size: var(--text-lg);
  font-weight: 500;
  letter-spacing: -0.018em;
  max-width: 26ch;
  margin-top: var(--space-xl);
  margin-bottom: var(--space-md);
}

.section__lede {
  font-size: var(--text-read);
  color: var(--ink-soft);
  max-width: 62ch;
  margin-top: var(--space-md);
}

/* =================================================================================================
   CHROME - duplicated into every page, kept identical by suites/run.py (ADR-0002)
   A sticky white bar: the name, three routes, and the one action the whole site exists to earn.
   ============================================================================================== */
.skip-link {
  position: absolute;
  left: var(--space-sm);
  top: var(--space-sm);
  z-index: 100;
  transform: translateY(-350%);
  background: var(--ink);
  color: var(--paper);
  font-weight: 600;
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-ring);
  text-decoration: none;
  transition: transform 180ms var(--ease);
}

.skip-link:focus {
  transform: translateY(0);
}

.skip-link:hover {
  color: var(--paper);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-chrome);
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  -webkit-backdrop-filter: saturate(1.4) blur(10px);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3xs) var(--space-md);
  min-height: 4rem;
  padding-block: var(--space-2xs);
}

.brand {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: var(--text-brand);
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
}

.brand:hover {
  color: var(--ink);
}

.nav__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) clamp(0.9rem, 2.4vw, 2rem);
}

.nav__link {
  display: inline-block;
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  color: var(--ink-soft);
  padding-block: 0.4rem;
  border-bottom: 2px solid transparent;
  transition: color 160ms var(--ease), border-color 160ms var(--ease),
    background-color 160ms var(--ease);
}

.nav__link:hover {
  color: var(--ink);
  border-bottom-color: var(--blue);
}

/* The active page. One class and one aria-current, and they are the ONLY thing suites/run.py
   permits a page's chrome to differ by. */
.nav__link.is-current {
  color: var(--ink);
  border-bottom-color: var(--blue);
}

.nav__cta,
.nav__cta:hover {
  color: var(--blue-deep);
  border: 1.5px solid var(--blue-deep);
  border-radius: var(--pill);
  padding: 0.4rem 0.95rem;
}

.nav__cta:hover,
.nav__cta.is-current {
  background: var(--blue-deep);
  color: var(--paper);
}

/* On a phone the routes get their own row under the name, spread across it, rather than being
   hidden: a recruiter who opened the link from an email is the reader most likely to be on one. */
@media (max-width: 30rem) {
  .nav {
    flex-basis: 100%;
  }

  .nav__list {
    justify-content: space-between;
    gap: var(--space-2xs) var(--space-xs);
  }

  .nav__link {
    font-size: var(--text-compact);
  }

  .nav__cta,
  .nav__cta:hover {
    padding: 0.3rem 0.7rem;
  }
}

.site-footer {
  background: var(--navy-deep);
  color: var(--on-dark-mute);
  font-size: var(--text-sm);
}

.site-footer__inner {
  display: grid;
  gap: var(--space-lg) var(--space-xl);
  padding-block: var(--space-xl) var(--space-lg);
}

.site-footer__name {
  font-family: var(--font-serif);
  font-size: var(--text-name);
  font-weight: 500;
  color: var(--paper);
  margin: 0 0 var(--space-2xs);
}

.site-footer__where {
  margin: 0;
  max-width: 34ch;
}

.site-footer__heading {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--paper);
  margin-bottom: var(--space-xs);
}

.site-footer__list li {
  margin-bottom: 0.35rem;
}

.site-footer__list a {
  color: var(--on-dark);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.site-footer__list a:hover {
  color: var(--paper);
  text-decoration: underline;
}

.site-footer__note {
  grid-column: 1 / -1;
  border-top: 1px solid var(--rule-on-dark);
  padding-top: var(--space-md);
  max-width: 70ch;
  margin: 0;
}

.site-footer :focus-visible {
  outline-color: var(--paper);
}

@media (min-width: 52rem) {
  .site-footer__inner {
    grid-template-columns: 1.4fr 1fr 1fr;
    align-items: start;
  }
}

/* =================================================================================================
   HERO - what Jacob does, in one headline, beside the range of the work
   The headline is the page's <h1>; the name sits under it with the photograph, because a recruiter
   reads what somebody does before who. The Engagement overview is the hero's Exhibit.
   ============================================================================================== */
.hero {
  padding-block: clamp(2.5rem, 6vw, 5.5rem) clamp(2.5rem, 5vw, 4.5rem);
  border-bottom: 1px solid var(--line);
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}

@media (min-width: 62rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  }
}

.hero__headline {
  font-size: var(--text-3xl);
  font-weight: 500;
  letter-spacing: var(--tracking-hero);
  line-height: 1.02;
  max-width: 15ch;
}

.hero__dek {
  font-size: var(--text-md);
  color: var(--ink-soft);
  max-width: 44ch;
  margin-top: var(--space-md);
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-lg);
}

.hero__who {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: var(--space-lg) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.hero__who img {
  flex: none;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--mist);
}

.hero__who strong {
  display: block;
  color: var(--ink);
}

/* The hero's Exhibit sits in the grid, not in a reading column, and stops growing where a 2x2
   stops getting easier to read. */
.exhibit--hero {
  margin: 0;
  max-width: 40rem;
}

/* An arrow on a link or a button that leads on. */
.btn svg,
.more svg {
  flex: none;
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 200ms var(--ease);
}

.btn:hover svg,
.btn:focus-visible svg,
.more:hover svg,
.more:focus-visible svg {
  transform: translateX(3px);
}

.more {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 600;
  color: var(--blue-deep);
  text-decoration: none;
}

/* =================================================================================================
   BUTTONS - one shape; the ground decides the fill. Primary and solid are the same button, as are
   ghost and outline: v1 needed two pairs because its hero was navy, and now only the contact band
   is a coloured ground, so it is the one place that says otherwise.
   ============================================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  /* A button label is a label. `anywhere` also lowers the min-content width, which is what stops
     a long one from widening the whole page at 320px rather than just spilling out of its box. */
  max-width: 100%;
  overflow-wrap: anywhere;
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  padding: 0.8rem 1.35rem;
  border: 1.5px solid transparent;
  border-radius: var(--pill);
  cursor: pointer;
  transition: background-color 180ms var(--ease), color 180ms var(--ease),
    border-color 180ms var(--ease);
}

.btn--primary,
.btn--solid {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.btn--primary:hover,
.btn--solid:hover {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--paper);
}

.btn--ghost,
.btn--outline {
  background: transparent;
  border-color: var(--line);
  color: var(--ink);
}

.btn--ghost:hover,
.btn--outline:hover {
  background: var(--mist);
  border-color: var(--ink);
  color: var(--ink);
}

/* An outline button that toggles (aria-pressed) fills as the solid one does while it is pressed. */
.btn--outline[aria-pressed="true"],
.btn--outline[aria-pressed="true"]:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* A button among an Exhibit's controls: the small print's size, on a tighter pad. */
.btn--small {
  padding: var(--space-2xs) var(--space-sm);
  font-size: var(--text-xs);
}

/* =================================================================================================
   STAT CALLOUTS - the figures strip. The only figures on the homepage (ADR-0001).
   ============================================================================================== */
.stats {
  background: var(--navy-deep);
  color: var(--paper);
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
}

/* The homepage's strip: a line saying what the figures add up to, then the three. */
.stats__grid {
  display: grid;
  gap: var(--space-lg);
}

@media (min-width: 48rem) {
  .stats__grid {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 3fr);
    align-items: end;
  }
}

.stats__intro {
  font-size: var(--text-lg);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--paper);
  max-width: 16ch;
}

.stats__list {
  display: grid;
  gap: var(--space-lg);
}

@media (min-width: 46rem) {
  .stats__list {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-xl);
  }
}

.stat {
  border-top: 1px solid var(--rule-on-dark);
  padding-top: var(--space-sm);
}

.stat__figure {
  display: flex;
  align-items: baseline;
  gap: var(--space-2xs);
  flex-wrap: wrap;
  margin: 0;
}

.stat__value {
  font-family: var(--font-serif);
  font-size: var(--text-figure);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  /* Tabular figures so a counting animation does not make the row jitter as digits change. */
  font-variant-numeric: tabular-nums lining-nums;
}

.stat__unit {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--blue-on-dark);
}

.stat__label {
  display: block;
  margin-top: var(--space-xs);
  color: var(--on-dark-mute);
  font-size: var(--text-sm);
  max-width: 32ch;
}

/* THE NUMBERS ROW. On a Case Study the three Stat Callouts are not a band but a row in the page
   head, on white: the frame, then the figures, then the argument. */
.stats--row {
  /* Back to the light-ground emphasis: `initial` unsets the band's, and the fallback applies. */
  --emphasis: initial;
  background: var(--paper);
  color: var(--ink);
  padding-block: 0 clamp(2.5rem, 5vw, 3.5rem);
  border-bottom: 1px solid var(--line);
}

.stats--row .eyebrow {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--ink);
  max-width: none;
  margin-bottom: var(--space-sm);
}

.stats--row .stats__list {
  gap: var(--space-md);
}

.stats--row .stat {
  border-top: 2px solid var(--ink);
}

.stats--row .stat__value {
  font-size: var(--text-xl);
}

.stats--row .stat__unit {
  color: var(--blue-deep);
}

.stats--row .stat__label {
  color: var(--ink-soft);
}

/* =================================================================================================
   ABOUT BAND
   ============================================================================================== */
.about__grid {
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}

@media (min-width: 52rem) {
  .about__grid {
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: var(--space-xl);
  }
}

.about__portrait {
  width: 100%;
  max-width: 15rem;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-photo);
  background: var(--mist);
}

/* Three short facts in a row, each a label over its value. */
.about__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-lg);
  margin: var(--space-md) 0 0;
  font-size: var(--text-sm);
}

.about__facts dt {
  color: var(--ink-mute);
}

.about__facts dd {
  margin: 0;
  font-weight: 600;
}

/* The way out of the summary and into the depth: the last line of a Research Track. Weighted like
   a decision, and blue because it is one. */
.track__more {
  margin-top: var(--space-md);
  font-size: var(--text-sm);
  font-weight: 600;
}

.track__more a {
  color: var(--blue-deep);
  text-decoration: none;
}

.track__more a:hover {
  text-decoration: underline;
}

/* =================================================================================================
   ENGAGEMENT PREVIEW CARDS - and the two things that share their shape
   An editorial list rather than a row of boxes: a rule, an index, a title, a line.
   ============================================================================================== */
.engagements__list,
.routes {
  display: grid;
  gap: var(--space-lg) var(--space-md);
  margin-top: var(--space-xl);
}

@media (min-width: 42rem) {
  .engagements__list,
  .routes {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 66rem) {
  .engagements__list {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* An education credential is the same card, and so is a route off the 404 page. Sharing the rule
   is what stops the three drifting apart. The credential is the one that is not interactive, so
   it is the one without the hover. */
.engagement-card,
.credential,
.route {
  border-top: 2px solid var(--ink);
  padding-top: var(--space-sm);
  display: grid;
  align-content: start;
  gap: var(--space-2xs);
  transition: border-top-color 200ms var(--ease);
}

.engagement-card--linked:hover,
.route:hover {
  border-top-color: var(--blue);
}

.engagement-card--linked:hover .engagement-card__link,
.route:hover .route__link {
  color: var(--blue-deep);
}

.engagement-card__index {
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  margin: 0;
}

/* Three places carry a card-sized title, a small supporting label and a grey line: the Engagement
   preview, the About page's education cards, and the routes off the 404 page. */
.engagement-card__name,
.credential__what,
.route__name {
  font-size: var(--text-lg);
  font-weight: 500;
  letter-spacing: -0.018em;
  line-height: 1.15;
}

.engagement-card__sponsor,
.credential__where,
.track__sponsor {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
  margin: 0;
}

.engagement-card__line,
.credential__meta,
.route__line {
  font-size: var(--text-sm);
  color: var(--ink-soft);
  margin: var(--space-3xs) 0 0;
}

/* The line under the Engagement list, and the line under the four routes on the 404. */
.engagements__note,
.routes__note {
  margin-top: var(--space-lg);
  font-size: var(--text-sm);
  color: var(--ink-mute);
  max-width: 62ch;
}

/* =================================================================================================
   FEATURED WORK - the homepage's three Engagements shown working, and the one set in a pull quote
   Each feature is its Case Study's argument beside a compact copy of one of its Exhibits. Until that
   Exhibit is added a feature is its argument alone, at its reading width.
   ============================================================================================== */
.section__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: end;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: clamp(2rem, 5vw, 3.5rem);
}

.section__head .section__title {
  max-width: 20ch;
}

.section__head .section__lede {
  max-width: 42ch;
  margin: 0;
}

.feature {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  padding-block: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--line);
}

.feature:first-child {
  border-top: 0;
  padding-top: 0;
}

/* Until the features carry their Exhibits, the three arguments sit side by side, ruled like cards. */
@media (min-width: 60rem) {
  .features:not(:has(.exhibit)) {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-lg) var(--space-md);
  }

  .features:not(:has(.exhibit)) .feature {
    padding: var(--space-sm) 0 0;
    border-top: 2px solid var(--ink);
  }

  .features:not(:has(.exhibit)) .feature--flip > .feature__copy {
    order: 0;
  }
}

@media (min-width: 60rem) {
  .feature:has(> .exhibit) {
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    align-items: start;
  }

  .feature--flip:has(> .exhibit) {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  }

  .feature--flip > .feature__copy {
    order: 2;
  }
}

.feature__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin: 0 0 var(--space-sm);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--ink-mute);
}

.feature__meta span + span::before {
  content: "\00b7";
  margin-right: var(--space-2xs);
  color: var(--line);
}

.feature__title {
  font-size: var(--text-lg);
  font-weight: 500;
  letter-spacing: -0.018em;
  line-height: 1.15;
  max-width: 22ch;
}

.feature__body {
  color: var(--ink-soft);
  margin-top: 1.1rem;
  max-width: 46ch;
}

.feature__why {
  margin-top: 1.25rem;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
  color: var(--ink-soft);
  max-width: 46ch;
}

.feature__why b {
  color: var(--ink);
}

.feature .more,
.feature__action {
  margin-top: 1.25rem;
}

/* The Case Study set in a pull quote, on the shaded band. */
.feature--quote {
  padding: 0;
  border: 0;
}

@media (min-width: 60rem) {
  .feature--quote {
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    align-items: start;
  }
}

.pull-quote {
  display: grid;
  gap: var(--space-sm);
  margin: 0;
}

.pull-quote .pull-quote__text {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: 1.3;
  color: var(--ink);
  margin: 0;
}

.pull-quote .pull-quote__from {
  font-size: var(--text-sm);
  color: var(--ink-mute);
  margin: 0;
}

/* All six, under the three shown working. */
.engagements {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  padding-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--line);
}

.engagements__title {
  font-size: var(--text-lg);
  font-weight: 500;
  letter-spacing: -0.018em;
}

.engagements .engagements__list {
  margin-top: var(--space-lg);
}

/* =================================================================================================
   CONTACT BAND - the one blue ground on the site
   ============================================================================================== */
.contact {
  background: var(--blue);
  color: var(--paper);
}

.contact__title {
  font-size: var(--text-xl);
  font-weight: 500;
  letter-spacing: var(--tracking-display);
  max-width: 18ch;
}

.contact__lede {
  color: var(--paper);
  max-width: 50ch;
  margin-top: var(--space-sm);
  font-size: var(--text-md);
}

.contact__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-lg);
}

/* The homepage's band: the invitation, and the two actions set against it on a wide screen. */
.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
  align-items: end;
  padding-block: clamp(3.5rem, 7vw, 5.5rem);
}

.contact__grid .contact__actions {
  margin-top: 0;
}

@media (min-width: 56rem) {
  .contact__grid {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  }

  .contact__grid .contact__actions {
    justify-content: flex-end;
  }
}

.contact .btn--primary {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}

.contact .btn--primary:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.contact .btn--ghost {
  border-color: var(--rule-on-blue);
  color: var(--paper);
}

.contact .btn--ghost:hover {
  background: transparent;
  border-color: var(--paper);
  color: var(--paper);
}

/* The address itself, at its own size. It is the thing a reader copies. */
.contact__address {
  margin-top: var(--space-md);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.contact__address a,
.contact__address a:hover {
  color: var(--paper);
  text-decoration-color: var(--rule-on-blue);
}

.contact :focus-visible {
  outline-color: var(--paper);
}

/* =================================================================================================
   PAGE AND CASE STUDY HEADS - white ground, and the headline carries the page
   ============================================================================================== */
.page-head,
.case-head {
  padding-block: clamp(2.5rem, 6vw, 5rem) clamp(2rem, 4vw, 3.5rem);
  border-bottom: 1px solid var(--line);
}

.page-head__title,
.case-head__title {
  font-size: var(--text-2xl);
  font-weight: 500;
  letter-spacing: -0.026em;
  line-height: 1.04;
  max-width: 20ch;
}

.page-head__lede,
.case-head__lede {
  font-size: var(--text-md);
  color: var(--ink-soft);
  max-width: 58ch;
  margin-top: var(--space-md);
}

.crumb {
  font-size: var(--text-sm);
  color: var(--ink-mute);
  margin-bottom: var(--space-md);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.crumb__link {
  color: var(--ink-soft);
}

.case-head {
  border-bottom: 0;
  padding-bottom: var(--space-lg);
}

/* The frame before the detail: Sponsor, status, governing constraint, reading time. */
.case-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-lg);
  margin: var(--space-lg) 0 0;
  padding-block: var(--space-sm);
  border-block: 1px solid var(--line);
  font-size: var(--text-sm);
}

.case-meta dt {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
}

.case-meta dd {
  margin: var(--space-3xs) 0 0;
  font-weight: 600;
}

/* Four short facts stacked one above another take a phone's whole first screen; two by two they
   take half of it. */
@media (max-width: 40rem) {
  .case-meta {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* The one line on the page that says an Engagement is not finished. It is a statement about the
   claims on this page, so it reads as a caption and not as a badge. */
.case-head__status {
  margin-top: var(--space-sm);
  font-size: var(--text-sm);
  color: var(--ink-soft);
  max-width: 58ch;
}

.case-head__flag {
  color: var(--ink);
  margin-right: var(--space-2xs);
}

/* =================================================================================================
   THE SPINE - five sections, same order, every Case Study
   ============================================================================================== */
.case-body {
  padding-block: clamp(var(--space-xl), 6vw, var(--space-2xl));
}

/* The reading column is narrower than the page, but it starts where the page starts. Centring it
   inside the full wrap would indent the body away from the head's left edge, and the eye reads
   that as two different pages stitched together. */
.case-body__tools,
.section__tools,
.spine,
.case-foot {
  max-width: var(--measure);
}

/* The Spine's table of contents beside the prose on a wide screen, and gone on a narrow one, where
   the page is one column and the headings are the contents. */
.case-body__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
}

.toc {
  display: none;
  font-size: var(--text-sm);
}

@media (min-width: 64rem) {
  .case-body__grid {
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: var(--space-2xl);
  }

  .toc {
    display: block;
    position: sticky;
    top: var(--chrome-clearance);
    align-self: start;
  }
}

.toc__title {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink);
  margin-bottom: var(--space-xs);
}

.toc__list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-left: 1px solid var(--line);
  counter-reset: spine;
}

.toc__list li {
  counter-increment: spine;
}

/* The Spine is the one place on the site a section number carries information: it is the same
   five in the same order on every Case Study, and "section 4" means the same thing on all six. */
.toc__link {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  padding: 0.45rem 0 0.45rem var(--space-sm);
  margin-left: -1px;
  border-left: 2px solid transparent;
  text-decoration: none;
  color: var(--ink-mute);
  line-height: 1.3;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}

.toc__link::before {
  content: counter(spine, decimal-leading-zero);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  padding-top: 0.12rem;
}

.toc__link[aria-current] {
  color: var(--ink);
  border-left-color: var(--blue);
  font-weight: 600;
}

/* The reading-progress rule, drawn by motion.js under the chrome. It exists only when the script
   does, because a progress bar that cannot move is not a progress bar. */
.reading-progress {
  position: fixed;
  left: 0;
  top: 0;
  z-index: var(--z-over-chrome);
  width: 100%;
  height: 2px;
  background: var(--blue);
  transform-origin: 0 50%;
  transform: scaleX(0);
  pointer-events: none;
}

/* The same control in the same place, on a page that is not a Case Study. */
.case-body__tools,
.section__tools {
  display: flex;
  justify-content: flex-end;
  margin: 0 0 var(--space-lg);
}

.expand-all {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--blue-deep);
  background: none;
  border: 0;
  padding: var(--space-3xs) 0;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  cursor: pointer;
}

.expand-all:hover {
  color: var(--ink);
}

.spine {
  padding-top: var(--space-xl);
  border-top: 1px solid var(--line);
  margin-top: var(--space-xl);
}

.spine:first-of-type {
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}

.spine__title {
  font-size: var(--text-xl);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-md);
}

.spine p {
  max-width: 68ch;
  color: var(--ink-soft);
  font-size: var(--text-read);
  line-height: 1.7;
  margin-bottom: var(--space-sm);
}

/* The drop cap: the first letter of The Situation, and nowhere else. */
.case-body__prose > .spine:first-of-type > p:first-of-type::first-letter {
  float: left;
  font-family: var(--font-serif);
  font-size: 4.1em;
  font-weight: 600;
  line-height: 0.82;
  padding: 0.08em 0.1em 0 0;
  color: var(--ink);
}


/* Two flags, one treatment, and the difference between them is load-bearing. A Pending Result is an
   outcome nobody has measured yet (CONTEXT.md); a withheld one was measured and is not being
   restated until it has been re-derived. They read the same on the page and must not be written the
   same in the markup, because borrowing the reserved term for the second case is what makes it stop
   meaning anything in the first. The dashed, hatched panel says "not yet" without a colour. */
.pending-result,
.withheld-result {
  border: 1px dashed var(--ink-mute);
  border-radius: var(--radius);
  background: repeating-linear-gradient(135deg, transparent 0 9px, var(--hatch) 9px 10px);
  padding: var(--space-sm) var(--space-md);
  margin-top: var(--space-md);
}

.pending-result__flag,
.withheld-result__flag {
  color: var(--ink);
  display: block;
  margin-bottom: var(--space-3xs);
}

/* =================================================================================================
   EXPANDABLE SECTIONS - native <details>, so the content is in the page while collapsed
   The whole point of not hand-rolling these: keyboard operation and state announcement come from
   the element, in-page search finds the closed body, and print can be made to open them.
   ============================================================================================== */
.disclosure {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin: var(--space-md) 0 var(--space-lg);
  background: var(--paper);
}

.disclosure__summary {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--ink);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius);
  cursor: pointer;
  display: flex;
  gap: var(--space-xs);
  align-items: baseline;
  transition: background-color 180ms var(--ease);
}

.disclosure__summary:hover {
  background: var(--mist);
}

/* The default triangle is replaced by a drawn plus that loses its upright when open, and
   `list-style: none` is the only way to remove the triangle in WebKit as well as in Blink - hence
   both spellings. */
.disclosure__summary {
  list-style: none;
}

.disclosure__summary::-webkit-details-marker {
  display: none;
}

.disclosure__summary::before {
  content: "";
  flex: none;
  width: 0.75rem;
  height: 0.75rem;
  align-self: center;
  background:
    linear-gradient(var(--blue-deep), var(--blue-deep)) center / 100% 2px no-repeat,
    linear-gradient(var(--blue-deep), var(--blue-deep)) center / 2px 100% no-repeat;
  transition: background-size 200ms var(--ease);
}

.disclosure[open] > .disclosure__summary::before {
  background-size: 100% 2px, 2px 0;
}

.disclosure__body {
  padding: var(--space-sm);
  border-top: 1px solid var(--line-soft);
}

.disclosure__body p {
  font-size: var(--text-base);
}

/* =================================================================================================
   EXHIBITS - a numbered, sourced figure that states a finding (CONTEXT.md). One component.
   The markup contract is in the header of exhibit.js. The data is the table in the markup (ADR-0004);
   the stage is empty until the script draws into it, and takes no room while it is.
   ============================================================================================== */
/* The prose column is the container a wide Exhibit measures itself against, so it can run past the
   reading measure to the column's edge and never past it. */
.case-body__prose {
  container-type: inline-size;
}

.exhibit {
  margin: var(--space-lg) 0 var(--space-xl);
  min-width: 0;
}

@media (min-width: 64rem) {
  .exhibit--wide {
    width: 100cqi;
  }
}

.exhibit__head {
  border-top: 2px solid var(--ink);
  padding-top: var(--space-xs);
  margin-bottom: var(--space-sm);
}

.exhibit__title {
  font-size: var(--text-md);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

/* Blue, for the Exhibit label, is one of the places the palette reserves it. */
.exhibit__label {
  display: block;
  margin-bottom: var(--space-3xs);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--blue-deep);
}

.exhibit .exhibit__instruction {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink-mute);
}

/* A thumb landing on a stage scrolls the page. Only a draggable mark inside it - the slider - claims
   the gesture; suites/run.py fails any other element in an Exhibit that does. */
.exhibit__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-sm);
  touch-action: pan-x pan-y pinch-zoom;
}

.exhibit__stage:empty {
  display: none;
}

.exhibit__controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;
}

/* A readout's labelled figures: a <dl> of label and figure pairs, in a two-column grid with lining
   figures. Each kind sets its own columns and emphasis on top. */
.exhibit__pairs {
  display: grid;
  margin: 0;
  font-variant-numeric: tabular-nums lining-nums;
}

.exhibit__pairs dd {
  margin: 0;
  color: var(--ink);
}

/* A slider's visible name (and its value, where the kind shows it), written before it for a sighted
   reader; the slider announces its own. The slider after it fills the row up to a readable length. */
.exhibit__slider-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-soft);
}

.exhibit__slider-label b {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.exhibit__controls .exhibit__slider-label + .slider {
  flex: 1 1 12rem;
  max-width: 22rem;
}

/* A diagram that cannot reflow keeps a readable width and scrolls sideways in its own frame, never
   the page's. */
.exhibit__frame,
.exhibit__table {
  /* Positioned, so anything absolutely placed inside the diagram - a hub, a visually hidden label -
     is clipped by the frame instead of escaping it and widening the page. */
  position: relative;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

/* A frame that scrolls says so: a soft shade at whichever edge has more diagram past it. The two
   paper-coloured covers scroll with the content and hide each shade once its edge is reached, so a
   diagram that fits shows none. A phone's scrollbar is hidden until touched, and half a chart with
   no cue reads as the whole of one. */
/* The literals are this one gradient's geometry, not spacing or colour the site reuses: a 2rem
   cover, wider than the 0.9rem shade so it hides the shade completely at an edge, and ink at 22%, a
   shadow a reader notices without reading it as a rule. A data table that scrolls gets the same cue. */
.exhibit__frame,
.exhibit__table {
  background:
    linear-gradient(to right, var(--paper) 40%, transparent) left / 2rem 100% no-repeat local,
    linear-gradient(to left, var(--paper) 40%, transparent) right / 2rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--ink) 22%, transparent), transparent)
      left / 0.9rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--ink) 22%, transparent), transparent)
      right / 0.9rem 100% no-repeat scroll;
}

.exhibit .exhibit__count {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.exhibit__count b {
  font-family: var(--font-serif);
  font-size: 2rem;
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-right: var(--space-3xs);
  transition: color 200ms var(--ease);
}

.exhibit__readout.is-hot .exhibit__count b {
  color: var(--bad);
}

.exhibit .exhibit__note {
  margin: var(--space-3xs) 0 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink-soft);
  min-height: 1.5em;
}

.exhibit__data {
  margin-top: var(--space-sm);
  font-size: var(--text-sm);
}

.exhibit__data-toggle {
  display: inline-flex;
  gap: var(--space-2xs);
  align-items: baseline;
  list-style: none;
  cursor: pointer;
  font-weight: 600;
  color: var(--blue-deep);
}

.exhibit__data-toggle::-webkit-details-marker {
  display: none;
}

.exhibit__data-toggle::before {
  content: "";
  flex: none;
  width: 0.65rem;
  height: 0.65rem;
  align-self: center;
  background:
    linear-gradient(currentColor, currentColor) center / 100% 2px no-repeat,
    linear-gradient(currentColor, currentColor) center / 2px 100% no-repeat;
}

.exhibit__data[open] > .exhibit__data-toggle::before {
  background-size: 100% 2px, 2px 0;
}

.exhibit__table {
  margin-top: var(--space-2xs);
}

.exhibit__table table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums lining-nums;
}

.exhibit__table caption {
  caption-side: top;
  text-align: left;
  padding-bottom: var(--space-2xs);
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.exhibit__table th,
.exhibit__table td {
  padding: var(--space-3xs) var(--space-sm) var(--space-3xs) 0;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: top;
}

.exhibit__table thead th {
  border-bottom-color: var(--line);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
}

.exhibit__table tbody th {
  font-weight: 400;
}

.exhibit__table td {
  text-align: right;
}

.exhibit__source {
  margin-top: var(--space-sm);
  padding-top: var(--space-2xs);
  border-top: 1px solid var(--line);
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--ink-mute);
}

.exhibit__source b {
  font-weight: 600;
  color: var(--ink-soft);
}

/* A segmented choice: one pressed option among a few, each a real button. */
.seg {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  max-width: 100%;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 1.25rem;
  background: var(--mist);
}

.seg__option {
  border: 0;
  border-radius: var(--pill);
  padding: var(--space-2xs) var(--space-sm);
  background: transparent;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease);
}

.seg__option:hover,
.seg__option:focus-visible {
  color: var(--ink);
}

.seg__option[aria-pressed="true"] {
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 0 0 1px var(--line), 0 1px 3px var(--line);
}

/* Too narrow for a group's options on one line, the options stand as separate pills that wrap
   as a row, rather than one rounded track broken over two lines. */
@media (max-width: 30rem) {
  .seg {
    padding: 0;
    border: 0;
    background: none;
    gap: var(--space-2xs);
  }

  .seg__option {
    border: 1px solid var(--line);
    background: var(--mist);
  }

  /* After the tray's pressed rule, so it wins: a pill's own border is its outline, and the
     tray's raised shadow would draw a second one around it. */
  .seg__option[aria-pressed="true"] {
    border-color: var(--ink-soft);
    box-shadow: none;
  }
}

/* THE SHARED SLIDER. The one mark that claims a touch gesture, because it is the one that is dragged.
   The padding keeps the thumb inside the control at either end; the height is a comfortable target. */
.slider {
  display: flex;
  align-items: center;
  min-width: 8rem;
  height: 2.75rem;
  padding-inline: var(--slider-inset);
  touch-action: none;
  cursor: pointer;
}

.slider__track {
  position: relative;
  flex: 1;
  height: 4px;
  border-radius: var(--pill);
  background: var(--line);
}

.slider__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--pill);
  background: var(--blue);
}

.slider__thumb {
  position: absolute;
  top: 50%;
  width: calc(2 * var(--slider-inset));
  height: calc(2 * var(--slider-inset));
  border: 2px solid var(--blue);
  border-radius: 50%;
  background: var(--paper);
  transform: translate(-50%, -50%);
}

.slider:focus-visible {
  outline: none;
}

.slider:focus-visible .slider__thumb {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* ---- kind: blast-radius --------------------------------------------------------------------------
   Workstations along the top, the customer files along the bottom, and between them either a
   straight line from each analyst to their own group or, with a server, every line running through
   it. One column per workstation, and the lines are drawn in the same columns. */
.blast {
  min-width: 34rem;
  display: grid;
  grid-template-columns: repeat(var(--blast-columns), minmax(0, 1fr));
}

.blast__machines,
.blast__links,
.blast__files {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
}

.blast__machine {
  margin: 0 var(--space-3xs);
  padding: var(--space-2xs) var(--space-3xs);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.25;
  cursor: pointer;
  transition: border-color 200ms var(--ease), background-color 200ms var(--ease);
}

.blast__machine:hover,
.blast__machine:focus-visible {
  border-color: var(--blue);
}

.blast__machine[aria-pressed="true"] {
  border-color: var(--bad);
  background: color-mix(in srgb, var(--bad) 8%, var(--paper));
}

.blast__name {
  display: block;
}

/* Never colour alone: a compromised machine says so in words, beside the red. */
.blast__tag {
  display: none;
  margin-top: 0.15rem;
  font-weight: 700;
  color: var(--bad);
}

.blast__tag::before {
  content: "\00d7\00a0";
}

.blast__machine[aria-pressed="true"] .blast__tag {
  display: block;
}

.blast__links {
  position: relative;
  height: 7rem;
}

.blast__links svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.blast__link {
  fill: none;
  stroke: var(--line);
  stroke-width: 1.5;
  transition: stroke 200ms var(--ease);
}

.blast__link.is-dashed {
  stroke-dasharray: 3 3;
}

.blast__link.is-hot {
  stroke: var(--bad);
  stroke-width: 2.5;
}

.blast__machine--hub {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  min-width: 9rem;
  margin: 0;
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

.blast__machine--hub[aria-pressed="true"] {
  border-color: var(--bad);
  background: var(--bad);
}

.blast__machine--hub .blast__tag {
  color: var(--paper);
}

.blast__group {
  display: grid;
  grid-template-columns: repeat(4, 0.85rem);
  gap: 0.3rem;
  justify-content: center;
}

/* Exposed is filled; untouched is an outline. The shape changes as well as the colour. */
.blast__file {
  height: 1.1rem;
  border: 1.5px solid var(--ink-mute);
  border-radius: 2px;
  background: var(--paper);
  transition: background-color 300ms var(--ease), border-color 300ms var(--ease);
}

.blast__file.is-exposed {
  border-color: var(--bad);
  background: var(--bad);
}

.exhibit .blast__shelf {
  grid-column: 1 / -1;
  margin: var(--space-xs) 0 0;
  text-align: center;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

/* ---- kind: three-stage ---------------------------------------------------------------------------
   A swimlane drawn in SVG: the analyst's work along the top, a block per step coloured by its kind,
   the lender's grey track below, and a playhead that sweeps the file. A question is a pennant flag
   (staged) or an arrow down to a call (interleaved); each stays faint until the playhead reaches
   it. Below a readable width the drawing keeps that width and scrolls inside its frame. */
.lanes__controls {
  justify-content: space-between;
  gap: var(--space-xs) var(--space-sm);
  margin-bottom: var(--space-sm);
}

.lanes__keys {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  align-items: center;
}

.lanes__play {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border: 1.5px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease);
}

.lanes__play svg {
  width: 0.9rem;
  height: 0.9rem;
}

.lanes__play:hover,
.lanes__play:focus-visible {
  background: var(--ink);
  color: var(--paper);
}

.lanes__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

.lanes__legend li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
}

.lanes__legend li::before {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 2px;
  background: var(--kind);
}

svg.lanes {
  display: block;
  width: 100%;
  min-width: 35rem;
  height: auto;
  font-family: var(--font-sans);
}

.lanes__who {
  font-size: 13px;
  font-weight: 700;
  fill: var(--ink);
}

.lanes__track {
  stroke: var(--line-soft);
  stroke-width: 22;
  stroke-linecap: round;
}

.lanes__block {
  fill: var(--kind);
}

.lanes__flag {
  fill: var(--ink);
  stroke: var(--ink);
  stroke-width: 1.5;
}

.lanes__arrow {
  stroke: var(--bad);
  stroke-width: 2;
}

.lanes__head,
.lanes__pill {
  fill: var(--bad);
}

.lanes__pill-words {
  font-size: 9.5px;
  font-weight: 700;
  fill: var(--paper);
  text-anchor: middle;
}

.lanes__held {
  font-size: 10px;
  font-weight: 700;
  fill: var(--ink);
  text-anchor: middle;
}

/* A question or a call the playhead has not reached yet. */
.lanes__faint {
  opacity: 0.12;
}

.lanes__playhead {
  stroke: var(--ink);
  stroke-width: 1.5;
}

.lanes__playdot {
  fill: var(--ink);
}

.lanes__mark {
  font-size: 10.5px;
  fill: var(--ink-mute);
}

/* Three counts side by side in ruled boxes, each figure above its words. Detect, correct and flag
   uses the same tally. */
.lanes__tally,
.prep__tally {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  margin-top: var(--space-sm);
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--line);
  overflow: hidden;
}

.exhibit .lanes__tally .exhibit__count,
.exhibit .prep__tally .exhibit__count {
  padding: 0.8rem 1rem;
  background: var(--paper);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
  overflow-wrap: break-word;
}

.lanes__tally .exhibit__count b,
.prep__tally .exhibit__count b {
  display: block;
  margin: 0 0 0.2rem;
  font-size: 1.9rem;
  line-height: 1.05;
}

/* ---- kind: detect-correct-flag --------------------------------------------------------------------
   A preview of the export as a grid: its line number, then one column per field. A cell the tool
   detected is a button, so the keyboard reaches its reason. As exported, it is outlined dashed;
   prepared, a corrected cell is washed blue with a corner mark, a cell left empty is hatched and says
   so, and the title row is struck through and tagged. Never colour alone: each state changes the
   words or the mark too. Below a readable width the grid keeps it and scrolls inside its frame. */
.prep {
  display: grid;
  grid-template-columns: 2rem repeat(var(--prep-columns), auto);
  align-items: center;
  min-width: 31rem;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums lining-nums;
}

.prep__row {
  display: contents;
}

.prep__line,
.prep__cell {
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
}

.prep__line {
  padding-left: 0;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.prep__row--head .prep__cell {
  border-bottom-color: var(--line);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
}

button.prep__cell {
  position: relative;
  padding: calc(0.4rem - 1.5px) calc(0.5rem - 1.5px);
  border: 1.5px solid transparent;
  border-radius: 4px;
  background: none;
  font: inherit;
  color: var(--ink);
  cursor: help;
  transition: background-color 200ms var(--ease), border-color 200ms var(--ease);
}

.prep__cell--title {
  grid-column: 2 / -1;
}

.prep__cell.is-detected {
  border-color: var(--ink-mute);
  border-style: dashed;
}

.prep__cell.is-corrected {
  border-color: var(--blue);
  background: var(--blue-wash);
}

/* A corrected cell's corner mark, so a correction is not told by colour alone. */
.prep__cell--flag.is-corrected::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  border-style: solid;
  border-width: 0 0.55rem 0.55rem 0;
  border-color: transparent var(--blue-deep) transparent transparent;
}

.prep__cell.is-empty {
  border-color: var(--ink-mute);
  background: repeating-linear-gradient(-45deg, var(--hatch) 0 4px, transparent 4px 8px);
}

.prep__value.is-none {
  font-style: italic;
  color: var(--ink-mute);
}

.prep__cell--title.is-corrected .prep__value {
  text-decoration: line-through;
  color: var(--ink-mute);
}

.prep__tag {
  display: none;
  margin-left: var(--space-xs);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--blue-deep);
}

.prep__cell--title.is-corrected .prep__tag {
  display: inline;
}

button.prep__cell:hover,
button.prep__cell:focus-visible {
  outline: 2px solid var(--blue-deep);
  outline-offset: 1px;
}

.exhibit .prep__why {
  min-height: 3em;
}

/* ---- kind: triage ---------------------------------------------------------------------------------
   The documents as a column of buttons, and the tool's answer beside them: a pill with an icon and
   the answer in words, what the tool says, and the income statement it produced. The pill's colour
   is never the only signal - its icon and its words change with it. A held line is washed in the
   third data colour, the one colour here that means neither pass nor fail. Nothing is too wide to
   reflow, so the stage stacks on a phone rather than scrolling. */
.triage {
  display: grid;
  gap: var(--space-sm);
}

@media (min-width: 40rem) {
  .triage {
    grid-template-columns: 14rem minmax(0, 1fr);
  }
}

.triage__docs {
  display: grid;
  gap: var(--space-2xs);
  align-content: start;
}

.triage__doc {
  display: grid;
  gap: 0.1rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 180ms var(--ease), background-color 180ms var(--ease);
}

.triage__doc small {
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.triage__doc[aria-pressed="true"] {
  border-color: var(--blue);
  background: var(--blue-wash);
}

.triage__out {
  min-width: 0;
  min-height: 13rem;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--paper);
  font-size: var(--text-sm);
}

.exhibit .triage__out p {
  margin: 0 0 var(--space-2xs);
  font-size: inherit;
  line-height: 1.5;
}

.exhibit .triage__verdict {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.8rem;
  padding: 0.3rem 0.7rem;
  border-radius: var(--pill);
  font-size: var(--text-xs);
  font-weight: 700;
}

.triage__icon svg {
  display: block;
  width: 0.95rem;
  height: 0.95rem;
}

.triage__verdict--extracted {
  background: var(--good-wash);
  color: var(--good-ink);
}

.triage__verdict--held {
  background: color-mix(in srgb, var(--data-3) 16%, var(--paper));
  color: color-mix(in srgb, var(--data-3) 45%, var(--ink));
}

.triage__verdict--refused {
  background: var(--bad-wash);
  color: var(--bad-ink);
}

.exhibit .triage__aside {
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.stmt {
  margin: 0.4rem 0 0;
  font-variant-numeric: tabular-nums;
}

.stmt__row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 var(--space-sm);
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--line-soft);
}

.stmt dt {
  font-weight: 400;
}

.stmt dd {
  margin: 0 0 0 auto;
  font-weight: 600;
  text-align: right;
}

.stmt__row.is-held {
  padding-inline: 0.4rem;
  background: color-mix(in srgb, var(--data-3) 16%, var(--paper));
}

.stmt__row.is-total {
  border-bottom: 2px solid var(--ink);
  font-weight: 700;
}

.stmt__row.is-total dt {
  font-weight: 700;
}

/* ---- kind: green-suite -----------------------------------------------------------------------------
   One square per assertion, filling in good green, over a legend that keys it: passing carries a
   tick as well as the colour, so green is never the only signal. The grid scales with the column
   rather than scrolling, since a field of squares reads the same at any size. Below it, the defects,
   two to a row on wider screens, each with a pill saying whether it would reach figures. Neither pill
   is pass or fail, so they take the fourth and third data colours rather than the status ones. */
.exhibit .suite__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.2rem;
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

.suite__legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.suite__legend b {
  font-variant-numeric: tabular-nums;
}

.suite__key {
  display: inline-block;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 2px;
}

.suite__key--pass {
  color: var(--good);
}

.suite__key--pass rect {
  fill: currentColor;
}

.suite__key--pass path {
  fill: none;
  stroke: var(--paper);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.suite__key--idle {
  background: var(--line);
}

.suite__grid {
  display: block;
  width: 100%;
  border-radius: 8px;
  background: var(--paper);
}

.exhibit .suite__status {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
}

.suite__defects {
  display: grid;
  gap: 0.4rem;
}

@media (min-width: 40rem) {
  .suite__defects {
    grid-template-columns: 1fr 1fr;
  }
}

.suite__defects[hidden] {
  display: none;
}

.suite__defect {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.6rem;
  align-items: start;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  font: inherit;
  font-size: var(--text-sm);
  line-height: 1.35;
  text-align: left;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 160ms var(--ease), background-color 160ms var(--ease);
}

.suite__defect:hover,
.suite__defect[aria-pressed="true"] {
  border-color: var(--ink);
}

.suite__defect[aria-pressed="true"] {
  background: var(--mist);
}

.suite__sev {
  margin-top: 0.1rem;
  background: color-mix(in srgb, var(--data-3) 16%, var(--paper));
  color: color-mix(in srgb, var(--data-3) 45%, var(--ink));
}

/* A chip: a word in a pill beside the thing it qualifies. A trouble chip is the one that says
   something is wrong - a defect that would reach figures, a company flagged - in the red categorical
   colour, always with its word. Placed after the chips it overrides. */
.chip {
  padding: var(--chip-pad);
  border-radius: var(--pill);
  font-size: 0.7rem;
  font-weight: 700;
  white-space: nowrap;
}

.chip--trouble {
  background: var(--trouble-wash);
  color: var(--trouble-ink);
}

/* A plain chip: the same word in an outlined pill, for the verdict that says nothing is wrong. */
.chip--plain {
  box-shadow: inset 0 0 0 1px var(--line);
}

.exhibit .suite__detail {
  min-height: 1.6em;
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

/* QUARTILE SPREAD - a result reported with its size. One column per period, each bar drawn above or
   below a zero line to a length set by its spread, all on one scale. The status colours are the
   right place for it: the top quartile ahead or behind is a pass or a fail, and each bar is paired
   with its figure in words. */
.spread {
  padding: var(--space-sm);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.exhibit .spread__heading {
  margin: 0 0 var(--space-xs);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
}

.spread__plot {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.spread__period {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: var(--space-3xs);
  text-align: center;
}

.spread__period[hidden] {
  display: none;
}

/* The zero line is drawn per column and runs edge to edge, so the columns share one unbroken axis;
   the words beneath carry the gutter instead. The bar grows from the line, up or down. */
.spread__mark {
  position: relative;
  display: block;
  width: 100%;
  height: 8rem;
  background: linear-gradient(var(--line), var(--line)) center / 100% 1px no-repeat;
}

.spread__bar {
  position: absolute;
  left: 50%;
  width: 2rem;
  max-width: 60%;
  transform: translateX(-50%);
}

.is-up .spread__bar {
  bottom: 50%;
  border-radius: 2px 2px 0 0;
  background: var(--good);
}

.is-down .spread__bar {
  top: 50%;
  border-radius: 0 0 2px 2px;
  background: var(--bad);
}

.spread__value {
  font-size: var(--text-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.is-up .spread__value {
  color: var(--good);
}

.is-down .spread__value {
  color: var(--bad);
}

.spread__value,
.spread__label {
  padding-inline: var(--space-3xs);
}

.spread__label {
  font-size: var(--text-xs);
  line-height: 1.35;
  color: var(--ink-soft);
  overflow-wrap: break-word;
}

.exhibit .spread__scale {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

/* MENTIONS AGAINST THE RUBRIC - the same sentences counted and scored. One card per sentence, its
   text with every mention of AI marked, then a bar for its mentions and one for its score, each on
   its own scale with the figure in words beside it. The cards reflow into one column, so nothing here needs a frame of its own. */
.mentions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.mentions__passage {
  display: grid;
  align-content: start;
  gap: var(--space-2xs);
  padding: var(--space-sm);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.exhibit .mentions__label {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
}

.exhibit .mentions__text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.5;
}

.mentions__term {
  padding: 0 0.08em;
  border-radius: 2px;
  background: color-mix(in srgb, var(--data-2) 20%, var(--paper));
  color: var(--ink);
  font-weight: 600;
}

.mentions__term.is-added {
  outline: 1px dashed var(--data-2);
  outline-offset: -1px;
}

.mentions__score {
  display: grid;
  grid-template-columns: 5.25rem minmax(0, 1fr) auto;
  gap: var(--space-2xs);
  align-items: center;
  font-size: var(--text-xs);
}

.mentions__name {
  font-weight: 600;
  color: var(--ink-soft);
}

.mentions__track {
  position: relative;
  height: 0.5rem;
  border-radius: var(--pill);
  background: var(--line-soft);
}

.mentions__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--pill);
  transition: width 200ms var(--ease);
}

.is-frequency .mentions__fill {
  background: var(--data-2);
}

.is-score .mentions__fill {
  background: var(--data-1);
}

.mentions__value {
  min-width: 4.75rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.exhibit .mentions__signs {
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.4;
  color: var(--ink-mute);
}

.case-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--line);
}

/* =================================================================================================
   THE WORK INDEX - the same cards as the homepage, linked where a page exists
   ============================================================================================== */
.engagements__list--index {
  margin-top: 0;
}

/* The Engagement overview at full size: wider than the hero's copy, and held to the reading measure,
   where the 2x2 still fits a laptop screen. The cards follow straight on, so the section gives up its foot. */
.section--overview {
  padding-bottom: 0;
}

.exhibit--index {
  margin: 0;
  max-width: var(--measure);
}

/* Where the map above puts the Engagement: the one thing a card on the index adds. */
.engagement-card__place {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--blue-deep);
  margin: var(--space-3xs) 0 0;
}

.engagement-card__link {
  text-decoration: none;
}

/* The whole card is the hit area, which is what makes the hover state a promise rather than
   decoration. The link keeps its own focus ring - the overlay is behind it, not over it. */
.engagement-card--linked {
  position: relative;
}

.engagement-card--linked .engagement-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.engagement-card__cta {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--blue-deep);
  margin: var(--space-2xs) 0 0;
}

.engagement-card__cta--pending {
  color: var(--ink-mute);
}

/* ---- KIND: engagement-overview ------------------------------------------------------------------
   Six Engagements on a 2x2. The ground and the dividing lines are the drawing; every word and every
   mark over it is HTML, placed by percentage, so it keeps the page's own size at any width. Narrower
   than the names need, the diagram keeps its width and scrolls sideways in its frame; at phone width
   it reflows instead (below). */
.overview {
  position: relative;
  min-width: 28rem;
}

.overview svg {
  display: block;
  width: 100%;
  height: auto;
}

.overview__ground {
  fill: var(--mist);
}

.overview__lead-quadrant {
  fill: var(--mist-deep);
}

.overview__divide {
  fill: none;
  stroke: var(--paper);
  stroke-width: 3;
  vector-effect: non-scaling-stroke;
}

.overview__quadrant,
.overview__axis,
.overview__mark {
  position: absolute;
  left: var(--x);
  top: var(--y);
}

.overview__quadrant {
  white-space: nowrap;
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0.01em;
  color: var(--ink-mute);
}

.overview__quadrant.is-low {
  transform: translateY(-100%);
}

.overview__axis {
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  color: var(--ink-soft);
}

.overview__axis.is-end {
  transform: translateX(-100%);
}

/* The vertical axis reads upward, set to the left of the plot. */
.overview__axis.is-y {
  transform-origin: 0 0;
  transform: rotate(-90deg) translateY(-100%);
}

.overview__axis.is-y.is-end {
  transform: rotate(-90deg) translate(-100%, -100%);
}

.overview__marks {
  position: absolute;
  inset: 0;
}

/* A mark is its numbered dot with its name beside it; the dot's centre is the mark's place, which
   the script gives in two layouts (see the kind in exhibit.js): the wide one here, the narrow one at
   phone width below. */
.overview__mark {
  --x: var(--wide-x);
  --y: var(--wide-y);
  --dot: 1.75rem;
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  transform: translate(calc(var(--dot) / -2), -50%);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

/* In a right-hand quadrant the name runs to the left of its dot, toward the middle. */
.overview__mark.is-left-label {
  flex-direction: row-reverse;
  transform: translate(calc(-100% + var(--dot) / 2), -50%);
}

.overview__dot {
  flex: none;
  display: grid;
  place-items: center;
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 4px var(--paper), 0 0 0 5px var(--line);
  color: var(--paper);
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background-color 200ms var(--ease), transform 220ms var(--ease),
    box-shadow 200ms var(--ease);
}

.overview__label {
  text-shadow: 0 0 3px var(--paper), 0 0 3px var(--paper);
}

.overview__mark:hover,
.overview__mark:focus-visible {
  color: var(--blue-deep);
}

.overview__mark:hover .overview__dot,
.overview__mark:focus-visible .overview__dot {
  transform: scale(1.2);
}

.overview__mark:focus-visible {
  outline: none;
}

.overview__mark:focus-visible .overview__dot {
  box-shadow: 0 0 0 4px var(--paper), 0 0 0 6px var(--blue);
}

.overview__mark.is-shown .overview__dot {
  background: var(--blue);
}

/* At phone width the diagram reflows instead of scrolling: half a 2x2 off-frame reads as the whole
   of one, and the overview's opaque quadrants cover the frame's edge shade. It draws to the column's
   width, the quadrant names step aside as they do on the compact copy (the readout's "described as"
   line and the table carry them), and the marks take the narrow layout, each name wrapping beside
   its dot inside its own quadrant. */
@media (max-width: 30rem) {
  .overview {
    min-width: 0;
  }

  /* Drawn taller than the wide copy (the SVG stretches; its words keep their size), so that each
     upright axis name fits beside its own half and a name wrapped to three lines stays in its
     quadrant. */
  .overview svg {
    height: 30rem;
  }

  .overview__quadrant {
    display: none;
  }

  .overview__mark {
    --x: var(--narrow-x);
    --y: var(--narrow-y);
    --dot: 1.5rem;
  }

  /* A name keeps to its own quadrant: the room between a dot and the divider is about this wide. */
  .overview__label {
    max-width: 4.75rem;
    white-space: normal;
  }
}

/* What a mark leads to, shown when it is hovered, focused or tapped. */
.overview__card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.2rem var(--space-sm);
  align-items: start;
  min-height: 6.4rem;
  padding: var(--space-sm) 1.1rem;
  background: var(--mist);
  border-radius: var(--radius);
}

.overview__no {
  grid-row: span 3;
  font-family: var(--font-serif);
  font-size: 2rem;
  line-height: 1;
  color: var(--blue-deep);
}

.overview__name {
  font-weight: 700;
}

.overview__says {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
}

.exhibit .overview__line {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

/* =================================================================================================
   MOTION - every hiding rule below is inside .js-motion, and that is load-bearing
   One entrance, on first view: the element rises, sharpens and arrives, decelerating.
   ============================================================================================== */
.js-motion .reveal {
  opacity: 0;
  transform: translateY(14px);
  filter: blur(3px);
  transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease),
    filter var(--speed) var(--ease);
}

.js-motion .reveal.is-revealed {
  opacity: 1;
  transform: none;
  filter: none;
}

/* The green suite's defects arrive one after another when the change is read as prose. */
.js-motion .suite__defects.is-arriving .suite__defect {
  opacity: 0;
  transform: translateY(6px);
}

.js-motion .suite__defects.is-arriving.is-here .suite__defect {
  opacity: 1;
  transform: none;
  transition: opacity 500ms var(--ease), transform 500ms var(--ease),
    border-color 160ms var(--ease), background-color 160ms var(--ease);
  transition-delay: calc(var(--defect-at, 0) * 90ms), calc(var(--defect-at, 0) * 90ms), 0ms, 0ms;
}

/* The overview's marks gather at the centre, then spread to their quadrants, one after another. */
.js-motion .overview__marks.is-arriving .overview__mark {
  left: var(--cx);
  top: var(--cy);
  opacity: 0;
  scale: 0.4;
}

.js-motion .overview__marks.is-arriving.is-here .overview__mark {
  left: var(--x);
  top: var(--y);
  opacity: 1;
  scale: 1;
  transition: left 900ms var(--ease), top 900ms var(--ease), scale 900ms var(--ease),
    opacity 700ms var(--ease);
  transition-delay: calc(250ms + var(--i, 0) * 110ms);
}

/* =================================================================================================
   REDUCED MOTION - a blanket, deliberately
   The alternative is a per-component opt-in, which the next component forgets. suites/run.py
   fails the run if this block stops collapsing both durations on *.
   ============================================================================================== */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  html {
    scroll-behavior: auto;
  }
}

/* =================================================================================================
   PRINT - a recruiter saving this as a PDF gets the whole page
   ============================================================================================== */
/* The Resume page: one entry per role, the card rule the credentials use on top, the dates set
   right of the title from tablet width up. The page is also the source of the PDF download, so
   the print rules below keep it to plain type on paper. */
.resume-entry {
  border-top: 2px solid var(--ink);
  padding-top: var(--space-sm);
  margin-top: var(--space-lg);
  max-width: 48rem;
}

.resume-entry__head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 var(--space-sm);
  align-items: baseline;
}

.resume-entry__role {
  font-family: var(--font-serif);
  font-size: var(--text-md);
  margin: 0;
}

.resume-entry__when {
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  margin: 0;
  white-space: nowrap;
}

.resume-entry__where {
  grid-column: 1 / -1;
  color: var(--ink-soft);
  margin: var(--space-3xs) 0 0;
}

.resume-entry__sub {
  font-size: var(--text-base);
  margin: var(--space-md) 0 0;
}

.resume-entry__sub span {
  font-weight: 400;
  color: var(--ink-mute);
  margin-left: var(--space-2xs);
}

.resume-entry__list {
  margin: var(--space-2xs) 0 0;
  padding-left: 1.25rem;
  display: grid;
  gap: var(--space-2xs);
}

@media (max-width: 36rem) {
  .resume-entry__head {
    grid-template-columns: 1fr;
  }
}

.resume-contact {
  color: var(--ink-soft);
  margin: var(--space-2xs) 0 0;
}

@media print {
  /* The Resume page prints as a resume: letter paper, the name on top, sections packed together
     instead of spaced for a screen, and no heading left alone at the foot of a page. */
  @page {
    size: letter;
    margin: 0.45in 0.5in;
  }

  body:has(.resume-entry) {
    font-size: 9.5pt;
    line-height: 1.35;
  }

  body:has(.resume-entry) .wrap {
    max-width: none;
    padding-inline: 0;
  }

  body:has(.resume-entry) .page-head,
  body:has(.resume-entry) .section {
    padding-block: 0.08in;
    background: none;
  }

  body:has(.resume-entry) .page-head__title {
    font-size: 22pt;
    margin: 0;
  }

  body:has(.resume-entry) .page-head__lede {
    font-size: 9.5pt;
    max-width: none;
    margin-top: 0.06in;
  }

  body:has(.resume-entry) .section__title {
    font-size: 12.5pt;
    max-width: none;
    margin: 0;
    break-after: avoid;
  }

  body:has(.resume-entry) .resume-entry {
    max-width: none;
    margin-top: 0.08in;
    padding-top: 0.04in;
    border-top-width: 1px;
  }

  body:has(.resume-entry) .resume-entry__head {
    grid-template-columns: 1fr auto;
  }

  body:has(.resume-entry) .resume-entry__role {
    font-size: 10.5pt;
  }

  body:has(.resume-entry) .resume-entry__when,
  body:has(.resume-entry) .resume-entry__where {
    font-size: 9pt;
  }

  body:has(.resume-entry) .resume-entry__sub {
    font-size: 9.5pt;
    margin-top: 0.05in;
    break-after: avoid;
  }

  body:has(.resume-entry) .resume-entry__list {
    gap: 0.02in;
    margin-top: 0.03in;
  }

  body:has(.resume-entry) .credentials,
  body:has(.resume-entry) .skillset {
    margin-top: 0.06in;
  }

  body:has(.resume-entry) .skillset {
    grid-template-columns: repeat(3, 1fr);
    gap: 0.1in 0.25in;
  }

  body:has(.resume-entry) .skillset__name {
    font-size: 10pt;
    padding-bottom: 0.02in;
    border-bottom-width: 1px;
  }

  body:has(.resume-entry) .credential {
    padding-top: 0.04in;
    margin-top: 0.06in;
    border-top-width: 1px;
  }

  body:has(.resume-entry) .credential__what {
    font-size: 10.5pt;
    margin: 0;
  }

  body:has(.resume-entry) .credential__where,
  body:has(.resume-entry) .credential__meta {
    font-size: 9pt;
    margin: 0.02in 0 0;
  }

  body:has(.resume-entry) .skillset__list li {
    padding-block: 0;
    border: 0;
    font-size: 9pt;
  }
}

@media print {
  /* The resume PDF is built from the Resume page, so on that page the footer goes too. */
  body:has(.resume-entry) .site-footer {
    display: none;
  }

  .resume-entry {
    break-inside: avoid;
  }

  .site-header,
  .skip-link,
  .hero__ctas,
  .contact__actions,
  .expand-all,
  .crumb,
  .toc,
  .reading-progress {
    display: none;
  }

  /* A recruiter saving a Case Study gets the WHOLE Case Study. A closed <details> hides its body
     through the UA stylesheet - historically `display: none` on the non-summary children, and in
     current Chrome `content-visibility: hidden` on the ::details-content pseudo-element. Both
     spellings are overridden, because which one is in force depends on the browser doing the
     printing and a section missing from a PDF announces nothing. */
  details > *:not(summary) {
    display: block !important;
  }

  details::details-content {
    content-visibility: visible !important;
    display: block !important;
  }

  .disclosure__summary::before {
    display: none !important;
  }

  /* A timeline node and a skills rule are structure here rather than decoration - without them the
     printed About page is an undifferentiated column - so they are asked to survive a print path
     that would otherwise drop them as background colour. */
  .course__node,
  .course__swatch,
  .skillset__name {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  .hero,
  .stats,
  .contact,
  .page-head,
  .case-head,
  .site-footer {
    background: var(--paper) !important;
    color: var(--ink) !important;
  }

  .stats .eyebrow,
  .site-footer__name,
  .site-footer__heading,
  .site-footer__list a,
  .contact__address a {
    color: var(--ink) !important;
  }

  .stat__unit {
    color: var(--blue-deep) !important;
  }

  .stat__label,
  .hero__lede,
  .contact__lede,
  .page-head__lede,
  .case-head__lede,
  .case-head__status,
  .site-footer__note {
    color: var(--ink-soft) !important;
  }

  .spine {
    break-inside: avoid-page;
  }

  /* A printed Exhibit is its title, its table and its source line. The drawing is a live control
     surface and prints as buttons, so it stays on screen. */
  .exhibit__stage {
    display: none;
  }

  .exhibit__data-toggle::before {
    display: none !important;
  }

  .exhibit {
    break-inside: avoid-page;
  }
}

/* =================================================================================================
   THE ABOUT PAGE - the stance, the timeline, education, skills

   Only the shapes that are unique to /about/ live here. The card shell, the card title, the small
   label and the grey supporting line are shared with the Engagement preview card above. Nothing
   below names a colour or a size that a token already names.
   ============================================================================================== */

/* The one paragraph a skimming reader has to meet whatever else they skip: the editorial stance on
   /about/, and the withdrawal on /research/. Both are the paragraph the page would be dishonest
   without. Set apart by a heavy rule above and a larger size, not by a coloured edge. */
.stance {
  margin-top: var(--space-md);
  border-top: 2px solid var(--ink);
  padding-top: var(--space-sm);
  max-width: 62ch;
  font-size: var(--text-md);
  line-height: 1.55;
  color: var(--ink);
}

/* ---- timeline: Exhibit 1 -------------------------------------------------------------------------
   Every entry is a stop on one rail, newest first, because that order is the content rather than a
   presentation choice: the current role leads. Down the page on a phone, across it once there is
   room for seven columns. The rail is drawn per entry, so it grows with the list and cannot come
   apart from it at a width nobody tested; the first entry's rail starts at its node, and the last
   one's stops at its node.

   Three node states, each paired with the key's words: the current role (blue, the selected state
   of the list), an entry still under way (an open ring) and one that has ended (a filled dot). */
.course {
  --node: 0.75rem;
  --node-top: 0.8rem;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
}

.course__entry {
  position: relative;
}

.course__entry::before {
  content: "";
  position: absolute;
  left: calc(0.5rem - 0.5px);
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}

.course__entry:first-child::before {
  top: calc(var(--node-top) + var(--node) / 2);
}

.course__entry:last-child::before {
  bottom: auto;
  height: calc(var(--node-top) + var(--node) / 2);
}

.course__stop {
  position: relative;
  display: grid;
  grid-template-columns: 1rem minmax(0, 1fr);
  column-gap: var(--space-sm);
  align-content: start;
  width: 100%;
  border: 0;
  border-radius: var(--radius-ring);
  padding: 0.5rem var(--space-xs) 0.5rem 0;
  background: transparent;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: background-color 200ms var(--ease);
}

.course__stop:hover {
  background: var(--mist);
}

.course__node,
.course__swatch {
  box-sizing: border-box;
  width: var(--node);
  height: var(--node);
  border-radius: 50%;
  background: var(--ink-mute);
}

.course__node {
  grid-column: 1;
  grid-row: 1 / span 3;
  justify-self: center;
  margin-top: calc(var(--node-top) - 0.5rem);
}

.is-running > .course__stop .course__node,
.course__swatch.is-running {
  border: 2px solid var(--ink);
  background: var(--paper);
}

.is-now > .course__stop .course__node,
.course__swatch.is-now {
  background: var(--blue);
  box-shadow: 0 0 0 4px var(--blue-wash);
}

.course__when,
.course__what,
.course__flag {
  grid-column: 2;
}

.course__when {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.course__what {
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: break-word;
}

.course__stop[aria-pressed="true"] {
  background: var(--blue-wash);
}

.course__stop[aria-pressed="true"] .course__what {
  color: var(--blue-deep);
}

.course__flag {
  justify-self: start;
  margin-top: var(--space-3xs);
  border-radius: var(--pill);
  padding: 0 var(--space-2xs);
  background: var(--blue);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--paper);
}

.exhibit .course__key {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3xs) var(--space-2xs);
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.course__swatch {
  --node: 0.65rem;
  display: inline-block;
}

.course__swatch.is-now {
  box-shadow: 0 0 0 3px var(--blue-wash);
}

.course__tally {
  margin-left: var(--space-2xs);
  font-weight: 700;
  color: var(--ink-soft);
}

.course__readout {
  border-top: 1px solid var(--line);
  padding-top: var(--space-sm);
}

.exhibit .course__readout p {
  margin: 0;
}

.exhibit .course__readout-when {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.exhibit .course__readout-what {
  margin-top: var(--space-3xs);
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.018em;
}

.exhibit .course__readout-where {
  margin-top: var(--space-3xs);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
}

.exhibit .course__readout-note {
  margin-top: var(--space-2xs);
  max-width: 62ch;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink-soft);
}

/* Across the page: seven equal columns, the node above its words, and the rail horizontal. */
@media (min-width: 60rem) {
  .course {
    --node-top: 0.25rem;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }

  .course__entry::before {
    left: 0;
    right: 0;
    top: calc(var(--node-top) + var(--node) / 2);
    bottom: auto;
    width: auto;
    height: 1px;
  }

  .course__entry:first-child::before {
    left: calc(var(--space-2xs) + var(--node) / 2);
  }

  .course__entry:last-child::before {
    right: calc(100% - var(--space-2xs) - var(--node) / 2);
    height: 1px;
  }

  .course__stop {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-3xs);
    height: 100%;
    padding: var(--node-top) var(--space-2xs) var(--space-xs);
  }

  .course__node {
    grid-row: auto;
    justify-self: start;
    margin: 0 0 var(--space-2xs);
  }

  .course__when,
  .course__what,
  .course__flag {
    grid-column: 1;
  }
}

/* The table's last column is a sentence or three, so it reads left-aligned and keeps a readable
   width, scrolling sideways in its own frame on a phone. */
.exhibit__table--prose td {
  min-width: 12rem;
  text-align: left;
}

/* ---- education --------------------------------------------------------------------------------
   Three cards of the same shape a reader has already learned upstairs. */
.credentials {
  display: grid;
  gap: var(--space-lg) var(--space-md);
  margin-top: var(--space-xl);
}

@media (min-width: 52rem) {
  .credentials {
    grid-template-columns: repeat(3, 1fr);
  }
}

.credential__where {
  margin: var(--space-3xs) 0 0;
}

/* ---- skills -----------------------------------------------------------------------------------
   Four groups. The first three are the resume's; AI is split out of Technical, which is a claim
   the copy makes in words as well. */
.skillset {
  display: grid;
  gap: var(--space-lg);
  margin-top: var(--space-xl);
}

@media (min-width: 42rem) {
  .skillset {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-lg) var(--space-xl);
  }
}

@media (min-width: 66rem) {
  .skillset {
    grid-template-columns: repeat(4, 1fr);
  }
}

.skillset__name {
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--ink);
  padding-bottom: var(--space-2xs);
  border-bottom: 2px solid var(--ink);
}

.skillset__list {
  margin-top: var(--space-2xs);
}

.skillset__list li {
  font-size: var(--text-sm);
  color: var(--ink-soft);
  padding-block: var(--space-2xs);
  border-bottom: 1px solid var(--line);
}

/* =================================================================================================
   THE RESEARCH OVERVIEW - two Research Tracks, framed and handed on (CONTEXT.md)

   A Track is a reading column and not a card, because the page's job is to frame two bodies of work
   well enough that each stands on its own and THEN send the reader into the Case Study.

   The standing list is the part that keeps the framing from reading as an advertisement: three
   labelled rows, one of which says what would show the work to be wrong. So it is a <dl> - the
   labels are terms and the rest is their definition - set as a ruled table a reader who skims the
   page still meets.
   ============================================================================================== */
.track {
  max-width: var(--measure);
  border-top: 1px solid var(--line);
  padding-top: var(--space-xl);
  margin-top: var(--space-xl);
}

.track:first-of-type {
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}

.track__name {
  font-size: var(--text-xl);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-2xs);
}

.track p {
  max-width: 68ch;
  color: var(--ink-soft);
  font-size: var(--text-read);
  margin-bottom: var(--space-sm);
}

/* The same shape, twice on one page, deliberately: a Track's standing and the closing list of what
   the page withholds are both a short column of labelled rows that a reader is meant to be able to
   take in without reading the prose around them. Two names rather than one because only the first
   is a Research Track - collect.js harvests `.track__standing` as a fact about a Track, and folding
   the closing list under that name would report it as a third track. */
.track__standing,
.omissions {
  margin: var(--space-md) 0 0;
  border-top: 2px solid var(--ink);
}

.track__standing dt,
.omissions dt {
  color: var(--ink);
  padding-top: var(--space-xs);
}

.track__standing dd,
.omissions dd {
  margin: var(--space-3xs) 0 0;
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--line-soft);
  max-width: 60ch;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

@media (min-width: 40rem) {
  .track__standing,
  .omissions {
    display: grid;
    grid-template-columns: 11rem minmax(0, 1fr);
    column-gap: var(--space-md);
  }

  .track__standing dt,
  .omissions dt {
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--line-soft);
  }

  .track__standing dd,
  .omissions dd {
    margin: 0;
    padding-top: var(--space-xs);
  }
}

/* ---- status board: Exhibit 1 --------------------------------------------------------------------
   Each Research Track is a row of its three standing slots, and each slot names itself, so the
   board reads the same as one column on a phone as it does as three from 48rem. The selected slot
   takes the list's selected state; nothing else is coloured, because no slot is a pass or a fail. */
.board {
  display: grid;
  gap: var(--space-md);
}

.exhibit .board__name {
  margin: 0 0 var(--space-2xs);
  font-size: var(--text-md);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}

.board__slots {
  display: grid;
  gap: var(--space-2xs);
}

.board__slot {
  display: grid;
  align-content: start;
  gap: var(--space-3xs);
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-ring);
  padding: var(--space-xs) var(--space-sm);
  background: var(--paper);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: background-color 200ms var(--ease), border-color 200ms var(--ease);
}

.board__slot:hover {
  background: var(--mist);
}

.board__slot[aria-pressed="true"] {
  border-color: var(--blue);
  background: var(--blue-wash);
}

.board__term {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-mute);
}

.board__slot[aria-pressed="true"] .board__term {
  color: var(--blue-deep);
}

.board__words {
  font-size: var(--text-sm);
  line-height: 1.4;
  overflow-wrap: break-word;
}

.exhibit .board__more {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-sm);
  font-weight: 600;
}

.board__readout {
  border-top: 1px solid var(--line);
  padding-top: var(--space-sm);
}

.exhibit .board__readout p {
  margin: 0;
}

.exhibit .board__readout-what {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.018em;
}

.exhibit .board__readout-full {
  margin-top: var(--space-2xs);
  font-size: var(--text-sm);
}

@media (min-width: 48rem) {
  .board__slots {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* =================================================================================================
   CONTACT CHANNELS, AND THE ROUTES OFF THE 404
   ============================================================================================== */
/* The three ways in, on /contact/. Not folded under `.track__standing` above, for the same reason
   `.omissions` is not: collect.js harvests `.channels` as the contact page's own fact, and one
   class name over two lists would report one of them as the other. */
.channels {
  margin: var(--space-lg) 0 0;
  max-width: 58ch;
  border-top: 2px solid var(--ink);
}

.channels dt {
  color: var(--ink-mute);
  padding-top: var(--space-sm);
}

.channels dd {
  margin: var(--space-3xs) 0 0;
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-md);
  /* An email address and a profile URL are the two strings on this site a reader copies by hand,
     and neither carries a space to break at. Without this, one of them sets the width of the
     column on a 320px screen and the page scrolls sideways. */
  overflow-wrap: anywhere;
}

/* The 404's one job is its four routes, so each reads as a link at rest - in the link colour -
   and not only once a pointer finds it; a touch reader never hovers. */
.route__link {
  color: var(--blue-deep);
  text-decoration: none;
}

/* ---- kind: age-serial ----------------------------------------------------------------------------
   The date-of-birth slider with the floor marked beneath its track, the serial arithmetic as a short
   list, and a strip of the table's age bands. The band the map filed an age in is blue; when that band
   is wrong it turns to the fail colour with a cross and says so, and the band the age belongs in is
   outlined and says so. Never colour alone: every state has its words. Below a readable width the
   strip keeps it and scrolls inside its frame. */
/* In the flow under the track, so a label that wraps on a phone pushes the readout down rather than
   covering it; it wraps inside what is left of the track to its right. */
.exhibit .agecheck__floor {
  margin: 0 0 0 calc(var(--slider-inset) + var(--at) * (100% - 2 * var(--slider-inset)));
  max-width: calc((1 - var(--at)) * (100% - 2 * var(--slider-inset)) + var(--slider-inset));
  padding: var(--space-2xs) 0 0 var(--space-3xs);
  border-left: 2px solid var(--ink);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}

.agecheck__sums {
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3xs) var(--space-sm);
  font-size: var(--text-sm);
}

.agecheck__sums dt {
  color: var(--ink-mute);
}

.agecheck__sums dd[data-result] {
  font-family: var(--font-serif);
  font-size: var(--text-name);
  font-weight: 500;
  line-height: 1;
}

.agecheck__readout.is-hot .agecheck__sums dd[data-result] {
  color: var(--bad);
}

.agecheck__bands {
  display: grid;
  grid-template-columns: repeat(var(--bands), minmax(0, 1fr));
  gap: 2px;
  min-width: calc(var(--bands) * 3rem);
  margin: 0;
  padding: 0 0 var(--space-3xs);
  list-style: none;
  font-size: var(--text-xs);
}

.agecheck__band {
  display: grid;
  grid-template-rows: auto 3.25rem;
  gap: var(--space-3xs);
  margin: 0;
}

.agecheck__band-name {
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums lining-nums;
}

.agecheck__tag {
  display: flex;
  align-items: flex-end;
  padding: var(--space-3xs);
  border: 1px solid var(--line-soft);
  background: var(--paper);
  font-weight: 600;
  line-height: 1.15;
}

.agecheck__band.is-filed .agecheck__tag {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--paper);
}

.agecheck__band.is-wrong .agecheck__tag {
  border-color: var(--bad);
  background: var(--bad);
  color: var(--paper);
}

.agecheck__band.is-belongs .agecheck__tag {
  border: 2px dashed var(--ink);
  color: var(--ink);
}

/* =================================================================================================
   THE RESEARCH: A RANKING WEARING A PROBABILITY'S CLOTHES (kind: ranking)
   One row per company in the sample, in table order with the newcomer last: its name, its reading,
   a track from nought to one with its score as a dot, the score in figures, and a flag. A ring stays
   where a score was before the last company joined, so the move is seen and not only read. Flagged
   is a trouble chip and always carries the word, so the colour is never the only signal. Headers
   wrap between words, never inside one. Below 24rem the tracks and gaps tighten so a whole row, score
   and flag included, fits a 288px column.
   ============================================================================================== */
.rank {
  min-width: 17rem;
}

.rank__head,
.rank__company {
  display: grid;
  grid-template-columns: 4.25rem 3.25rem minmax(4rem, 1fr) 2.75rem 4rem;
  column-gap: var(--space-xs);
  align-items: center;
}

.rank__head {
  padding-bottom: var(--space-3xs);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1.3;
  overflow-wrap: normal;
  color: var(--ink-soft);
}

.rank__head-score {
  grid-column: 3 / -1;
}

@media (max-width: 24rem) {
  .rank__head,
  .rank__company {
    grid-template-columns: 3.5rem 3.25rem minmax(2rem, 1fr) 2.25rem 3.5rem;
    column-gap: var(--space-2xs);
  }
}

.rank__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.rank__company {
  padding: var(--space-3xs) 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--text-sm);
}

.rank__company[hidden] {
  display: none;
}

.rank__company.is-new {
  background: var(--mist);
}

.rank__reading,
.rank__score {
  font-variant-numeric: tabular-nums lining-nums;
  text-align: right;
}

.rank__reading {
  color: var(--ink-mute);
}

.rank__score {
  font-weight: 600;
  color: var(--ink);
}

.rank__track {
  position: relative;
  height: 1.25rem;
  background: linear-gradient(var(--line), var(--line)) center / 100% 1px no-repeat;
}

.rank__dot,
.rank__ghost {
  position: absolute;
  top: 50%;
  width: 0.7rem;
  height: 0.7rem;
  margin: -0.35rem 0 0 -0.35rem;
  border-radius: 50%;
}

.rank__dot {
  background: var(--ink-soft);
  transition: left 400ms var(--ease), background-color 200ms var(--ease);
}

.rank__ghost {
  border: 1.5px solid var(--ink-mute);
  background: var(--paper);
}

.rank__flag {
  justify-self: start;
}

.is-flagged .rank__dot {
  background: var(--data-4);
}

/* =================================================================================================
   THE RESEARCH: BREAKS WHERE NOTHING HAPPENED (kind: placebo-break)
   The series as points, the quarters after the break washed, the break as a dashed rule, and the
   fitted specification in the first data colour on top. The chart is an SVG scaled to its frame, so it fits any
   width; its only words are HTML around it. Beneath, every tested date: a filled mark where it came
   back significant and a hollow one where it did not, so the shape carries the verdict and the
   colour never does alone. The date in hand is outlined, and written above the slider. The
   readout's p is not held to the Spine's reading measure.
   ============================================================================================== */
.placebo__pick {
  flex: 1 1 12rem;
}

.exhibit .placebo__caption {
  max-width: none;
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums lining-nums;
}

.placebo__caption b {
  color: var(--ink);
}

.exhibit .placebo__heading,
.exhibit .placebo__axis {
  max-width: none;
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.exhibit .placebo__heading {
  font-weight: 700;
  color: var(--ink-soft);
}

.placebo__plot {
  display: block;
  width: 100%;
  height: auto;
}

.placebo__after {
  fill: var(--mist);
}

.placebo__point {
  fill: var(--ink-mute);
}

.placebo__fit {
  stroke: var(--data-1);
  stroke-width: 3;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.placebo__break {
  stroke: var(--ink);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
  vector-effect: non-scaling-stroke;
}

.exhibit .placebo__axis {
  display: flex;
  justify-content: space-between;
}

.placebo__dates {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-xs);
  margin: var(--space-xs) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink-soft);
}

.placebo__date {
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--chip-pad);
  border: 1px solid transparent;
  border-radius: var(--pill);
}

.placebo__date.is-current {
  border-color: var(--ink);
  color: var(--ink);
}

.placebo__mark {
  width: 0.6rem;
  height: 0.6rem;
  border: 1.5px solid var(--data-4);
  border-radius: 50%;
}

.is-significant .placebo__mark {
  background: var(--data-4);
}

.exhibit .placebo__at {
  max-width: none;
  margin: var(--space-3xs) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums lining-nums;
}

.placebo__at b {
  color: var(--ink);
}

.placebo__t {
  white-space: nowrap;
}

/* =================================================================================================
   MACROSENSE
   ============================================================================================== */

/* ---- The Exhibits' small print ------------------------------------------------------------------
   A control's label, an axis label, a day, a lane name or a step's number: small, bold and muted,
   the same in MacroSense's pipeline, regime vector, decision window and scheduler's week, and in
   TradeLog's Decision Review Cycle, Recorded and Reported Adherence, Versioned Investment Rules
   and Read-Only Broker Integration. */
.regime__ctl,
.dwin__label,
.sched__ask,
.sched__day,
.sched__job,
.cycle__ask,
.cycle__num,
.pipe__term,
.gap__ask,
.gap__day,
.gap__cat,
.gap__key,
.vers__ask,
.integ__ask,
.integ__name {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
}

/* ---- regime-vector -------------------------------------------------------------------------------
   One bar per regime, each a button that makes it the leading regime, so the whole vector is the
   control and not only its winner. Each regime keeps its data colour wherever it ranks, and every
   bar carries its name and its probability in words beside it, so no reading rests on colour. The
   bars' widths ease between calls, and the reduced-motion blanket collapses that. */
.regime {
  --regime-end: 0 4px 4px 0;
  display: grid;
  gap: var(--space-3xs);
}

.regime__bar {
  display: grid;
  grid-template-columns: minmax(6.5rem, 9rem) minmax(0, 1fr) 2.75rem;
  gap: var(--space-xs);
  align-items: center;
  min-height: 2.75rem;
  padding: 0 var(--space-3xs);
  border: 0;
  border-radius: var(--radius-ring);
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}

.regime__bar:hover .regime__name,
.regime__bar:focus-visible .regime__name {
  color: var(--blue-deep);
}

.regime__bar[aria-pressed="true"] .regime__name {
  font-weight: 700;
}

.regime__bar[aria-pressed="true"] .regime__name::after {
  content: " \2014 lead";
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--ink-mute);
}

.regime__track {
  position: relative;
  height: 1rem;
  border-radius: var(--regime-end);
  background: var(--line-soft);
}

.regime__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--regime-end);
  background: var(--data-1);
  transition: width 420ms var(--ease);
}

.regime__bar:nth-child(2) .regime__fill { background: var(--data-2); }
.regime__bar:nth-child(3) .regime__fill { background: var(--data-3); }
.regime__bar:nth-child(4) .regime__fill { background: var(--data-4); }

.regime__val {
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* The slider row takes .exhibit__controls' layout; the slider fills what the two ends leave. */
.regime__ctl .slider {
  flex: 1 1 8rem;
}

.exhibit .regime__readout p {
  margin: 0;
}

.exhibit .regime__read {
  font-family: var(--font-serif);
  font-size: var(--text-md);
  line-height: 1.35;
  min-height: 2.7em;
  color: var(--ink);
}

.exhibit .regime__readout .regime__cons {
  margin-top: var(--space-2xs);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink-soft);
}

/* ---- decision-window -----------------------------------------------------------------------------
   Two tiers: a long axis for the several years a figure would need, with the whole history a sliver
   at its right end, and that sliver enlarged beneath it, one tick per trading day. A decision is a
   taller tick in the first data colour; the count beneath says it in words, so no reading rests on
   colour. The ticks ease between frequencies, and the reduced-motion blanket collapses that. */
.dwin__years,
.dwin__zoom {
  display: grid;
  gap: var(--space-3xs);
}

.dwin__label {
  margin: 0;
}

.dwin__label--end {
  text-align: right;
}

.dwin__axis {
  position: relative;
  height: 0.75rem;
  border-radius: var(--radius-ring);
  background: var(--line-soft);
}

/* The window, at the end of the axis. */
.dwin__sliver {
  position: absolute;
  inset: 0 0 0 auto;
  width: 4px;
  background: var(--data-1);
}

.dwin__zoom {
  padding: var(--space-2xs);
  border: 1px dashed var(--data-1);
  border-radius: var(--radius-ring);
}

.dwin__days {
  display: flex;
  align-items: flex-end;
  gap: 1px;
  height: 2.5rem;
}

/* Past a hundred ticks the 1px gaps would outnumber the pixels a 320px stage has for them. */
.dwin__days--dense {
  gap: 0;
}

.dwin__day {
  flex: 1 1 0;
  height: 30%;
  background: var(--line);
  transition: height 200ms var(--ease), background-color 200ms var(--ease);
}

.dwin__day.is-decision {
  height: 100%;
  background: var(--data-1);
}

/* ---- scheduler-week ------------------------------------------------------------------------------
   A week as a strip: a lane per job, a column per day, Monday first. A run is a filled block in the
   first data colour, and the strip's aria-label and the sentence below say the same in words. The
   weekend, when no trading day falls, is hatched and its day names are fainter; the day the month
   turns on is outlined. Lane labels wrap, and the kind refuses a word too long for one, so seven
   days fit beside them at 320px. The hatch here is drawn at 3:1 against the cells, where the site's
   is a faint texture: the weekend is a fact this drawing turns on, not a background. */
.sched {
  --hatch: color-mix(in srgb, var(--ink) 50%, transparent);
  display: grid;
  grid-template-columns: minmax(0, 6.5rem) repeat(7, minmax(0, 1fr));
  gap: 2px;
  align-items: stretch;
}

.sched__day {
  padding-bottom: var(--space-3xs);
  text-align: center;
  border-bottom: 2px solid transparent;
}

.sched__day.is-shut {
  color: var(--ink-faint);
}

.sched__day.is-first {
  color: var(--ink);
  border-bottom-color: var(--data-1);
}

.sched__job {
  align-self: center;
  padding-right: var(--space-2xs);
  line-height: 1.25;
}

.sched__cell {
  position: relative;
  min-height: 1.75rem;
  border-radius: var(--radius-ring);
  background: var(--mist);
}

.sched__cell.is-shut {
  background: repeating-linear-gradient(135deg, var(--hatch) 0 1.5px, transparent 1.5px 6px), var(--mist);
}

.sched__cell.is-first {
  box-shadow: inset 0 0 0 1px var(--data-1);
}

.sched__cell.is-run::after {
  content: "";
  position: absolute;
  inset: 25%;
  border-radius: var(--radius-ring);
  background: var(--data-1);
}

.exhibit .sched__read {
  margin: 0;
  font-size: var(--text-base);
  color: var(--ink);
}

/* On a phone the lane labels wrap into a narrower column, so each day keeps room for its name; the
   columns close up, and the lanes keep a gap and a height of their own so a wrapped label still
   reads as one row. */
@media (max-width: 30rem) {
  .sched {
    grid-template-columns: fit-content(4.25rem) repeat(7, minmax(0, 1fr));
    gap: var(--space-2xs) 1px;
  }

  .sched__cell {
    min-height: 2.5rem;
  }
}

/* ---- kind: radius-map ----------------------------------------------------------------------------
   Controls above; the map beside its totals from 40rem, stacked below. The map keeps its table's
   proportions, and its locations are buttons placed by percentage over the drawing. */
.rmap__control {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: center;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink);
}

.rmap__sizer {
  flex: 1 1 14rem;
}

.rmap__sizer .slider {
  flex: 1;
}

.rmap__control-value {
  font-weight: 600;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.rmap {
  display: grid;
  gap: var(--space-sm);
}

@media (min-width: 40rem) {
  .rmap {
    grid-template-columns: minmax(0, 1.5fr) minmax(12rem, 1fr);
  }
}

.rmap__map {
  position: relative;
  aspect-ratio: var(--rmap-ratio);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--mist-deep);
  cursor: crosshair;
}

.rmap__map svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The map's strokes are in screen pixels (vector-effect: non-scaling-stroke), so a narrow map draws
   them no thinner. */
.rmap__river {
  fill: none;
  stroke: var(--blue-wash);
  stroke-width: 12;
  stroke-linecap: round;
}

.rmap__road {
  fill: none;
  stroke: var(--paper);
  stroke-width: 4;
}

.rmap__ring {
  fill: color-mix(in srgb, var(--blue) 8%, transparent);
  stroke: var(--blue);
  stroke-width: 2;
  stroke-dasharray: 5 4;
}

.rmap__pin {
  fill: var(--blue);
}

.rmap__area,
.rmap__hint,
.rmap__spot {
  position: absolute;
  left: var(--x);
  top: var(--y);
}

/* An Area's name sits over the drawing, so the circle can pass under it. A halo in the map's own
   ground knocks the ring out behind the letters, as the Gate's values do with a paper stroke; a
   text-shadow rather than paint-order, which HTML text does not take in every browser. */
.rmap__area {
  transform: translate(-50%, -100%);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-mute);
  white-space: nowrap;
  pointer-events: none;
  text-shadow: 0 0 2px var(--mist-deep), 0 0 2px var(--mist-deep), 0 0 4px var(--mist-deep);
}

.rmap__hint {
  left: var(--space-xs);
  top: var(--space-xs);
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--pill);
  background: var(--paper);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-soft);
  pointer-events: none;
}

/* A location: a 24px target around a small dot, coloured by its net - rose for a loss, blue for a
   gain, stronger the larger it is - and faded when it sits outside the circle. The target is the
   button; the dot is drawn inside it, so the hit area is larger than what it shows. */
.rmap__spot {
  --dot-size: var(--space-2xs);
  width: var(--space-md);
  height: var(--space-md);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  transform: translate(-50%, -50%);
  cursor: pointer;
}

.rmap__spot::before {
  content: "";
  position: absolute;
  inset: calc(50% - var(--dot-size) / 2);
  border: 1px solid var(--paper);
  border-radius: 50%;
  background: color-mix(in srgb, var(--dot) calc(var(--k) * 100%), var(--line));
  opacity: 0.4;
  transition: inset 200ms var(--ease), opacity 200ms var(--ease);
}

.rmap__spot.is-gain {
  --dot: var(--blue);
}

.rmap__spot.is-loss {
  --dot: var(--data-4);
}

.rmap__spot.is-inside::before {
  --dot-size: var(--space-xs);
  opacity: 1;
}

/* The map clips to its rounded corners, so a location's focus ring is drawn inside its target. */
.rmap__spot:focus-visible {
  outline-offset: -2px;
}

.rmap__spot.is-shown::before {
  box-shadow: 0 0 0 2px var(--ink);
}

.rmap__panel {
  display: grid;
  gap: var(--space-xs);
  align-content: start;
  font-size: var(--text-sm);
}

.rmap__sums,
.rmap__tip-rows {
  grid-template-columns: minmax(0, 1fr) auto;
}

.rmap__sums dt,
.rmap__sums dd {
  padding-block: var(--space-3xs);
  border-bottom: 1px solid var(--line-soft);
}

.rmap__sums dt,
.rmap__tip-rows dt {
  color: var(--ink-soft);
}

.rmap__sums dd,
.rmap__tip-rows dd {
  text-align: right;
  font-weight: 600;
}

.rmap__sums .rmap__where {
  grid-column: 1 / -1;
  border-bottom: 0;
  padding-block: 0;
  text-align: left;
}

.rmap__sums dt.rmap__where {
  font-size: var(--text-xs);
}

.rmap__sums dd.rmap__count {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: 500;
  line-height: 1;
}

.rmap__sums .rmap__net {
  border-bottom: 2px solid var(--ink);
  font-size: var(--text-md);
}

.rmap__sums dd.rmap__net {
  color: var(--blue-deep);
}

.rmap__label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink);
}

.rmap__ages {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-3xs) var(--space-2xs);
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-soft);
}

.rmap__ages dd {
  display: flex;
  gap: var(--space-2xs);
  align-items: center;
  margin: 0;
}

.rmap__bar {
  flex: 1;
  height: var(--space-2xs);
  background: var(--line-soft);
}

.rmap__bar i {
  display: block;
  height: 100%;
  background: var(--blue);
  transition: width 380ms var(--ease);
}

.rmap__ages b {
  min-width: 2ch;
  text-align: right;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.rmap__tip {
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius);
  background: var(--mist);
}

.rmap__tip-name {
  margin-bottom: var(--space-3xs);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
}

/* ---- kind: judges-rewarded -----------------------------------------------------------------------
   Two structures for one paper, side by side where there is room and stacked on a phone. Each part is
   a line of words (its name, its minutes, its verdict) over a bar on one shared axis of minutes, and
   the judge's time is an ink tick at the same place on every bar. A part the time runs out on is hatched and says
   "Not checked"; the verdict is always in words, never the fill alone. Everything reflows, so no
   frame is needed. */
.exhibit .judged__paper {
  margin: 0;
  max-width: none;
  font-size: var(--text-sm);
  color: var(--ink-mute);
}

.judged {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: var(--space-md);
}

.judged__structure {
  display: grid;
  align-content: start;
  gap: var(--space-2xs);
  min-width: 0;
}

.exhibit .judged__name {
  margin: 0;
  font-weight: 700;
  color: var(--ink);
}

.exhibit .judged__total {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.judged__parts {
  display: grid;
  gap: var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.judged__part {
  display: grid;
  gap: var(--space-3xs);
  font-size: var(--text-xs);
}

.judged__head {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-2xs);
  align-items: baseline;
}

.judged__label {
  flex: 1 1 10rem;
  min-width: 0;
  color: var(--ink);
  overflow-wrap: break-word;
}

.judged__minutes {
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.judged__verdict {
  font-weight: 700;
  color: var(--ink);
}

.is-unchecked .judged__verdict {
  font-weight: 600;
  color: var(--ink-mute);
}

.judged__track {
  position: relative;
  height: 0.625rem;
  border-radius: var(--radius-ring);
  background: var(--line-soft);
}

.judged__span {
  position: absolute;
  inset-block: 0;
  border-radius: var(--radius-ring);
  background: var(--data-1);
  box-shadow: inset -1px 0 0 var(--paper);
}

.is-unchecked .judged__span {
  background: repeating-linear-gradient(-45deg, var(--hatch) 0 4px, transparent 4px 8px), var(--mist-deep);
  outline: 1px dashed var(--ink-mute);
  outline-offset: -1px;
}

/* The judge's time. Placed inside its bar's track and drawn over it, so at either end of the slider it
   stays within the track's own width. */
.judged__edge {
  --tick-width: 2px;
  --tick-overhang: 3px;
  position: absolute;
  inset-block: calc(-1 * var(--tick-overhang));
  width: var(--tick-width);
  /* Its right edge on the minute, so at the far end it ends where the track does. */
  margin-left: calc(-1 * var(--tick-width));
  background: var(--ink);
}

/* ---- review-cycle --------------------------------------------------------------------------------
   The Decision Review Cycle as a square read clockwise: step 1 top left, 2 top right, 3 bottom right,
   4 bottom left, an arrow in each gap and the fourth pointing back up to the first. Two columns at
   every width, so the square stays a cycle on a phone; the kind refuses a word too long for a corner
   at 320px. The chosen step is washed and outlined in the first data colour. The arrows are drawn
   by the stylesheet inside the drawing, whose aria-label says the same in words. The control's label
   and each step's number are the Exhibits' small print, set with MacroSense's above. */
.cycle {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-areas: "one two" "four three";
  gap: var(--space-lg);
  max-width: 36rem;
}

.cycle__step {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--space-3xs);
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--mist);
  transition: background-color 200ms var(--ease), border-color 200ms var(--ease);
}

.cycle__step:nth-child(1) { grid-area: one; }
.cycle__step:nth-child(2) { grid-area: two; }
.cycle__step:nth-child(3) { grid-area: three; }
.cycle__step:nth-child(4) { grid-area: four; }

.cycle__step.is-on {
  border-color: var(--data-1);
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 1px var(--data-1);
}

.cycle__name {
  font-family: var(--font-serif);
  font-size: var(--text-md);
  line-height: 1.2;
  color: var(--ink);
}

.cycle__by {
  font-size: var(--text-xs);
  line-height: 1.35;
  color: var(--ink-soft);
}

.cycle__step::after {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-md);
  line-height: 1;
  color: var(--ink-faint);
}

.cycle__step:nth-child(1)::after,
.cycle__step:nth-child(3)::after {
  top: 50%;
  width: var(--space-lg);
  transform: translateY(-50%);
}

.cycle__step:nth-child(2)::after,
.cycle__step:nth-child(4)::after {
  left: 50%;
  height: var(--space-lg);
  transform: translateX(-50%);
}

.cycle__step:nth-child(1)::after { content: "\2192"; left: 100%; }
.cycle__step:nth-child(2)::after { content: "\2193"; top: 100%; }
.cycle__step:nth-child(3)::after { content: "\2190"; right: 100%; }
.cycle__step:nth-child(4)::after { content: "\2191"; bottom: 100%; }

/* The readout of every TradeLog Exhibit: what the reader's choice produced, in plain words. */
.cycle__read,
.gap__read,
.vers__read,
.integ__read {
  max-width: var(--measure);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--ink);
}

.cycle__read {
  display: grid;
  gap: var(--space-3xs);
}

.cycle__where {
  font-weight: 600;
}

.cycle__carried {
  color: var(--ink-soft);
}

@media (max-width: 30rem) {
  .cycle {
    gap: var(--space-md);
  }

  .cycle__step {
    padding: var(--space-2xs) var(--space-xs);
  }

  .cycle__name {
    font-size: var(--text-base);
  }

  .cycle__step:nth-child(1)::after,
  .cycle__step:nth-child(3)::after {
    width: var(--space-md);
  }

  .cycle__step:nth-child(2)::after,
  .cycle__step:nth-child(4)::after {
    height: var(--space-md);
  }
}

/* =================================================================================================
   THE RESEARCH: A PROMPT IS A REQUEST (kind: edit-intensity)
   The prompt once, then the ladder: three rungs, lightest first, each holding the revisions its
   measures put there as plain chips; the revision in hand is filled in ink. Beneath the readout,
   that revision as running text, the words it struck from the original crossed through and the
   words it added underlined, so the change is read, not only counted. Divs, not p, so the Spine's
   reading measure does not clip them.
   ============================================================================================== */
.intensity__prompt {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.intensity__prompt q {
  display: block;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--ink);
}

.intensity__ladder {
  margin: 0 0 var(--space-sm);
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.intensity__rung {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  align-items: center;
  gap: var(--space-xs);
  min-height: 2.25rem;
  padding: var(--space-2xs) 0;
  border-bottom: 1px solid var(--line);
}

.intensity__rung-name {
  font-size: var(--text-sm);
  font-weight: 600;
}

.intensity__members {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}

.intensity__member.is-current {
  background: var(--ink);
  box-shadow: none;
  color: var(--paper);
}

.intensity__detail {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2xs) var(--space-sm);
}

.intensity__measure {
  font-variant-numeric: tabular-nums;
}

.intensity__text {
  margin: var(--space-sm) 0 var(--space-2xs);
  padding: var(--space-sm);
  border-left: 2px solid var(--line);
  background: var(--mist);
}

.intensity__text del {
  color: var(--ink-soft);
}

.intensity__text ins {
  text-decoration: underline;
  text-decoration-color: var(--data-1);
  text-decoration-thickness: 0.125em;
  text-underline-offset: 0.22em;
}

/* ---- gate: The Research's pre-registered pass bar, and its homepage copy -------------------------
   The Detectability Curve is drawn in its own units and scales to the frame; under a readable width
   the frame scrolls sideways. Each point is the shared slider on a mark, so it alone claims a
   touch; the plot leaves panning to the page. 35rem is the drawing's own width (560 units), so on a
   phone its words stand at their set size rather than shrinking below legibility. */
.gate__plot {
  display: block;
  width: 100%;
  min-width: 35rem;
  height: auto;
  font-family: var(--font-sans);
}

.gate__grid {
  stroke: var(--line);
}

.gate__base {
  stroke: var(--ink);
}

.gate__tick,
.gate__axis-name,
.gate__chance-name {
  font-size: 13px;
  fill: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.gate__axis-name {
  font-weight: 700;
  fill: var(--ink-soft);
}

.gate__x-name {
  margin-top: var(--space-3xs);
  font-size: var(--text-xs);
  color: var(--ink-mute);
  text-align: center;
}

.gate__zone {
  fill: var(--blue-wash);
}

.gate__bar {
  stroke: var(--blue);
  stroke-width: 2.5;
}

/* A halo in the paper's colour, as on the points' values, so the curve never strikes it through. */
.gate__bar-name {
  font-size: 13px;
  font-weight: 700;
  fill: var(--blue-deep);
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 4px;
}

.gate__chance {
  stroke: var(--ink-mute);
  stroke-dasharray: 4 4;
}

.gate__rung {
  font-size: 14px;
  font-weight: 600;
  fill: var(--ink);
}

.gate__rung--gate {
  font-weight: 700;
}

.gate__curve {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linejoin: round;
}

.gate__handle {
  cursor: ns-resize;
  touch-action: none;
}

/* The point's own ring stands for the focus outline, which an SVG group draws as a box. */
.gate__handle:focus-visible {
  outline: none;
}

.gate__ring {
  fill: none;
  stroke: none;
}

.gate__handle:focus-visible .gate__ring {
  stroke: var(--blue);
  stroke-width: 2;
}

.gate__hit {
  fill: transparent;
}

.gate__mark {
  fill: var(--paper);
  stroke: var(--ink);
  stroke-width: 2.5;
}

.gate__mark--pass {
  stroke: var(--good);
}

.gate__mark--fail {
  stroke: var(--bad);
}

.gate__value {
  font-size: 13px;
  font-weight: 700;
  fill: var(--ink);
  font-variant-numeric: tabular-nums;
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 4px;
}

.gate__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-sm);
}

@media (min-width: 36rem) {
  .exhibit:not([data-compact]) .gate__row {
    grid-template-columns: minmax(16rem, 1fr) minmax(0, 1fr);
    align-items: center;
  }
}

/* The rate's name and value share a line where the name fits on one; where it does not, the value
   takes a line of its own rather than sitting beside a wrapped name. */
.gate__fpr-name {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  column-gap: var(--space-sm);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
}

.gate__fpr-value {
  font-variant-numeric: tabular-nums;
}

.gate__verdict {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  font-weight: 700;
  transition: background-color 220ms var(--ease), color 220ms var(--ease);
}

.gate__verdict svg {
  display: block;
  width: 1.1rem;
  height: 1.1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.gate__verdict svg circle {
  fill: currentColor;
  fill-opacity: 0.15;
  stroke: none;
}

.gate__verdict--pass {
  background: var(--good-wash);
  color: var(--good-ink);
}

.gate__verdict--fail {
  background: var(--bad-wash);
  color: var(--bad-ink);
}

.gate__why {
  margin-top: var(--space-3xs);
  min-height: 2.6em;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

/* =================================================================================================
   MACROSENSE: THE PIPELINE
   The components stand in the slots of Jacob's sketch on a sheet of fixed proportions, the wires
   behind them in one SVG in the sheet's own units, and the chosen component's input and output
   below. The sheet keeps a readable minimum width and scrolls inside its frame on a phone.
   ============================================================================================== */
.pipe__controls {
  margin-top: var(--space-xs);
}

/* 38rem holds "Portfolio optimizer" on two lines inside its box at the small print's size. */
.pipe {
  min-width: 38rem;
  padding-block: var(--space-sm);
}

.pipe__head {
  margin-bottom: var(--space-2xs);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-mute);
}

.pipe__hint {
  margin-top: var(--space-3xs);
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.pipe__sheet {
  position: relative;
}

/* Visible overflow: the Scheduler's ring, spreading from the top edge, is not cut at the sheet. */
.pipe__wires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.pipe__wire {
  fill: none;
  stroke: var(--ink-mute);
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}

.pipe__arrow {
  fill: var(--ink-mute);
}

.pipe__packet {
  fill: var(--data-1);
}

.pipe__pulse {
  fill: none;
  stroke: var(--data-1);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}

/* The components are one segmented group (segmented() with its own class), each option placed
   by the script in its slot, in percent of the sheet. */
.pipe__board {
  position: absolute;
  inset: 0;
}

.pipe__board .seg__option {
  position: absolute;
  display: grid;
  place-items: center;
  padding: var(--space-3xs);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  font-size: var(--text-sm);
  line-height: 1.2;
  text-align: center;
  transition: background-color 200ms var(--ease), border-color 200ms var(--ease);
}

/* Lit: the flow has just reached it. Chosen: its input and output are the ones read out below. */
.pipe__board .seg__option.is-lit,
.pipe__board .seg__option[aria-pressed="true"] {
  background: var(--blue-wash);
  border-color: var(--data-1);
}

.pipe__board .seg__option[aria-pressed="true"] {
  box-shadow: 0 0 0 1px var(--data-1);
}

/* Under reduced motion, each component's step in the order, on its top-left corner. */
.pipe__step {
  position: absolute;
  display: grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  translate: -40% -40%;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.pipe__io {
  margin-top: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  background: var(--mist);
  font-size: var(--text-sm);
}

.pipe__name {
  margin-bottom: var(--space-2xs);
  font-weight: 700;
}

.pipe__dl {
  display: grid;
  gap: var(--space-3xs) var(--space-md);
  margin: 0;
}

@media (min-width: 36rem) {
  .pipe__dl {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto;
    grid-auto-flow: column;
  }
}

.pipe__said {
  margin: 0;
  color: var(--ink-soft);
}

/* ---- adherence-gap -------------------------------------------------------------------------------
   Recorded and Reported Adherence: a Rule to a row and a day to a column, each cell split into the
   recorded grade (left) and the reported one (right), each a tick or a cross on its own tone, so no
   grade rests on colour. A cell whose two grades disagree is ringed in the trouble colour. Combined,
   the grades and rings fade to plain mist, and one count replaces the two above the grid. The kind
   refuses more days than fit across a phone, and the grid sits in the shared frame. The control's
   label, the days, the categories and the key are the Exhibits' small print, set with those above. */
.gap__tally {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-sm);
}

.gap__count {
  display: grid;
  flex: 1 1 8rem;
  align-content: start;
  gap: var(--space-3xs);
  max-width: 14rem;
}

.gap__count[hidden],
.gap__key[hidden] {
  display: none;
}

.gap__figure {
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  font-weight: 400;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.gap__name {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
}

.gap__words {
  font-size: var(--text-xs);
  line-height: 1.35;
  color: var(--ink-soft);
}

.gap {
  display: grid;
  padding: var(--radius-ring);
  grid-template-columns: minmax(4.5rem, auto) repeat(var(--gap-days), minmax(2.25rem, 1fr));
  align-items: center;
  gap: var(--space-3xs);
  max-width: 36rem;
}

.gap__day {
  text-align: center;
}

.gap__rule {
  display: grid;
  line-height: 1.2;
  font-size: var(--text-sm);
  color: var(--ink);
}

.gap__cell {
  display: flex;
  height: 2.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: box-shadow var(--settle) var(--ease), border-color var(--settle) var(--ease);
}

.gap__half {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xs);
  font-weight: 700;
  transition: background-color var(--settle) var(--ease), color var(--settle) var(--ease);
}

.gap__half--rec {
  border-right: 1px solid var(--line);
}

.gap__half.is-kept {
  background: var(--blue-wash);
  color: var(--data-1);
}

.gap__half.is-broken {
  background: repeating-linear-gradient(-45deg, var(--hatch) 0 4px, transparent 4px 8px),
    var(--trouble-wash);
  color: var(--trouble-ink);
}

/* The ring a TradeLog Exhibit draws round what disagrees or was rewritten: the trouble colour, in
   a mark the readout also names. */
.gap__cell.is-gap,
.vers__day.is-rewritten {
  border-color: var(--data-4);
  box-shadow: 0 0 0 var(--radius-ring) var(--data-4);
}

.gap.is-combined .gap__half {
  background: var(--mist);
  color: transparent;
}

.gap.is-combined .gap__half--rec {
  border-right-color: transparent;
}

.gap__key {
  max-width: var(--measure);
  margin-top: var(--space-sm);
}

/* ---- versioned-rules -----------------------------------------------------------------------------
   Versioned Investment Rules: the week as a ledger, a day to a row, each with the version it was
   recorded under in a chip and its grade as a tick or a cross with its word, so no grade rests on
   colour. A dashed rule between two days marks the revision. Edited in place, the chips go plain,
   the rule goes solid in the trouble colour, and a day whose grade the edit changed is ringed, with
   the grade it had struck through on a line of its own. The control's label is the Exhibits' small
   print, set with those above. */
.vers {
  display: grid;
  gap: var(--space-3xs);
  max-width: 36rem;
  padding: var(--radius-ring);
}

.vers__day {
  display: grid;
  grid-template-columns: 5.75rem auto 1fr;
  align-items: center;
  gap: var(--space-3xs) var(--space-sm);
  padding: var(--space-2xs) var(--space-sm);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: box-shadow var(--settle) var(--ease), border-color var(--settle) var(--ease);
}

.vers__name {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
}

.vers__tag {
  justify-self: start;
  background: var(--blue-wash);
  color: var(--blue-deep);
}

.vers__tag.chip--plain {
  background: transparent;
  color: var(--ink-mute);
}

.vers__grade {
  font-size: var(--text-sm);
  font-weight: 600;
  transition: color var(--settle) var(--ease);
}

.vers__grade.is-kept {
  color: var(--data-1);
}

.vers__grade.is-broken {
  color: var(--trouble-ink);
}

.vers__was {
  grid-column: 2 / -1;
  font-size: var(--text-xs);
  color: var(--ink-mute);
}

.vers__was:empty {
  display: none;
}

.vers__cut {
  padding: var(--space-2xs) 0;
  border-top: 2px dashed var(--blue);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--blue-deep);
}

.vers__cut.is-edited {
  border-top-style: solid;
  border-top-color: var(--data-4);
  color: var(--trouble-ink);
}

/* ---- broker-integration --------------------------------------------------------------------------
   TradeLog's Read-Only Broker Integration: a path to a row, the built one whole, each path not built
   struck through with its failure modes struck under it. A failed connection breaks the built path
   and puts a stale notice above everything, in the trouble colour and in words, so the warning never
   rests on colour. The control's label and each path's name are the Exhibits' small print. */
.integ {
  display: grid;
  gap: var(--space-2xs);
  max-width: 36rem;
}

.integ__notice {
  padding: var(--space-xs) var(--space-sm);
  border-left: 4px solid var(--data-4);
  background: var(--trouble-wash);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--trouble-ink);
}

.integ__path {
  display: grid;
  gap: var(--space-3xs);
  padding: var(--space-2xs) var(--space-sm);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.integ__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink);
}

.integ__end {
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--radius);
  background: var(--blue-wash);
  color: var(--blue-deep);
}

.integ__arrow {
  flex: 1 1 4rem;
  border-bottom: 2px solid var(--blue);
  font-size: var(--text-xs);
  text-align: center;
  color: var(--blue-deep);
  transition: color var(--settle) var(--ease), border-color var(--settle) var(--ease);
}

.integ__path.is-failed .integ__arrow {
  border-bottom-style: dashed;
  border-bottom-color: var(--data-4);
  color: var(--trouble-ink);
}

.integ__path.is-struck .integ__line {
  text-decoration: line-through;
  color: var(--ink-mute);
}

.integ__path.is-struck .integ__end {
  background: var(--hatch);
  color: var(--ink-mute);
}

.integ__path.is-struck .integ__arrow {
  border-bottom-style: dashed;
  border-bottom-color: var(--ink-mute);
  color: var(--ink-mute);
}

.integ__modes {
  display: grid;
  gap: var(--space-3xs);
  padding-left: var(--space-lg);
}

.integ__mode {
  font-size: var(--text-xs);
  color: var(--ink-mute);
}
