/* ===========================================================================
   MDvsNOE — frontend styles

   Direction: a measuring instrument's faceplate over its worksheet.

   Three faces, three jobs, never mixed. The serif states the science — the
   headline and the display equations, set the way the method section they will
   be cited in is set. The sans gives instructions to the operator. The mono
   carries every label, unit, filename and number, because anything measured or
   literal must look measured or literal.

   The signature is the graduated rule: a strip of hairline ticks, every fifth
   one long, running under the masthead and above the footer. It is a ruler's
   edge. It is also the only ornament on the page.

   Geometry is square and ruled — no radius, no shadow, no gradient except the
   graduations. Fields are white because the result charts are laid beside the
   published figures, which are on white; the page ground is a cool grey so the
   white reads as a printed field rather than as more page.

   The chrome is achromatic on purpose. The diverging data scale (cool / mid /
   warm) is the only colour on the page, so a hue anywhere else would compete
   with the reading. Red is reserved for errors and never appears beside data.

   Self-contained: system fonts, no CDN, no external asset. A strict CSP is
   enforced at the proxy, so nothing here may fetch anything.

   Load-bearing for app.js: --dev-cool / --dev-mid / --dev-warm / --text /
   --muted / --border-2 / --surface are read from the computed style, --sp-4
   and --sp-6 are used in inline styles, and dark is detected by testing --bg
   against exactly #0d0d0d. Do not rename or re-format those. The --dev-* trio
   must stay 7-character hex: devColour() slices the string.
   =========================================================================== */

/* -------------------------------- tokens --------------------------------- */
:root {
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace;
  /* A text serif, not a display one: Charter / New York / Cambria all set a
     hairline-free, high-x-height page that survives at 15px. Times is the
     last resort before the generic. */
  --font-serif: ui-serif, "Iowan Old Style", Charter, "Bitstream Charter",
    "Charis SIL", Cambria, Constantia, "Palatino Linotype", Palatino, Georgia,
    "Liberation Serif", "DejaVu Serif", "Times New Roman", serif;

  /* One ramp, ~1.2 between neighbours. 2xs is the mono micro-label and the
     smallest thing on the page; xs is dense tabular data; base is prose. */
  --fs-2xs: .75rem;    /* 12px */
  --fs-xs: .8125rem;   /* 13px */
  --fs-sm: .9375rem;   /* 15px */
  --fs-base: 1.0625rem;/* 17px */
  --fs-md: 1.1875rem;  /* 19px */
  --fs-lg: 1.375rem;   /* 22px */
  --fs-xl: 1.75rem;    /* 28px */
  --fs-2xl: 2.375rem;  /* 38px */
  --fs-3xl: clamp(1.75rem, 1.15rem + 1.9vw, 2.5rem);

  --lh-body: 1.6;
  --lh-snug: 1.45;
  --lh-tight: 1.14;
  --tracking-caps: .13em;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;

  /* Light: a cool grey faceplate, printed fields in white, blue-black ink. */
  --bg: #e9ebea;
  --surface: #ffffff;
  --surface-2: #f2f4f3;
  --surface-3: #dbdedd;
  --border: #d2d6d5;
  --border-2: #8e9694;
  --text: #0f1517;
  --text-2: #2e3739;
  --muted: #4b5553;
  --faint: #5f6865;
  --hatch: rgba(15, 21, 23, .055);

  /* the primary action wears ink, not a brand colour */
  --btn-bg: #0f1517;
  --btn-fg: #f2f4f3;
  --btn-bg-hover: #000000;

  /* Diverging scale for signed relative deviation. Validated: CVD dE 10.3
     light / 10.0 dark, normal-vision 16.2 / 17.0, contrast >=3:1 on both
     surfaces. The midpoint is achromatic by design. Never re-pick by eye and
     never spend one on an annotation. */
  --dev-cool: #2a78d6; --dev-mid: #898781; --dev-warm: #c9502a;

  /* reserved: errors only, never a data hue */
  --bad: #b62222;

  --shell: 1200px;
  --bar-h: 62px;
  --w-num: 148px;
}

