/* Math Art companion site.
 *
 * One stylesheet, no framework, no build step. The palette is dark
 * because every subject is a lit object photographed against black
 * velvet in the documentation renders, and the thumbnails in
 * web/thumbs/ carry that black background with them -- a light page
 * would frame each tile in a hard black square.
 */

:root {
  --bg: #0b0d10;
  --bg-raised: #14171c;
  --bg-sunken: #080a0c;
  --line: #262b33;
  --line-soft: #1c2027;
  --ink: #e8eaee;
  --ink-dim: #9aa3af;
  --ink-faint: #6b7480;
  --accent: #6fb3f2;
  --accent-dim: #2b4a68;
  --radius: 10px;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
}

* { box-sizing: border-box; }

/* `hidden` must win.
 *
 * The browser's own [hidden] rule is display:none at UA specificity, so
 * ANY class rule that sets display beats it. `.grid { display: grid }`
 * did exactly that: switching to the clustered view set grid.hidden and
 * the grid stayed on screen, 12,000 pixels of it, below the canvas. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); }

/* ---------------------------------------------------------- home */

.hero {
  max-width: 62rem;
  margin: 0 auto;
  padding: 5rem 1.5rem 2.5rem;
}

.hero h1 {
  font-family: var(--serif);
  font-size: clamp(2.6rem, 7vw, 4.2rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 0 0 0.6rem;
}

.lede {
  font-size: clamp(1.02rem, 2vw, 1.2rem);
  color: var(--ink-dim);
  max-width: 44rem;
  margin: 0;
}

.modules {
  max-width: 62rem;
  margin: 0 auto;
  padding: 1rem 1.5rem 4rem;
  display: grid;
  /* Cards do not stretch to fill the row: the page lists only the modules
   * that exist, so with one module a full-width card would read as a
   * banner rather than as one of a set. */
  grid-template-columns: repeat(auto-fill, minmax(17rem, 22rem));
  gap: 1.25rem;
  align-items: start;
}

.module-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s, transform 0.15s;
}

.module-card.live:hover {
  border-color: var(--accent-dim);
  transform: translateY(-2px);
}

.module-card img {
  width: 100%;
  height: auto;
  display: block;
  background: #000;
}

.module-card-body { padding: 1.1rem 1.2rem 1.3rem; }

.module-card h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.4rem;
  margin: 0 0 0.15rem;
}

.module-count {
  margin: 0 0 0.6rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-faint);
}

.module-card p:last-child {
  margin: 0;
  font-size: 0.92rem;
  color: var(--ink-dim);
}

/* --------------------------------------------------------- chrome */

.site-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1.5rem;
  border-bottom: 1px solid var(--line-soft);
}

.brand {
  font-family: var(--serif);
  font-size: 1.15rem;
  text-decoration: none;
  color: var(--ink);
}

.site-head nav a {
  font-size: 0.9rem;
  color: var(--ink-dim);
  text-decoration: none;
}
.site-head nav a:hover { color: var(--accent); }

.site-foot {
  border-top: 1px solid var(--line-soft);
  padding: 1.5rem;
  color: var(--ink-faint);
  font-size: 0.85rem;
  text-align: center;
}

.module-intro {
  padding: 2rem 1.5rem 0.5rem;
  max-width: 62rem;
}

.module-intro h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  margin: 0 0 0.35rem;
}

.module-intro p {
  margin: 0;
  color: var(--ink-dim);
  max-width: 46rem;
}

/* --------------------------------------------------- module layout */

/* Viewer left, catalogue right. The viewer column is CAPPED and the
 * catalogue takes every additional pixel, so widening the window buys
 * more of the catalogue rather than a bigger model -- the model is
 * already legible at this size and the list is what benefits from room.
 */
.module-layout {
  display: grid;
  grid-template-columns: minmax(19rem, 34rem) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
  padding: 1.5rem;
  max-width: 110rem;
  margin: 0 auto;
}

