/* home.css -- Components that exist only on the home page and are deliberately not reused.
 *
 * Part 9 of 9. These files were one style.css and are still one
 * stylesheet: equal-specificity rules resolve by document order, so the
 * <link> tags in every page must stay in this order and nothing may move
 * between files without checking what it lands after.
 *   tokens.css -> layout.css -> type.css -> prose.css -> charts.css -> sources.css -> paper.css -> interactive.css -> home.css
 *
 * See site/README.md for the rules that govern all of them. */

/* --- The home page -------------------------------------------------------- */

/* Four components, all of which exist only here. The home page is the one page
 * on this site with no chart and no continuous argument: it states the size of
 * the study, three findings, what the study does not establish, and where to
 * go. Everything below serves that and is deliberately not reused elsewhere --
 * a "card" that appears on one page is a layout, and a card that appears on
 * four becomes a design system nobody asked for. */

/* The same two accents the argument uses, and for the same reason: green is the
 * site, the clay means "this is what the study does not establish". The blue and
 * the violet that used to open this page bought a change of color per section
 * and cost the clay its meaning -- one warning hue among four decorative ones is
 * not a warning. */
/* The front page's limits section moved to research.html#limits (2026-08-18);
 * that page's own rules color it. */

/* The one figure the page leads on. A hero number rather than a chart: a single
 * quantity with nothing to compare it against has no shape to plot, and a bar
 * of one bar is a number wearing a costume.
 *
 * Left, not centerd. Everything else on this site sits against one left edge --
 * every heading, every paragraph, every figure and every caption -- and this was
 * the single object that stepped out of that column to be admired in the middle
 * of it. A centerd block with a giant number in it is the shape of a landing
 * page, and the arrangement it interrupted is the shape of a document. */
.bignum {
  margin: var(--space-5) 0;
}

.bignum__lead,
.bignum__unit {
  margin: 0;
  color: var(--text-primary);
  font-size: var(--size-3);
}

/* The top of the scale, which is where the page's title is -- and on this page
 * that is the right size for this number, because the h1 above it ("there is a
 * city under your field") and this figure are the same claim said twice. It used
 * to run to 5.5rem, near 99px: two and a half times the headline, which made the
 * headline the caption of the number rather than the other way round. */
.bignum__value {
  margin: var(--space-1) 0 var(--space-2);
  font-size: var(--size-6);
  line-height: var(--lh-tight);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

/* The highlighter wash under a data slot is sized for a number inside a
 * sentence. At display size it becomes a band across the figure, so the slot
 * keeps its weight and loses the marking -- the size is doing that job here. */
.bignum__value b[data-stat] {
  background-image: none;
  font-weight: 600;
}

/* The `.tiles` strip that stood here -- four bordered cells, each a large figure
 * over a small gray label, holding the plant count, the marker count, the read
 * depth and "one pasture, sampled once" -- is gone, and the four facts are a
 * sentence in index.html instead.
 *
 * The argument for it was that burying the size of the study in prose is how
 * thirteen cores get read as a hundred. That argument was right about the danger
 * and wrong about the remedy: the strip is the stock statistics component of
 * every generated landing page, and dressing a small honest study in it made the
 * study look inflated rather than the numbers look plain. A sentence naming all
 * four in a row is harder to skim past than four boxes, not easier. */

/* Three findings, each a heading and a paragraph. Not a list: each one is a
 * claim with its own evidence, and list markers would imply an order of
 * importance the page does not intend. */
.finding + .finding { margin-top: var(--space-5); }

.finding h3 {
  font: 600 var(--size-3)/var(--lh-snug) var(--font-display);
  color: var(--text-primary);
  margin-bottom: var(--space-2);
  /* Headings elsewhere balance their lines; here that broke a two-line
   * heading early and left the first line short. Fill the line instead, and
   * hang the wrap under the text rather than under the dot. */
  text-wrap: pretty;
  padding-left: calc(0.5rem + var(--space-2));
  text-indent: calc(-1 * (0.5rem + var(--space-2)));
}

.finding h3::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-round);
  background: var(--accent);
  margin-right: var(--space-2);
  vertical-align: 0.08em;
}

/* What the study does not establish, at the same weight as what it does. The
 * left rule is the section's accent, which here is the red already used for the
 * limits section of the argument -- so the two pages agree on what color a
 * caveat is. */
