/* LabNB — one stylesheet, no build step, light and dark.
   Tokens first, then layout, then components. */

:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --surface-2: #f0f2f5;
  --border: #dde1e7;
  --border-strong: #c3cad4;
  --text: #16191d;
  --text-dim: #5b6472;
  --text-faint: #8a94a3;
  --accent: #2f6f4f;
  --accent-hover: #275c42;
  --accent-soft: #e6f0ea;
  --danger: #b3261e;
  --danger-soft: #fdecea;
  --warn: #8a6100;
  --warn-soft: #fdf3e0;
  --info: #1f5c8b;
  --info-soft: #e7f0f7;
  --ok: #2f6f4f;
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 12px rgba(16, 24, 40, 0.06);
  --radius: 10px;
  --radius-sm: 6px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --sidebar-w: 232px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171b;
    --surface: #1b1f24;
    --surface-2: #23282f;
    --border: #2e343c;
    --border-strong: #3d454f;
    --text: #e8ebef;
    --text-dim: #a4adb9;
    --text-faint: #7b8493;
    --accent: #6bbf90;
    --accent-hover: #83cfa4;
    --accent-soft: #1e2c25;
    --danger: #f2857c;
    --danger-soft: #2c1d1c;
    --warn: #e0b45f;
    --warn-soft: #2b2417;
    --info: #7db4dd;
    --info-soft: #1a252e;
    --ok: #6bbf90;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 14px rgba(0, 0, 0, 0.3);
  }
}

* { box-sizing: border-box; }

/* The UA rule for [hidden] is display:none, but it loses to any class that sets
   display (.row, .grid, .card). Without this, hiding an element by setting
   .hidden = true silently does nothing. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 0.4em; line-height: 1.25; font-weight: 620; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.1rem; }
h3 { font-size: 0.98rem; }
p { margin: 0 0 0.7em; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono, .code { font-family: var(--mono); font-size: 0.92em; }

/* ------------------------------------------------------------- app shell */
.boot { display: grid; place-items: center; gap: 1rem; min-height: 70vh; color: var(--text-dim); }

.shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.brand { padding: 1.1rem 1rem 0.9rem; border-bottom: 1px solid var(--border); }
.brand-name { font-weight: 700; font-size: 1.02rem; letter-spacing: -0.01em; }
.brand-lab { color: var(--text-faint); font-size: 0.8rem; }

.nav { padding: 0.6rem 0.5rem; flex: 1; }
.nav-section { margin: 0.9rem 0 0.3rem; padding: 0 0.6rem; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-faint); }
.nav a {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.45rem 0.6rem; margin: 0.1rem 0;
  border-radius: var(--radius-sm); color: var(--text); font-weight: 500;
}
.nav a:hover { background: var(--surface-2); text-decoration: none; }
.nav a.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.nav .nav-icon { width: 1.15rem; text-align: center; opacity: 0.85; }
.nav-badge { margin-left: auto; background: var(--danger); color: #fff; border-radius: 999px; font-size: 0.68rem; padding: 0 0.36rem; }

.sidebar-foot { border-top: 1px solid var(--border); padding: 0.7rem; }
.who { display: flex; align-items: center; gap: 0.55rem; }
.avatar {
  width: 30px; height: 30px; border-radius: 50%; background: var(--accent-soft);
  color: var(--accent); display: grid; place-items: center; font-weight: 700; font-size: 0.78rem;
}
.who-name { font-weight: 600; font-size: 0.85rem; }
.who-role { color: var(--text-faint); font-size: 0.74rem; }

.main { min-width: 0; }
.topbar {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.6rem 1.4rem; border-bottom: 1px solid var(--border);
  background: var(--surface); position: sticky; top: 0; z-index: 20;
}
.topbar .global-search { flex: 1; max-width: 460px; }
.route { padding: 1.4rem; max-width: 1500px; }

/* ---------------------------------------------------------------- pages */
.page-head { margin-bottom: 1.1rem; }
.page-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.page-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.subtitle { color: var(--text-dim); margin: 0.1rem 0 0; }
.crumbs { font-size: 0.82rem; color: var(--text-faint); margin-bottom: 0.35rem; }
.crumbs a { color: var(--text-dim); }
.crumbs > *:not(:last-child)::after { content: "/"; margin: 0 0.4rem; color: var(--border-strong); }

.grid { display: grid; gap: 1rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.row-between { display: flex; gap: 0.6rem; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 0.75rem; }
.spacer { flex: 1; }

/* ---------------------------------------------------------------- cards */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
  margin-bottom: 1rem;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.7rem 1rem; border-bottom: 1px solid var(--border); background: var(--surface);
}
.card-head h2 { margin: 0; font-size: 0.95rem; }
.card-actions { display: flex; gap: 0.4rem; }
.card-body { padding: 1rem; }
.card-body > :last-child { margin-bottom: 0; }
.card.flush .card-body { padding: 0; }

.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.85rem 1rem; }
.stat-value { font-size: 1.6rem; font-weight: 680; line-height: 1.1; }
.stat-label { color: var(--text-dim); font-size: 0.82rem; }

/* -------------------------------------------------------------- controls */
.btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.42rem 0.8rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); font: inherit; font-weight: 550; cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--surface-2); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover { filter: brightness(1.08); }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover { background: var(--surface-2); }
.btn-sm { padding: 0.24rem 0.5rem; font-size: 0.83rem; }
.btn-icon { line-height: 1; }

.input {
  width: 100%; padding: 0.44rem 0.6rem; font: inherit; color: var(--text);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}
.input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.input[type="search"] { border-radius: 999px; padding-left: 0.85rem; }
select.input { cursor: pointer; }
textarea.input { resize: vertical; min-height: 4.5em; font-family: inherit; }

.field { margin-bottom: 0.85rem; }
.field > label { display: block; font-weight: 560; font-size: 0.86rem; margin-bottom: 0.22rem; }
.field .req { color: var(--danger); margin-left: 0.15rem; }
.field .help { color: var(--text-faint); font-size: 0.8rem; margin: 0.22rem 0 0; }
.field-error { color: var(--danger); font-size: 0.82rem; margin: 0.22rem 0 0; }
.field-check .check { display: flex; align-items: center; gap: 0.45rem; font-weight: 500; }
.field-check input { width: auto; }
.form-actions { display: flex; gap: 0.5rem; margin-top: 1rem; }
.form-error { background: var(--danger-soft); color: var(--danger); padding: 0.55rem 0.7rem; border-radius: var(--radius-sm); margin-bottom: 0.6rem; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 0 1rem; }

/* --------------------------------------------------------------- tables */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table th, .table td { padding: 0.5rem 0.7rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
.table th { font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); background: var(--surface-2); position: sticky; top: 0; }
.table tbody tr:last-child td { border-bottom: none; }
.table tr.clickable { cursor: pointer; }
.table tr.clickable:hover td { background: var(--surface-2); }
.table td.num, .table th.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------------- misc */
.badge { display: inline-block; padding: 0.1rem 0.45rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; background: var(--surface-2); color: var(--text-dim); }
.badge-ok, .badge-complete { background: var(--accent-soft); color: var(--ok); }
.badge-warn, .badge-running { background: var(--warn-soft); color: var(--warn); }
.badge-danger, .badge-failed { background: var(--danger-soft); color: var(--danger); }
.badge-info, .badge-reserved, .badge-planned { background: var(--info-soft); color: var(--info); }

.formula { font-family: var(--mono); white-space: nowrap; }
.formula sub { font-size: 0.72em; }
.formula.is-tentative { border-bottom: 1px dashed var(--warn); }
.code-chip { font-family: var(--mono); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.05rem 0.35rem; font-size: 0.84rem; }

.kv { display: grid; grid-template-columns: minmax(120px, auto) 1fr; gap: 0.3rem 0.9rem; margin: 0; }
.kv dt { color: var(--text-dim); font-size: 0.85rem; }
.kv dd { margin: 0; }

.empty { text-align: center; color: var(--text-dim); padding: 2rem 1rem; }
.loading { display: flex; align-items: center; gap: 0.6rem; color: var(--text-dim); padding: 1.4rem; justify-content: center; }
.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--border-strong); border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.alert { padding: 0.7rem 0.9rem; border-radius: var(--radius-sm); margin-bottom: 0.9rem; border: 1px solid transparent; }
.alert p:last-child { margin-bottom: 0; }
.alert-error { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); }
.alert-warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.alert-info { background: var(--info-soft); color: var(--info); border-color: var(--info); }