/* Dark, shared by auto-dark and the explicit override.
   --bg MUST stay exactly #0d0d0d: app.js tests it to decide which theme it is in. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d0d0d;
    --surface: #16191a;
    --surface-2: #1c2021;
    --surface-3: #2a2f30;
    --border: #262b2c;
    --border-2: #4b5354;
    --text: #eef1f1;
    --text-2: #c3cacb;
    --muted: #949c9d;
    --faint: #757d7e;
    --hatch: rgba(238, 241, 241, .06);
    --btn-bg: #eef1f1;
    --btn-fg: #0d0d0d;
    --btn-bg-hover: #ffffff;
    --dev-cool: #3987e5; --dev-mid: #898781; --dev-warm: #e35f36;
    --bad: #ff6b63;
  }
}
:root[data-theme="dark"] {
  --bg: #0d0d0d;
  --surface: #16191a;
  --surface-2: #1c2021;
  --surface-3: #2a2f30;
  --border: #262b2c;
  --border-2: #4b5354;
  --text: #eef1f1;
  --text-2: #c3cacb;
  --muted: #949c9d;
  --faint: #757d7e;
  --hatch: rgba(238, 241, 241, .06);
  --btn-bg: #eef1f1;
  --btn-fg: #0d0d0d;
  --btn-bg-hover: #ffffff;
  --dev-cool: #3987e5; --dev-mid: #898781; --dev-warm: #e35f36;
  --bad: #ff6b63;
}

/* --------------------------------- base ---------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { font-size: 100%; -webkit-text-size-adjust: 100%; }

/* [hidden] must always win: several toggled elements carry a class or id that
   sets display, which would otherwise out-specify the UA rule. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;          /* wide content scrolls in its own box, never here */
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0; line-height: var(--lh-tight); font-weight: 700; }
p, dd, dl, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding-left: 1.25em; }
li { margin: 0; }
img, svg, canvas { max-width: 100%; }
hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-6) 0; }
small { font-size: var(--fs-xs); color: var(--muted); }
sub, sup { line-height: 0; }
a { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
code, .mono, kbd, samp {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: .94em;
  letter-spacing: -.01em;
}
pre {
  font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.6;
  margin: 0; padding: var(--sp-4); overflow-x: auto;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-2);
}
kbd {
  padding: 1px 5px; border: 1px solid var(--border-2);
  background: var(--surface-2); font-size: .85em;
}
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }

/* ONE micro-label spec. Every eyebrow, section marker, table head, stat key,
   spec term and chart title binds here — the page is labelled like an
   instrument panel, and they must all agree. */
.eyebrow, .step, th, dt, .tile__k, .viz h3, .disclose__sum, .filecell__k, .lbl,
.def__k, .panel > h2:not([class]), legend {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--muted);
}

.shell {
  width: 100%; max-width: var(--shell);
  margin-inline: auto; padding-inline: var(--sp-7);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--text);
  padding: var(--sp-3) var(--sp-4); border: 1px solid var(--text);
}
.skip-link:focus { left: 0; }

/* ------------------------- the graduated rule ---------------------------- */
/* The house mark: a ruler's edge. Hairline ticks every 8px sitting on a
   baseline, every fifth one full height. Two repeating gradients, pinned to
   the bottom of the strip so the short ticks rise off the same line. */
.grad {
  height: 13px;
  margin: 0;
  border-bottom: 1px solid var(--border-2);
  background-image:
    repeating-linear-gradient(to right, var(--border-2) 0 1px, transparent 1px 8px),
    repeating-linear-gradient(to right, var(--border-2) 0 1px, transparent 1px 40px);
  background-size: 100% 5px, 100% 13px;
  background-position: left bottom, left bottom;
  background-repeat: no-repeat;
}

/* -------------------------------- masthead -------------------------------- */
/* a masthead rule, not a hairline: two lines, 3px apart, the way a journal
   nameplate is ruled off from the page under it */
.mast {
  position: sticky; top: 0; z-index: 30;
  background: var(--bg);
  border-bottom: 1px solid var(--border-2);
}
.mast::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px;
  background: var(--border-2);
}
.mast__in {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); min-height: var(--bar-h);
}
.brand { display: inline-flex; align-items: center; gap: 12px; text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand__mark { display: inline-flex; flex: none; color: var(--text); }
.mk-b { stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.mk-d { stroke: var(--muted); stroke-width: 1.5; stroke-linecap: round; }

/* the wordmark pivots on "vs": two serif halves and a light italic hinge */
.wm {
  font-family: var(--font-serif); font-size: 1.3rem; font-weight: 400;
  letter-spacing: -.005em; line-height: 1; color: var(--text);
}
.wm b { font-weight: 700; }
.vs {
  font-weight: 400; font-style: italic; color: var(--muted);
  font-size: .68em; padding: 0 .14em; letter-spacing: 0;
}
.wm--sm { font-size: 1.05rem; }

.tgl {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  cursor: pointer; color: var(--muted);
  background: transparent; border: 1px solid var(--border-2); border-radius: 0;
  transition: color .12s, border-color .12s;
}
.tgl:hover { color: var(--text); border-color: var(--text); }
.tgl svg { width: 18px; height: 18px; }
/* light is the default face; each override flips both icons, in both directions */
.tgl .t-sun { display: none; }
.tgl .t-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tgl .t-sun { display: block; }
  :root:not([data-theme="light"]) .tgl .t-moon { display: none; }
}
:root[data-theme="dark"] .tgl .t-sun { display: block; }
:root[data-theme="dark"] .tgl .t-moon { display: none; }
:root[data-theme="light"] .tgl .t-sun { display: none; }
:root[data-theme="light"] .tgl .t-moon { display: block; }

/* --------------------------------- lede ---------------------------------- */
/* One statement, two definitions, three conditions. No figure of measured
   data: the only drawing is the criterion, which is a definition. */
.lede { padding: var(--sp-5) 0 var(--sp-7); min-width: 0; }
.lede .grad { margin-bottom: var(--sp-6); }
.eyebrow { display: block; margin-bottom: var(--sp-4); }
.lede__h {
  font-family: var(--font-serif);
  font-size: var(--fs-3xl); font-weight: 700; line-height: 1.16;
  letter-spacing: -.012em; max-width: 30ch; text-wrap: balance;
  color: var(--text);
}

/* The definition plate, ruled like a datasheet's parameter table: a numbered
   mono key in a fixed left column, the quantity itself on the right. Two rows,
   nothing else — these are the only two things the tool computes. */
.defs {
  margin-top: var(--sp-7);
  border-top: 1px solid var(--border-2);
}
.def {
  min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-3);
  padding: var(--sp-5) 0 var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.def:last-child { border-bottom: 1px solid var(--border-2); }
.def__k { display: flex; align-items: baseline; gap: var(--sp-3); color: var(--text); }
.def__no { color: var(--faint); font-weight: 600; }
/* the quantity and the sentence that qualifies it, side by side */
.def__b { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr); min-width: 0; }
.def__t {
  font-size: var(--fs-sm); line-height: var(--lh-snug);
  color: var(--muted); max-width: 46ch;
}
.def__t i { font-family: var(--font-serif); font-style: italic; font-size: 1.05em; }
@media (min-width: 900px) {
  .def { grid-template-columns: 10rem minmax(0, 1fr); column-gap: var(--sp-6); }
  .def__k { padding-top: .55em; }
  .def__b {
    grid-template-columns: minmax(0, 430px) minmax(0, 1fr);
    column-gap: var(--sp-7); align-items: center;
  }
}