.ledger {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
}

.ledger li {
  position: relative;
  padding: var(--space-1) 0 var(--space-1) var(--space-4);
  margin: 0 0 var(--space-4);
}

/* The rule is a child rather than a border so it can be drawn.
 *
 * Each one rules a claim out, and it draws downward as the claim is reached --
 * the same gesture as the accent tab at the head of a section, pointed at the
 * part of this site that matters most. It is a picture of nothing: no length
 * here encodes a quantity, which is what licenses it to start collapsed at all.
 *
 * Drawn by the scroll position rather than by a script. Without support the
 * animation never attaches and every rule stands at full height, which is
 * exactly what it looked like before this existed. */
.ledger li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--accent) 55%, var(--tint-base));
  transform-origin: top center;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .ledger li::before {
      animation: rule-down linear both;
      animation-timeline: view();
      /* Finished well before the item reaches the middle of the window: the
       * rule should be complete by the time the sentence beside it is being
       * read, not still creeping down past it. */
      animation-range: entry 15% cover 35%;
    }
  }
}
@keyframes rule-down {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

.ledger li:last-child { margin-bottom: 0; }

.ledger strong { font-weight: 600; }

/* Three doors. Whole-card links rather than a heading with a "read more" under
 * it: the target is the size of the card, which is the difference between a
 * link that can be hit on a phone and one that has to be aimed at. */
.doors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-3);
}

.door {
  display: block;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-4) var(--space-4);
  background: var(--surface);
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.door:hover,
.door:focus-visible {
  border-color: color-mix(in oklab, var(--accent) 45%, var(--border));
  transform: translateY(-2px);
  /* Fallback spelled out: the token is newer than this rule, and a
   * box-shadow that resolves to nothing is a card that stops responding
   * to the pointer for no visible reason. */
  box-shadow: var(--shadow-soft, 0 4px 16px rgb(0 0 0 / 0.10));
}

.door h3 {
  color: var(--accent);
  margin-bottom: var(--space-2);
}

.door h3::after {
  content: "\2192";
  /* The arrow is decoration on a link that is already a link. Hidden from
   * assistive technology, which announces the destination from the href.
   * Its own box, so it can step toward the destination under the pointer. */
  speak: never;
  display: inline-block;
  margin-left: 0.3em;
  transition: transform 0.15s ease;
}
.door:hover h3::after,
.door:focus-visible h3::after { transform: translateX(0.22em); }

.door p {
  margin: 0;
  font-size: var(--size-2);
  line-height: var(--lh-body);
  color: var(--text-primary);
}

@media (prefers-reduced-motion: reduce) {
  .door { transition: border-color 0.15s ease; }
  .door:hover, .door:focus-visible { transform: none; }
  .door h3::after { transition: none; }
  .door:hover h3::after, .door:focus-visible h3::after { transform: none; }
}

/* --- The lab-site front door (2026-08) ------------------------------------ */

/* The home page was rebuilt to read like a small research-group site: an
 * overview masthead, a photograph beside a short description of the author,
 * the short version of the farm result, and a grid of doors to every other
 * page. Three of those blocks want to be wider than the prose column, so
 * this page and only this page carries a second track. It is scoped to
 * .page-home; the note beside --measure in tokens.css about there being no
 * wider figure track still holds for every argument page. */
.page-home {
  --measure-wide: min(72rem, 92vw);
}

/* Break a block out of the centerd prose column to the wide track. Works
 * because main/header are centerd with auto margins: half the difference
 * between the two widths, taken from each side. */
.page-home .wide {
  width: var(--measure-wide);
  max-width: none;
  margin-left: calc(50% - var(--measure-wide) / 2);
  margin-right: calc(50% - var(--measure-wide) / 2);
}

/* The masthead: the site's name at display size and one line beneath it. */
.masthead {
  padding-bottom: var(--space-4);
}

.masthead h1 {
  font-size: var(--size-6);
  margin-bottom: var(--space-2);
}

.masthead__sub {
  margin: 0;
  font-size: var(--size-4);
  color: var(--text-secondary);
  max-width: 46rem;
}

/* Profile (about.html): photograph on the left half, a short description on
 * the right, inside the same prose column as everything else. On narrow
 * screens the two stack, photo first. Kept in this file because it began on
 * the home page and every page loads all nine sheets regardless. */