/* The viewer stays put while the page scrolls, so the model remains in
 * view as the catalogue is browsed past it -- clicking a tile 300 solids
 * down should not update something off-screen.
 *
 * It is bounded and scrolls internally rather than being sticky at its
 * natural height: the detail panel is often taller than the viewport, and
 * a sticky element taller than the viewport pins its top and puts its
 * bottom permanently out of reach. */
.workspace-sticky {
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

@media (max-width: 62rem) {
  /* minmax(0, …), not 1fr: a bare 1fr is minmax(AUTO, 1fr), and that
   * auto minimum lets a wide equation stretch the single column on a
   * narrow window -- the same failure the two-column layout had. */
  .module-layout { grid-template-columns: minmax(0, 1fr); }
  .workspace-sticky {
    position: static;
    max-height: none;
    overflow: visible;
  }
  .catalog-head { position: static !important; }
}

/* -------------------------------------------------------- catalog */

/* The catalogue has NO inner scroll region: it flows down the page, so
 * the browser's own scrollbar is what moves through it. An inner scroll
 * box meant the window scrollbar did nothing useful here and the list had
 * to be scrolled in a second, smaller gesture. */
.catalog {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
}

/* Search and facets stick to the top of the viewport as the page scrolls,
 * so filtering stays reachable however far down the list you are. */
.catalog-head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-raised);
  padding-bottom: 0.5rem;
  margin: -0.25rem 0 0.25rem;
  padding-top: 0.25rem;
}

.catalog-controls {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
}

.search, .sort, .convex {
  width: 100%;
  padding: 0.5rem 0.65rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: 7px;
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
}

.search:focus, .sort:focus, .convex:focus {
  outline: 2px solid var(--accent-dim);
  outline-offset: 1px;
}

.facet { margin-bottom: 0.85rem; }

.facet h3 {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  margin: 0 0 0.4rem;
  font-weight: 600;
}

.chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }

.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.22rem 0.5rem;
  font: inherit;
  font-size: 0.8rem;
  color: var(--ink-dim);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
}

.chip:hover { border-color: var(--accent-dim); color: var(--ink); }

.chip.on {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: #fff;
}

.chip-count { font-size: 0.7rem; color: var(--ink-faint); }
.chip.on .chip-count { color: #cfe6ff; }

.catalog-status {
  margin: 0.4rem 0 0.7rem;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  gap: 0.5rem;
}

.tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 0.35rem 0.25rem 0.5rem;
  background: var(--bg-sunken);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  color: var(--ink-dim);
  text-align: center;
}

.tile:hover { border-color: var(--line); color: var(--ink); }

.tile.on {
  border-color: var(--accent);
  background: #101922;
  color: var(--ink);
}

.tile img {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border-radius: 5px;
  background: #000;
  object-fit: cover;
}

.tile img.missing { visibility: hidden; }

.tile-name {
  font-size: 0.72rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.tile-meta {
  font-family: var(--mono);
  font-size: 0.63rem;
  color: var(--ink-faint);
}

.empty { color: var(--ink-faint); grid-column: 1 / -1; }

/* ---------------------------------------------------------- stage */

/* THE COLUMN MUST NOT BE SIZED BY ITS CONTENTS.
 *
 * This is a grid, and a grid's implicit column is `auto` -- it sizes to
 * the widest thing in it. The detail panel is in it, and a long implicit
 * equation is very wide, so the column grew, the stage stretched to
 * match, and the canvas became wider than the panel it sits in. The
 * surface is drawn centred in the canvas, so it sailed off to the right
 * and out of view.
 *
 * minmax(0, 1fr) says the track may never exceed the space available,
 * whatever is inside it. The equation then scrolls inside its own box,
 * which is what .eqn-math's overflow-x is for, instead of pushing the
 * viewer around. */
.workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
  min-width: 0;
}

.stage-wrap {
  margin: 0;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

#stage {
  display: block;
  width: 100%;
  /* Square-ish in the narrower column, and short enough that the stage
   * plus its controls leave room for some of the detail panel above the
   * fold. */
  height: min(46vh, 26rem);
  background:
    radial-gradient(ellipse at 50% 42%, #171b21 0%, #0a0c0f 72%);
  touch-action: none;
  cursor: grab;
}

#stage:active { cursor: grabbing; }

.stage-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.8rem;
  border-top: 1px solid var(--line-soft);
}