/* display maths: variables italic, operators spaced, the serif throughout.
   The defining equation is the largest thing on the page after the headline,
   which is the correct order of importance for this tool. */
.eq {
  margin: 0;
  font-family: var(--font-serif); font-size: clamp(1.6rem, 1.05rem + 1.5vw, 2.15rem);
  line-height: 1.25; color: var(--text); letter-spacing: .01em;
  font-variant-numeric: lining-nums;
}
.eq i { font-style: italic; }
.eq sub { font-size: .5em; vertical-align: -.12em; letter-spacing: .04em; }
.eq sup { font-size: .55em; vertical-align: .62em; }
.eq .eq__o { padding: 0 .22em; color: var(--muted); }
.eq .eq__p { vertical-align: .58em; }
/* the angle brackets are frequently missing from a text serif; let the browser
   fall back for this glyph alone and hold its size steady */
.ang {
  font-family: "DejaVu Math TeX Gyre", "Cambria Math", "Latin Modern Math",
    "DejaVu Sans", var(--font-serif);
  font-size: 1.06em; padding: 0 .02em;
}
.eq--sm { font-size: var(--fs-xl); }

/* The criterion, drawn. Definition only — no measured value appears here.
   The band wears the same 45-degree hatch as an empty upload field, so "a
   region on the page" means one thing throughout. */