.profile {
  display: grid;
  /* The portrait is taller than it is wide, so it takes the narrower share of
   * the row and the text the wider; the two then finish at about the same
   * height. */
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--space-5);
  align-items: start;
  /* No extra margin: the standard section padding is the whole gap, so the
   * bio runs on into the project the way one section follows another. */
  padding-bottom: var(--space-5);
}

.profile__photo { margin: 0; }

/* Until a photograph exists: a dashed box at the proportions the photograph
 * will take, so the layout reads correctly before it is in. Used by the
 * profile on about.html and the photo strip on the home page. Delete this
 * rule when the last <picture> goes in. */
.img-placeholder {
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  border: 2px dashed var(--border);
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: var(--size-3);
  background: var(--surface);
}

.profile__text .lede { margin-top: 0; }

.profile__links {
  font-size: var(--size-2);
  color: var(--text-secondary);
}

@media (max-width: 600px) {
  .profile { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
}

/* A row of photographs across the prose column, three abreast; two on a
 * tablet, one on a phone. Each is a standard .photo so the frame, shadow and
 * caption match every other photograph on the site. */
.photo-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-4);
}

.photo-row .photo { margin: 0; }

/* Rounded corners on the photo row and the profile photograph. The rest of the
 * site keeps --radius (0): these are pictures set beside each other rather
 * than figures in a column of text, and the softer edge reads as a gallery.
 * The frame already clips its contents, so rounding the frame rounds the
 * picture; the shadow follows the same shape. */
.photo-row .photo__frame,
.profile .photo__frame,
.profile .img-placeholder {
  border-radius: var(--radius-photo);
}

/* Eight doors instead of three: two across in the prose column, one on a
 * phone. */
.doors--all {
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

/* The findings end and the doors begin: one gap, not the section padding
 * stacked on both sides of the boundary. The last finding is the end of the
 * argument, and the doors are the way out of it, so they sit close. */
#read {
  margin-bottom: var(--space-6);
  padding-top: var(--space-4);
}
.page-home #found { padding-bottom: var(--space-3); }


/* --- The photographic backdrop, the card surfaces, and a tighter top -------- */

/* A photograph of the farm gate behind the whole page, at an opacity low
 * enough that it stays under the prose. Fixed, so it does not scroll with the
 * text and the eye never tracks it. On every page. The look (saturation 0.6,
 * blur 2.6px -- some color and focus kept, since 2026-08-31) is baked into
 * the file (media/backdrop-1600) so there is no filter running on a
 * full-window layer; rebake from media/backdrop-raw-1600.jpg to retune. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: url("../media/backdrop-1600.webp") center / cover no-repeat;
  opacity: var(--backdrop-opacity);
  pointer-events: none;
}

/* The robotics page trades the farm gate for the Worlds arena, at the same
 * opacity and the same rules: desaturated in the file
 * (media/backdrop-robotics-1600), fixed, faint. Everything above still
 * applies; only the picture changes. */
body.page-robotics::before {
  background-image: url("../media/backdrop-robotics-1600.jpg");
}
/* The robotics page runs the dark palette (data-theme="dark" on its <html>):
 * an arena is a dark room with coloured light, and the faint-gray-on-paper
 * treatment read as fog rather than venue. The backdrop file carries its
 * look baked in (saturation 0.55, blur 3px, from the colour original) and
 * shows through stronger than the site default. */
body.page-robotics { --backdrop-opacity: 0.28; }
/* The photographs ARE this page; the dark palette's polite dimming would
 * mute the one thing it exists to show. Full brightness, like lit screens
 * in a dark venue. */
:root[data-theme="dark"] .page-robotics .photo__frame img {
  /* Out-specifies the dark palette's dimming rule in prose.css (0-3-1). */
  filter: none;
}
/* The dome gives way to daylight at the workshops: js/robotics.js flips
 * `is-daylight` on the body as #workshops arrives, tokens.css hands back the
 * light values, and these transitions turn the swap into a slow crossfade.
 * The arena stays as a whisper on the paper rather than vanishing -- the
 * venue seen with the house lights on (0.14 after two rounds of the author asking for more; it began at 0). !important with an extra class, so it also beats the
 * debug panel's own !important while that is installed. */
