/* One stylesheet, shipped in the repo.
 *
 * The applications this replaces fetch Pico from jsdelivr, so none of them
 * render on an air-gapped or offline host -- the tables are still there, but
 * unstyled and unreadable at this density.  This is hand-written CSS with
 * custom properties: no npm, no bundler, no build step, because this is a
 * Python project and a build step is a thing that breaks when nobody is
 * looking.
 *
 * Direction: a dense, quiet, technical reading surface.  Data first, chrome
 * last.  Tables are the product.
 */

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: light dark;

  /* Type. One variable UI face, one monospace face for identifiers.
   * Identifiers -- uuids, CAS numbers, SMILES, InChIKeys -- are read
   * character by character, so they need unambiguous, tabular glyphs. */
  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.8125rem;
  --text-base: 0.875rem;
  --text-lg: 1.0625rem;
  --text-xl: 1.375rem;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  --radius: 5px;
  --radius-sm: 3px;

  --measure: 68ch;
  --nav-height: 3rem;
}

/* Light is the default set; dark is applied by the media query below and by
 * an explicit `data-theme`, which the toggle stamps on <html> and which must
 * win in both directions -- a reader on a dark OS who chose light gets light. */
:root,
:root[data-theme="light"] {
  --bg: #ffffff;
  --bg-sunken: #f6f7f9;
  --bg-raised: #ffffff;
  --bg-hover: #f0f2f5;

  --border: #d9dde3;
  --border-strong: #b6bcc6;

  --fg: #16191d;
  --fg-muted: #5c6572;
  --fg-faint: #858e9c;

  --accent: #1a4fd6;
  --accent-fg: #ffffff;
  --accent-bg: #e8eeff;

  --ok: #1a7f45;
  --ok-bg: #e6f4ec;
  --warn: #8a5a00;
  --warn-bg: #fdf3e0;
  --danger: #b3261e;
  --danger-bg: #fceceb;

  --diff-added: #1a7f45;
  --diff-added-bg: #e6f4ec;
  --diff-removed: #b3261e;
  --diff-removed-bg: #fceceb;

  --focus: #1a4fd6;
  --shadow: 0 1px 2px rgba(16, 20, 26, 0.06), 0 2px 8px rgba(16, 20, 26, 0.05);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171b;
    --bg-sunken: #0f1216;
    --bg-raised: #1b1f25;
    --bg-hover: #232830;

    --border: #2c323b;
    --border-strong: #414954;

    --fg: #e6e9ee;
    --fg-muted: #9aa3b0;
    --fg-faint: #6f7885;

    --accent: #7ba2ff;
    --accent-fg: #10141a;
    --accent-bg: #1b2438;

    --ok: #5cc98b;
    --ok-bg: #16281e;
    --warn: #e0b25e;
    --warn-bg: #2a2114;
    --danger: #f2867d;
    --danger-bg: #2e1917;

    --diff-added: #5cc98b;
    --diff-added-bg: #16281e;
    --diff-removed: #f2867d;
    --diff-removed-bg: #2e1917;

    --focus: #7ba2ff;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3);
  }
}

:root[data-theme="dark"] {
  --bg: #14171b;
  --bg-sunken: #0f1216;
  --bg-raised: #1b1f25;
  --bg-hover: #232830;

  --border: #2c323b;
  --border-strong: #414954;

  --fg: #e6e9ee;
  --fg-muted: #9aa3b0;
  --fg-faint: #6f7885;

  --accent: #7ba2ff;
  --accent-fg: #10141a;
  --accent-bg: #1b2438;

  --ok: #5cc98b;
  --ok-bg: #16281e;
  --warn: #e0b25e;
  --warn-bg: #2a2114;
  --danger: #f2867d;
  --danger-bg: #2e1917;

  --diff-added: #5cc98b;
  --diff-added-bg: #16281e;
  --diff-removed: #f2867d;
  --diff-removed-bg: #2e1917;

  --focus: #7ba2ff;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3);
}

/* ----------------------------------------------------------------- reset */

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

h1,
h2,
h3 {
  margin: 0 0 var(--space-3);
  font-weight: 600;
  line-height: 1.25;
}

h1 {
  font-size: var(--text-xl);
}
h2 {
  font-size: var(--text-lg);
}
h3 {
  font-size: var(--text-base);
}

