﻿/* ---------------------------------------------------------------------------
   nothingbutdatacenters.com

   Two rules hold this together.

   1. TYPE ENCODES THE BOUNDARY. Anything generated from the project record is
      set in mono. Anything a person wrote is set in sans. The README draws
      that line; the typography keeps it.

   2. NOTHING IS BRIGHTER THAN THE RENDER. The pass log reports a maximum
      measured luminance of 179.6 of 255. --lum-max is that value. No text,
      rule or fill on this site exceeds it, so the page cannot be brighter
      than the brightest frame in the work.

   One hue, three fills. Solid amber means a verified manifest. Hollow means
   observed in the log but not verified. Dashed means no record at all. That
   is the whole legend, and it is the record's own epistemology.
--------------------------------------------------------------------------- */

:root {
  /* Greys sampled up the luminance ramp the render actually occupied. */
  --ground:     #0a0a0b;
  --ground-2:   #111112;
  --ground-3:   #171718;
  --rule:       #242425;
  --rule-soft:  #1b1b1c;
  --dim:        #807e79;   /* labels, secondary        4.9:1 */
  --text:       #95938e;   /* body                     6.4:1 */
  --lum-max:    #b4b2ac;   /* headings. luminance 179.6 of 255. the ceiling. */

  --amber:      #e8a33d;
  --amber-mid:  #a8752b;
  --amber-low:  #6d4c1c;
  --amber-wash: rgba(232, 163, 61, 0.09);

  /*
     ONE FACE. Monospace, everywhere, including running prose.

     This went through a text serif and then a neutral grotesque, and both were
     wrong for the same reason: they made the page read as a document ABOUT the
     work instead of an instrument attached to it. The brief is a ship's
     terminal from a 1979 film, rebuilt in the 2030s for datacenter telemetry
     -- so the body text is not a companion to the mono, it IS the mono.

     Everything that makes it feel like a terminal rather than a code listing
     is treatment, not typeface: even colour, open leading, uppercase micro
     labels on wide tracking, amber on near-black, and a block cursor.

     Cascadia Mono leads. It ships with Windows Terminal, which is the closest
     thing to a modern version of the reference, and its zero and one are
     unambiguous on a page that is mostly frame numbers and hashes.
  */
  --mono: "Cascadia Mono", ui-monospace, "SF Mono", "JetBrains Mono",
          "IBM Plex Mono", "Roboto Mono", Consolas, "Lucida Console", monospace;

  /* Phosphor, at the threshold of visible. No scanlines, no curvature, no
     flicker -- those are the costume. This is just the sense that the bright
     text is emitting rather than printed. */
  --glow-warm: 0 0 24px rgba(232, 163, 61, 0.14);
  --glow-cool: 0 0 20px rgba(180, 178, 172, 0.10);

  /* One scale, used everywhere. */
  --t-micro: 0.66rem;   /* eyebrows, legends, tile captions */
  --t-data:  0.76rem;   /* tables, fields, nav */
  --t-body:  0.92rem;   /* mono runs large; this is optically ~1rem of sans */
  --t-lede:  clamp(0.95rem, 0.9rem + 0.35vw, 1.1rem);
  --t-h3:    0.95rem;
  --t-h2:    clamp(1.1rem, 2vw, 1.4rem);
  --t-h1:    clamp(1.6rem, 4.2vw, 2.7rem);

  /* Tracking tightens as size grows, which is what keeps mono from looking
     like code when it is being used as display type. */
  --track-display: -0.045em;
  --track-h1: -0.035em;
  --track-h2: -0.025em;
  --track-label: 0.18em;

  --gut: clamp(1.25rem, 4vw, 3.25rem);
  --measure: 36rem;
  --max: 74rem;
  /* The sticky masthead's height. Anything else that sticks hangs off this. */
  --masthead-h: 3.4rem;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--mono);
  font-size: var(--t-body);
  /* Mono at length needs air. This is what makes it readable rather than
     merely on-theme, and it is the whole cost of running one face. */
  line-height: 1.72;
  letter-spacing: -0.004em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Every number on this site is meant to be compared with the one above it. */
code, .fields, .ruler, table, .lbm, .stat__v, .rt { font-variant-numeric: tabular-nums; }

/* Faint horizon so the page does not read as a flat void. 43,200 frames wide. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto 0;
  height: 60vh;
  background: radial-gradient(120% 100% at 50% 0%, rgba(232,163,61,0.045), transparent 70%);
  pointer-events: none;
  z-index: 0;
}

main, .masthead, .foot { position: relative; z-index: 1; }

a { color: var(--amber); text-decoration-color: var(--amber-low); text-underline-offset: 0.2em; }
a:hover { text-decoration-color: var(--amber); }

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

.skip {
  position: absolute; left: -9999px;
  font-family: var(--mono); font-size: 0.82rem;
}
.skip:focus { left: var(--gut); top: 0.6rem; z-index: 50; background: var(--ground-3); padding: 0.5rem 0.8rem; }

/* --- masthead ------------------------------------------------------------ */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem 1.6rem;
  padding: 1.05rem var(--gut) 1rem;
  border-bottom: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.78rem;
  /* The pass-02 wall is 46,888px tall. Navigation has to come with you. */
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--ground);
}
/* The burger is positioned against this. */
.masthead { position: sticky; }

.masthead__id { text-decoration: none; color: inherit; display: flex; align-items: baseline; gap: 0.6rem; }
.masthead__name { color: var(--lum-max); letter-spacing: -0.01em; }
.masthead__id:hover .masthead__name { color: var(--amber); }
.masthead__role { color: var(--dim); }
.masthead__role::before { content: "/ "; color: var(--rule); }

/*
   The mobile menu is a checkbox, not a script.

   The nav is six links; on a 375px screen they wrap to three rows and the
   masthead becomes a third of the viewport. A checkbox toggle needs no
   JavaScript, survives with scripting off like everything else here, and is
   focusable and operable from the keyboard because it is a real form control
   rather than a div pretending to be one.
*/
.nav-check { position: absolute; width: 1px; height: 1px; opacity: 0; }
.nav-burger { display: none; }