/* ----------------------------------------------------------------- tabs */
.tabs { display: flex; gap: 0.15rem; border-bottom: 1px solid var(--border); overflow-x: auto; margin-bottom: 1rem; }
.tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: 0.5rem 0.8rem; font: inherit; font-weight: 550; color: var(--text-dim);
  cursor: pointer; white-space: nowrap;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-count { margin-left: 0.35rem; background: var(--surface-2); border-radius: 999px; padding: 0 0.34rem; font-size: 0.75rem; }

/* ---------------------------------------------------------------- modal */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(10, 14, 20, 0.45);
  display: grid; place-items: center; padding: 1.2rem; z-index: 100;
  overflow-y: auto;
}
.modal { background: var(--surface); border-radius: var(--radius); box-shadow: 0 20px 50px rgba(0,0,0,0.28); width: 100%; max-height: 90vh; display: flex; flex-direction: column; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--border); }
.modal-head h2 { margin: 0; }
.modal-close { background: none; border: none; font-size: 1.4rem; line-height: 1; cursor: pointer; color: var(--text-dim); }
.modal-body { padding: 1.1rem; overflow-y: auto; }
.modal-foot { padding: 0.8rem 1.1rem; border-top: 1px solid var(--border); display: flex; gap: 0.5rem; justify-content: flex-end; }

/* --------------------------------------------------------------- toasts */
.toast-host { position: fixed; right: 1rem; bottom: 1rem; display: flex; flex-direction: column; gap: 0.5rem; z-index: 200; }
.toast {
  background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--info);
  border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: 0.6rem 0.9rem; max-width: 380px;
  animation: slide-in 0.18s ease-out;
}
.toast-success { border-left-color: var(--ok); }
.toast-error { border-left-color: var(--danger); }
.toast-warn { border-left-color: var(--warn); }
.toast.out { opacity: 0; transition: opacity 0.3s; }
@keyframes slide-in { from { transform: translateX(12px); opacity: 0; } }