.crit { display: block; width: 100%; max-width: 430px; height: auto; }
.crit__h { stroke: var(--border-2); stroke-width: .9; opacity: .55; }
.crit__band { fill: url(#crit-hatch); stroke: var(--border-2); stroke-width: 1; }
.crit__d { stroke: var(--text); stroke-width: 1.8; }
.crit__ax { fill: none; stroke: var(--border-2); stroke-width: 1.2; }
.crit__in { fill: var(--text); }                     /* closed: 0.8d is included */
.crit__ex { fill: var(--bg); stroke: var(--text); stroke-width: 1.8; }  /* open: 1.2d is not */
/* the labels are set as the equations are, so d means the same thing in both */
.crit__t { font-family: var(--font-serif); font-size: 15px; fill: var(--muted); }
.crit__v { font-style: italic; }
.crit__sub { font-size: 10px; font-style: normal; }
.crit__t--ink { fill: var(--text); font-weight: 700; }

/* the three conditions, as a spec plate divided into cells */
.spec {
  display: grid; grid-template-columns: minmax(0, 1fr);
  border-bottom: 1px solid var(--border-2);
}
.spec__i { padding: var(--sp-4) 0; border-top: 1px solid var(--border); min-width: 0; }
.spec__i:first-child { border-top: 0; }
.spec dd {
  margin-top: var(--sp-2); font-size: var(--fs-sm); line-height: var(--lh-snug);
  color: var(--text-2); max-width: 40ch;
}
.spec dd .mono { color: var(--text); }
@media (min-width: 760px) {
  .spec { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .spec__i { border-top: 0; padding: var(--sp-5) var(--sp-6); border-left: 1px solid var(--border); }
  .spec__i:first-child { padding-left: 0; border-left: 0; }
  .spec__i:last-child { padding-right: 0; }
}

/* ------------------------------- workbench ------------------------------- */
.work { padding-bottom: var(--sp-9); }
.stack { display: flex; flex-direction: column; min-width: 0; counter-reset: step; }

/* no cards. A section is a rule, a numbered marker riding it, and room to
   breathe. The number comes from a counter, so a hidden step does not leave a
   gap in the sequence. */
.panel {
  position: relative;
  border-top: 1px solid var(--border-2);
  padding: var(--sp-7) 0;
  min-width: 0;
}
.stack > .panel:has(> .step),
.stack > .panel:has(> h2:not([class])) { counter-increment: step; }
.step, .panel > h2:not([class]) {
  position: absolute; top: 0; left: 0; transform: translateY(-50%);
  display: flex; align-items: baseline; gap: var(--sp-3);
  background: var(--bg); padding-right: var(--sp-4); color: var(--text);
}
.stack > .panel > .step::before,
.stack > .panel > h2:not([class])::before {
  content: counter(step, decimal-leading-zero);
  color: var(--faint);
}
.panel__head { margin-bottom: var(--sp-5); }
.panel__title {
  font-size: var(--fs-xl); font-weight: 700; letter-spacing: -.02em;
  line-height: var(--lh-tight); color: var(--text);
  font-family: var(--font-serif);
}
.panel__sub {
  margin-top: var(--sp-3); font-size: var(--fs-sm); line-height: var(--lh-snug);
  color: var(--muted); max-width: 68ch;
}
.panel__sub:empty { display: none; }

.hint {
  margin-top: var(--sp-3); font-size: var(--fs-sm); line-height: var(--lh-snug);
  color: var(--muted); max-width: 72ch;
}
.hint--tight { margin-top: 0; margin-bottom: var(--sp-4); }
.hint:empty { display: none; }
.hint .mono { color: var(--text-2); }

/* generic fallbacks, so markup written elsewhere still lands somewhere sane */
.note { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--muted); }
.muted { color: var(--muted); }
.card {
  background: var(--surface); border: 1px solid var(--border-2);
  padding: var(--sp-5) var(--sp-6);
}
.stat { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); }

/* ------------------------------- file cells ------------------------------ */
/* Each cell is a <label> wrapping its hidden input, so the picker opens with no
   script; app.js toggles .filled and .drag and writes .filecell__name.
   Empty reads as an unfilled field on a form — hatched, dashed, transparent. */
.filegrid { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
/* label at the head of the field, accepted extensions at its foot */
.filecell {
  position: relative; display: grid; grid-template-rows: auto 1fr auto; gap: var(--sp-2);
  min-height: 116px; padding: var(--sp-4) var(--sp-5); cursor: pointer;
  border: 1px dashed var(--border-2); background: transparent;
  background-image: repeating-linear-gradient(45deg, var(--hatch) 0 1px, transparent 1px 9px);
  transition: border-color .12s, background-color .12s;
}
.filecell:hover, .filecell.drag { border-color: var(--text); }
.filecell.drag { border-style: solid; background-color: var(--surface-2); }
.filecell:focus-within { outline: 2px solid var(--text); outline-offset: 2px; }
.filecell.filled {
  border-style: solid; border-color: var(--text);
  background-color: var(--surface); background-image: none;
}
.filecell__k { color: var(--text); }
.filecell__x { align-self: end; font-size: var(--fs-xs); color: var(--faint); }
.filecell__name {
  font-size: var(--fs-xs); color: var(--text-2); line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.filecell__name:empty { display: none; }
/* a filled cell earns a tick, drawn in CSS so no script is needed */
.filecell.filled::after {
  content: ""; position: absolute; top: 18px; right: 18px;
  width: 12px; height: 7px; border-left: 2px solid var(--text);
  border-bottom: 2px solid var(--text); transform: rotate(-45deg);
}

/* -------------------------------- controls -------------------------------- */
.lbl { display: block; margin-bottom: var(--sp-2); color: var(--text); }

input[type="text"], input[type="number"], input[type="search"], textarea, select {
  width: 100%; font-family: var(--font-mono); font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums; line-height: 1.5;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-2); border-radius: 0;
  padding: 9px var(--sp-3);
  transition: border-color .12s;
}
input:focus, textarea:focus, select:focus {
  outline: 2px solid var(--text); outline-offset: -1px; border-color: var(--text);
}
input::placeholder, textarea::placeholder { color: var(--faint); }
textarea { min-height: 170px; resize: vertical; max-width: 62ch; display: block; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--text); }
input[type="range"] { accent-color: var(--text); width: 100%; max-width: 260px; }
progress { width: 100%; max-width: 320px; height: 6px; accent-color: var(--text); }

.input-unit { position: relative; display: flex; align-items: center; max-width: var(--w-num); }
.input-unit input { padding-right: 46px; }
.input-unit__u { position: absolute; right: var(--sp-3); font-size: var(--fs-2xs); color: var(--faint); pointer-events: none; }

.select { position: relative; max-width: 210px; }
.select::after {
  content: ""; position: absolute; right: 13px; top: 50%; width: 7px; height: 7px;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: translateY(-70%) rotate(45deg); pointer-events: none;
}
.select select { appearance: none; -webkit-appearance: none; cursor: pointer; padding-right: 36px; }

.params {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: var(--sp-5) var(--sp-7); max-width: 640px;
  margin-top: var(--sp-6); padding-top: var(--sp-6); border-top: 1px solid var(--border);
}
.params .hint { margin-top: var(--sp-2); }
.field { min-width: 0; }

/* ------------------------------- disclosure ------------------------------ */
.disclose { margin-top: var(--sp-6); }
.disclose__sum {
  display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer;
  list-style: none; max-width: 100%; color: var(--text-2);
}
.disclose__sum::-webkit-details-marker { display: none; }
.disclose__sum::before {
  content: "+"; flex: none; display: grid; place-items: center;
  width: 18px; height: 18px; line-height: 1;
  border: 1px solid var(--border-2); color: var(--muted); font-weight: 400;
}
.disclose[open] .disclose__sum::before { content: "−"; }
.disclose__sum:hover { color: var(--text); }
.disclose__body { margin-top: var(--sp-5); }
summary { cursor: pointer; }

/* --------------------------------- buttons -------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  line-height: 1; cursor: pointer; text-decoration: none;
  padding: 15px var(--sp-6); border: 1px solid var(--text); border-radius: 0;
  transition: background .12s, color .12s, border-color .12s, opacity .12s;
}
.btn--primary { background: var(--btn-bg); color: var(--btn-fg); }
.btn--primary:hover { background: var(--btn-bg-hover); border-color: var(--btn-bg-hover); color: var(--btn-fg); text-decoration: none; }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border-2); }
.btn--ghost:hover { border-color: var(--text); text-decoration: none; }
/* inert, but still readable: a greyed-out block you cannot read is not a label */
.btn:disabled {
  background: var(--surface-3); color: var(--muted);
  border-color: var(--border-2); cursor: not-allowed;
}

.actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-5);
  margin-top: var(--sp-6);
}
.actions__note { font-size: var(--fs-sm); color: var(--muted); }