.nav { display: flex; flex-wrap: wrap; gap: 0 1.05rem; margin-left: auto; }
.nav a { color: var(--dim); text-decoration: none; }
.nav a:hover { color: var(--lum-max); }
.nav a[aria-current="page"] { color: var(--amber); }
.nav a[aria-current="page"]::before { content: "\00b7"; margin-right: 0.3em; }

.masthead__out { text-decoration: none; color: var(--dim); }
.masthead__out:hover { color: var(--amber); }

/* --- page shell ---------------------------------------------------------- */

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--gut); }
.wrap--narrow { max-width: 52rem; }

/*
   Section rhythm.

   This was 70px top and bottom, which put 141px of nothing between every pair
   of sections. On a page of short sections -- the design system page is eight
   of them -- the padding was the loudest element on screen. A hairline already
   marks the boundary; the space only has to let it breathe.
*/
.section { padding-block: clamp(1.9rem, 4vw, 3rem); }
.section + .section { border-top: 1px solid var(--rule-soft); }
.section--flush { padding-top: clamp(1.5rem, 3vw, 2.2rem); }

/* --- type ---------------------------------------------------------------- */

/* The micro-label. Uppercase mono, widely tracked, dim: the one device that
   makes a block read as an instrument panel rather than a document header. */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 1.1rem;
}

.display {
  font-family: var(--mono);
  font-size: clamp(2.3rem, 8.2vw, 5rem);
  line-height: 0.93;
  letter-spacing: var(--track-display);
  font-weight: 600;
  color: var(--lum-max);
  margin: 0;
  text-wrap: balance;
  text-shadow: var(--glow-cool);
}

/* A terminal waiting for input, which is what this site is: a readout of a
   machine that is still running. Sized in em, so it scales with whatever
   heading it follows. */
.display::after,
h1.page-title::after {
  content: "";
  display: inline-block;
  width: 0.46em;
  height: 0.78em;
  margin-left: 0.14em;
  background: var(--amber);
  box-shadow: var(--glow-warm);
  animation: caret 1.25s steps(1) infinite;
  vertical-align: baseline;
}
@keyframes caret { 50% { opacity: 0; } }

h1.page-title, h2 { text-shadow: var(--glow-cool); }
.stat__v, .lb__id { text-shadow: var(--glow-cool); }

h1.page-title {
  font-family: var(--mono);
  font-size: var(--t-h1);
  line-height: 1.04;
  letter-spacing: var(--track-h1);
  font-weight: 600;
  color: var(--lum-max);
  margin: 0 0 0.75rem;
}

h2 {
  font-family: var(--mono);
  font-size: var(--t-h2);
  line-height: 1.2;
  letter-spacing: var(--track-h2);
  font-weight: 600;
  color: var(--lum-max);
  margin: 0 0 1.1rem;
}

/* A heading that follows content needs air; one that opens a block does not. */
:is(p, dl, table, ul, ol, .tablewrap, .panel, .legend, .ruler, .caveat, .stats, .prose) + h2 {
  margin-top: clamp(2.2rem, 5vw, 3.2rem);
}

h3 {
  font-family: var(--mono);
  font-size: var(--t-h3);
  letter-spacing: 0;
  font-weight: 600;
  color: var(--lum-max);
  margin: 2rem 0 0.7rem;
}

.lede {
  font-size: var(--t-lede);
  line-height: 1.75;
  color: var(--lum-max);
  max-width: var(--measure);
  margin: 0 0 1.5rem;
  text-wrap: pretty;
}
.lede--hero {
  margin-top: clamp(1.4rem, 3vw, 2.1rem);
  max-width: 44rem;
  margin-bottom: 0.9rem;
  /* A readout line, not a paragraph: it hangs off a rule the way a value
     hangs off a label everywhere else on the site. */
  border-left: 1px solid var(--amber-low);
  padding-left: clamp(0.9rem, 2vw, 1.4rem);
}
.lede--sub { font-size: var(--t-data); color: var(--dim); max-width: 40rem; letter-spacing: 0.01em; }

.prose { max-width: var(--measure); }
.prose p, .prose ul, .prose ol, .prose blockquote { margin: 0 0 1.05em; text-wrap: pretty; }
.prose > :last-child { margin-bottom: 0; }
.prose li { margin-bottom: 0.35em; }
.prose blockquote {
  border-left: 2px solid var(--amber-low);
  padding-left: 1.1rem;
  margin-left: 0;
  color: var(--dim);
}
.prose code, code { font-family: var(--mono); font-size: 0.86em; color: var(--lum-max); }
.prose h2 { margin-top: 2.2rem; }

.note {
  font-size: 0.82rem;
  line-height: 1.8;
  color: var(--dim);
  max-width: var(--measure);
}

/* --- the frame ruler ----------------------------------------------------- */
/*
   One shared coordinate system: frame 1 at the left edge, frame 43,200 at the
   right, which is also 00:00 to 30:00 at 24fps. Passes, runs, treatments,
   crashes and chain breaks are all placed on it, so spans are comparable
   between pages without reading a single number.
*/

.ruler { font-family: var(--mono); margin: 0 0 0.4rem; }

.ruler__axis {
  display: flex;
  justify-content: space-between;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.3rem;
  margin-bottom: 0.65rem;
}
.ruler__axis span { position: relative; }

.ruler__row {
  display: grid;
  grid-template-columns: 19ch 1fr minmax(8ch, auto);
  align-items: center;
  gap: 0 0.9rem;
  padding: 0.28rem 0;
  font-size: 0.74rem;
}
.ruler__row + .ruler__row { border-top: 1px solid var(--rule-soft); }