.segmented { display: inline-flex; }

.seg {
  font: inherit;
  font-size: 0.82rem;
  padding: 0.3rem 0.7rem;
  background: var(--bg-sunken);
  color: var(--ink-dim);
  border: 1px solid var(--line);
  cursor: pointer;
}

.seg:first-child { border-radius: 7px 0 0 7px; }
.seg:last-child { border-radius: 0 7px 7px 0; }
.seg + .seg { border-left: none; }
.seg:hover { color: var(--ink); }
.seg.on { background: var(--accent-dim); color: #fff; border-color: var(--accent); }

.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.82rem;
  color: var(--ink-dim);
  cursor: pointer;
}

.ghost {
  font: inherit;
  font-size: 0.82rem;
  margin-left: auto;
  padding: 0.3rem 0.7rem;
  background: transparent;
  color: var(--ink-dim);
  border: 1px solid var(--line);
  border-radius: 7px;
  cursor: pointer;
}
.ghost:hover { color: var(--ink); border-color: var(--accent-dim); }

figcaption {
  padding: 0 0.85rem 0.7rem;
  font-family: var(--serif);
  font-size: 1.05rem;
  color: var(--ink-dim);
}

/* --------------------------------------------------------- detail */

.detail {
  /* Also a grid item, and also not allowed to size its own track: see
   * the note on .workspace. */
  min-width: 0;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.4rem 1.5rem 1.8rem;
}

.detail-head h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.7rem;
  margin: 0 0 0.15rem;
}

.alt-names {
  margin: 0 0 0.6rem;
  color: var(--ink-faint);
  font-size: 0.88rem;
}

.tag-row { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.4rem; }

.tag {
  font-size: 0.7rem;
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  color: var(--ink-faint);
}

.counts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  margin: 1.1rem 0 0.4rem;
  max-width: 24rem;
}

.count-cell {
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  padding: 0.55rem 0.4rem;
  text-align: center;
}

.count-value {
  font-family: var(--mono);
  font-size: 1.25rem;
  color: var(--ink);
}

.count-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}

/* Stat sections in two columns wherever there is room. auto-fit collapses
 * to one column in a narrow panel without a media query, so this tracks
 * the panel's real width rather than the window's. */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0 1.6rem;
  align-items: start;
}

.panel-section { margin-top: 1.5rem; min-width: 0; }

.panel-section h3 {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  margin: 0 0 0.5rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--line-soft);
  font-weight: 600;
}

.panel-section h4 {
  font-size: 0.8rem;
  color: var(--ink-dim);
  margin: 0.9rem 0 0.3rem;
}

/* Labels sized for the two-column panel grid: a wider minimum pushed the
 * values off the end of a 13rem column. */
.deflist {
  display: grid;
  grid-template-columns: minmax(5.5rem, auto) minmax(0, 1fr);
  gap: 0.2rem 0.8rem;
  margin: 0;
  font-size: 0.86rem;
}

.deflist dt { color: var(--ink-faint); }
.deflist dd { margin: 0; font-family: var(--mono); font-size: 0.85rem; }

/* Metrics use the same label/value columns as the definition lists above
 * them, so the values line up down the panel instead of drifting to the
 * right edge at a width set by whatever the longest label happens to be. */
.metric {
  display: grid;
  grid-template-columns: minmax(5.5rem, auto) minmax(0, 1fr);
  gap: 0.2rem 0.8rem;
  align-items: baseline;
  padding: 0.24rem 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.86rem;
}

.metric:last-child { border-bottom: none; }
.metric-label { color: var(--ink-faint); }
.metric-value { text-align: left; font-family: var(--mono); }

/* The exact form is the true statement; the decimal is a measurement
 * of it, so it reads quieter. */
.exact { color: var(--ink); font-size: 0.92rem; }
.approx { color: var(--ink-faint); font-size: 0.8rem; }