/* ------------------------------------------------------ stoichiometry grid */
.stoich { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.stoich table { border-collapse: collapse; min-width: 100%; font-size: 0.88rem; }
.stoich th, .stoich td { border: 1px solid var(--border); padding: 0.2rem 0.35rem; }
.stoich thead th { background: var(--surface-2); font-size: 0.78rem; white-space: nowrap; }
.stoich .reagent-cell { min-width: 210px; }
.stoich .batch-col { min-width: 140px; }
.stoich input.cell { width: 100%; border: none; background: transparent; font: inherit; font-family: var(--mono); padding: 0.2rem; color: var(--text); }
.stoich input.cell:focus { outline: 2px solid var(--accent); border-radius: 3px; }
.stoich .computed { color: var(--text-faint); font-family: var(--mono); font-size: 0.78rem; display: block; }
.stoich .cell-error { color: var(--danger); font-size: 0.74rem; display: block; }
.stoich tfoot td { background: var(--surface-2); font-weight: 600; }
.stoich tr.is-flux td:first-child { border-left: 3px solid var(--warn); }

/* ----------------------------------------------------------- temperature */
.tprofile-seg { display: grid; grid-template-columns: repeat(4, minmax(90px, 1fr)) auto; gap: 0.4rem; align-items: center; margin-bottom: 0.4rem; }
.tprofile-chart { width: 100%; height: 190px; }
.tprofile-chart .axis { stroke: var(--border-strong); }
.tprofile-chart .trace { fill: none; stroke: var(--accent); stroke-width: 2; }
.tprofile-chart .label { fill: var(--text-faint); font-size: 10px; }

/* -------------------------------------------------------------- calendar */
.calendar { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.cal-head { background: var(--surface-2); padding: 0.35rem; text-align: center; font-size: 0.76rem; font-weight: 600; color: var(--text-dim); }
.cal-day { background: var(--surface); min-height: 84px; padding: 0.3rem; font-size: 0.8rem; position: relative; }
.cal-day.other-month { background: var(--surface-2); color: var(--text-faint); }
.cal-day.today { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-day.selectable { cursor: pointer; }
.cal-day.selectable:hover { background: var(--accent-soft); }
.cal-day.selected { background: var(--accent-soft); }
.cal-date { font-weight: 600; font-size: 0.78rem; }
.cal-chip { display: block; margin-top: 2px; padding: 1px 4px; border-radius: 3px; font-size: 0.7rem; background: var(--info-soft); color: var(--info); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-chip.mine { background: var(--accent-soft); color: var(--accent); }

/* --------------------------------------------------------------- timeline */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: grid; grid-template-columns: auto 1fr; gap: 0.7rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.timeline li:last-child { border-bottom: none; }
.timeline .when { color: var(--text-faint); font-size: 0.8rem; white-space: nowrap; }
.timeline .what { font-size: 0.88rem; }
.timeline .diff { font-family: var(--mono); font-size: 0.78rem; color: var(--text-dim); margin-top: 0.2rem; }
.timeline .diff del { color: var(--danger); text-decoration: line-through; }
.timeline .diff ins { color: var(--ok); text-decoration: none; }

/* ------------------------------------------------------------ provenance */
.provenance { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; font-family: var(--mono); font-size: 0.85rem; }
.provenance .arrow { color: var(--text-faint); }
.tree { list-style: none; padding-left: 1rem; margin: 0; border-left: 1px solid var(--border); }
.tree li { padding: 0.2rem 0 0.2rem 0.6rem; position: relative; }
.tree li::before { content: ""; position: absolute; left: -1px; top: 0.85rem; width: 0.5rem; border-top: 1px solid var(--border); }

/* ------------------------------------------------------------- cif viewer */
.cif-viewer { width: 100%; height: 400px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); position: relative; }
.cif-fallback { padding: 1rem; color: var(--text-dim); }

/* ------------------------------------------------------------------ auth */
.auth-shell { display: grid; place-items: center; min-height: 100vh; padding: 1.5rem; }
.auth-card { width: 100%; max-width: 420px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 1.6rem; }
.auth-card h1 { font-size: 1.25rem; }
.auth-lab { color: var(--text-dim); margin-bottom: 1.2rem; }
.auth-alt { margin-top: 1rem; text-align: center; color: var(--text-dim); font-size: 0.86rem; }
.qr-box { display: grid; place-items: center; padding: 0.9rem; background: #fff; border-radius: var(--radius-sm); margin-bottom: 0.8rem; }
.qr-box svg { width: 190px; height: 190px; }
.secret { font-family: var(--mono); background: var(--surface-2); padding: 0.5rem; border-radius: var(--radius-sm); word-break: break-all; text-align: center; }
.recovery-codes { columns: 2; font-family: var(--mono); background: var(--surface-2); padding: 0.8rem; border-radius: var(--radius-sm); }

/* ---------------------------------------------------------------- mobile */
@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar { position: static; height: auto; flex-direction: row; align-items: center; overflow-x: auto; }
  .nav { display: flex; gap: 0.2rem; padding: 0.4rem; }
  .nav-section, .sidebar-foot, .brand-lab { display: none; }
  .route { padding: 0.9rem; }
}

@media print {
  .sidebar, .topbar, .page-actions, .btn, .tabs { display: none !important; }
  .card { break-inside: avoid; box-shadow: none; }
  body { background: #fff; }
}

/* A card that is flagging a problem rather than just reporting state. */
.card.warn { border-color: var(--warn); }
.card.warn .card-head { background: var(--warn-soft); color: var(--warn); }

/* An identifier that navigates. The chip keeps its own look; only the
   affordance changes on hover, because these appear inside dense tables. */
a.code-link { text-decoration: none; }
a.code-link:hover { text-decoration: none; }
a.code-link:hover .code-chip {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

/* A furnace tile that opens its schedule. */
.furnace-card { transition: border-color 0.12s, box-shadow 0.12s; }
.furnace-card:hover,
.furnace-card:focus-visible {
  border-color: var(--accent);
  box-shadow: var(--shadow);
  outline: none;
}

/* ------------------------------------------------------- rich text fields */
.rt-field { margin-bottom: 0.6rem; }
.rt-label { display: block; font-weight: 560; font-size: 0.86rem; margin-bottom: 0.22rem; }
.rt-toolbar { align-items: flex-end; gap: 0.5rem; margin-bottom: 0.3rem; }
.rt-tabs { border-bottom: none; margin-bottom: 0; }
.rt-tabs .tab { padding: 0.25rem 0.6rem; font-size: 0.84rem; }
.rt-status { font-size: 0.8rem; color: var(--text-faint); min-width: 6rem; text-align: right; }
.rt-input { font-family: var(--mono); font-size: 0.9rem; line-height: 1.5; }
.rt-input.rt-dropping { outline: 2px dashed var(--accent); outline-offset: -2px; }

.rt-preview {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.7rem 0.9rem;
  background: var(--surface);
  min-height: 3rem;
}
/* Split view puts the source and the result side by side on a wide screen. */
.rt-mode-split .rt-input,
.rt-mode-split .rt-preview { width: calc(50% - 0.3rem); display: inline-block; vertical-align: top; }
.rt-mode-split .rt-preview { margin-left: 0.5rem; }
@media (max-width: 800px) {
  .rt-mode-split .rt-input,
  .rt-mode-split .rt-preview { width: 100%; margin-left: 0; }
}

.richtext > :first-child { margin-top: 0; }
.richtext > :last-child { margin-bottom: 0; }
.richtext h3, .richtext h4, .richtext h5, .richtext h6 { margin: 0.9em 0 0.35em; }
.richtext ul, .richtext ol { margin: 0 0 0.7em; padding-left: 1.4rem; }
.richtext li { margin-bottom: 0.2em; }
.richtext code {
  background: var(--surface-2);
  padding: 0.05em 0.3em;
  border-radius: 3px;
}
.rt-image {
  max-width: 100%;
  max-height: 420px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  display: block;
  margin: 0.5em 0;
}
.rt-display { margin: 0.8em 0; text-align: center; overflow-x: auto; }
.richtext math { font-size: 1.05em; }
/* Browsers without MathML fall back to the element's text, which is at least
   readable rather than blank. */
@supports not (display: math) {
  .richtext math { font-family: var(--mono); }
}

/* ---- the visual prose editor --------------------------------------------
   The surface is the rendered text itself, so it inherits .richtext styling
   and only needs the affordances of being editable. */
.rt-visual .rt-surface {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  outline: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.rt-visual .rt-surface:focus-within,
.rt-visual .rt-surface:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft, rgba(80, 140, 255, 0.16));
}
.rt-visual .rt-surface > *:first-child { margin-top: 0; }
.rt-visual .rt-surface > *:last-child { margin-bottom: 0; }

/* The placeholder, which a contenteditable does not get for free. */
.rt-visual .rt-surface.rt-empty::before {
  content: attr(data-placeholder);
  color: var(--text-faint);
  pointer-events: none;
  display: block;
  height: 0;
}

/* A typeset construct: one thing, not a row of characters. Clicking it puts
   the caret next to it, which reopens it as source. */
.rt-atom {
  display: inline-block;
  border-radius: 3px;
  padding: 0 1px;
  cursor: text;
}
.rt-atom:hover {
  background: var(--accent-soft, rgba(80, 140, 255, 0.14));
}
.rt-atom-display {
  display: block;
  margin: 0.5rem auto;
  text-align: center;
}
.rt-visual a.rt-link { cursor: text; }

/* ---- prefill picker ------------------------------------------------------ */
.pick-row:hover { background: var(--surface-alt, rgba(127, 127, 127, 0.08)); }
.pick-row.picked {
  background: var(--accent-soft, rgba(80, 140, 255, 0.16));
  box-shadow: inset 3px 0 0 var(--accent);
}

/* ---- prose previews on cards --------------------------------------------
   The same rich text as the record, but it has to behave like one paragraph
   in a list rather than like a document. */
.rt-excerpt {
  color: var(--text-dim);
  font-size: 0.9rem;
}
.rt-excerpt > * { margin: 0; }
.rt-excerpt > * + * { margin-top: 0.25rem; }
.rt-excerpt p {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rt-excerpt math { font-size: 0.95em; }
.rt-excerpt h3, .rt-excerpt h4, .rt-excerpt h5, .rt-excerpt h6 {
  font-size: 0.95rem;
  margin: 0;
}
.rt-excerpt ul, .rt-excerpt ol { margin: 0; padding-left: 1.1rem; }

/* ---- navigation feedback -------------------------------------------------
   The router keeps the previous page on screen until the next one is ready,
   which is right — blanking the content makes a fast app feel slower. What
   was missing was any sign that a click registered.

   The bar is delayed, so a navigation that resolves quickly (most of them,
   since the data is usually one small request) never flashes it. Only a wait
   long enough to doubt whether the click landed shows anything. */
.loading-route::before {
  content: '';
  position: fixed;
  inset: 0 auto auto 0;
  height: 2px;
  width: 100%;
  z-index: 60;
  background: linear-gradient(90deg,
    transparent 0%, var(--accent) 40%, var(--accent) 60%, transparent 100%);
  transform: translateX(-100%);
  animation: route-progress 1.1s ease-in-out 0.15s infinite;
  pointer-events: none;
}
@keyframes route-progress {
  to { transform: translateX(100%); }
}
/* Someone who has asked for less motion gets a static bar, not a moving one. */
@media (prefers-reduced-motion: reduce) {
  .loading-route::before {
    animation: none;
    transform: none;
    opacity: 0.55;
  }
}

/* ---- picking a date range on the calendar -------------------------------
   Choosing a span blind — click, then click again and hope — is where people
   get this wrong, and the four-day quota makes a wrong guess cost a
   correction. So the range is shown while it is being chosen. */
.cal-day.in-range {
  background: var(--accent-soft, rgba(80, 140, 255, 0.16));
}
.cal-day.range-start,
.cal-day.range-end {
  background: var(--accent);
  color: #fff;
}
.cal-day.range-start .cal-date,
.cal-day.range-end .cal-date { color: #fff; font-weight: 700; }
.cal-day.range-start { border-radius: 5px 0 0 5px; }
.cal-day.range-end { border-radius: 0 5px 5px 0; }
.cal-day.range-start.range-end { border-radius: 5px; }

/* ---- adding a reagent inline -------------------------------------------- */
.add-reagent-row td { border-top: 1px dashed var(--border); background: transparent; }
.add-reagent-cell { min-width: 18rem; }
.reagent-suggest {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 30;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  max-height: 15rem;
  overflow-y: auto;
}
.reagent-option { padding: 0.4rem 0.55rem; cursor: pointer; }
.reagent-option + .reagent-option { border-top: 1px solid var(--border); }
.reagent-option.active,
.reagent-option:hover { background: var(--accent-soft, rgba(80, 140, 255, 0.16)); }

/* ---- adding a batch, beside the batches ---------------------------------
   Present but quiet: the column holds its width so the sheet does not jump,
   and the button only resolves when you are actually over the table. */
.add-batch-col { width: 2.5rem; padding: 0 0.2rem !important; }
.add-batch-col .btn { opacity: 0; transition: opacity 0.12s ease; }
.stoich:hover .add-batch-col .btn,
.add-batch-col .btn:focus-visible { opacity: 1; }

/* ---- mentions and their completion --------------------------------------
   @someone and #something, tinted so a note reads as a note rather than as
   markup, and quiet enough not to compete with the prose around them. */
.mention {
  border-radius: 3px;
  padding: 0 2px;
  text-decoration: none;
  font-weight: 500;
}
.mention-person { color: var(--accent); background: var(--accent-soft, rgba(80, 140, 255, 0.13)); }
.mention-thing { color: var(--accent); background: var(--accent-soft, rgba(80, 140, 255, 0.13)); font-family: var(--mono, ui-monospace, monospace); font-size: 0.94em; }
a.mention:hover { text-decoration: underline; }

/* The completion offered after the caret. Not selectable, not copyable, and
   skipped by the serialiser — it is a hint, not text. */
.rt-ghost {
  color: var(--text-faint);
  opacity: 0.75;
  pointer-events: none;
  user-select: none;
  white-space: pre;
}

/* ---- picking a sample on the measurement card --------------------------- */
.sample-results {
  margin-top: 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  max-height: 14rem;
  overflow-y: auto;
}
.sample-option { padding: 0.4rem 0.55rem; cursor: pointer; }
.sample-option + .sample-option { border-top: 1px solid var(--border); }
.sample-option:hover { background: var(--accent-soft, rgba(80, 140, 255, 0.14)); }

/* ---- choosing how a reaction starts -------------------------------------- */
.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.5rem;
  margin: 0.4rem 0;
}
.template-card {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  text-align: left;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  font: inherit;
  cursor: pointer;
}
.template-card:hover { border-color: var(--accent); }
.template-card.picked {
  border-color: var(--accent);
  background: var(--accent-soft, rgba(80, 140, 255, 0.14));
  box-shadow: inset 0 0 0 1px var(--accent);
}
.pick-list {
  border: 1px solid var(--border);
  border-radius: 6px;
  max-height: 12rem;
  overflow-y: auto;
  margin-top: 0.4rem;
}

/* ---- sheet sections, row editing and the batch popover ------------------- */
.stoich-section td {
  background: var(--surface-2, rgba(127, 127, 127, 0.07));
  padding: 0.3rem 0.55rem;
  font-size: 0.84rem;
  border-top: 1px solid var(--border);
}

/* The reagent name is a button, but should read as the name it is until you
   are about to use it. */
.reagent-name {
  font: inherit;
  font-weight: 700;
  background: none;
  border: none;
  border-bottom: 1px dashed transparent;
  padding: 0;
  cursor: pointer;
  color: inherit;
  text-align: left;
}
.reagent-name:hover,
.reagent-name:focus-visible { border-bottom-color: var(--accent); color: var(--accent); }
.swap-host { margin: 0.25rem 0; }

/* Adding batches, in a panel where the + is rather than in a dialog. */
.add-batch-control { position: relative; display: inline-block; }
.add-batch-panel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 40;
  min-width: 12rem;
  padding: 0.55rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.2);
  text-align: left;
}
/* The trigger stays visible while its panel is open, whatever the hover rule
   for the column says. */
.add-batch-control:hover .btn,
.add-batch-control:focus-within .btn { opacity: 1; }

/* ---- hazard assessment --------------------------------------------------- */
.hazard-controls {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.25rem 1rem;
  margin-top: 0.3rem;
}
.hazard-control { align-items: flex-start; gap: 0.45rem; }

/* ---- dated observation entries ------------------------------------------
   Each sitting is its own card, so a multi-day growth reads as a sequence
   rather than as one wall of text. */
.card.note { margin-bottom: 0.7rem; }
.note-head {
  padding-bottom: 0.4rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
  align-items: baseline;
}
.note-body > *:first-child { margin-top: 0; }
.note-body > *:last-child { margin-bottom: 0; }

/* --- photographs of products ------------------------------------------- */
/* A strip of small pictures under the numbers that describe a product. It
   wraps rather than scrolls: a growth with eight photographs should show all
   eight, not hide seven behind a gesture nobody discovers. */
.photo-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.4rem 0 0.9rem;
}

.photo-thumb {
  margin: 0;
  width: 8.5rem;
  cursor: zoom-in;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
  transition: border-color 0.12s ease;
}

.photo-thumb:hover { border-color: var(--accent); }

.photo-thumb img {
  display: block;
  width: 100%;
  height: 6.4rem;
  object-fit: cover;
}

.photo-thumb figcaption {
  padding: 0.3rem 0.4rem;
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--text-dim);
  /* Two lines, then ellipsis: the full caption is in the viewer. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A file the browser cannot render — a TIFF, most often. */
.photo-noimage {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 6.4rem;
  background: var(--surface-2);
}

.photo-ext {
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  color: var(--text-dim);
}

.photo-add {
  width: 8.5rem;
  height: 6.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  cursor: pointer;
  color: var(--text-dim);
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  font: inherit;
}

.photo-add:hover { border-color: var(--accent); color: var(--accent); }
.photo-add span { font-size: 1.3rem; line-height: 1; }
.photo-add small { font-size: 0.72rem; }

.photo-preview img {
  max-width: 100%;
  max-height: 14rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  margin: 0.3rem 0;
}

/* The viewer. The picture gets the room; the details sit under it. */
.photo-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  border-radius: var(--radius);
  min-height: 12rem;
  overflow: hidden;
}

.photo-stage img {
  max-width: 100%;
  /* Bounded by the viewport so a portrait photograph cannot push the details
     and the Remove button below the fold. */
  max-height: min(52vh, 34rem);
  object-fit: contain;
}

.photo-meta { margin-top: 0.7rem; }

/* ============================================================== lab handbook
   Standard operating procedures and the responsibility sheet.

   The house style is the one the ampoule-line SOP was written in: a teal
   accent, a numbered rule under every section heading, callouts that are fully
   boxed rather than only barred, and tables set in their own bordered block
   with monospaced small-caps headers. The other documents were rewritten into
   it, so this is the single place their formatting is decided; the standalone
   copies in SOPs/ carry a light-mode version of the same rules, and the two are
   meant to be kept in step.

   Two palettes are in play and it is worth being clear about why:

     --sop-*      theme-aware, used by everything the notebook draws. Follows
                  light and dark like the rest of the application.
     the block on .sop-doc   the source documents' own variable names, pinned
                  to their light values. Imported figures are inline SVG whose
                  CSS refers to them by name, and half of those drawings also
                  carry hard-coded hex fills — so a figure is a sheet of white
                  paper in both themes rather than a diagram with three of its
                  nine colours inverted.

   On width: prose is capped at 70ch because a 1400px line is unreadable, but
   tables, figures and phase blocks are deliberately uncapped and fill the
   panel. Those are the things people came to the document to look at. */

:root {
  --sop-accent: #0d6874;
  --sop-accent-2: #0a505a;
  --sop-accent-wash: #dfeff1;
  --sop-ink: var(--text);
  --sop-ink-2: #33474a;
  --sop-muted: #61767a;
  --sop-rule: #d2dcdc;
  --sop-rule-2: #b3c3c3;
  --sop-paper: #ffffff;
  --sop-paper-2: #f5f8f8;
  /* The four callout tones, in the source document's own hazard colours. */
  --sop-danger: #af2a1b;
  --sop-danger-bg: #fae7e4;
  --sop-warn: #95610a;
  --sop-warn-bg: #faf0dc;
  --sop-note: #1e5b9c;
  --sop-note-bg: #e2ecf8;
  --sop-ok: #1b6b4a;
  --sop-ok-bg: #e3f1ea;
  /* One measure for the whole document. Prose used to stop at 70ch while a
     table ran the full panel, which read as two documents side by side. Now
     text, tables, callouts and lists all start and stop on the same two
     lines. Only figures are allowed past it — a diagram squeezed to the prose
     measure is a diagram nobody can read. */
  --sop-measure: 96ch;
  --sop-display: "Archivo", "Segoe UI Variable Display", var(--sans);
  --sop-body: "IBM Plex Sans", var(--sans);
  --sop-mono: "IBM Plex Mono", var(--mono);
}

@media (prefers-color-scheme: dark) {
  :root {
    --sop-accent: #4cbac6;
    --sop-accent-2: #7fd3dc;
    --sop-accent-wash: #0e3238;
    --sop-ink-2: #bacacb;
    --sop-muted: #88999c;
    --sop-rule: #263335;
    --sop-rule-2: #38494b;
    --sop-paper: #141c1e;
    --sop-paper-2: #1a2426;
    --sop-danger: #f1786a;
    --sop-danger-bg: #331714;
    --sop-warn: #dca33c;
    --sop-warn-bg: #2e230e;
    --sop-note: #7baee4;
    --sop-note-bg: #12233a;
    --sop-ok: #6bbf90;
    --sop-ok-bg: #14291f;
  }
}

/* ---------------------------------------------------------- the document */
.sop-doc {
  /* The imported figures' own variable names. Light values only — see above. */
  --ink: #101718; --ink-2: #33474a; --ink-3: #6b747e; --muted: #61767a;
  --gray: #6b6b6b; --rule: #d2dcdc; --rule-2: #b3c3c3;
  --paper: #ffffff; --wash: #f5f6f7; --fill: #eeeeee; --panel: #f7f6f4;
  --surface: #ffffff; --surface-2: #f5f8f8; --ground: #edf1f1;
  --scarlet: #a6192e; --hot: #c0392b; --warm: #e08c30; --cool: #3d6fa5;
  --danger: #b3261e; --danger-bg: #fdf1f0;
  --warn: #9a5b00; --warn-bg: #fdf6ea;
  --note: #1f5673; --note-bg: #eef4f8;
  --ok: #1b5e3a; --ok-bg: #eef6f1;
  --accent: #0d6874; --accent-2: #0a505a; --accent-wash: #dfeff1;
  --burn: #af2a1b; --burn-wash: #fae7e4;
  --press: #95610a; --press-wash: #faf0dc;
  --cryo: #1e5b9c; --cryo-wash: #e2ecf8;
  --tox: #63428f; --tox-wash: #ede6f7;
  --glass: rgba(13, 104, 116, 0.07);

  color: var(--sop-ink);
  font-family: var(--sop-body);
  font-size: 15.5px;
  line-height: 1.62;
}
.sop-doc a { color: var(--sop-accent); }

/* Everything is capped at the one measure; see --sop-measure. */
.sop-doc p, .sop-doc ul, .sop-doc ol, .sop-doc dl { max-width: var(--sop-measure); }
.sop-doc p { margin: 0 0 0.9rem; }
.sop-doc ul, .sop-doc ol { padding-left: 1.4rem; margin: 0 0 1rem; }
.sop-doc li { margin-bottom: 0.45rem; }
.sop-doc h3 {
  font-family: var(--sop-display); font-weight: 640; font-size: 1.09rem;
  letter-spacing: -0.005em; margin: 2.1rem 0 0.75rem;
}
.sop-doc h4 {
  font-family: var(--sop-mono); font-size: 0.69rem; font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--sop-muted);
  margin: 1.6rem 0 0.6rem;
}
.sop-doc h3:first-child, .sop-doc h4:first-child { margin-top: 0; }
.sop-doc strong { font-weight: 600; }
.sop-doc code {
  font-family: var(--sop-mono); font-size: 0.88em;
  background: var(--sop-paper-2); padding: 0.05em 0.3em; border-radius: 2px;
}
.sop-doc sub, .sop-doc sup { font-size: 0.72em; }