.ruler__name { color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
a.ruler__name { text-decoration: none; }
.ruler__row:hover .ruler__name, a.ruler__name:hover { color: var(--lum-max); }
.ruler__val { color: var(--dim); text-align: right; font-size: 0.7rem; white-space: nowrap; }

.ruler__track {
  position: relative;
  height: 0.85rem;
  background:
    repeating-linear-gradient(90deg, var(--rule-soft) 0 1px, transparent 1px 25%);
  border-bottom: 1px solid var(--rule-soft);
}

/*
   The default state is VISIBLE. That is the whole rule.

   This used to carry `animation: draw ... both`, which holds the from-state
   (scaleX(0)) until the animation runs -- so a bar only existed once its
   animation completed, and a tab that loaded in the background rendered every
   ruler permanently blank. Content must never depend on an animation
   finishing. Motion is added in the media query below, never subtracted here.
*/
.span {
  position: absolute;
  top: 0.14rem;
  bottom: 0.14rem;
  min-width: 2px;
  transform-origin: left center;
}

/* solid: covered by a verified manifest */
.span--verified { background: var(--amber); }
/* hollow: observed in the render log, not verified by a manifest */
.span--observed { border: 1px solid var(--amber-mid); background: var(--amber-wash); }
/* dashed: no record */
.span--none { border: 1px dashed var(--amber-low); }
/* Recovered from local staging after a crash, then verified. Tens of frames out of
   43,200, so it is given a floor width; the pages that use it say so. */
.span--rescued {
  min-width: 3px;
  background: var(--amber-wash);
  border-top: 1px solid var(--amber); border-bottom: 1px solid var(--amber);
  background-image: repeating-linear-gradient(45deg, var(--amber-mid) 0 1px, transparent 1px 4px);
}

@keyframes draw { from { transform: scaleX(0); } }

/*
   MOTION IS ADDITIVE. Everything below adds movement to content that is
   already on the page and already correct. Turn all of it off and the site
   loses nothing but its manners.

   Two tiers:
     1. Scroll-driven (`animation-timeline: view()`), where supported. A ruler
        draws when you arrive at it, not 0.55s after page load -- which is why
        the motion was invisible on every ruler below the fold.
     2. A time-based fallback for the first paint elsewhere.
*/
/*
   ONE-SHOT ON ARRIVAL, never scrubbed.

   An earlier version used `animation-timeline: view()`, which binds progress
   to scroll POSITION -- so scrolling back up ran everything in reverse and the
   rulers animated up and down under the reader's thumb. Wrong feel entirely: a
   reveal is an entrance, and an entrance happens once. The observer in
   static/js/site.js unobserves each element the moment it fires, so nothing
   ever replays.

   The hidden state is scoped to `.js-reveal`, a class the script adds to <html>
   only once it is running. No script, no hiding, complete page.
*/
@media (prefers-reduced-motion: no-preference) {

  .js-reveal [data-reveal],
  .js-reveal .panel,
  .js-reveal .caveat,
  .js-reveal .stats,
  .js-reveal .jump,
  .js-reveal .gcard,
  .js-reveal .stage,
  .js-reveal .filmstrip,
  .js-reveal .swatches,
  .js-reveal .strip,
  .js-reveal .player,
  .js-reveal .updown,
  .js-reveal .lede--hero {
    opacity: 0;
    transform: translateY(20px);
    transition:
      opacity 0.8s cubic-bezier(0.19, 1, 0.22, 1) var(--d, 0s),
      transform 0.8s cubic-bezier(0.19, 1, 0.22, 1) var(--d, 0s);
  }
  .js-reveal .in { opacity: 1; transform: none; }

  /*
     The ruler does not rise and does not fade. Its BARS draw, left to right,
     staggered 45ms a row -- the page rendering the way the work did. That was
     always the right motion; it only ever fired at the wrong time, 0.55s after
     page load, where every ruler below the fold finished before anyone
     arrived. Now it fires when the reader does.
  */
  .js-reveal .ruler .span { transform: scaleX(0); }
  .js-reveal .ruler.in .span {
    transform: none;
    transition: transform 0.6s cubic-bezier(0.22, 0.85, 0.25, 1)
                calc(var(--i, 0) * 45ms);
  }
}

.mark {
  position: absolute;
  top: -0.1rem; bottom: -0.1rem;
  width: 1px;
  background: var(--lum-max);
}
.mark--break { background: var(--amber-mid); top: 0.3rem; bottom: 0.3rem; }
.mark--stop::after {
  content: "";
  position: absolute; left: -2px; top: -2px;
  width: 5px; height: 5px;
  background: var(--lum-max);
}

.legend {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem;
  font-family: var(--mono); font-size: 0.68rem; color: var(--dim);
  margin-top: 1rem; padding-top: 0.8rem; border-top: 1px solid var(--rule-soft);
}
.legend__item { display: flex; align-items: center; gap: 0.45rem; }
.legend__swatch { width: 1.6rem; height: 0.6rem; flex: none; }

/* --- fields (label / value pairs) ---------------------------------------- */

.fields {
  display: grid;
  grid-template-columns: minmax(8.5rem, 15rem) minmax(0, 1fr);
  gap: 0 1.4rem;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.8rem;
  line-height: 1.55;
}
.fields .k {
  color: var(--dim);
  padding: 0.5rem 0;
  border-top: 1px solid var(--rule-soft);
  letter-spacing: 0.01em;
}
.fields .v {
  color: var(--text);
  margin: 0;
  padding: 0.5rem 0;
  border-top: 1px solid var(--rule-soft);
  overflow-wrap: anywhere;
}
.fields .v strong { color: var(--lum-max); font-weight: 500; }
.fields--tight { font-size: 0.74rem; }
:is(.prose, p, .note, .caveat, .tablewrap) + .fields { margin-top: 1.6rem; }
.fields .v em { font-style: normal; color: var(--dim); }

/* A value the record says was inferred rather than measured. */
.inferred {
  text-decoration: underline dotted var(--amber-mid);
  text-underline-offset: 0.28em;
  cursor: help;
}

.hash { color: var(--dim); font-size: 0.95em; }

/* --- panels -------------------------------------------------------------- */

.panel {
  background: var(--ground-2);
  border: 1px solid var(--rule);
  padding: clamp(1.1rem, 2.4vw, 1.7rem);
}
.panel + .panel { margin-top: 1rem; }
.panel__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1rem;
  margin-bottom: 1rem;
}
.panel__title { font-family: var(--mono); font-size: 0.98rem; color: var(--lum-max); margin: 0; letter-spacing: -0.01em; }
.panel__title a { text-decoration: none; color: inherit; }
.panel__title a:hover { color: var(--amber); }