p {
  margin: 0 0 var(--space-3);
  max-width: var(--measure);
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

a:hover {
  text-decoration-thickness: 2px;
}

code,
kbd,
.mono {
  font-family: var(--font-mono);
  font-size: 0.9375em;
  font-variant-numeric: tabular-nums;
}

/* Colour is never the only carrier of meaning: every focusable thing gets a
 * ring, and it is the same ring everywhere. */
:where(a, button, input, select, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* -------------------------------------------------------------- skip link */

.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -3rem;
  z-index: 100;
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: var(--radius);
  transition: top 120ms ease;
}

.skip-link:focus {
  top: var(--space-2);
}

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

.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--nav-height);
  padding: 0 var(--space-4);
  background: var(--bg-raised);
  border-bottom: 1px solid var(--border);
}

.masthead__brand {
  font-weight: 600;
  color: var(--fg);
  text-decoration: none;
  white-space: nowrap;
}

.masthead__nav {
  display: flex;
  gap: var(--space-1);
  flex: 1;
  min-width: 0;
  overflow-x: auto;
}

.masthead__link {
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius);
  color: var(--fg-muted);
  text-decoration: none;
  white-space: nowrap;
}

.masthead__link:hover {
  background: var(--bg-hover);
  color: var(--fg);
}

/* The current section is marked twice: with colour, and with a rule under it.
 * Colour alone fails anyone who cannot distinguish these two greys. */
.masthead__link[aria-current="page"] {
  color: var(--fg);
  background: var(--accent-bg);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.masthead__tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* --------------------------------------------------------------- buttons */

.button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--fg-muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}

.button:hover {
  color: var(--fg);
  border-color: var(--border-strong);
  background: var(--bg-hover);
}

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

.page {
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-8);
}

.page__header {
  margin-bottom: var(--space-6);
}

.page__lede {
  color: var(--fg-muted);
  max-width: var(--measure);
}

.section {
  margin-bottom: var(--space-8);
}

.section__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

/* A sentence explaining the table under it. Full width, unlike a paragraph of
 * prose: it is read against the columns it names, and a line ending at 68
 * characters over a table three times that wide reads as a caption for
 * something else. */
.section__note {
  max-width: none;
  color: var(--fg-muted);
}

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

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-3);
}

.card {
  padding: var(--space-3) var(--space-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.card__label {
  display: block;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}

.card__value {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  font-variant-numeric: tabular-nums;
}

.card__note {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--fg-faint);
}

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

/* The scrolling box a table sits in, and the only scroll container involved:
 * `overflow-x` makes an element a scroll container in *both* axes, so a header
 * inside it sticks to this box, never to the viewport.  That is why the box is
 * also bounded vertically -- a header that sticks to a box which cannot scroll
 * is a header that never moves, and one offset by the navigation bar's height
 * inside such a box is pushed down over the first rows instead.  Bounded here,
 * the fifty rows of a page scroll under their own header, which stays put. */
.table-scroll {
  overflow: auto;
  max-height: calc(100vh - var(--nav-height) - var(--space-8));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-raised);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

/* Sticks to the top of `.table-scroll`, so the offset is zero: the box's own
 * top edge is already below the masthead.  The rule under the row is a shadow
 * rather than a border because a collapsed border belongs to the table grid,
 * not to the cell, and so stays behind while the cell scrolls over the rows. */
thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-weight: 600;
  color: var(--fg-muted);
  background: var(--bg-sunken);
  box-shadow: inset 0 -1px 0 var(--border-strong);
  white-space: nowrap;
}

tbody td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

tbody tr:last-child td {
  border-bottom: 0;
}

tbody tr:hover td {
  background: var(--bg-hover);
}

/* Numbers align right and share a width, so a column of counts can be
 * compared by eye rather than read. */
.num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.muted {
  color: var(--fg-muted);
}

/* ------------------------------------------------------------ definition */

.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  font-size: var(--text-sm);
}

.facts dt {
  color: var(--fg-muted);
}

.facts dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The flows an unmatched row could have landed on, listed inside a table
 * cell. Small and unbulleted: the cell already sits in a row of its own
 * columns, and a bullet beside a score reads as a second level of nesting the
 * data does not have. The score keeps a fixed width so a column of them lines
 * up against the contexts they scored. */