body.page-robotics.is-daylight::before { opacity: 0.14 !important; }
@media (prefers-reduced-motion: no-preference) {
  body.page-robotics { transition: background-color 0.9s ease, color 0.9s ease; }
  body.page-robotics::before { transition: opacity 0.9s ease; }
  .page-robotics section { transition: border-color 0.9s ease; }
  .page-robotics h2, .page-robotics .lede, .page-robotics p, .page-robotics figcaption {
    transition: color 0.9s ease;
  }
}

/* The bar leaves the farm at the arena door (2026-08-31, at the author's
 * request; dialed back the same day): on this one page the pine bar goes
 * dark graphite with the faintest blue cast and a muted teal accent -- more
 * instrument panel than synthwave poster. The teal is spent only where green
 * was spent before (the current tab's underline, the reading-progress line,
 * and a one-pixel line along the bar's bottom edge).
 *
 * Hex values here, not in tokens.css, for the same reason the bar's own pine
 * is in layout.css: this is chrome, identical in both palettes, and the
 * robotics page pins dark anyway. The daylight rule below hands back the
 * exact pine set from layout.css -- change either together. */
body.page-robotics .topbar {
  --bar-ink: #edeff2;
  --bar-ink-dim: #99a0ac;
  --bar-line: #3a414c;
  --accent-on-dark: #7fb0c4;
  background: #15171d;
  box-shadow: 0 1px 0 rgba(127, 176, 196, 0.12);
}
/* Back to the farm as the daylight crossfade runs: same 0.9s curve as the
 * page around it, so the bar and the venue change together. */
body.page-robotics.is-daylight .topbar {
  --bar-ink: #f4f2ea;
  --bar-ink-dim: #a9b3a4;
  --bar-line: #527560;
  --accent-on-dark: #8FBE72;
  background: #35523f;
  box-shadow: none;
}
@media (prefers-reduced-motion: no-preference) {
  body.page-robotics .topbar {
    transition: background-color 0.9s ease, box-shadow 0.9s ease;
  }
  body.page-robotics .topbar a,
  body.page-robotics .topbar .tabs__more { transition: color 0.9s ease; }
}

/* --- The engineering dress (robotics only) ---------------------------------
 *
 * 2026-08-31, at the author's request ("engineering af"): the page reads as
 * an engineering document, in four quiet moves. Monospace small labels with
 * drawn figure numbers; viewfinder corners on every photograph; a dot grid
 * under the dark half and graph paper under the daylight half; and end ticks
 * on the counters' rules, like dimension lines. Nothing here shouts -- the
 * flavor is in the details, which is where engineering keeps it. */
body.page-robotics {
  /* The page accent leaves the farm with the bar: headings' rules, focus
   * rings and hovers run instrument teal here, not pine. */
  --accent: #6cc4c6;
}
/* Darker on cream, where the dark half's teal washes out; focus rings and
 * heading rules need to survive daylight. */
body.page-robotics.is-daylight { --accent: #2f8b8d; }

/* The engineering dress ran its small labels in mono until 2026-09-02
 * (stat labels first, then the print labels, seats and closing note the
 * same day, all at the author's request): against the rest of the site
 * the mono read as a second voice rather than a flavor. Everything wears
 * the site's own label face now; the drawn FIG numbers, the viewfinder
 * corners and the grids carry the drafting look on their own. */

/* Figure numbers, drawn by the stylesheet rather than typed: every real
 * print across both mosaics takes the next number, so the season runs
 * FIG 01.. and the workshops continue the sequence. Typed numbers would go
 * stale the first time a photograph moved. */
body.page-robotics { counter-reset: fig; }
.page-robotics .mosaic__item:not(.mosaic__soon) { counter-increment: fig; }
.page-robotics .mosaic__label::before {
  content: "FIG " counter(fig, decimal-leading-zero);
  display: inline-block;
  margin-right: var(--space-2);
  color: #6cc4c6;
}

/* Viewfinder corners. Square prints (the rounded tile is the farm's shape),
 * and a thin bracket in each corner of the photograph, drawn as eight
 * one-pixel strokes on one overlay so they ride every hover translate. */
.page-robotics .mosaic__item,
.page-robotics .mosaic__item .photo__frame,
.page-robotics .mosaic__label,
.page-robotics .mosaic__soon { border-radius: 0; }
.page-robotics .mosaic__item:not(.mosaic__soon)::after {
  content: "";
  position: absolute;
  inset: 8px;
  z-index: 2;
  pointer-events: none;
  --tick: rgba(237, 239, 242, 0.6);
  background:
    linear-gradient(var(--tick), var(--tick)) top left    / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) top left    / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) top right   / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) top right   / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) bottom left / 12px 1px,
    linear-gradient(var(--tick), var(--tick)) bottom left / 1px 12px,
    linear-gradient(var(--tick), var(--tick)) bottom right/ 12px 1px,
    linear-gradient(var(--tick), var(--tick)) bottom right/ 1px 12px;
  background-repeat: no-repeat;
}