.metric-note {
  margin: 0 0 0.4rem;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.dual-link { margin: 0.6rem 0 0; font-size: 0.9rem; }

.ext-links { display: flex; gap: 0.9rem; margin: 0.6rem 0 0; font-size: 0.85rem; }

.provenance {
  margin: 0.5rem 0 0;
  font-size: 0.82rem;
  color: var(--ink-faint);
  line-height: 1.5;
}

.sources {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.sources li { margin-bottom: 0.25rem; }

/* ------------------------------------------------- surfaces module */

/* A surface's definition is a formula, not a value, so it gets its own
 * row shape: a narrow label and the expression running full width under
 * it if need be. The deflist's two rigid columns would either truncate a
 * parametrisation or force the labels absurdly wide. */
.formula {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.15rem 0.5rem;
  align-items: baseline;
  padding: 0.16rem 0;
  font-size: 0.86rem;
}

.formula-label {
  color: var(--ink-faint);
  font-style: italic;
  font-family: var(--serif);
}

.formula code {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* Said plainly, because an empty stage otherwise reads as a page that
 * failed to load rather than a surface nothing builds yet. */
.notice {
  margin: 0.9rem 0 0;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-dim);
  border-radius: 6px;
  background: var(--bg-sunken);
  color: var(--ink-dim);
  font-size: 0.86rem;
}

#stage.empty { opacity: 0.25; }

/* Stands in for a tile whose surface no generator builds, so the grid
 * keeps its rhythm instead of collapsing those cards to a line of text. */
.tile-blank {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 5px;
  background:
    repeating-linear-gradient(45deg, #0d1013 0 6px, #101419 6px 12px);
}

/* ------------------------------------------------ clustered view */

.view-modes { margin-bottom: 0.7rem; }

.cluster-wrap {
  position: relative;
  width: 100%;
  height: min(76vh, 46rem);
  background:
    radial-gradient(ellipse at 50% 45%, #12161b 0%, #0a0c0f 78%);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.cluster-canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
}

.cluster-canvas:active { cursor: grabbing; }

/* Definition notes run to a hundred words; the two-column panel grid
 * would set them three or four words to a line. They sit below it, at
 * the panel's full width, with a measure that stays readable. */
.detail-notes { max-width: 46rem; }

.detail-notes .provenance {
  font-size: 0.88rem;
  line-height: 1.62;
  color: var(--ink-dim);
}

/* Inline maths inside prose: ASCII expressions read better set apart
 * from the surrounding sentence. */
.detail-notes code,
.provenance code {
  font-family: var(--mono);
  font-size: 0.85em;
  color: var(--ink);
  background: var(--bg-sunken);
  padding: 0.05em 0.3em;
  border-radius: 3px;
}

/* ------------------------------------------------------------------
 * The generated object and index pages (web/catalog/), written by
 * tools/site_seo.py.
 *
 * These are static documents rather than app views: one object, its
 * properties and its sources, with a link into the interactive module.
 * They exist so that each surface and solid has a real URL a search
 * engine can index, and they are read by people who arrive from one,
 * so they get a plain centred measure rather than the module layout.
 * ------------------------------------------------------------------ */

.object-page,
.object-index {
  max-width: 62rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
}

.object-head {
  display: flex;
  gap: 1.5rem;
  align-items: flex-start;
  flex-wrap: wrap;
  margin-bottom: 2rem;
}

.object-figure {
  width: 260px;
  height: 260px;
  flex: 0 0 auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-sunken);
}

/* A record with no mesh still gets a figure slot, so the page does not
 * reflow into a different shape for the sake of a missing tile. */
.object-figure-blank {
  display: grid;
  place-items: center;
  color: var(--ink-faint);
  font-size: 0.85rem;
}

.object-lead { flex: 1 1 22rem; min-width: 0; }
.object-lead h1 { margin: 0 0 0.5rem; font-size: 1.9rem; }

.object-summary {
  color: var(--ink-dim);
  font-size: 1.02rem;
  line-height: 1.6;
  margin: 0 0 1rem;
  max-width: 42rem;
}

.object-open {
  display: inline-block;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  color: var(--accent);
  text-decoration: none;
  font-size: 0.92rem;
}
.object-open:hover { background: var(--accent-dim); color: var(--ink); }

.object-page .panel-section { margin-bottom: 2rem; }
.object-page .panel-section h2 {
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 0.4rem;
  margin-bottom: 0.9rem;
}

/* Two columns of term/value, which is what the properties actually are;
 * the module detail panel uses the same shape. */
.object-page .deflist {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1.25rem;
  margin: 0;
  font-size: 0.92rem;
}
.object-page .deflist dt { color: var(--ink-faint); }
.object-page .deflist dd { margin: 0; color: var(--ink); }

/* The A-Z index. Multi-column so 473 names are a page rather than a
 * scroll, and grouped by initial so the eye can jump. */
.index-group { margin-bottom: 1.75rem; }
.index-group h2 {
  font-size: 1.1rem;
  color: var(--accent);
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 0.3rem;
  margin: 0 0 0.75rem;
}
.index-list {
  columns: 4 14rem;
  column-gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.92rem;
}
.index-list li { break-inside: avoid; margin-bottom: 0.3rem; }
.index-list a { color: var(--ink-dim); text-decoration: none; }
.index-list a:hover { color: var(--accent); text-decoration: underline; }

/* The link from a module page across to its static index. */
.index-link {
  display: block;
  margin-top: 0.75rem;
  font-size: 0.88rem;
  color: var(--ink-faint);
}
.index-link a { color: var(--ink-dim); }

/* ------------------------------------------------------------------
 * Descriptions and formulae.
 *
 * NOTE ON THE CLASS NAME. These are `.eqn`, not `.formula`, because
 * `.formula` was already taken -- by the definition rows further up,
 * which are a grid with a 1.6rem label column. Reusing the name put
 * the caption, the relation and the copy button into that column: the
 * label wrapped to a stack, and the relation landed on top of it.
 *
 * The maths is MathML, rendered by the browser itself -- there is no
 * library here and no web font. What the stylesheet has to do is give a
 * long equation somewhere to go: a decic runs to several hundred
 * characters, and left to itself it would either force the page to
 * scroll sideways or be clipped. It scrolls inside its own box.
 * ------------------------------------------------------------------ */

.description-prose,
.object-prose {
  color: var(--ink);
  line-height: 1.65;
  max-width: 46rem;
  margin: 0 0 0.9rem;
}

/* Under curated prose the templated line is supporting detail, so it is
 * set quieter rather than competing with the paragraph above it. */
.description-summary {
  color: var(--ink-dim);
  font-size: 0.9rem;
  line-height: 1.55;
  max-width: 46rem;
  margin: 0 0 0.9rem;
}

.eqn {
  min-width: 0;
  max-width: 100%;
  margin: 0 0 1.1rem;
  padding: 0.75rem 0.9rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}

.eqn figcaption,
.eqn .eqn-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  margin-bottom: 0.45rem;
}

/* The equation, and the one rule that matters: it may scroll, the page
 * may not. */
.eqn-math {
  /* The equation scrolls; the page and the viewer do not move. */
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 0.2rem;
  color: var(--ink);
}

.eqn-math math {
  font-size: 1.05rem;
  /* Some engines still centre display maths in the available width,
   * which puts a long equation's start off-screen to the left. */
  text-align: left;
}

.eqn-copy {
  margin-top: 0.5rem;
  padding: 0.25rem 0.6rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
}
.eqn-copy:hover { color: var(--accent); border-color: var(--accent-dim); }

/* A browser with no MathML support drops to the LaTeX, which should at
 * least look like code rather than like prose. */
.eqn-math code {
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--ink-dim);
  white-space: pre-wrap;
  word-break: break-word;
}

/* The stage bar's action button (STL export), and the line it writes
 * after a download. The bar already holds the viewpoint segments and the
 * two switches, so this sits at the end of it rather than starting a
 * row of its own. */
.stage-action {
  margin-left: auto;
  padding: 0.3rem 0.7rem;
  font: inherit;
  font-size: 0.8rem;
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}
.stage-action:hover:not(:disabled) {
  color: var(--accent);
  border-color: var(--accent-dim);
}
.stage-action:disabled { opacity: 0.4; cursor: default; }

.stage-note {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--ink-faint);
  max-width: 42rem;
}