.candidates {
  list-style: none;
  margin: var(--space-1) 0 0;
  padding: 0;
  font-size: var(--text-xs);
}

.candidates li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 1px 0;
}

.candidates__score {
  min-width: 1.5em;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
}

/* ----------------------------------------------------------- documentation */

/* The `docs/` tree, rendered into this layout rather than served as a built
 * mkdocs site: one navigation bar, one stylesheet, no build step.  The sidebar
 * is the `nav` block of `mkdocs.yml`, so the order a reader sees here is the
 * order the documentation was written in. */
.docs {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
}

/* Sticky under the masthead, and scrollable on its own: the contents list runs
 * to twenty-two entries and the pages it points at are long. */
.docs__nav {
  position: sticky;
  top: calc(var(--nav-height) + var(--space-4));
  max-height: calc(100vh - var(--nav-height) - var(--space-8));
  overflow-y: auto;
  font-size: var(--text-sm);
}

.docs__group {
  margin: var(--space-4) 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-faint);
}

.docs__group:first-child {
  margin-top: 0;
}

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

.docs__list li {
  margin: 0;
}

.docs__list a {
  display: block;
  padding: 2px var(--space-2);
  border-left: 2px solid transparent;
  color: var(--fg-muted);
  text-decoration: none;
}

.docs__list a:hover {
  color: var(--fg);
  background: var(--bg-hover);
}

/* The page being read is marked by a rule as well as by weight, because the
 * two live entries either side of it are the same colour. */
.docs__list a[aria-current="page"] {
  color: var(--accent);
  font-weight: 600;
  border-left-color: var(--accent);
  background: var(--accent-bg);
}

.docs__page {
  min-width: 0;
  max-width: 52rem;
}

.docs__page h1 {
  margin-bottom: var(--space-4);
}

/* Headings past the first get room above them: a reference page is read by
 * scanning for the section that answers the question, and a heading that sits
 * as close to the paragraph above it as to the one below is no landmark. */
.docs__page h2,
.docs__page h3,
.docs__page h4 {
  margin-top: var(--space-6);
}

.docs__page h4 {
  font-size: var(--text-base);
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.docs__page ul,
.docs__page ol {
  max-width: var(--measure);
  padding-left: var(--space-6);
}

.docs__page li {
  margin-bottom: var(--space-1);
}

.docs__page img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
}

.docs__page blockquote {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-4);
  border-left: 2px solid var(--border-strong);
  color: var(--fg-muted);
}

.docs__page pre {
  overflow-x: auto;
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  line-height: 1.45;
}

.docs__page :not(pre) > code {
  padding: 1px 4px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* Nothing draws the mermaid fence here -- that takes a script this application
 * will not fetch -- so it is labelled as what it is rather than left looking
 * like a code block that failed to run. */
.docs__page pre.mermaid::before {
  content: "Diagram source";
  display: block;
  margin-bottom: var(--space-2);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  color: var(--fg-faint);
}

.docs__contents {
  margin-bottom: var(--space-6);
  font-size: var(--text-sm);
}

.docs__contents ul {
  list-style: none;
  padding-left: var(--space-4);
}

.docs__contents > .toc > ul {
  padding-left: 0;
}

/* An admonition, from the `admonition` extension the documentation already
 * uses.  A note and a warning differ by colour and by their own title, never
 * by colour alone. */
.docs__page .admonition {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-left: 3px solid var(--fg-faint);
  border-radius: var(--radius);
}

.docs__page .admonition > :last-child {
  margin-bottom: 0;
}

.docs__page .admonition-title {
  margin: 0 0 var(--space-2);
  font-weight: 600;
}

.docs__page .admonition.warning {
  border-left-color: var(--warn);
  background: var(--warn-bg);
}

.docs__page .admonition.warning .admonition-title {
  color: var(--warn);
}

.docs__page .admonition.note,
.docs__page .admonition.info {
  border-left-color: var(--accent);
  background: var(--accent-bg);
}

.docs__page .admonition.note .admonition-title,
.docs__page .admonition.info .admonition-title {
  color: var(--accent);
}

/* Syntax colours for the code samples, mapped onto the tokens this stylesheet
 * already defines rather than pulled from a Pygments theme: five roles, both
 * palettes, and anything unclassified reads as ordinary text. */
.docs__page .c,
.docs__page .c1,
.docs__page .cm,
.docs__page .sd {
  color: var(--fg-faint);
  font-style: italic;
}

.docs__page .k,
.docs__page .kc,
.docs__page .kd,
.docs__page .kn,
.docs__page .kt,
.docs__page .ow {
  color: var(--accent);
}

.docs__page .s,
.docs__page .s1,
.docs__page .s2,
.docs__page .sb,
.docs__page .se {
  color: var(--ok);
}

.docs__page .m,
.docs__page .mi,
.docs__page .mf {
  color: var(--warn);
}

/* A key in YAML or JSON, and an attribute in markup: what the value belongs
 * to, which is what a reader scans a configuration block for. */
.docs__page .nt,
.docs__page .na,
.docs__page .nv {
  color: var(--fg);
  font-weight: 600;
}

.docs__page .gd {
  color: var(--diff-removed);
}

.docs__page .gi {
  color: var(--diff-added);
}

/* One column below the width the sidebar and a readable measure need. */
@media (max-width: 60rem) {
  .docs {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
  }

  .docs__nav {
    position: static;
    max-height: none;
  }
}

/* ------------------------------------------------------------------ tags */

.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: 600;
  border-radius: 999px;
  border: 1px solid transparent;
}