/* The ground the page is drawn on. A dot grid under the arena half, laid
 * over the backdrop photograph; it fades out with the dome. Fixed, like the
 * backdrop, so the paper holds still while the prints slide over it. */
body.page-robotics::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(rgba(237, 239, 242, 0.055) 1px, transparent 1.4px)
    0 0 / 24px 24px;
}
body.page-robotics.is-daylight::after { opacity: 0; }
/* Graph paper for the daylight half, on the root's own overlay (the body's
 * two pseudo-elements are spoken for), faded in as the workshops arrive. */
html:has(.page-robotics)::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(rgba(35, 45, 30, 0.05) 1px, transparent 1px) 0 0 / 28px 28px,
    linear-gradient(90deg, rgba(35, 45, 30, 0.05) 1px, transparent 1px) 0 0 / 28px 28px;
  opacity: 0;
}
html:has(.page-robotics.is-daylight)::after { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  body.page-robotics::after,
  html:has(.page-robotics)::after { transition: opacity 0.9s ease; }
}

/* Dimension ticks: the counters' rules get end stops, like a measured
 * callout on a drawing. */
.page-robotics .stat { position: relative; }
.page-robotics .stat::before,
.page-robotics .stat::after {
  content: "";
  position: absolute;
  top: -3px;
  width: 2px;
  height: 8px;
  background: #4a5260;
}
.page-robotics .stat::before { left: 0; }
.page-robotics .stat::after { right: 0; }

/* The doors get a surface of their own, so the grid reads as a set of
 * cards rather than a table of links drawn on the paper. The green top
 * edge is earned, not resting: nothing at rest (a short resting dash
 * looked like a broken border -- 2026-08-31, at the author's request),
 * and under the pointer it sweeps in from the left across the full
 * width, which is the card's way of saying it goes somewhere. */
.doors--all .door {
  background-color: var(--surface-raised);
  border-radius: 0.5rem;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: left top;
  background-size: 0% 3px;
  transition: border-color 0.15s ease, transform 0.15s ease,
              box-shadow 0.15s ease, background-size 0.28s ease;
}
.doors--all .door:hover,
.doors--all .door:focus-visible { background-size: 100% 3px; }
@media (prefers-reduced-motion: reduce) {
  .doors--all .door { transition: border-color 0.15s ease; background-size: 0% 3px; }
  .doors--all .door:hover,
  .doors--all .door:focus-visible { background-size: 100% 3px; }
}

/* Less air between the masthead, the photographs and the first findings: the
 * top of this page is a sequence, not three separate sections. */
.page-home .masthead { padding-bottom: var(--space-3); }
.page-home #photos { padding-top: var(--space-4); padding-bottom: var(--space-4); }
.page-home #found { padding-top: var(--space-4); }

/* Get the findings above the fold. The masthead loses its top air, the lede
 * drops to body size (it runs to several lines), the photographs run as a
 * shorter 16:9 strip with one-line captions, and the findings section starts
 * closer behind them. */
.page-home .hero { padding-top: var(--space-5); }
.page-home .masthead .lede { font-size: var(--size-2); }
.page-home .masthead h1 { margin-bottom: var(--space-2); }
.photo-row figcaption { font-size: var(--size-1); margin-top: var(--space-2); }
.page-home #photos { padding-top: var(--space-3); padding-bottom: var(--space-3); }
.page-home #found { padding-top: var(--space-3); }
.page-home #found > .lede { font-size: var(--size-3); }