/* --------------------------------- tables --------------------------------- */
.grid-wrap {
  overflow-x: auto;                 /* wide tables scroll here, never the page */
  border: 1px solid var(--border-2); background: var(--surface);
}
table { border-collapse: collapse; width: 100%; font-size: var(--fs-xs); }
th, td {
  padding: 11px var(--sp-4); text-align: left;
  border-bottom: 1px solid var(--border); white-space: nowrap; line-height: 1.45;
}
th {
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-2);
  padding-block: var(--sp-3);
  color: var(--text-2);
}
td { color: var(--text-2); }
td.num, th.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* every table here leads with an index or an atom number, and app.js marks only
   the cells as .num — align the head with the column it labels */
th:first-child { text-align: right; }
td:first-child { color: var(--text); font-weight: 600; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface-2); }
/* selection is weight and position, never a hue: a hue here would read as data */
tbody tr.sel { background: var(--surface-3); box-shadow: inset 3px 0 0 var(--text); }
tbody tr.sel td { color: var(--text); }
/* The signed deviation column. The swatch carries the encoding and sits on the
   cell's left edge so the column reads as a strip; the digits stay in ink,
   because a hue validated for a chart mark is not a text colour. */
td.dev { position: relative; padding-left: 34px; color: var(--text); font-weight: 700; }
.sw {
  position: absolute; left: var(--sp-4); top: 50%;
  width: 11px; height: 11px; transform: translateY(-50%);
}

.chip, .tag, .badge {
  display: inline-block; font-family: var(--font-mono); font-size: .6875rem;
  letter-spacing: .06em; text-transform: uppercase; padding: 2px 7px;
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--border-2);
  vertical-align: .06em; white-space: nowrap; line-height: 1.4;
}

/* ----------------------------- notes / errors ----------------------------- */
/* machine speech: the parsed summary, backend notes. Set in mono behind a rule. */
.interp {
  font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.7;
  color: var(--text-2); border-left: 2px solid var(--border-2);
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-4);
  margin: var(--sp-4) 0; max-width: 90ch;
}
.interp:empty { display: none; }
.interp + .interp { margin-top: var(--sp-3); }

.warn-list { list-style: none; padding: 0; margin: var(--sp-5) 0 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.warn-list:empty { display: none; }
.warn-list li {
  font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--text-2);
  padding: var(--sp-3) var(--sp-4); background: var(--surface-2);
  border: 1px solid var(--border); border-left: 3px solid var(--text);
}
/* app.js appends .err into #step1-err / #step3-err / #job-err */
.err {
  margin-top: var(--sp-5); padding: var(--sp-4) var(--sp-5);
  font-family: var(--font-mono); font-size: var(--fs-xs); line-height: 1.7;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border); border-left: 3px solid var(--bad);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* --------------------------------- track ---------------------------------- */
/* app.js toggles .done and .active on #track li[data-s] */
.track { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: var(--sp-5) 0; }
.track li {
  position: relative; flex: 1 1 auto; min-width: 140px;
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-3);
  padding-right: var(--sp-5);
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: .08em; text-transform: uppercase; line-height: 1.2;
  color: var(--faint);
}
.track li::before {
  content: ""; position: absolute; left: 14px; right: 5px; top: 5px; height: 1px;
  background: var(--border-2); z-index: 0;
}
.track li:last-child { flex: 0 0 auto; padding-right: 0; }
.track li:last-child::before { display: none; }
.track__dot {
  position: relative; z-index: 1; flex: none; width: 11px; height: 11px;
  background: var(--bg); border: 1px solid var(--border-2);
}
.track li.done { color: var(--text-2); }
.track li.done .track__dot,
.track li.active .track__dot { background: var(--text); border-color: var(--text); }
.track li.done::before { background: var(--text); }
.track li.active { color: var(--text); font-weight: 700; }
.track li.active .track__dot { box-shadow: 0 0 0 4px var(--surface-3); }