.status {
  font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--rule); padding: 0.12rem 0.5rem;
  white-space: nowrap;
}
.status--live { color: var(--amber); border-color: var(--amber-low); }
.status--gone { color: var(--dim); border-style: dashed; }

/*
   A caveat carried straight out of the record. Never shrunk, never hidden.

   This was a translucent amber wash over near-black, which is just brown: the
   panel read muddy and flat and undid the contrast everything else works to
   keep. The fill is now a neutral raised ground and the amber is spent on
   EDGES instead -- corner brackets, a status lamp, and a slow sweep.

   The brackets are not generic sci-fi. The machine-vision overlay in the
   treatments draws corner brackets on every detection at bracket_len 56; the
   record says so. The site annotating its own gaps the way the work annotates
   what it sees is the aesthetic thread doing actual work.
*/
.annot,
.caveat,
.player__note {
  --annot-edge: var(--amber);
  position: relative;
  background:
    linear-gradient(var(--annot-edge) 0 0) 0 0 / 15px 1px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 0 0 / 1px 15px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 100% 0 / 15px 1px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 100% 0 / 1px 15px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 0 100% / 15px 1px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 0 100% / 1px 15px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 100% 100% / 15px 1px no-repeat,
    linear-gradient(var(--annot-edge) 0 0) 100% 100% / 1px 15px no-repeat,
    linear-gradient(180deg, rgba(232,163,61,0.05), transparent 38%),
    var(--ground-2);
  border: 1px solid var(--rule);
  padding: 1.05rem 1.25rem;
  margin: 1.3rem 0;
  font-size: 0.82rem;
  line-height: 1.8;
  color: var(--text);
  max-width: 46rem;
  overflow: hidden;
}

/* A sweep, once every twelve seconds. Slow enough to be noticed rather than
   watched, which is the line between a live instrument and a screensaver. */
.annot::after,
.caveat::after,
.player__note::after {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 34%;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--amber), transparent);
  opacity: 0.85;
  animation: sweep 12s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
@keyframes sweep {
  0%   { transform: translateX(-110%); }
  55%  { transform: translateX(400%); }
  100% { transform: translateX(400%); }
}

.caveat__k {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 0.5rem;
}
/* The status lamp. One per panel, breathing. */
.caveat__k::before {
  content: "";
  flex: none;
  width: 6px; height: 6px;
  background: var(--amber);
  box-shadow: 0 0 8px var(--amber);
  animation: lamp 2.6s ease-in-out infinite;
}
@keyframes lamp { 50% { opacity: 0.25; box-shadow: none; } }

.caveat p, .player__note { margin: 0; }
.caveat p + p { margin-top: 0.7rem; }

/* The note above the player is the same object, so it looks like one. */
.player__note { margin: 0 0 0.9rem; max-width: none; }

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

.tablewrap { overflow-x: auto; margin: 1.2rem 0; }
table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 0.76rem; }
th, td { text-align: left; padding: 0.5rem 1.1rem 0.5rem 0; border-top: 1px solid var(--rule-soft); vertical-align: top; white-space: nowrap; }
thead th {
  border-top: 0; border-bottom: 1px solid var(--rule);
  color: var(--dim); font-weight: 400;
  font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
  padding-bottom: 0.6rem;
}
td.num, th.num { text-align: right; padding-right: 1.1rem; }
tbody tr:hover td { color: var(--lum-max); }
td.wrap-cell { white-space: normal; min-width: 18rem; }
.t-dim { color: var(--dim); }
.t-on { color: var(--lum-max); }

/* label frequency: a table with a rule in it, not a chart */
.bar { position: relative; display: block; height: 0.5rem; background: var(--amber-low); min-width: 1px; }
.bar--key { background: var(--amber); }
.barcell { width: 55%; padding-top: 0.72rem; padding-right: 0; }

/* --- grids --------------------------------------------------------------- */

.cols { display: grid; gap: clamp(1.4rem, 3vw, 2.6rem); }
@media (min-width: 60rem) {
  .cols--2 { grid-template-columns: 1fr 1fr; }
  /*
     CONTRACT: the first child holds a HEADING AND NOTHING ELSE. It is 13rem
     wide. Put prose, a field list or anything with its own internal columns in
     there and it will not wrap, it will collapse into a vertical stack of
     single words -- which is exactly what happened to the About page's "What
     this site is" section. Content goes in the second child. Two equal halves
     is `cols--2`.

     The label column was 20rem holding two words, which left a void the width
     of a paragraph beside every section. Narrower, and the heading now travels
     with its content instead of scrolling away from it -- so the column reads
     as a margin note rather than as space nobody filled.
  */
  .cols--side { grid-template-columns: minmax(0, 13rem) minmax(0, 1fr); }
  .cols--side > :first-child {
    position: sticky;
    top: calc(var(--masthead-h) + 1.6rem);
    align-self: start;
  }
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1px;
  background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
  margin: 0;
}
.stat {
  background: var(--ground);
  padding: 0.9rem 1rem;
  position: relative;
  transition: background 0.16s linear;
}
/* A tick in the corner of every cell, the way a readout marks a live channel. */
.stat::before {
  content: "";
  position: absolute; top: 0; left: 0;
  width: 8px; height: 1px;
  background: var(--amber-mid);
  transition: width 0.22s cubic-bezier(0.22, 0.85, 0.25, 1), background 0.22s linear;
}
.stats:hover .stat { background: var(--ground); }
.stat:hover { background: var(--ground-3); }
.stat:hover::before { width: 100%; background: var(--amber); }
.stat__v { font-family: var(--mono); font-size: 1.28rem; color: var(--lum-max); letter-spacing: -0.03em; display: block; line-height: 1.1; }
.stat__k { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--dim); display: block; margin-top: 0.4rem; }

/* --- player -------------------------------------------------------------- */