/* Smaller page title, and the results heading underlined in the accent so it
 * reads as the section the page is for. */
/* The results heading is underlined in the accent, and "Results" also carries
 * the highlighter stroke the numbers wear, a little taller. */
.page-home #found > h2 {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 3px;
  text-underline-offset: 0.2em;
}
.page-home #found > h2 mark {
  color: inherit;
  /* A plain highlight over the whole word, in a wash of the accent. */
  background-color: color-mix(in oklab, var(--accent) calc(32% * var(--tint-strength)), var(--tint-base));
  padding: 0 0.12em;
  margin: 0 -0.12em;
}
.doors--all a[href="about.html"] h3 { text-decoration: underline; text-underline-offset: 0.15em; }

/* Underlined phrases in the lede: the site's green, set a little below the
 * baseline so it does not cut through descenders. */
.page-home .lede u {
  text-decoration: underline;
  /* A wash of the accent rather than the accent itself, and a hairline: it
   * should read as emphasis, not as a link. */
  text-decoration-color: color-mix(in oklab, var(--accent) 45%, var(--tint-base));
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

/* about.html: the name sits close over the portrait row rather than a full
 * section gap above it. */
.hero--tight { padding-bottom: var(--space-2); }
.hero--tight + main #profile { padding-top: var(--space-3); }

/* The field of kinds under the hero number: one dot per kind, the width of
 * the column, filled in as the counter runs. */
.bignum__field {
  display: block;
  margin-top: var(--space-3);
  max-width: 100%;
}
.bignum__note {
  margin: var(--space-1) 0 0;
  font: 500 var(--size-1)/var(--lh-body) var(--font-display);
  color: var(--text-muted);
}

/* The nitrogen-proxy legend carries seven entries -- two groups and five plant
 * shapes -- which at the site's usual legend spacing ran the full width of the
 * column and crowded the right edge. Tighter spacing, and a ceiling on the
 * width so it wraps to a second line rather than running to the margin. */
#chart-nproxies .legend {
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  /* Held to the width the panels use, so the row cannot reach the right edge
   * of the column; it wraps onto a second line instead. */
  max-width: 42rem;
}

/* A rule under the hero number, drawn as the site's own soil profile rather
 * than as a line: the three horizons the brand mark carries -- litter, root
 * zone, mineral subsoil -- laid left to right beneath the count of what lives
 * in them. It sits under the number only, not the width of the column, because
 * it belongs to the figure and not to the page. */
.bignum__value {
  display: inline-block;
  position: relative;
  padding-bottom: 0.06em;
}
.bignum__value::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* Just clear of the baseline, where an underline sits, rather than under the
   * whole line box: at this size the descender space is a third of an inch of
   * blank paper and the rule was floating in it. */
  bottom: 0.1em;
  height: 4px;
  border-radius: 3px;
  background: linear-gradient(
    90deg,
    var(--accent) 0%,
    color-mix(in oklab, var(--accent) 70%, var(--accent-red)) 38%,
    var(--accent-red) 68%,
    color-mix(in oklab, var(--accent-red) 55%, var(--text-primary)) 100%);
  /* The rule stops for the comma the way a real underline skips a descender.
   * page.js measures the comma's edges into --cut-l / --cut-r; the mask is
   * opaque everywhere except that stretch. With both at 0 the gap has no width
   * and the rule is unbroken, so a browser that never ran the script -- or a
   * figure without a comma -- gets the plain bar. */
  -webkit-mask-image: linear-gradient(90deg,
    #000 0, #000 var(--cut-l, 0px),
    transparent var(--cut-l, 0px), transparent var(--cut-r, 0px),
    #000 var(--cut-r, 0px));
  mask-image: linear-gradient(90deg,
    #000 0, #000 var(--cut-l, 0px),
    transparent var(--cut-l, 0px), transparent var(--cut-r, 0px),
    #000 var(--cut-r, 0px));
}


/* --- The award counters ----------------------------------------------------
 *
 * The season's numbers, large and first, and nothing else -- the full
 * arena-scoreboard treatment (neon headline, drawn elimination wire,
 * registration marks) was built on 2026-08-31 and pulled back the same
 * day at the author's request: counters that flaunt, without the set
 * dressing. Hex values per the same documented exception as the bar
 * above: this page pins dark and these are its fixtures.
 *
 * js/counters.js winds the numerals up when they scroll into view; with
 * scripts off or motion reduced they are simply already their values. */
/* The board sits between the hero and the photographs (moved back above
 * the team mosaic 2026-09-02, at the author's request, "make the vertical
 * spaces as concise as possible"), and the whole run is set tight: the
 * hero's tight bottom hands straight to the figures, the statnote hands
 * to the photographs on about a rem, and #team sheds its section border
 * so the statnote's own rule is the only line between the board and the
 * prints. The workshops' padding closes the section from below. */
.page-robotics #season-board { padding-top: 0; padding-bottom: 0; }
.page-robotics #team { border-top: none; padding-top: var(--space-2); padding-bottom: 0; }
.page-robotics #team .mosaic { margin-top: var(--space-2); margin-bottom: var(--space-4); }
.statrow {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-4);
  margin: 0 0 var(--space-5);
  text-align: center;
}
/* The world-scale figure takes the whole first row as a hero; the other
 * four figures sit in a rank beneath it. On a phone the columns collapse
 * and every figure takes the line. */