/* Each severity carries a word as well as a colour. */
.tag--blocking {
  color: var(--danger);
  background: var(--danger-bg);
  border-color: var(--danger);
}

.tag--review {
  color: var(--warn);
  background: var(--warn-bg);
  border-color: var(--warn);
}

.tag--info {
  color: var(--fg-muted);
  background: var(--bg-sunken);
  border-color: var(--border);
}

.tag--ok {
  color: var(--ok);
  background: var(--ok-bg);
  border-color: var(--ok);
}

/* --------------------------------------------------------------- notices */

.notice {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--bg-raised);
  margin-bottom: var(--space-4);
}

.notice--warn {
  border-left-color: var(--warn);
  background: var(--warn-bg);
}

.notice--action {
  border-left-color: var(--accent);
  background: var(--accent-bg);
}

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

.notice__title {
  font-weight: 600;
  margin-bottom: var(--space-1);
}

/* ----------------------------------------------------------- empty state */

.empty {
  max-width: var(--measure);
  margin: var(--space-8) auto;
}

.empty pre {
  padding: var(--space-3);
  overflow-x: auto;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* ------------------------------------------------------------- shortcuts */

dialog.shortcuts {
  padding: var(--space-6);
  max-width: 26rem;
  color: var(--fg);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

dialog.shortcuts::backdrop {
  background: rgba(0, 0, 0, 0.45);
}

kbd {
  padding: 0 var(--space-1);
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------- filters */

.filters {
  margin-bottom: var(--space-4);
}

.filters__search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.filters__search input[type="search"] {
  flex: 1;
  min-width: 16rem;
  padding: var(--space-2) var(--space-3);
  font: inherit;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}

.filters__selects {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-2) var(--space-3);
}

.filters__field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* Every select is labelled. The page this replaces had eleven bare dropdowns,
 * and the only way to learn what one filtered was to change it. */
.filters__field label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}

select {
  width: 100%;
  padding: var(--space-1) var(--space-2);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  color: var(--fg-muted);
  white-space: nowrap;
}

/* ----------------------------------------------------------------- chips */

.chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
}

.chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  color: var(--fg);
  text-decoration: none;
  background: var(--accent-bg);
  border: 1px solid var(--accent);
  border-radius: 999px;
}

.chip:hover {
  background: var(--bg-hover);
}

.chip__label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-muted);
}

.chip__value {
  font-weight: 600;
}

/* ---------------------------------------------------------------- pager */

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin: var(--space-3) 0;
  font-size: var(--text-sm);
}

.pager__links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.pager [aria-disabled="true"] {
  opacity: 0.45;
  cursor: default;
}

th a.sortable {
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

th a.sortable:hover {
  text-decoration: underline;
}

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

.detail-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--space-6);
  align-items: start;
}

.card--link {
  display: block;
  color: inherit;
  text-decoration: none;
}