/*
   THE STATEMENT, set like the work's second channel.

   Channel 2 of the piece is the prompt rendered as monospaced text on black,
   frame-locked to the image. Setting the statement in that same face is not a
   costume: it is the one block on the site written in the work's own voice, so
   it gets the work's own typography. Looser leading and a shorter measure keep
   mono readable at length, which is the usual objection to it.
*/
.prose--statement {
  font-size: clamp(0.86rem, 0.82rem + 0.22vw, 0.98rem);
  line-height: 1.95;
  color: var(--lum-max);
  max-width: 34rem;
  text-shadow: var(--glow-cool);
}
.prose--statement p + p { margin-top: 1.5em; }
.prose--statement em { color: var(--amber); font-style: normal; }

/* --- landing: the stage and the filmstrip -------------------------------- */

/* Wider than the text column on purpose. An embedded player's resolution is
   chosen from its rendered size, so pixels on screen are pixels served. */
.stage { max-width: min(68rem, 100%); margin: 0 auto; }
.stage__note { max-width: 46rem; margin: 0.9rem auto 0; text-align: center; }

/*
   A scrollable run of stills across the whole pass. Full-bleed out of the
   text column on purpose -- it is the one element on the landing page allowed
   to break the measure, because the work is 43,200 frames wide and the page
   should say so before it says anything else.
*/
.filmstrip {
  margin-top: 1rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--amber-low) transparent;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4rem, #000 calc(100% - 4rem), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 4rem, #000 calc(100% - 4rem), transparent);
}
.filmstrip:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
.filmstrip__rail {
  display: flex;
  gap: 3px;
  /* Starts on the text column and bleeds off the right. Padding of just --gut
     put the first tile 360px left of every label on the page, which read as a
     misalignment rather than as a full-bleed. Aligned start, open end. */
  padding-left: max(var(--gut), calc((100% - var(--max)) / 2 + var(--gut)));
  padding-right: var(--gut);
  width: max-content;
}
.filmstrip .tile { flex: 0 0 auto; width: clamp(96px, 12vw, 150px); scroll-snap-align: center; }
.filmstrip .tile img { width: 100%; }
.filmstrip::-webkit-scrollbar { height: 6px; }
.filmstrip::-webkit-scrollbar-thumb { background: var(--amber-low); }
.filmstrip::-webkit-scrollbar-track { background: var(--rule-soft); }

.player { max-width: 34rem; }
.stage .player { max-width: none; }
.player__frame {
  position: relative;
  aspect-ratio: var(--ar, 8 / 7);
  background: var(--ground-2);
  border: 1px solid var(--rule);
}
.player__frame video, .player__frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  border: 0;
  background: #000;
}
.player__empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(1rem, 3vw, 1.8rem);
  background-image:
    linear-gradient(90deg, transparent 0 calc(50% - 0.5px), var(--rule) calc(50% - 0.5px) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}
.player__empty p { margin: 0; max-width: 26rem; color: var(--dim); font-size: 0.95rem; }
.player__empty .eyebrow { margin-bottom: 0.5rem; }
.player__meta {
  display: flex; flex-wrap: wrap; gap: 0.3rem 1.2rem;
  font-family: var(--mono); font-size: 0.68rem; color: var(--dim);
  margin-top: 0.6rem;
}

/* --- the gallery --------------------------------------------------------- */
/*
   Two geometries, measured rather than assumed. Pass stills come from a
   768x1344 channel and land at 514x900; treatment stills come from the
   1536x1344 side-by-side and land at 1028x900. build.py reads each file's
   real dimensions and writes them as width/height attributes, so the scroll
   height is correct before a single byte of image arrives.
*/

.wall {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(74px, 9vw, 116px), 1fr));
  gap: 2px;
}
/*
   Wide columns for stills that carry BOTH channels -- a paired view, or a
   treatment rendered from the 1536x1344 side-by-side. Those land at 1028x900
   against a pass channel's 514x900, so a column sized for the vertical still
   reduces them to a 118x103 sliver with two illegible halves. Roughly double
   the column, and the same amount of picture is actually visible.
*/
.wall--pair,
.wall--wide {
  grid-template-columns: repeat(auto-fill, minmax(clamp(150px, 18vw, 232px), 1fr));
  gap: 4px;
}

.tile {
  display: block;
  position: relative;
  text-decoration: none;
  background: var(--ground-2);
  line-height: 0;
  cursor: zoom-in;
  scroll-margin: 7rem 0 4rem;
}
.tile--link { cursor: pointer; }
.tile img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--ground-3);
  transition: opacity 0.14s linear;
}
.wall:hover .tile:not(:hover) img { opacity: 0.62; }
.tile:hover img, .tile:focus-visible img { outline: 1px solid var(--amber); outline-offset: -1px; }

/* Closing the lightbox returns here, and says so. */
.tile:target::after {
  content: "";
  position: absolute;
  inset: -2px;
  border: 1px solid var(--amber);
  pointer-events: none;
}

/* The filename is the provenance. Frame number always, timecode on wider tiles. */
.tile__f, .tile__t {
  display: block;
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.04em;
  line-height: 1.5;
  color: var(--dim);
  padding: 0.2rem 0.25rem 0;
}
.tile__t { display: none; padding-top: 0; color: var(--amber-mid); }
.tile:hover .tile__f, .tile--pair .tile__f { color: var(--lum-max); }
@media (min-width: 60rem) {
  .wall--pair .tile__t, .strip--wide .tile__t { display: block; }
}

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--rule-soft); }
.pair img { width: 100%; height: auto; display: block; }
.tile--pair { background: var(--ground-2); }