/* ------------------------------------------------------------------
 * The periodic table of polyhedra.
 *
 * Columns are ragged -- 9 solids in the noble-gas column against 19 in
 * the widest -- so the table is a row of independent columns rather than
 * a grid with empty cells. A real periodic table is ragged too; forcing
 * every column to the same height would invent alignments that mean
 * nothing, since row N of one column has no relation to row N of the
 * next.
 * ------------------------------------------------------------------ */

.periodic-page {
  max-width: 100rem;
  margin: 0 auto;
  padding: 0 1.25rem 3rem;
}

.periodic-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.1rem;
  margin: 0 0 1rem;
  font-size: 0.82rem;
  color: var(--ink-dim);
}
.legend-item { display: inline-flex; align-items: center; gap: 0.4rem; }
.legend-swatch {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 3px;
  border: 1px solid var(--line);
}

/* The table scrolls sideways rather than the page: nine columns of
 * thumbnails do not fit a laptop, and squeezing them until they do makes
 * every tile too small to recognise, which is the one thing this view
 * cannot afford. */
.periodic-grid {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  overflow-x: auto;
  padding-bottom: 0.75rem;
}

.periodic-column {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
  flex: 0 0 auto;
}

/* The noble gases get the emphasis their position earns. */
.periodic-column.noble {
  padding: 0 0.35rem;
  border-left: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  background: linear-gradient(180deg, rgba(111,179,242,0.07), transparent 60%);
}