.stat { grid-column: span 3; }
.stat--hero { grid-column: 1 / -1; }
@media (max-width: 760px) {
  .statrow { grid-template-columns: 1fr; }
  .stat, .stat--hero { grid-column: auto; }
}

/* The hero: the crowd stamped faint and enormous behind the result cut
 * from it. The ghost is drawn as an outline (stroke, no fill) so it reads
 * as the drawing's construction layer; browsers without text-stroke show
 * nothing there, and lose only decoration. */
.stat--hero { position: relative; padding-bottom: var(--space-2); }
.stat--hero .stat__ghost {
  position: absolute;
  inset: 0;
  display: grid;
  /* The "+" is its own span (styled solid below), which grid treats as a
   * second item and, left to itself, stacks on a second ROW under the
   * digits (that was the stray plus floating over the label, 2026-09-02).
   * Column flow puts it back beside them; baseline keeps the smaller
   * mark sitting on the digits' baseline like ordinary text. */
  grid-auto-flow: column;
  align-items: baseline;
  place-content: center;
  /* Cut by a third, then by another quarter, on 2026-09-01 ("tone down
   * the big numbers", twice), along with both numeral sizes below --
   * same proportions, lower voice. */
  font: 700 clamp(3.25rem, 8.5vw, 5.25rem)/1 var(--font-display);
  letter-spacing: 0.02em;
  color: transparent;
  -webkit-text-stroke: 1px #57636e;
  opacity: 0.9;
  /* A breath of the panel's cold light around the field the result beat
   * (2026-09-01, "really emphasize that 11k"): stroke lifted a step and a
   * wide faint halo -- the glow of a readout, not a sign. */
  text-shadow: 0 0 28px rgba(127, 176, 196, 0.16);
}
/* The plus, stroked and full-size like the digits (2026-09-02, "just
 * stroked like the other 11,000... the same big as 11k"; it had been
 * filled solid and a step smaller since 2026-09-01). It cannot be the
 * font's glyph: that glyph is built from two overlapping boxes, and
 * stroked text draws both of them crossing. So it is a one-path svg
 * cross (in robotics.html), given the digits' own 1px hairline --
 * non-scaling, so the viewBox math never thickens it -- and stood on
 * their shared baseline at cap height. */