/* A short horizontal sample: the home page and the gallery index cards. */
.strip {
  display: grid;
  grid-auto-flow: column;
  /* A floor, so a wide still is never squeezed below legibility. The strip
     already scrolls, so exceeding the width costs nothing. */
  grid-auto-columns: minmax(var(--strip-min, 0px), 1fr);
  gap: 2px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.strip--wide-tiles { --strip-min: 168px; }
.strip--wide { gap: 3px; }
.strip .tile img { width: 100%; }

/* A frame a run produced, inside that run's panel. Provenance and picture in
   the same box, which is the point of putting it there. */
.run-still { margin: 1rem 0 0; }
.run-still a {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  text-decoration: none;
  font-family: var(--mono);
  font-size: var(--t-micro);
  color: var(--text);
}
.run-still img {
  width: 3.4rem;
  height: auto;
  flex: none;
  border: 1px solid var(--rule);
  background: var(--ground-3);
}
.run-still a:hover img { border-color: var(--amber); }
.run-still a:hover { color: var(--amber); }

.gcard { border-top: 1px solid var(--rule); padding: 1.6rem 0; }
.gcard:last-child { border-bottom: 1px solid var(--rule); }
.gcard__meta { font-family: var(--mono); font-size: 0.7rem; color: var(--dim); margin-left: auto; }
.gcard__foot {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem;
  font-family: var(--mono); font-size: 0.74rem; margin: 0.9rem 0 0;
}
.gcard__views { display: flex; flex-wrap: wrap; gap: 0 1rem; }
.gcard__foot a { text-decoration: none; }
.gcard__foot a:hover { text-decoration: underline; }
.gcard__foot > a:last-child { margin-left: auto; color: var(--dim); }
.gcard__foot > a:last-child:hover { color: var(--amber); }

.views {
  display: flex; flex-wrap: wrap; gap: 0 1.1rem;
  font-family: var(--mono); font-size: 0.78rem;
  margin: 0 0 1.6rem; padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--rule);
}
.views a { color: var(--dim); text-decoration: none; }
.views a:hover { color: var(--lum-max); }
.views [aria-current="page"] { color: var(--amber); }
.views [aria-current="page"]::before { content: "\00b7"; margin-right: 0.3em; }

/* --- the lightbox -------------------------------------------------------- */
/*
   :target, not JavaScript. The consequence is better than the constraint: the
   fragment IS the source frame number, so every still on this site is a URL
   that can be sent to someone, browser Back closes it, and the whole thing
   survives with scripting off. Hidden panels are display:none, so none of the
   4,090 lightbox images load until one is opened.
*/

.lb {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
  padding: clamp(0.75rem, 3vw, 2.5rem);
}
.lb:target {
  display: grid;
  align-content: center;
  animation: lb-in 0.18s cubic-bezier(0.22, 0.85, 0.25, 1) both;
}
@keyframes lb-in { from { opacity: 0; } }

.lb__scrim {
  position: fixed;
  inset: 0;
  background: rgba(6, 6, 7, 0.94);
  cursor: zoom-out;
}

.lb__inner {
  position: relative;
  display: grid;
  grid-template-columns: 2.75rem minmax(0, auto) minmax(0, 20rem) 2.75rem;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 2.5vw, 2rem);
  max-width: var(--max);
  margin: 0 auto;
  width: 100%;
}

.lb__fig {
  margin: 0;
  line-height: 0;
  animation: lb-rise 0.22s cubic-bezier(0.22, 0.85, 0.25, 1) both;
}
@keyframes lb-rise { from { transform: translateY(6px) scale(0.985); } }

/*
   Never upscale past native, never crop, never distort.

   `height: auto` is load-bearing, not tidiness. These images carry width and
   height attributes, so with only max-width and max-height set, the two
   clamps apply INDEPENDENTLY: a 1028x900 still hit max-width at 680 and
   max-height at 747 and rendered 680x747 -- squeezed to 0.910 from 1.142.
   A vertical still hid the bug completely, because only the height constraint
   ever bound. With both dimensions auto the browser reduces proportionally.
*/
.lb__fig img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(82vh, 900px);
}
.lb__fig .pair { display: flex; gap: 1px; background: var(--rule); max-width: 100%; }
.lb__fig .pair img { width: auto; height: auto; max-width: 50%; max-height: min(82vh, 900px); }

.lb__meta { font-family: var(--mono); min-width: 0; }
.lb__id {
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  letter-spacing: -0.04em;
  color: var(--lum-max);
  margin: 0;
  line-height: 1;
}
.lb__sub { font-size: 0.74rem; color: var(--dim); margin: 0.45rem 0 1.2rem; }
.lbm {
  display: grid;
  grid-template-columns: minmax(6rem, 8rem) minmax(0, 1fr);
  gap: 0 1.1rem;
  margin: 0;
  font-size: 0.74rem;
  line-height: 1.5;
}
.lbm dt { color: var(--dim); padding: 0.45rem 0; border-top: 1px solid var(--rule-soft); }
.lbm dd { color: var(--text); margin: 0; padding: 0.45rem 0; border-top: 1px solid var(--rule-soft); overflow-wrap: anywhere; }
.lbm dd strong { color: var(--lum-max); font-weight: 500; }

/* The prompt line. Long enough that a narrow value column would shred it, so
   the row spans and the label sits above the text. */
.lbm__w { grid-column: 1 / -1; }
dt.lbm__w {
  padding-bottom: 0;
  font-size: var(--t-micro);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
dd.lbm__w { border-top: 0; padding-top: 0.2rem; }
.lbq { color: var(--lum-max); }
.lb__pos {
  font-size: 0.72rem; color: var(--lum-max);
  margin: 1.2rem 0 0; padding-top: 0.7rem;
  border-top: 1px solid var(--rule);
}

/*
   Only on the readout: the two strings side by side. This is the one place the
   site can show what the audience read and what the model was told at the same
   time, on the same frame, and that difference is the piece's argument.
*/
.lbp { margin-top: 1.1rem; }
.lbp__k {
  display: block;
  font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--dim); margin-top: 0.9rem;
}
.lbp__t {
  font-size: 0.78rem; line-height: 1.5; color: var(--text);
  margin: 0.3rem 0 0;
}
.lbp__t--sent { color: var(--dim); }
.lbp__x { color: var(--amber); }

/* Closing the lightbox returns to the row it was opened from. */
.rt tr:target td { background: var(--amber-wash); color: var(--lum-max); }
.rt tr:target .rt__f { color: var(--amber); }