.card--link:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
}

.labels {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}

.labels li {
  padding: 1px var(--space-2);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.structure {
  max-width: 100%;
  height: auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

details summary {
  cursor: pointer;
  padding: var(--space-2) 0;
  color: var(--accent);
}

pre.json {
  max-height: 32rem;
  overflow: auto;
  padding: var(--space-3);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-sm);
}

/* ------------------------------------------------------------- values */

/* The vocabulary term beside a property's name, on the flow-object page.
 *
 * On its own line and smaller than the label, because it is the answer to a
 * second question -- "what is this keyed by in the published document?" -- and
 * a reader who is not asking it should be able to skim past. */
.term {
  display: block;
  font-size: var(--text-xs);
}

/* A cross-reference URL, which is long, unspaced and not worth truncating: the
 * resource is at the front and the record's identifier at the end, and a
 * reader checking which record was linked needs both ends.
 *
 * The floor is the same one `td.value-cell` needs and for the same reason --
 * a cell whose content can break between any two characters has a minimum
 * width of one character, and an auto table layout will hand it exactly
 * that. */
td.url {
  min-width: 34ch;
}

td.url .mono {
  overflow-wrap: anywhere;
}

/* An old and a new value in a change-log row.
 *
 * Marked by a left rule as well as by colour, and by their column headers
 * ("From", "To"): the diff highlighting this replaces used colour alone, so
 * a reader who cannot distinguish red from green got two identical cells. */
.value {
  display: inline-block;
  max-width: 42ch;
  padding: 0 var(--space-2);
  border-left: 3px solid var(--border-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}

/* The cell a `.value` sits in, wide enough to read the value in.
 *
 * `overflow-wrap: anywhere` above lets the span break between any two
 * characters, so the narrowest the cell can be is one character wide -- and an
 * auto table layout will make it exactly that, handing the width to the "Why"
 * column beside it, whose ordinary prose breaks only at spaces. Both diff
 * columns then arrived as a ragged one-character ribbon of the value they were
 * supposed to show.
 *
 * A floor on the cell rather than on the span: a span with a minimum width
 * would draw its tinted box that wide for an em dash as well, and most of what
 * a change log records is a field that was empty before. Where the floors add
 * up to more than the page, `.table-scroll` scrolls. */
td.value-cell {
  min-width: 28ch;
}

.value--old {
  color: var(--diff-removed);
  border-left-color: var(--diff-removed);
  background: var(--diff-removed-bg);
}

.value--new {
  color: var(--diff-added);
  border-left-color: var(--diff-added);
  background: var(--diff-added-bg);
}

/* A fragment being fetched. No spinner: the `<details>` is already open and
 * the link inside it still works, so this only dims what is about to be
 * replaced. */
[aria-busy="true"] {
  opacity: 0.55;
}

/* ------------------------------------------------------------- tallies */

/* A stage's counters, wrapped inline rather than one per row: a stage has
 * between two and a dozen of them, and a table per stage would be twelve
 * tables where the reader wants one scan. */
.stat {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  margin: 0 var(--space-2) var(--space-1) 0;
  padding: 0 var(--space-2);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.stat__key {
  color: var(--fg-muted);
}

.stat__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* ------------------------------------------------- implementation stack */

/* What every input implementation states, inside one table cell.
 *
 * A column per implementation reads well at two and stops reading at six, and
 * the number of implementations is data: the factors section will hold more
 * methods than EF 3.1 and read more lists than the JRC's and ecoinvent's. A
 * stack makes a new one a new line rather than a new column, and the two-track
 * grid keeps the numbers aligned down the table the way a column would. */
.sources {
  display: grid;
  grid-template-columns: 1fr max-content;
  gap: 0 var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}

.sources__row {
  display: contents;
}

.sources__label {
  color: var(--fg-muted);
  overflow-wrap: anywhere;
}

.sources__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* A card whose number is zero. Dimmed rather than hidden: "this implementation
 * published nothing" is an answer, and the note beside it says why. */
.card--empty {
  background: var(--bg-sunken);
}

.card--empty .card__value {
  color: var(--fg-muted);
}

/* ------------------------------------------------------- screen reader */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

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

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------- flow-object sub-views

 * The ways into the flow objects, under the page heading rather than in the
 * masthead.  An element, an agricultural chemical and a land class are all
 * flow objects; promoting each way of looking at them to a top-level section
 * would put four spellings of one noun in the navigation. */

.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-6);
  border-bottom: 1px solid var(--border);
}

.subnav__link {
  padding: var(--space-2) var(--space-3);
  color: var(--fg-muted);
  font-size: var(--text-sm);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.subnav__link:hover {
  color: var(--fg);
  background: var(--bg-hover);
}

.subnav__link[aria-current="page"] {
  color: var(--fg);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* ---------------------------------------------------------- the taxonomies

 * One tree style for both: the ChEBI role forest and an element's nuclides and
 * ions.  They are the same shape -- a named thing, a count, and what hangs off
 * it -- and drawing them twice would let the two drift.
 *
 * The indent guide is a border on the nested list rather than a glyph per row,
 * so a row can wrap without the guide breaking. */

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

.tree .tree {
  margin-left: var(--space-3);
  padding-left: var(--space-4);
  border-left: 1px solid var(--border);
}

.tree__item {
  padding: var(--space-1) 0;
}

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

.tree__name {
  font-weight: 600;
}

/* The count reads as a quantity rather than as part of the name: same
 * treatment as a numeric table cell, which is what it is. */
.tree__count {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--fg-muted);
  padding: 0 var(--space-2);
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.tree__note {
  max-width: var(--measure);
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--fg-muted);
}

/* ---------------------------------------------------------- periodic table

 * Eighteen fixed columns, because the table *is* the layout: a grid that
 * reflowed to fit a narrow screen would put nitrogen next to lithium and stop
 * being a periodic table.  It scrolls inside `.table-scroll` instead, which is
 * how every other wide thing in this application behaves. */

.ptable {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 62rem;
}

.ptable__row {
  display: grid;
  grid-template-columns: repeat(18, 1fr);
  gap: 2px;
}

.ptable__spacer {
  height: var(--space-4);
}

.ptable__gap {
  display: block;
}

.ptable__cell {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 4.25rem;
  padding: 3px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
}

a.ptable__cell:hover {
  border-color: var(--accent);
  background: var(--bg-hover);
}

/* The three states, drawn apart because they mean different things.  An
 * element with a flow object EF 3.1 references is what the list carries; one
 * with an unreferenced object is a curation question; one with no object at
 * all is a scope question. */
.ptable__cell--linked {
  background: var(--ok-bg);
  border-color: var(--ok);
}

.ptable__cell--unreferenced {
  background: var(--warn-bg);
  border-color: var(--warn);
}

.ptable__cell--missing {
  background: var(--bg-sunken);
  color: var(--fg-faint);
  border-style: dashed;
}

.ptable__z {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  color: var(--fg-muted);
  line-height: 1.2;
}

.ptable__symbol {
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1.1;
}

/* The name is the first thing to go when a cell is too narrow for it: the
 * symbol and the atomic number identify the element on their own, and a
 * truncated name is more use than a cell that grows to fit the longest one. */
.ptable__name {
  font-size: 0.625rem;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ptable__extra {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  color: var(--fg-muted);
  line-height: 1.2;
  min-height: 0.75rem;
}

/* The two f-block markers.  A cell rather than a gap, so the row keeps its
 * shape and the reader is told where the lanthanides went. */
.ptable__marker {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 4.25rem;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--fg-muted);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
}

/* A hierarchy, as an indent on a taxonomy page.
 *
 * Both taxonomies have a depth and neither has a nested list: the rows are a
 * sortable table and a tree would give up the sort.  What the depth counts
 * differs -- a land class's is how many qualifiers it states, because dropping
 * the most specific one gives its parent, and a kind of water's is the length
 * of its chain of `skos:broader` up to a root -- so one rule serves both
 * rather than each page carrying its own copy of the same declaration. */

.tree__indent {
  display: inline-block;
  width: calc(var(--depth, 0) * var(--space-3));
}

/* Several short statements in one table cell.
 *
 * A list because they are a list -- a kind of water can state its own parent
 * and its own identifier, and running them together as a sentence would hide
 * that those are two separate claims -- and unbulleted because a bullet inside
 * a table cell reads as a second column. */

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