/* ---------------------------------------------------------------------------
   Mapping: the 2D structure and the four routes into it
   --------------------------------------------------------------------------- */

/* The structure is the reference and stays put; the label column is the work.
   Fixing the right column's width keeps every row the same length, so the
   column reads as a checklist rather than as a stack of unrelated boxes. */
.maptop {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 400px);
  gap: var(--sp-6);
  align-items: start;
  margin-top: var(--sp-5);
}
@media (max-width: 960px) { .maptop { grid-template-columns: minmax(0, 1fr); } }

.mapstruct { min-width: 0; position: sticky; top: calc(var(--bar-h) + var(--sp-5)); }
@media (max-width: 960px) { .mapstruct { position: static; } }
.mapstruct .hint { margin-top: 0; margin-bottom: var(--sp-3); }
/* A sticky block taller than the viewport can never be scrolled to its foot;
   cap it and let the drawing scroll inside its own box instead. */
.mapstruct .structbox {
  max-height: calc(100vh - var(--bar-h) - 140px);
  min-height: 240px;
  overflow: auto;
}

/* The 2D drawing arrives from the backend on its own white ground, so the box
   is a frame around a printed sheet and carries no padding of its own — an
   inset would otherwise read as a mount in dark mode. */
.structbox {
  border: 1px solid var(--border-2);
  background: var(--surface);
  overflow-x: auto;                 /* wide molecules scroll here, never the page */
}
/* The drawing's carbon skeleton and atom notes are `currentColor`, so the container
   sets the ink and the same SVG reads correctly in both themes. */
.struct { width: 100%; height: auto; display: block; color: var(--text); }

/* Hit targets sit above the drawing. The visible dot is small so it does not bury the
   numbering; the transparent ring around it is what the pointer actually catches. */
.hit { cursor: pointer; }
.hit__t { fill: transparent; }
/* A hollow ring, not a filled dot: the numbering is already drawn underneath and a solid
   mark would bury it. Empty ring = free, filled = assigned to the label being edited. */
.hit__d {
  fill: var(--surface);
  fill-opacity: .45;
  stroke: var(--muted);
  stroke-width: 2.6;              /* the canvas is scaled to ~0.53, so this lands near 1.4px */
  transition: fill .12s, stroke .12s, stroke-width .12s;
}
.hit:hover .hit__d { stroke: var(--text); stroke-width: 2.4; }
.hit--taken .hit__d { stroke-dasharray: 3 2.5; }
/* Assigned to the label being edited. Weight and a ring, not a data hue: --dev-cool and
   --dev-warm carry signed deviation on the result page and must not mean two things.
   --text is ink, which is exactly what an annotation is allowed to wear. */
.hit--on .hit__d {
  fill: var(--text);
  opacity: 1;
  stroke: var(--surface);
  stroke-width: 2;
}

.mapside { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
/* the progress readout leads the column, so the count is read before the list */
#map-count {
  margin: 0; padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border-2);
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--text);
}

/* the ink edge fills in as labels are mapped, so the column reads as progress */
.maplist { display: grid; gap: var(--sp-2); grid-template-columns: minmax(0, 1fr); }
.maprow {
  display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) auto;
  align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border); border-left: 3px solid var(--text);
  transition: border-color .12s, background-color .12s;
}
.maprow.unmapped { border-left-color: var(--border-2); background: transparent; }
.maprow:hover { border-color: var(--border-2); }
.maprow__label {
  font-family: var(--font-mono);
  font-size: var(--fs-sm); font-weight: 700; color: var(--text);
  overflow: hidden; text-overflow: ellipsis;
}
.maprow input { min-width: 0; padding: 6px var(--sp-3); }
.maprow input:focus { outline: none; border-color: var(--text); }
.maprow .chip { justify-self: end; }
/* the label being edited: weight and a ring, matching the structure's hit state */
.maprow--active {
  outline: 2px solid var(--text);
  outline-offset: 1px;
  background: var(--surface);
  border-color: var(--border-2);
}

.maptools { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.maptools .btn { padding: 11px var(--sp-4); }
#map-tool-note { margin-top: 0; font-size: var(--fs-xs); }

/* --------------------------------- result --------------------------------- */
/* Built entirely by app.js. It sets a few inline margins on the blocks it
   appends, so the ruled rhythm here owns the spacing and those are zeroed. */
#result { display: flex; flex-direction: column; min-width: 0; counter-reset: sect; }
#result > * { margin-block: 0 !important; }
#result > .tiles {
  border-top: 1px solid var(--border-2); border-bottom: 1px solid var(--border-2);
  padding: var(--sp-6) 0;
}
#result > .explorer { padding: var(--sp-7) 0; }
#result > .panel:first-child { border-top: 0; padding-top: 0; padding-bottom: var(--sp-7); }
/* the backend's notes are an aside between two readings, not a section of their own */
#result > .panel--note { border-top: 0; padding: var(--sp-6) 0 0; }
/* app.js drops a bare title straight onto a table: give that join its spacing */
.panel__title + .grid-wrap { margin-top: var(--sp-5); }
.panel > .interp:first-child { margin-top: 0; }