/* A subsection number: 1.1, A.4. Monospaced so a column of them lines up. */
.sop-num {
  font-family: var(--sop-mono); font-size: 0.8em; font-weight: 500;
  color: var(--sop-muted); margin-right: 0.6rem; letter-spacing: 0.03em;
}

.sop-lede { font-size: 1.03rem; color: var(--sop-ink-2); max-width: var(--sop-measure); }
.sop-small { font-size: 0.86rem; color: var(--sop-muted); }
.sop-where {
  display: block; font-size: 0.86em; color: var(--sop-muted);
  margin-top: 0.32rem; line-height: 1.45;
}
.sop-rule { border: 0; border-top: 1px solid var(--sop-rule); margin: 2.5rem 0; }
.sop-quote {
  margin: 1.3rem 0; padding: 1rem 1.25rem; max-width: var(--sop-measure);
  background: var(--sop-paper); border: 1px solid var(--sop-rule);
  border-left: 4px solid var(--sop-rule-2);
}
.sop-quote > :first-child { margin-top: 0; }
.sop-quote > :last-child { margin-bottom: 0; }

/* A blank in a draft, waiting for a pen. Shown as one, so it is obvious. */
.sop-blank {
  display: inline-block; min-width: 100px; color: var(--sop-muted);
  border-bottom: 1px dotted var(--sop-rule-2);
}