.lb__step {
  display: grid;
  place-items: center;
  align-self: stretch;
  font-family: var(--mono);
  font-size: 1.9rem;
  line-height: 1;
  color: var(--dim);
  text-decoration: none;
  border: 1px solid transparent;
  transition: color 0.12s linear, border-color 0.12s linear;
}
.lb__step:hover { color: var(--lum-max); border-color: var(--rule); }

.lb__close {
  position: absolute;
  top: clamp(0.75rem, 3vw, 2.5rem);
  right: clamp(0.75rem, 3vw, 2.5rem);
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  text-decoration: none;
}
.lb__close:hover { color: var(--amber); }

@media (max-width: 58rem) {
  .lb:target { align-content: start; overflow-y: auto; }
  .lb__inner {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "fig fig" "meta meta" "prev next";
    gap: 1rem;
  }
  .lb__fig { grid-area: fig; justify-self: center; }
  .lb__fig img, .lb__fig .pair img { max-height: 54vh; }
  .lb__meta { grid-area: meta; }
  /* Thumb-sized, not pointer-sized: 44px is the floor for a touch target and
     these are the only controls in the panel. */
  .lb__step {
    border: 1px solid var(--rule);
    background: var(--ground-2);
    min-height: 3rem;
    font-size: 1.4rem;
    color: var(--text);
  }
  .lb__step:first-of-type { grid-area: prev; }
  .lb__step:last-of-type { grid-area: next; }
  .lb__close {
    position: static;
    display: grid; place-items: center;
    min-height: 2.6rem; padding: 0 1rem;
    margin: 0 0 0.9rem auto; width: max-content;
    border: 1px solid var(--rule);
  }
}

/* Coarse pointers get no hover-only affordances and a bigger caret. */
@media (hover: none) {
  .wall:hover .tile:not(:hover) img { opacity: 1; }
  .tile__t { display: block; }
}

/* --- the prompt readout -------------------------------------------------- */
/*
   A spreadsheet of the work: one row per frame, paginated by minute. The
   closest thing on the site to a terminal, which is the right register --
   this is the text channel, and the text channel is a monospaced render of
   the same lines.
*/

/*
   NOT a scroll container, deliberately.

   overflow-x: auto here made this box the sticky containing block, so the
   sticky header resolved against the table instead of the viewport: top:3.4rem
   pushed it 3.4rem DOWN from the table's own top, leaving row 1 stranded above
   the header and row 2 hidden behind it. The readout wraps its prompt column
   instead of scrolling sideways, so it does not need the overflow at all.
*/
.tablewrap--rt { overflow-x: visible; }

.rt { font-size: 0.74rem; }
/* Sits directly under the sticky masthead. 1,440 rows need their column names. */
.rt th { position: sticky; top: var(--masthead-h); background: var(--ground); z-index: 5; }
.rt td { padding: 0.3rem 1.1rem 0.3rem 0; border-top: 1px solid transparent; }
.rt tbody tr:hover td { background: var(--ground-2); color: var(--lum-max); }
.rt__f { color: var(--dim); }
.rt tbody tr:hover .rt__f { color: var(--amber); }
.rt__p { white-space: normal; min-width: 22rem; color: var(--text); }
.tile { scroll-margin-top: calc(var(--masthead-h) + 2rem); }
.rt tr { scroll-margin-top: calc(var(--masthead-h) + 3rem); }

/* Narrow: the masthead wraps and gets taller, and the table has to shed a
   column rather than scroll sideways. */
/* Tied to the nav collapsing, so it moves with that breakpoint, not the
   table one: once the links are behind a burger the masthead is one line. */
@media (max-width: 59.99rem) {
  :root { --masthead-h: 3.5rem; }
}

/* A table concern, independent of the nav: below this the readout sheds its
   position column rather than scrolling sideways. */
@media (max-width: 48rem) {
  .rt th:nth-child(4), .rt td:nth-child(4) { display: none; }
  .rt__p { min-width: 0; }
}

/* A rule where the viewpoint era changes: the 72/36/72/36/48/36/24/36 rhythm
   is the schedule's actual structure, so it is drawn rather than described. */
.rt tr.era td { border-top: 1px solid var(--rule); }
/* Full denoise: the feedback chain is cut and the frame starts clean. */
.rt tr.cut td { color: var(--lum-max); }
.rt tr.cut .rt__f::after { content: " \00b7 cut"; color: var(--amber); }

.minutes {
  display: flex; flex-wrap: wrap; gap: 0.3rem;
  font-family: var(--mono); font-size: 0.74rem; margin: 0.9rem 0 0;
}
.minutes a, .minutes [aria-current] {
  display: grid; place-items: center;
  min-width: 2.1rem; padding: 0.3rem 0.35rem;
  border: 1px solid var(--rule);
  text-decoration: none; color: var(--dim);
}
.minutes a:hover { border-color: var(--amber-low); color: var(--lum-max); }
.minutes [aria-current] { border-color: var(--amber); color: var(--amber); }
.minutes--strip { margin-top: 1.4rem; }

/* --- design system page -------------------------------------------------- */

.swatches {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1px;
  background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
}
.sw {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  grid-template-areas: "chip name" "chip hex" "chip what";
  align-items: center;
  gap: 0 0.85rem;
  background: var(--ground);
  padding: 0.75rem 0.9rem;
  font-size: var(--t-micro);
}
.sw__chip {
  grid-area: chip;
  align-self: stretch;
  min-height: 3.1rem;
  border: 1px solid var(--rule);
}
.sw__n { grid-area: name; color: var(--lum-max); }
.sw__h { grid-area: hex; color: var(--amber-mid); }
.sw__w { grid-area: what; color: var(--dim); line-height: 1.5; }

/* --- links list ---------------------------------------------------------- */