/* Every generated section is numbered on its own rule, so a long page of
   panels reads as one instrument's numbered readouts. */
#result .panel:has(> h3.panel__title) { counter-increment: sect; }
#result .panel > h3.panel__title {
  display: flex; align-items: baseline; gap: var(--sp-4);
  font-size: var(--fs-lg);
}
#result .panel > h3.panel__title::before {
  content: counter(sect, decimal-leading-zero);
  flex: none;
  font-family: var(--font-mono); font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--tracking-caps); color: var(--faint);
}
/* the system name is the page's title and outranks every section */
#result h2.panel__title { font-size: clamp(1.9rem, 1.2rem + 2vw, 2.6rem); }

/* A figure that arrives as a PNG from the package is the published figure, drawn
   on white and often with a transparent ground. Give every one of them the white
   it was drawn for, in both themes, so it matches the copy in the download. */
#result img, .figbox img {
  display: block; width: 100%; height: auto;
  background: #ffffff; border: 1px solid var(--border-2);
}
.figbox { min-width: 0; overflow-x: auto; }

/* One ruled row of readings, divided rather than boxed. The column count is
   explicit at every width: an auto-fit grid that wraps leaves a tile carrying a
   divider with nothing to its left. */
.tiles { display: grid; gap: 0; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tile { padding: 0 var(--sp-6); border-left: 1px solid var(--border); }
.tile:first-child { padding-left: 0; border-left: 0; }
@media (max-width: 1000px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--sp-5); }
  .tile { padding-inline: var(--sp-5); }
  .tile:nth-child(odd) { padding-left: 0; border-left: 0; }
}
@media (max-width: 520px) {
  .tiles { grid-template-columns: minmax(0, 1fr); }
  .tile { padding-inline: 0; border-left: 0; }
}
.tile__v {
  font-family: var(--font-mono); font-size: var(--fs-2xl); font-weight: 700;
  letter-spacing: -.035em; line-height: 1; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.tile__k { margin-top: var(--sp-3); line-height: 1.4; }

/* the paired panel: the whole comparison on the left, the selected restraint on
   the right. Both cards are one field, so they stretch to the same height. */
.explorer { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; align-items: stretch; }
@media (min-width: 940px) { .explorer { grid-template-columns: minmax(0, 1.2fr) minmax(0, .95fr); } }
.viz {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 250px;
  padding: var(--sp-5) var(--sp-6) var(--sp-6);
  background: var(--surface); border: 1px solid var(--border-2);
}
.viz h3 {
  margin-bottom: var(--sp-5); padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
/* the scatter's tolerance band is drawn past the viewBox on purpose; clip it to
   the plot rather than letting it bleed across the field */
.viz svg { overflow: hidden; }
/* axis titles are labels, not prose: they wear the mono face like every other */
.viz svg text { font-family: var(--font-mono); }
.viz .hint { margin-top: 0; }
/* stacked, a chart drawn at the full column width becomes absurdly tall */
@media (max-width: 939px) {
  .viz > svg { max-width: 620px; margin-inline: auto; }
}

.legend {
  display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-5);
  font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--muted);
  letter-spacing: .06em; text-transform: uppercase;
}
.legend__bar {
  height: 10px; width: 128px;
  background: linear-gradient(90deg, var(--dev-cool), var(--dev-mid), var(--dev-warm));
}
/* the selected restraint's numbers, at the foot of its card */
.readout {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  margin-top: auto; padding-top: var(--sp-4); border-top: 1px solid var(--border);
  font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--muted);
  letter-spacing: .06em; text-transform: uppercase;
}
.readout b { font-variant-numeric: tabular-nums; color: var(--text); font-weight: 700; font-size: var(--fs-xs); }