.sop-tag {
  display: inline-block; font-family: var(--sop-mono); font-size: 0.62rem;
  font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 0.12rem 0.45rem; border: 1px solid currentColor; border-radius: 2px;
}

/* ------------------------------------------------------------- callouts */
/* Boxed, not merely barred: the source style this is taken from gives a
   callout a full border so it reads as a panel set into the prose rather than
   as a paragraph that happens to be tinted. Four tones, merged down from the
   eleven kinds of coloured box the documents used between them — more than
   four and a reader has to learn a colour key before they can read a
   procedure. */
.sop-call {
  margin: 1.4rem 0; padding: 1rem 1.25rem; max-width: var(--sop-measure);
  background: var(--sop-paper);
  border: 1px solid var(--sop-rule);
  border-left: 4px solid var(--sop-accent);
}
.sop-call > :first-child { margin-top: 0; }
.sop-call > :last-child { margin-bottom: 0; }
.sop-call p:last-child { margin-bottom: 0; }
.sop-call-label {
  display: block; font-family: var(--sop-mono); font-size: 0.625rem;
  letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--sop-accent); margin-bottom: 0.45rem;
}
.sop-call-danger { border-left-color: var(--sop-danger); background: var(--sop-danger-bg); }
.sop-call-danger .sop-call-label { color: var(--sop-danger); }
.sop-call-warn { border-left-color: var(--sop-warn); background: var(--sop-warn-bg); }
.sop-call-warn .sop-call-label { color: var(--sop-warn); }
.sop-call-note { border-left-color: var(--sop-note); background: var(--sop-note-bg); }
.sop-call-note .sop-call-label { color: var(--sop-note); }
.sop-call-ok { border-left-color: var(--sop-ok); background: var(--sop-ok-bg); }
.sop-call-ok .sop-call-label { color: var(--sop-ok); }

/* -------------------------------------------------------- procedure steps */
.sop-steps { list-style: none; counter-reset: sop-step; padding-left: 0; max-width: var(--sop-measure); }
/* The marker is positioned out of flow rather than being a grid column.
   A grid container turns every child into a grid item, and a step that begins
   with a bolded phrase — which most of them do — then has three items instead
   of two: the marker, the <strong>, and the rest of the sentence. The third
   wraps onto the next row, into the 2.4rem marker column, and the remainder of
   the step renders one word per line. This layout cannot do that, whatever the
   step is made of. */
.sop-steps > li {
  counter-increment: sop-step;
  position: relative; padding-left: 2.6rem; margin-bottom: 0.7rem;
}
.sop-steps > li::before {
  content: counter(sop-step);
  position: absolute; left: 0; top: 0.18rem; width: 1.8rem; text-align: right;
  font-family: var(--sop-mono); font-size: 0.7rem; font-weight: 600;
  color: var(--sop-accent); letter-spacing: 0.03em;
}
/* Steps the document numbers itself ("A.4"), because the prose refers back to
   them by those labels. The counter is suppressed rather than duplicated. */
.sop-steps-labelled > li { padding-left: 3.2rem; }
.sop-steps-labelled > li::before { content: none; }
.sop-step-num {
  position: absolute; left: 0; top: 0.18rem; width: 2.4rem; text-align: right;
  font-family: var(--sop-mono); font-size: 0.7rem; font-weight: 500;
  color: var(--sop-muted); letter-spacing: 0.03em;
}

.sop-check { list-style: none; padding-left: 0; max-width: var(--sop-measure); }
.sop-check > li { position: relative; padding-left: 2.1rem; margin-bottom: 0.55rem; }
.sop-check > li::before {
  content: ""; position: absolute; left: 0; top: 0.38em;
  width: 0.85rem; height: 0.85rem;
  border: 1.5px solid var(--sop-rule-2); border-radius: 2px;
}

.sop-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 0 2.2rem;
}
.sop-cols ul, .sop-cols ol, .sop-cols p { max-width: none; }
.sop-refs { list-style: none; padding-left: 0; font-size: 0.92rem; max-width: var(--sop-measure); }
.sop-refs > li { margin-bottom: 0.65rem; }
.sop-legend {
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.15rem; margin: 0 0 0.9rem;
  font-family: var(--sop-mono); font-size: 0.66rem; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--sop-muted);
}
.sop-legend span { display: flex; align-items: center; gap: 0.45rem; }
.sop-legend i { width: 14px; height: 4px; border-radius: 1px; display: block; }

/* ---------------------------------------------------------------- tables */
/* Set in their own bordered block, full width of the panel. A hazard table is
   one of the things people open a procedure to read; making it share the
   prose measure would be reformatting it for the wrong reader. */