.column-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0.2rem 0.45rem;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 0.2rem;
  cursor: help;
}
.column-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  white-space: nowrap;
}
.column-count { font-size: 0.72rem; color: var(--ink-faint); opacity: 0.7; }
.periodic-column.noble .column-label { color: var(--accent); }

.periodic-cell {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-items: center;
  gap: 0 0.45rem;
  width: 11.5rem;
  padding: 0.25rem 0.4rem 0.25rem 0.25rem;
  font: inherit;
  text-align: left;
  color: var(--ink-dim);
  background: var(--bg-raised);
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--line);
  border-radius: 7px;
  cursor: pointer;
}
.periodic-cell:hover { border-color: var(--accent-dim); color: var(--ink); }
.periodic-cell.on {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-dim);
  color: var(--ink);
}

.periodic-cell img {
  grid-row: 1 / 3;
  width: 2.6rem;
  height: 2.6rem;
  display: block;
  border-radius: 4px;
}
.periodic-cell img.missing { visibility: hidden; }

/* Where the atomic number goes. */
.cell-number {
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--ink-faint);
}
.cell-name {
  grid-column: 2;
  font-size: 0.74rem;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-sym {
  grid-column: 2;
  font-family: var(--mono);
  font-size: 0.64rem;
  color: var(--ink-faint);
}

/* Classification stripe down the left edge of each cell. */
.k-platonic    { border-left-color: #6fb3f2; }
.k-archimedean { border-left-color: #7fd1a8; }
.k-catalan     { border-left-color: #e0b978; }
.k-johnson     { border-left-color: #9a8fd8; }
.k-kepler      { border-left-color: #e08c8c; }
.legend-swatch.k-platonic    { background: #6fb3f2; }
.legend-swatch.k-archimedean { background: #7fd1a8; }
.legend-swatch.k-catalan     { background: #e0b978; }
.legend-swatch.k-johnson     { background: #9a8fd8; }
.legend-swatch.k-kepler      { background: #e08c8c; }

/* The detail panel sits below the table rather than beside it: the table
 * wants the full width, and a side panel would squeeze it to nothing. */
.periodic-panel {
  position: relative;
  margin-top: 1.5rem;
  padding: 1.25rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: grid;
  grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
.periodic-panel .detail { background: none; border: 0; padding: 0; }

.panel-close {
  position: absolute;
  top: 0.5rem;
  right: 0.7rem;
  font: inherit;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--ink-faint);
  background: none;
  border: 0;
  cursor: pointer;
}
.panel-close:hover { color: var(--ink); }

@media (max-width: 62rem) {
  .periodic-panel { grid-template-columns: minmax(0, 1fr); }
}

/* The column-scheme picker above the table. */
.periodic-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  margin: 0 0 0.8rem;
}
.periodic-select {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.82rem;
  color: var(--ink-dim);
}
.periodic-select select { min-width: 12rem; }
.periodic-note {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--ink-faint);
  max-width: 52rem;
  flex: 1 1 20rem;
}

/* ------------------------------------------------------------------
 * The belt trick.
 *
 * One stage and a transport beneath it. The stage is the point of the
 * page, so it gets the width and most of the height; the controls are a
 * single band under it rather than a side panel that would narrow it.
 * ------------------------------------------------------------------ */

.belt-page {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 1.25rem 3rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

.belt-stage { position: relative; }
.belt-stage #stage {
  width: 100%;
  height: min(68vh, 44rem);
  display: block;
}

/* The landmark sentence, over the bottom of the stage while it applies. */
.belt-lesson {
  position: absolute;
  left: 1rem;
  right: 1rem;
  bottom: 0.9rem;
  margin: 0;
  padding: 0.55rem 0.8rem;
  font-size: 0.9rem;
  line-height: 1.45;
  color: var(--ink);
  background: rgba(8, 10, 12, 0.82);
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  pointer-events: none;
}

.belt-controls {
  display: grid;
  gap: 0.9rem;
  padding: 1rem 1.1rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.belt-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
}

.belt-field {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.82rem;
  color: var(--ink-dim);
}
.belt-field select { min-width: 13rem; }
.belt-speed select { min-width: 4.5rem; }

.belt-turn { display: grid; gap: 0.25rem; }
.belt-turn-head {
  display: flex;
  justify-content: space-between;
  font-size: 0.82rem;
  color: var(--ink-dim);
}
.belt-turn-head output {
  font-family: var(--mono);
  color: var(--ink);
}
.belt-turn input[type=range] { width: 100%; accent-color: var(--accent); }

/* The two landmarks under the slider. Positioned by percentage of the
 * track so they sit under the values they name. */
.turn-marks {
  position: relative;
  height: 1.1rem;
  font-size: 0.72rem;
  color: var(--ink-faint);
}
.turn-marks span {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  white-space: nowrap;
}
.turn-marks span:first-child { transform: none; }
.turn-marks span:last-child { transform: translateX(-100%); }
.turn-marks .mark-one { color: var(--ink-dim); }
.turn-marks .mark-two { color: var(--accent); }

.belt-transport .stage-action { margin-left: 0; }
.belt-snaps {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: auto;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

.belt-hint {
  margin: 0;
  font-size: 0.78rem;
  color: var(--ink-faint);
}
.belt-hint kbd {
  font-family: var(--mono);
  font-size: 0.74rem;
  padding: 0 0.3rem;
  border: 1px solid var(--line);
  border-radius: 4px;
}

/* The explanation under the controls: a reading column, set like the
 * intro, so the page reads as one piece from the title down. */
.belt-intro p + p { margin-top: 0.7rem; }

.belt-notes {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0.5rem 1.25rem 2.5rem;
}
.belt-notes > * { max-width: 46rem; }
.belt-notes h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.35rem;
  margin: 2rem 0 0.6rem;
  color: var(--ink);
}
.belt-notes h2:first-child { margin-top: 0.5rem; }
.belt-notes p {
  margin: 0 0 0.8rem;
  color: var(--ink-dim);
  line-height: 1.65;
}
.belt-notes p em, .belt-notes p var { color: var(--ink); }
.belt-sources {
  margin: 0;
  padding-left: 1.2rem;
  font-size: 0.85rem;
  color: var(--ink-faint);
  line-height: 1.55;
}
.belt-sources li { margin-bottom: 0.35rem; }

/* Chair44 reuses the belt page's frame; these are the two controls it
 * adds -- a labelled slider that sits in a row, and a readout line. */
.belt-slider {
  min-width: 13rem;
  flex: 1 1 13rem;
}
.belt-slider input[type="range"] { width: 100%; }
.belt-slider output {
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.78rem;
  margin-left: 0.35rem;
}