.stat--hero .stat__ghost svg {
  /* 0.68em is this font's numeral height, measured in-page, so the cross
   * stands exactly as tall as the digits. */
  height: 0.68em;
  width: auto;
  margin-left: 0.12em;
  fill: none;
  stroke: #57636e;
  overflow: visible;
}
.stat--hero .stat__ghost svg path {
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.stat--hero .stat__n,
.stat--hero .stat__label { position: relative; z-index: 1; }
.stat--hero .stat__n { font-size: clamp(2.1rem, 4.5vw, 2.9rem); }
/* The hero pair alone gets to shine (2026-09-01, "glow the top 20"): a
 * cold teal halo on the result, and the label's own 11,000+ raised to
 * numeral ink with a smaller echo of the same light. The rank figures
 * below stay matte -- if everything glows, nothing does. */
.stat--hero .stat__n {
  color: #f2f6f9;
  text-shadow: 0 0 16px rgba(127, 176, 196, 0.5), 0 0 44px rgba(127, 176, 196, 0.22);
}
/* Any label's emphasized figure (2026-09-02: the 11,696 under #60; the
 * hero label's 11,000+ carried this until it was cut the same day):
 * numeral ink, bold, a small echo of the hero's light. */
.stat__label strong {
  font-weight: 700;
  color: #e9edf1;
  text-shadow: 0 0 12px rgba(127, 176, 196, 0.45);
}
/* A to-scale proportion bar (.statscale) lived here until 2026-08-31;
 * removed with its markup in robotics.html. */
/* The title block under the figures: one line naming the season they all
 * belong to, dim and centered, ruled off like the strip at the foot of a
 * drawing. */
.statnote {
  /* The negative top rides against .statrow's bottom margin: the rule
   * lands 1.25rem under the figures (recalibrated 2026-09-02 when the
   * margin above shrank with the board's move up the page). */
  margin: calc(-1 * var(--space-3)) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid #3a414c;
  text-align: center;
  font: 600 var(--size-1)/var(--lh-tight) var(--font-display);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #99a0ac;
}
/* A dimension line, the way a drawing says "this span, measured": a
 * hairline with a tick at each end. */
.stat {
  position: relative;
  margin: 0;
  padding-top: var(--space-4);
  border-top: 1px solid #3a414c;
}
.stat::before,
.stat::after {
  content: "";
  position: absolute;
  top: -1px;
  width: 1px;
  height: 0.55rem;
  background: #3a414c;
}
.stat::before { left: 0; }
.stat::after { right: 0; }
/* Steel, not teal (2026-08-31, third pass, "more of an engineering
 * thing"): the numerals read as machined figures on the drawing, and the
 * page's one accent stays in the chrome. The size does the flaunting. */
.stat__n {
  display: block;
  font: 700 clamp(1.6rem, 3.2vw, 2.1rem)/1 var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: #e9edf1;
}
.stat__label {
  display: block;
  margin-top: var(--space-2);
  font: 600 var(--size-1)/var(--lh-snug) var(--font-display);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #99a0ac;
}

/* Daylight reaches the drawing (2026-09-01, "the white also overlaps with
 * the stats"): the board's inks above are mixed for the dome, and on tall
 * screens the workshop shift lands while the board is still in view, so
 * every one of them gets a daylight counterpart -- machined figures in the
 * paper's own dark ink, construction lines in pencil gray, the cold-light
 * halos faded to nothing rather than removed so the crossfade has
 * something to animate. Same 0.9s ride as the rest of the shift. */
@media (prefers-reduced-motion: no-preference) {
  .stat,
  .stat::before,
  .stat::after,
  .statnote,
  .stat__n,
  .stat__label,
  .stat--hero .stat__ghost,
  .stat--hero .stat__ghost svg,
  .stat--hero .stat__label strong {
    transition: color 0.9s ease, background-color 0.9s ease,
      border-color 0.9s ease, text-shadow 0.9s ease,
      -webkit-text-stroke-color 0.9s ease, stroke 0.9s ease;
  }
}
body.page-robotics.is-daylight .stat--hero .stat__ghost {
  -webkit-text-stroke-color: #c2bbaa;
  text-shadow: 0 0 28px rgba(127, 176, 196, 0);
}
body.page-robotics.is-daylight .stat--hero .stat__ghost svg {
  stroke: #c2bbaa;
}
body.page-robotics.is-daylight .stat--hero .stat__n {
  color: var(--text-primary);
  text-shadow: 0 0 16px rgba(127, 176, 196, 0), 0 0 44px rgba(127, 176, 196, 0);
}
body.page-robotics.is-daylight .stat--hero .stat__label strong {
  color: var(--text-primary);
  text-shadow: 0 0 12px rgba(127, 176, 196, 0);
}
body.page-robotics.is-daylight .stat__n { color: var(--text-primary); }
body.page-robotics.is-daylight .stat__label,
body.page-robotics.is-daylight .statnote { color: var(--text-muted); }
body.page-robotics.is-daylight .stat,
body.page-robotics.is-daylight .statnote { border-top-color: var(--border); }
body.page-robotics.is-daylight .stat::before,
body.page-robotics.is-daylight .stat::after { background: var(--border); }