.sop-tablewrap {
  overflow-x: auto; margin: 1.4rem 0; max-width: var(--sop-measure);
  border: 1px solid var(--sop-rule); background: var(--sop-paper);
}
.sop-tablewrap table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
.sop-tablewrap th {
  text-align: left; font-family: var(--sop-mono); font-size: 0.625rem;
  font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--sop-muted); background: var(--sop-paper-2);
  padding: 0.7rem 0.9rem; vertical-align: bottom;
  border-bottom: 1px solid var(--sop-rule-2);
}
.sop-tablewrap td {
  padding: 0.8rem 0.9rem; vertical-align: top; line-height: 1.5;
  border-bottom: 1px solid var(--sop-rule);
}
.sop-tablewrap tbody tr:last-child td { border-bottom: none; }
.sop-tablewrap td.num,
.sop-tablewrap .sop-numeric {
  font-family: var(--sop-mono); font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* The hazard table's coloured left edge is the one thing in these documents
   that a reader cannot get from the words, so it survives the merge. */
.sop-hazards td:first-child { border-left: 4px solid var(--sop-rule-2); }
.sop-hazard-thermal td:first-child { border-left-color: var(--sop-danger); }
.sop-hazard-pressure td:first-child { border-left-color: var(--sop-warn); }
.sop-hazard-cryo td:first-child { border-left-color: var(--sop-note); }
.sop-hazard-toxic td:first-child { border-left-color: #7a54ad; }

/* --------------------------------------------------------------- figures */
/* Deliberately a sheet of white paper in both themes. The drawings are inline
   SVG carrying hard-coded fills alongside their variables; inverting half of a
   diagram is worse than a bright rectangle on a dark page. */
.sop-figure {
  margin: 1.9rem 0; padding: 1.4rem 1.4rem 0;
  background: #ffffff; color: #33474a;
  border: 1px solid var(--sop-rule);
  overflow-x: auto;
}
.sop-figure svg,
.sop-figure img { display: block; width: 100%; height: auto; }
/* A drawn structure is a picture at a size that suits it, not a diagram that
   should fill the panel: a benzene ring stretched to 900px is absurd. */
.sop-figure svg.mol-svg { width: auto; max-width: 100%; margin: 0 auto; }
/* A drawing has a width below which its labels stop being readable, so it
   scrolls rather than shrinking past it. A photograph has no such floor. */
.sop-figure svg { min-width: 460px; }
.sop-figure figcaption {
  margin: 1.1rem 0 0; padding: 1rem 0 1.25rem;
  border-top: 1px solid #e4e7ea;
  font-size: 0.86rem; line-height: 1.55; color: #33474a;
  max-width: var(--sop-measure);
}
.sop-figure figcaption b {
  font-family: var(--sop-mono); font-size: 0.9em; font-weight: 600;
  letter-spacing: 0.04em; color: #0d6874; margin-right: 0.5rem;
}

/* ------------------------------------------------------- lettered phases */
.sop-phase {
  margin: 0 0 0.6rem; max-width: var(--sop-measure);
  border: 1px solid var(--sop-rule); background: var(--sop-paper);
}
.sop-phase-head {
  display: flex; align-items: center; gap: 0.9rem;
  padding: 0.85rem 1.25rem; background: var(--sop-paper-2);
  border-bottom: 1px solid var(--sop-rule);
}
.sop-phase-head h3 { margin: 0; font-size: 1.02rem; }
.sop-phase-id {
  flex: none; width: 26px; height: 26px; border-radius: 2px;
  display: flex; align-items: center; justify-content: center;
  background: var(--sop-accent); color: var(--sop-paper);
  font-family: var(--sop-mono); font-size: 0.75rem; font-weight: 600;
}
.sop-phase-body { padding: 1.1rem 1.25rem 0.4rem; }

/* ================================================== the SOP reader chrome */
/* The sheet fills the panel. The old version capped the whole document at a
   prose measure, which left two thirds of a wide screen empty and squeezed
   every table into a scroll box it did not need. */
.sop-sheet {
  background: var(--sop-paper);
  border: 1px solid var(--sop-rule);
  border-top: 5px solid var(--sop-accent);
  box-shadow: var(--shadow);
  padding: 2.1rem 2.2rem 3rem;
}
@media (max-width: 800px) { .sop-sheet { padding: 1.2rem 1.1rem 2rem; } }

.sop-code {
  font-family: var(--sop-mono); font-weight: 600; letter-spacing: 0.05em;
  color: var(--sop-accent);
}
.sop-masthead-org {
  font-family: var(--sop-mono); font-size: 0.69rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--sop-accent);
}
.sop-sheet > h1 {
  font-family: var(--sop-display); font-weight: 700;
  font-size: clamp(1.6rem, 2.6vw, 2.3rem); line-height: 1.1;
  letter-spacing: -0.018em; text-wrap: balance;
}

.sop-control {
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
  border-top: 1px solid var(--sop-rule);
  font-family: var(--sop-mono); font-size: 0.72rem;
  margin: 1.4rem 0 2rem;
}
.sop-control > div { padding: 0.75rem 1rem 0.75rem 0; border-bottom: 1px solid var(--sop-rule); }
.sop-control .k {
  display: block; font-size: 0.59rem; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--sop-muted); font-weight: 500;
  margin-bottom: 0.25rem;
}
.sop-control .v { color: var(--sop-ink); font-weight: 500; }

/* The section list, as the printed contents page it replaces. */
.sop-toc {
  columns: 2; column-gap: 2.4rem; margin: 0 0 0.5rem;
  font-family: var(--sop-mono); font-size: 0.74rem; letter-spacing: 0.02em;
}
@media (min-width: 1180px) { .sop-toc { columns: 3; } }
@media (max-width: 700px) { .sop-toc { columns: 1; } }
.sop-toc a {
  display: block; padding: 0.32rem 0; color: var(--sop-ink-2);
  border-bottom: 1px solid var(--sop-rule); break-inside: avoid;
  text-transform: uppercase; font-weight: 500;
}
.sop-toc a:hover { color: var(--sop-accent); text-decoration: none; }
.sop-toc .n { color: var(--sop-muted); margin-right: 0.6rem; }

.sop-section { scroll-margin-top: 4.5rem; }
.sop-section + .sop-section { margin-top: 3.4rem; }
.sop-section > h2 {
  display: flex; align-items: baseline; gap: 0.9rem;
  font-family: var(--sop-display); font-weight: 620; font-size: 1.5rem;
  letter-spacing: -0.012em; text-wrap: balance;
  border-bottom: 2px solid var(--sop-ink);
  padding-bottom: 0.55rem; margin: 0 0 1.6rem;
}
.sop-section > h2 .sop-secnum {
  flex: none;
  font-family: var(--sop-mono); font-size: 0.8rem; font-weight: 600;
  color: var(--sop-accent); letter-spacing: 0.06em;
}

/* Read-through progress. Not a gate — it is a reminder of how much is left,
   which a scrollbar on a 20,000-word document does not really give you. */
.sop-progress {
  position: sticky; top: 0; z-index: 5;
  height: 3px; background: var(--sop-rule); overflow: hidden;
}
.sop-progress span {
  display: block; height: 100%; width: 0;
  background: var(--sop-accent); transition: width 0.15s linear;
}

/* ------------------------------------------------------------ the library */
.sop-card-title { font-weight: 640; }

/* The onboarding block: shown instead of the notebook until the required
   reading is done. */
.sop-gate { max-width: 820px; }
.sop-gate-list { list-style: none; margin: 1rem 0 0; padding: 0; }
.sop-gate-list li {
  display: flex; align-items: center; gap: 0.8rem;
  padding: 0.75rem 0; border-bottom: 1px solid var(--border);
}
.sop-gate-list li:last-child { border-bottom: 0; }
.sop-gate-step {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: grid; place-items: center; font-weight: 700; font-size: 0.78rem;
  background: var(--surface-2); color: var(--text-dim);
}
.sop-gate-step.done { background: var(--accent-soft); color: var(--accent); }

/* ------------------------------------------------------------- the quiz */
.sop-quiz-q { padding: 1rem 0; border-bottom: 1px solid var(--border); }
.sop-quiz-q:last-of-type { border-bottom: 0; }
.sop-quiz-prompt { font-weight: 560; margin-bottom: 0.6rem; max-width: 78ch; }
.sop-quiz-prompt .n {
  font-family: var(--sop-mono); color: var(--text-faint);
  margin-right: 0.6rem; font-weight: 500;
}
.sop-choice {
  display: flex; gap: 0.6rem; align-items: flex-start; max-width: 78ch;
  padding: 0.45rem 0.6rem; border: 1px solid var(--border);
  margin-bottom: 0.35rem; cursor: pointer;
}
.sop-choice:hover { border-color: var(--border-strong); background: var(--surface-2); }
.sop-choice input { margin-top: 0.2rem; flex: none; }
.sop-choice.picked { border-color: var(--accent); background: var(--accent-soft); }
.sop-choice.right { border-color: var(--ok); background: var(--accent-soft); }
.sop-choice.wrong { border-color: var(--danger); background: var(--danger-soft); }
.sop-result {
  margin-top: 0.55rem; padding: 0.6rem 0.75rem; max-width: 78ch;
  font-size: 0.9rem; background: var(--surface-2);
  border-left: 3px solid var(--border-strong);
}
.sop-result.wrong { background: var(--danger-soft); border-left-color: var(--danger); }
.sop-score {
  font-family: var(--sop-mono); font-size: 2rem; font-weight: 700; line-height: 1;
}