.jobfoot { display: none; padding: var(--sp-7) 0; border-top: 1px solid var(--border-2); }
.stack:has(#result:not([hidden])) .jobfoot { display: block; }
/* a finished job owns the page: the stale form, the statement of method and the
   finished progress track all step aside */
body:has(#result:not([hidden])) .lede,
body:has(#result:not([hidden])) #step1,
body:has(#result:not([hidden])) #step2,
body:has(#result:not([hidden])) #step3,
body:has(#result:not([hidden])) #step3b,
body:has(#result:not([hidden])) #step4 { display: none; }
body:has(#result:not([hidden])) .work { padding-top: var(--sp-8); }

/* ---------------------------------------------------------------------------
   Result page: filters, distribution grid, satisfaction bars, arc map
   --------------------------------------------------------------------------- */

.filters { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-5) 0 var(--sp-5); }
.filter {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  line-height: 1;
  padding: 10px var(--sp-4);
  border: 1px solid var(--border-2);
  border-radius: 0;
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
.filter:hover { border-color: var(--text); color: var(--text); }
.filter--on { background: var(--btn-bg); color: var(--btn-fg); border-color: var(--btn-bg); }

.distgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
  gap: var(--sp-4);
}
.distcell {
  margin: 0;
  padding: var(--sp-3) var(--sp-3) var(--sp-2);
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  cursor: pointer;
  transition: border-color .12s;
}
.distcell:hover { border-color: var(--border-2); }
.distcell figcaption {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-2);
  margin-bottom: var(--sp-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.distcell figcaption b { color: var(--text); }
/* Selection is weight and a ring. Colour is already carrying deviation. */
.distcell--sel { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }

[data-bar] rect:first-child { transition: opacity .12s; }
.bar--sel rect:first-child { stroke: var(--text); stroke-width: 2; }

/* the arc map is a drawing, not a diagram that benefits from being enormous:
   cap it so the molecule stays a readable size on a wide screen */
.arcbox { max-width: 900px; }
.arc__t { fill: none; stroke: transparent; stroke-width: 16; }  /* fat invisible target */
.arc__v { fill: none; stroke-width: 1.6; opacity: .45; transition: stroke-width .12s, opacity .12s; }
.arc { cursor: pointer; }
.arc:hover .arc__v { opacity: 1; stroke-width: 2.6; }
.arc--bad .arc__v { opacity: .85; stroke-width: 2.2; }
.arc--sel .arc__v { opacity: 1; stroke-width: 3.4; }

/* --------------------------------- footer --------------------------------- */
.foot { margin-top: auto; padding-bottom: var(--sp-6); }
.foot .grad {
  /* the graduations hang from the rule at the foot, mirroring the head */
  border-bottom: 0; border-top: 1px solid var(--border-2);
  background-position: left top, left top;
  margin-bottom: var(--sp-5);
}
.foot__in {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--sp-2) var(--sp-6);
}
.foot__t { font-size: var(--fs-sm); color: var(--muted); }
.foot__t b { font-family: var(--font-mono); font-weight: 700; color: var(--text-2); }

/* ------------------------------- responsive ------------------------------- */
@media (max-width: 860px) {
  .shell { padding-inline: var(--sp-5); }
  .lede { padding-top: var(--sp-5); padding-bottom: var(--sp-7); }
  .lede .grad { margin-bottom: var(--sp-6); }
  .lede__h { max-width: none; }
  .defs { margin-top: var(--sp-6); }
  .panel { padding: var(--sp-6) 0; }
  .filecell { min-height: 100px; }
  /* narrow: one stage per row, marker beside its label */
  .track { gap: var(--sp-3) 0; }
  .track li { flex: 1 1 100%; min-width: 0; padding-right: 0; flex-direction: row; align-items: center; }
  .track li::before { display: none; }
}
@media (max-width: 520px) {
  .eq { font-size: var(--fs-lg); }
  .eq--sm { font-size: var(--fs-md); }
}

/* --------------------------------- motion --------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ---------------------------------------------------------------------------
   The diastereotopic decision. Both options measured, the user picks.
   --------------------------------------------------------------------------- */

.diacard {
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  padding: var(--sp-5);
  margin-bottom: var(--sp-4);
}
.diacard__h { margin: 0 0 4px; font-size: var(--fs-md); font-family: var(--font-serif); }

.diaopts { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 10px; margin-top: 12px; }
.diaopt {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--border-2);
  border-radius: 0;
  cursor: pointer;
  background: var(--surface-2);
}
.diaopt:hover { border-color: var(--text); }
/* Chosen = weight and an ink edge. Colour is reserved for signed deviation. */
.diaopt--on { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.diaopt input { margin-top: 3px; }
.diaopt__body { display: flex; flex-direction: column; gap: 3px; }
.diaopt__atoms { font-size: var(--fs-2xs); color: var(--text-2); }
.diaopt__num { font-size: var(--fs-sm); }

/* 3D viewer. Fixed height so the page does not reflow when frames arrive; the canvas
   3Dmol injects is positioned inside it. */
.viewer3d {
  position: relative;
  width: 100%;
  height: 420px;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  overflow: hidden;
}
.viewer3d canvas { display: block; }

/* Scatter legend, matching the reference figure's own key rather than a colour ramp. */
.legend__key { display: inline-flex; align-items: center; gap: 6px; margin-right: 16px; }
.legend__dot { width: 11px; height: 11px; border-radius: 50%; display: inline-block; }
.legend__line { width: 20px; height: 0; border-top: 1.5px solid #444444; display: inline-block; }
.legend__wedge { width: 20px; height: 11px; background: grey; opacity: .22; display: inline-block; }

/* The package's diastereotopic table marks the assignment the numbers were computed with
   as `td.sel`. The site already styles `tr.sel` for its own row selection, so the cell
   class fell through unstyled and the caption promised a highlight nothing drew. Weight
   and a ring, not a fill: colour on this page belongs to the figures. */
#result td.sel,
.figtable td.sel {
  font-weight: 700;
  color: var(--text);
  box-shadow: inset 0 0 0 2px var(--text);
}