.jump { display: grid; gap: 1px; background: var(--rule-soft); border-block: 1px solid var(--rule-soft); }
.jump a {
  background: var(--ground);
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline; gap: 1rem;
  padding: 0.85rem 0.2rem;
  text-decoration: none;
  font-family: var(--mono); font-size: 0.82rem;
  color: var(--lum-max);
}
.jump a { position: relative; padding-left: 1.6rem; transition: background 0.14s linear; }
/* A caret that advances on hover. The list reads as a command menu. */
.jump a::before {
  content: "\203A";
  position: absolute; left: 0.35rem;
  color: var(--amber-low);
  transition: transform 0.18s cubic-bezier(0.22, 0.85, 0.25, 1), color 0.18s linear;
}
.jump a:hover { background: var(--ground-3); color: var(--amber); }
.jump a:hover::before { transform: translateX(0.28rem); color: var(--amber); }
.jump .jump__d { color: var(--dim); font-size: 0.72rem; text-align: right; }
.jump a:hover .jump__d { color: var(--dim); }

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

.foot {
  border-top: 1px solid var(--rule);
  margin-top: clamp(3rem, 8vw, 6rem);
  padding: clamp(2.2rem, 5vw, 3.4rem) var(--gut) clamp(1.4rem, 3vw, 2rem);
}
.foot__top {
  max-width: var(--max); margin: 0 auto;
  display: grid; gap: clamp(2rem, 5vw, 3.5rem);
}
@media (min-width: 58rem) { .foot__top { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); } }

.foot__lede { margin: 0 0 0.9rem; color: var(--dim); max-width: 24rem; font-size: 0.92rem; }
.foot__big {
  font-family: var(--mono);
  font-size: clamp(1.1rem, 2.6vw, 1.55rem);
  letter-spacing: -0.03em;
  text-decoration: none;
}
.foot__big:hover { text-decoration: underline; text-decoration-color: var(--amber); }

.foot__nav { display: grid; gap: 1.8rem 2.5rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
.foot__set { display: flex; flex-direction: column; gap: 0.35rem; align-items: start; }
.foot__k {
  font-family: var(--mono); font-size: var(--t-micro);
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--dim); margin: 0 0 0.35rem;
}
.foot__set a {
  font-family: var(--mono); font-size: var(--t-data);
  color: var(--text); text-decoration: none;
}
.foot__set a:hover { color: var(--amber); }

.foot__bar {
  max-width: var(--max); margin: clamp(2rem, 5vw, 3rem) auto 0;
  padding-top: 1.2rem; border-top: 1px solid var(--rule-soft);
  display: grid; gap: 1rem;
}
@media (min-width: 58rem) { .foot__bar { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); gap: 2.5rem; } }
.foot__meta {
  margin: 0; font-family: var(--mono); font-size: var(--t-micro);
  display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.8rem;
}
.foot__meta dt { color: var(--dim); }
.foot__meta dd { color: var(--text); margin: 0; overflow-wrap: anywhere; }
.foot__note { font-size: 0.82rem; color: var(--dim); margin: 0; max-width: 42rem; }

/* --- pagination between record pages ------------------------------------- */

.updown {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem;
  font-family: var(--mono); font-size: 0.78rem;
  margin-top: 2.6rem; padding-top: 1rem; border-top: 1px solid var(--rule);
}
.updown a { text-decoration: none; }
.updown a:hover { text-decoration: underline; }

/* --- responsive ---------------------------------------------------------- */

/* --- mobile navigation --------------------------------------------------- */

/*
   60rem = 960px, not 768px.

   The header's content measures about 768px wide -- brand, six mono links,
   gaps and gutters -- so a 768px breakpoint meant the nav collapsed at the
   exact width it started wrapping, and was visibly cramped for the 200px
   above that. It also meant a window had to be dragged very narrow before
   anything happened, which reads as "the mobile view is broken" rather than
   "the breakpoint is low". Collapse early; the header is calmer for it.
*/
@media (max-width: 59.99rem) {
  .masthead { padding-right: calc(var(--gut) + 2.6rem); }

  .nav-burger {
    display: grid;
    place-items: center;
    position: absolute;
    top: 0.55rem;
    right: calc(var(--gut) - 0.4rem);
    width: 2.6rem;
    height: 2.6rem;
    cursor: pointer;
  }
  /* Three rules that become an X. */
  .nav-burger span,
  .nav-burger span::before,
  .nav-burger span::after {
    content: "";
    display: block;
    width: 1.15rem;
    height: 1px;
    background: var(--lum-max);
    transition: transform 0.22s cubic-bezier(0.22, 0.85, 0.25, 1), opacity 0.14s linear;
  }
  .nav-burger span::before { transform: translateY(-0.4rem); }
  .nav-burger span::after { transform: translateY(0.4rem); }

  .nav-check:checked ~ .nav-burger span { background: transparent; }
  .nav-check:checked ~ .nav-burger span::before { transform: rotate(45deg); background: var(--amber); }
  .nav-check:checked ~ .nav-burger span::after { transform: rotate(-45deg); background: var(--amber); }
  .nav-check:focus-visible ~ .nav-burger { outline: 2px solid var(--amber); outline-offset: 2px; }

  .nav {
    display: none;
    width: 100%;
    margin-left: 0;
    flex-direction: column;
    gap: 0;
    padding-top: 0.6rem;
  }
  .nav-check:checked ~ .nav { display: flex; }
  .nav a {
    padding: 0.7rem 0;
    border-top: 1px solid var(--rule-soft);
    font-size: 0.86rem;
  }
}

@media (max-width: 42rem) {
  .ruler__row { grid-template-columns: 1fr auto; row-gap: 0.25rem; }
  .ruler__track { grid-column: 1 / -1; }
  .fields { grid-template-columns: 1fr; gap: 0; }
  .fields .k { padding-bottom: 0; border-top: 1px solid var(--rule-soft); }
  .fields .v { padding-top: 0.15rem; border-top: 0; padding-bottom: 0.7rem; }
  .nav { width: 100%; margin-left: 0; }
  .jump a { grid-template-columns: 1fr; gap: 0.25rem; }
  .jump .jump__d { text-align: left; }
  .display { letter-spacing: -0.03em; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-delay: 0ms !important; transition-duration: 0.001ms !important; }
}

@media print {
  body::before { display: none; }
  body { background: #fff; color: #000; }
  .masthead, .nav, .updown { display: none; }
}