/* ------------------------------------------- responsibility and contacts */
.contact-grid { display: grid; gap: 0.9rem; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.contact-card {
  border: 1px solid var(--border); background: var(--surface);
  border-left: 3px solid var(--sop-accent);
  padding: 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.55rem;
}
.contact-card.urgent { border-left-color: var(--danger); }
.contact-card.vacant { border-style: dashed; }
.contact-title { font-family: var(--sop-display); font-weight: 640; }
.contact-who { display: flex; align-items: center; gap: 0.55rem; }
.contact-who .avatar { width: 26px; height: 26px; font-size: 0.7rem; }
.contact-lines { list-style: none; margin: 0; padding: 0; font-size: 0.88rem; color: var(--text-dim); }
.contact-lines li { position: relative; padding-left: 0.9rem; margin: 0.18rem 0; }
.contact-lines li::before { content: "·"; position: absolute; left: 0.2rem; color: var(--text-faint); }
.contact-reach { display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.9rem; }
.contact-reach a { font-family: var(--sop-mono); }
.contact-reach .label {
  color: var(--text-faint); font-size: 0.62rem; margin-right: 0.5rem;
  font-family: var(--sop-mono); letter-spacing: 0.1em; text-transform: uppercase;
}
.contact-missing { color: var(--warn); font-size: 0.86rem; }

/* Emergency numbers get their own treatment. Somebody reading this panel is
   having a bad day and should not have to pick it out of a grid. */
.contact-emergency { border: 1px solid var(--danger); overflow: hidden; }
.contact-emergency-head {
  background: var(--danger-soft); color: var(--danger);
  padding: 0.7rem 1.1rem; font-weight: 700;
  font-family: var(--sop-mono); font-size: 0.7rem;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.contact-emergency-row {
  display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  padding: 0.75rem 1.1rem; border-top: 1px solid var(--border);
}
.contact-emergency-row .num {
  font-family: var(--sop-mono); font-size: 1.3rem; font-weight: 700;
  color: var(--danger); letter-spacing: 0.02em;
}
.contact-emergency-row .what { flex: 1; min-width: 14rem; }

/* ------------------------------------------------- editing a section in place */
/* Wiki convention: the controls live beside the heading, in brackets, and read
   as part of the document rather than as a toolbar bolted above it. They are
   quiet until the section is hovered, because an administrator reads these
   documents far more often than they edit them. */
.sop-sec-tools {
  margin-left: 0.7rem; font-family: var(--sop-mono); font-size: 0.68rem;
  font-weight: 500; letter-spacing: 0.02em; white-space: nowrap;
  opacity: 0; transition: opacity 0.12s ease;
}
.sop-section:hover .sop-sec-tools,
.sop-sec-tools:focus-within { opacity: 1; }
.sop-sec-bracket { color: var(--sop-rule-2); }
.sop-sec-link {
  background: none; border: 0; padding: 0 0.3rem; cursor: pointer;
  font: inherit; color: var(--sop-accent);
}
.sop-sec-link:hover { text-decoration: underline; }
.sop-sec-link:focus-visible { outline: 2px solid var(--sop-accent); outline-offset: 2px; }

/* The "add a section here" rule between sections. Invisible until wanted, so a
   document being read does not look like a form. */
.sop-add-divider { position: relative; height: 0.9rem; margin: 0.6rem 0; }
.sop-add-divider::before {
  content: ""; position: absolute; inset: 50% 0 auto; height: 1px;
  background: var(--sop-rule); opacity: 0; transition: opacity 0.12s ease;
}
.sop-add-divider:hover::before { opacity: 1; }
.sop-add-here {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: var(--sop-paper); border: 1px dashed var(--sop-rule-2);
  color: var(--sop-muted); cursor: pointer;
  font-family: var(--sop-mono); font-size: 0.66rem; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 0.2rem 0.7rem;
  opacity: 0; transition: opacity 0.12s ease;
}
.sop-add-divider:hover .sop-add-here,
.sop-add-here:focus-visible { opacity: 1; }
.sop-add-here:hover { color: var(--sop-accent); border-color: var(--sop-accent); }

/* ----------------------------------------------------------- the editor */
.sop-edit { border: 1px solid var(--sop-accent); background: var(--sop-paper); }
.sop-edit-head {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.sop-edit-tabs { display: flex; gap: 0.15rem; }
.sop-edit-tab {
  background: none; border: 1px solid transparent; cursor: pointer;
  padding: 0.25rem 0.7rem; color: var(--text-dim);
  font-family: var(--sop-mono); font-size: 0.68rem;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.sop-edit-tab.active {
  background: var(--sop-paper); border-color: var(--border);
  color: var(--sop-accent); font-weight: 600;
}
.sop-edit-note { color: var(--text-faint); font-size: 0.78rem; }

.sop-edit-toolbar {
  display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem;
  padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.sop-tool-group {
  display: flex; align-items: center; gap: 0.15rem;
  padding-right: 0.5rem; border-right: 1px solid var(--border);
}
.sop-tool-group:last-child { border-right: 0; }
.sop-tool {
  background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
  cursor: pointer; padding: 0.22rem 0.5rem; color: var(--text);
  font-size: 0.8rem; line-height: 1.4; min-width: 1.8rem;
}
.sop-tool:hover { background: var(--sop-paper); border-color: var(--border); }
.sop-tool:focus-visible { outline: 2px solid var(--sop-accent); outline-offset: 1px; }
.sop-tool-b { font-weight: 700; }
.sop-tool-i { font-style: italic; font-family: Georgia, serif; }
.sop-tool-select { height: 1.9rem; padding: 0 0.4rem; font-size: 0.8rem; width: auto; }

.sop-tool-drop { position: relative; }
.sop-tool-menu {
  display: none; position: absolute; z-index: 30; top: 100%; left: 0;
  min-width: 13rem; background: var(--surface);
  border: 1px solid var(--border); box-shadow: var(--shadow); padding: 0.2rem;
}
.sop-tool-drop:hover .sop-tool-menu,
.sop-tool-drop:focus-within .sop-tool-menu { display: block; }
.sop-tool-menu-item {
  display: flex; align-items: center; gap: 0.55rem; width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 0.35rem 0.5rem; font: inherit; font-size: 0.85rem; color: var(--text);
}
.sop-tool-menu-item:hover { background: var(--surface-2); }
.sop-swatch { width: 4px; height: 1.1rem; flex: none; border-radius: 1px; }
.sop-swatch-danger { background: var(--sop-danger); }
.sop-swatch-warn { background: var(--sop-warn); }
.sop-swatch-note { background: var(--sop-note); }
.sop-swatch-ok { background: var(--sop-ok); }

/* The editing surface is the reading surface — same stylesheet, same measure —
   so what an author types is what a reader gets, without a preview pane. */
.sop-edit-surface {
  padding: 1.2rem 1.4rem; min-height: 12rem; outline: none;
}
.sop-edit-surface:focus { outline: none; }
.sop-edit-surface :where(p, li, td, th, h3, h4):empty::after {
  content: ""; display: inline-block; min-width: 1px;
}
.sop-edit-source {
  width: 100%; border: 0; border-radius: 0; resize: vertical;
  font-family: var(--sop-mono); font-size: 0.82rem; line-height: 1.55;
}
.sop-edit-source:focus { outline: 2px solid var(--sop-accent); outline-offset: -2px; }
.sop-edit-actions { margin-top: 0.7rem; }

/* A figure cannot be typed into: it is inline SVG with its own stylesheet, and
   a caret loose inside a <path> destroys it silently. */
.sop-edit-surface .sop-figure-locked { position: relative; user-select: none; }
.sop-edit-surface .sop-figure-locked::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  outline: 1px dashed var(--sop-rule-2); outline-offset: -3px;
}
.sop-figure-tools {
  position: absolute; top: 0.4rem; right: 0.4rem; z-index: 2;
  display: flex; gap: 0.25rem; user-select: none;
}

/* ------------------------------------------------------ reaction equations */
/* Set as a line of chemistry rather than as a paragraph: the terms sit on a
   common baseline, the arrow carries its conditions above and below it the way
   it is written on a board, and the whole thing scrolls rather than wrapping —
   an equation broken across two lines mid-arrow is unreadable. */
.rx-eq {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.15rem 0.45rem;
  margin: 1.2rem 0; padding: 0.9rem 1.1rem;
  max-width: var(--sop-measure);
  background: var(--sop-paper-2);
  border: 1px solid var(--sop-rule);
  border-left: 3px solid var(--sop-accent);
  font-size: 1.04rem; line-height: 1.35;
  overflow-x: auto;
  position: relative;
}
.rx-term { display: inline-flex; align-items: baseline; gap: 0.12rem; white-space: nowrap; }
.rx-coef { font-weight: 600; font-variant-numeric: tabular-nums; }
.rx-formula { font-family: var(--mono); white-space: nowrap; }
.rx-formula sub { font-size: 0.72em; }
.rx-op { color: var(--sop-muted); padding: 0 0.1rem; }

/* The arrow is a column so its conditions stack over and under the glyph. The
   glyph is stretched to at least the width of the longest condition, because an
   arrow shorter than the text above it looks like a mistake. */
.rx-arrow {
  display: inline-flex; flex-direction: column; align-items: center;
  margin: 0 0.5rem; min-width: 3.2rem;
}
.rx-glyph { font-size: 1.25rem; line-height: 1; color: var(--sop-accent); }
.rx-above, .rx-below {
  font-family: var(--sop-mono); font-size: 0.66rem; letter-spacing: 0.02em;
  color: var(--sop-muted); white-space: nowrap; line-height: 1.3;
}
.rx-above { margin-bottom: 0.1rem; }
.rx-below { margin-top: 0.1rem; }

/* In the editor an equation is an object, not text. */
.sop-edit-surface .rx-locked { user-select: none; }
.rx-tools {
  position: absolute; top: 0.3rem; right: 0.3rem;
  display: none; gap: 0.25rem;
}
.sop-edit-surface .rx-locked:hover .rx-tools,
.sop-edit-surface .rx-locked:focus-within .rx-tools { display: flex; }

/* ------------------------------------------------------- the equation builder */
.rx-builder .rx-sides {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 1.4rem;
}
@media (max-width: 640px) { .rx-builder .rx-sides { grid-template-columns: 1fr; } }
.rx-builder h4 {
  font-family: var(--sop-mono); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-dim);
  margin: 0.9rem 0 0.5rem;
}
.rx-rows { display: flex; flex-direction: column; gap: 0.3rem; align-items: flex-start; }
.rx-row { display: flex; gap: 0.3rem; align-items: center; width: 100%; }
.rx-coef-input { width: 3.4rem; flex: none; text-align: right; font-variant-numeric: tabular-nums; }
.rx-formula-input { flex: 1; font-family: var(--mono); }
.rx-arrow-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0 1rem; margin-top: 0.6rem;
}
.rx-preview {
  min-height: 4.2rem; display: flex; align-items: center;
  background: var(--surface-2); border: 1px dashed var(--border); padding: 0 0.7rem;
}
.rx-preview .rx-eq { margin: 0.7rem 0; border-left-width: 3px; max-width: none; }

.rx-balance { margin-top: 0.6rem; font-size: 0.88rem; color: var(--text-dim); }
.rx-balance-ok { color: var(--ok); font-weight: 560; }
.rx-balance-off { color: var(--warn); }
.rx-balance-bad { color: var(--danger); }
.rx-balance-note { color: var(--text-faint); }

/* -------------------------------------------------------- prose fields */
/* The read view of a prose field, and the quiet way into editing it. The
   affordance borrows the SOP section's bracketed link, because it is the same
   gesture doing the same thing. */
.prose-field { position: relative; }
.prose-view .prose-actions { margin-top: 0.4rem; opacity: 0; transition: opacity 0.12s ease; }
.prose-field:hover .prose-actions,
.prose-actions:focus-within { opacity: 1; }
.prose-empty { color: var(--text-faint); font-style: italic; margin: 0; }
.prose-editing .sop-edit { border-color: var(--accent); }

/* Prose is written at the notebook's own text size rather than the SOP's, and
   without the document's outer margins: it is already inside a card. */
.prose-field .sop-doc { font-size: 14.5px; }
.prose-field .sop-doc > :first-child { margin-top: 0; }
.prose-field .sop-doc > :last-child { margin-bottom: 0; }
.prose-field .sop-edit-surface { padding: 0.9rem 1rem; min-height: 7rem; }
.note-body .sop-doc > :first-child { margin-top: 0; }
.note-body .sop-doc > :last-child { margin-bottom: 0; }

/* A term drawn as a structure rather than written as a formula. */
.rx-structure { display: inline-flex; flex-direction: column; align-items: center; gap: 0.1rem; }
.rx-structure svg { display: block; width: auto; height: auto; max-width: 200px; max-height: 120px; }
.rx-structure-label { font-family: var(--mono); font-size: 0.72rem; color: var(--sop-muted); }
.rx-structure-label sub { font-size: 0.72em; }
/* An equation with a drawing in it needs room to breathe. */
.rx-eq:has(.rx-structure) { align-items: flex-end; gap: 0.3rem 0.6rem; }
.rx-caption {
  flex-basis: 100%; margin-top: 0.4rem; font-size: 0.86rem; color: var(--sop-muted);
}

/* The builder rows, now that a term can be written two ways. */
.rx-term-row { padding: 0.35rem 0; border-bottom: 1px dashed var(--border); }
.rx-term-row:last-of-type { border-bottom: 0; }
.rx-row-structure { margin-top: 0.2rem; }
.rx-smiles-input { flex: 1; font-family: var(--mono); font-size: 0.82rem; }
.rx-term-note { display: block; font-size: 0.78rem; margin-top: 0.15rem; min-height: 1rem; }

/* --------------------------------------------------- the structure builder */
.mol-builder .mol-preview-wrap { margin-top: 0.9rem; }
.mol-builder h4 {
  font-family: var(--sop-mono); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-dim);
  margin: 0 0 0.4rem;
}
.mol-preview {
  min-height: 9rem; display: grid; place-items: center; padding: 0.6rem;
  background: #ffffff; border: 1px dashed var(--border);
}
.mol-preview svg { max-width: 100%; max-height: 15rem; }
.mol-status { margin-top: 0.4rem; font-size: 0.86rem; color: var(--text-dim); }
.mol-ok { font-family: var(--mono); color: var(--text); font-weight: 560; }
.mol-bad { color: var(--danger); }
.mol-note { color: var(--text-faint); }
.mol-results { display: flex; flex-direction: column; gap: 0.15rem; margin-top: 0.4rem; max-height: 11rem; overflow-y: auto; }
.mol-hit {
  display: flex; align-items: baseline; gap: 0.6rem; width: 100%; text-align: left;
  background: none; border: 1px solid transparent; cursor: pointer;
  padding: 0.3rem 0.45rem; font: inherit; font-size: 0.88rem; color: var(--text);
}
.mol-hit:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border); }
.mol-hit:disabled { cursor: not-allowed; color: var(--text-faint); }
.mol-hit-name { flex: 1; }
.mol-hit-formula { font-family: var(--mono); font-size: 0.82rem; color: var(--text-dim); }
.mol-caption-fields { display: grid; grid-template-columns: minmax(120px, 1fr) 2fr; gap: 0 1rem; margin-top: 0.8rem; }
@media (max-width: 620px) { .mol-caption-fields { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------------ print */
/* A procedure gets printed and posted at the bench. That copy has to be as
   readable as the screen, and must not carry the application around it. */
@media print {
  .sop-sheet { border: 0; box-shadow: none; padding: 0; }
  .sop-doc { font-size: 10.5pt; }
  .sop-progress, .sop-toc,
  .sop-sec-tools, .sop-add-divider, .sop-edit-toolbar, .sop-edit-head,
  .rx-tools, .prose-actions { display: none; }
  .rx-eq { break-inside: avoid; page-break-inside: avoid; }
  .sop-section > h2 { break-after: avoid; page-break-after: avoid; }
  .sop-figure, .sop-tablewrap, .sop-call, .sop-phase {
    break-inside: avoid; page-break-inside: avoid;
  }
  .sop-figure svg, .sop-figure figcaption { min-width: 0; }
  .contact-grid { grid-template-columns: repeat(2, 1fr); }
  .contact-card { break-inside: avoid; }
}
