/* Recipe Kitchen — xkcd hand-drawn look */
@font-face {
  font-family: "xkcd Script";
  src: url("/static/fonts/xkcd-script.ttf") format("truetype");
  font-display: swap;
}

:root {
  --ink: #222;
  --paper: #fdfcf7;
  --accent: #b3552e;
  --ok: #3d7a34;
  --bad: #b02c2c;
  --panel: #fffef9;
  /* ONE definition, inherited everywhere. Almost every rule below already says
     `font-family: inherit`, precisely so the typeface has a single home. */
  --ui-font: "xkcd Script", "Comic Sans MS", "Comic Neue", cursive;
  font-family: var(--ui-font);
}

/* The reading face, for a day of work rather than a first look.
 *
 * The hand-drawn face is deliberate and good at what it does -- it says "this
 * is a draft, argue with it" -- but a novelty face is harder to read at small
 * sizes, and people sit in front of this for hours.
 *
 * A SYSTEM stack rather than a vendored webfont, on purpose:
 *   * it is already on the machine, so switching is instant, with no flash of
 *     unstyled text and no second HTTP request;
 *   * each entry is the face its own vendor tuned for exactly this -- hours of
 *     UI reading, hinted for that platform's rasteriser;
 *   * no third licence file to ship beside the xkcd one.
 * The cost is that it is not pixel-identical across machines. If a fixed
 * corporate face is ever wanted, vendor it beside xkcd-script.ttf and change
 * this one declaration; nothing else needs to move.
 */
html[data-font="pro"] {
  --ui-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
             Arial, "Noto Sans", "Liberation Sans", sans-serif;
}

/* Digits that line up in a column. This app is largely tables of numbers -- r
   values, scores, trace counts -- and proportional figures make a column
   jitter as the values change, which is exactly when you are comparing them.
   Only in the reading face: the hand-drawn one has no tabular set. */
html[data-font="pro"] table,
html[data-font="pro"] .data-strip,
html[data-font="pro"] .tie-r,
html[data-font="pro"] .dsp-qc-text,
html[data-font="pro"] .v-score { font-variant-numeric: tabular-nums; }

* { box-sizing: border-box; }
[hidden] { display: none !important; }   /* beat display:flex on overlays */
html, body { margin: 0; height: 100%; overflow: hidden; background: var(--paper); color: var(--ink); }

/* ---- toolbar ---- */
#toolbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 20;
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 8px 14px;
  background: var(--paper);
  border-bottom: 3px solid var(--ink);
}
#logo { font-size: 20px; font-weight: bold; letter-spacing: 0.5px; }
#logo em { font-style: normal; color: var(--accent); }
/* Which canvas you are on, next to the name of the app -- where a document
   title goes. Nothing said it before: a shared server with several saved
   canvases showed the same empty chrome whichever one you had open. */
#canvas-title {
  font-size: 15px; font-weight: bold;
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
  padding: 2px 10px;
  border: 2px dashed var(--ink);
  border-radius: 10px 12px 9px 11px / 12px 9px 11px 10px;
  transform: rotate(-0.5deg);
}
#canvas-title.unsaved { font-weight: normal; opacity: 0.65; }
/* The Whisk's plan view. Bordered like the sweep beside it so the two read as
   one instrument, and never stretched -- basemap.js keeps the aspect square
   and this must not fight it with a percentage width. */
.basemap-canvas {
  display: block; margin: 6px 0 2px;
  border: 2px solid var(--ink);
  border-radius: 10px 12px 9px 11px / 12px 9px 11px 10px;
  background: #fffdf7;
}
.tb-group { display: flex; gap: 6px; }
button {
  font-family: inherit; font-size: 15px;
  background: var(--panel); color: var(--ink);
  border: 2px solid var(--ink); border-radius: 8px 10px 9px 11px / 11px 9px 10px 8px;
  padding: 4px 12px; cursor: pointer;
  transform: rotate(-0.4deg);
  transition: transform 0.08s;
}
button:nth-child(even) { transform: rotate(0.5deg); }
button:hover { transform: rotate(0deg) scale(1.06); background: #f4efdd; }
button.primary { background: #ffd9a0; font-weight: bold; }
button.danger  { background: #ffb9b9; }
button:disabled { opacity: 0.45; cursor: default; }

/* ---- canvas ---- */
#canvas {
  position: fixed; inset: 0; top: var(--tb-h);
  width: 100%; height: calc(100% - var(--tb-h));
  touch-action: none; cursor: grab;
}
/* the side panel is painted over the canvas, so shrink the canvas instead of
   letting it hide (and swallow clicks on) the nodes underneath */
body.panel-open #canvas { width: calc(100% - 340px); }
#canvas.panning { cursor: grabbing; }
#canvas text {
  /* the SVG node labels follow the toggle too -- half the app in one face and
     half in the other is worse than either */
  font-family: var(--ui-font);
  user-select: none; -webkit-user-select: none;
}
.node { cursor: pointer; }
.node.dragging { cursor: grabbing; }
.node .hitbox { fill: transparent; stroke: none; }
.nub { cursor: crosshair; }
.nub.legal circle { stroke: var(--ok) !important; stroke-width: 3.5 !important; }
.wire-hit { stroke: transparent; stroke-width: 14; fill: none; cursor: pointer; }

/* ---- side panel ---- */
#side-panel {
  position: fixed; top: var(--tb-h); right: 0; bottom: 0; z-index: 15;
  width: 340px; overflow-y: auto;
  background: var(--panel);
  border-left: 3px solid var(--ink);
  padding: 10px 14px 30px;
}
#panel-head { display: flex; justify-content: space-between; align-items: center; }
#panel-title { font-size: 20px; font-weight: bold; }
#panel-close, #log-close {
  border: none; background: none; font-size: 22px; padding: 0 6px;
}
#panel-blurb { font-size: 13px; color: #666; margin: 2px 0 12px; }
.form-section h4 {
  margin: 14px 0 4px; border-bottom: 2px solid var(--ink);
  font-size: 16px;
}
.form-row { display: flex; align-items: center; gap: 8px; margin: 5px 0; }
.form-row label { flex: 1 1 55%; font-size: 13.5px; overflow-wrap: anywhere; }
.form-row label .req { color: var(--bad); }
.form-row input[type=text], .form-row input[type=number], .form-row select {
  flex: 1 1 45%; min-width: 0;
  font-family: inherit; font-size: 13.5px;
  border: 1.6px solid var(--ink); border-radius: 6px 8px 7px 9px / 9px 7px 8px 6px;
  background: #fff; padding: 2px 6px;
}
.form-row input.changed, .form-row select.changed { background: #fff3cd; }
.form-row.field-error input, .form-row.field-error select {
  border-color: var(--bad); border-width: 2.4px; background: #ffe9e9;
}
.form-row.field-error label { color: var(--bad); font-weight: bold; }
.form-note { font-size: 12.5px; color: #777; margin: 8px 0; }
.artifact-list { margin-top: 12px; }
.artifact-list img {
  width: 100%; border: 2px solid var(--ink);
  border-radius: 6px 9px 7px 10px / 10px 7px 9px 6px;
  margin: 4px 0; cursor: zoom-in; background: #fff;
}
.verdict { font-size: 17px; font-weight: bold; margin: 8px 0; }
.verdict.ok { color: var(--ok); }
.verdict.warn { color: #a3690b; }
.verdict.bad { color: var(--bad); }
.verdict.neutral { color: #555; }

/* ---- log drawer ---- */
/* measured at runtime -- the toolbar wraps to two rows on narrow screens and
   a hard-coded height let it cover the panel and swallow clicks */
:root { --log-h: 170px; --tb-h: 52px; }
#log-drawer {
  position: fixed; left: 0; right: 340px; bottom: 0; z-index: 14;
  height: var(--log-h); background: #1d1a14; color: #e8e2ce;
  border-top: 3px solid var(--ink);
  display: flex; flex-direction: column;
  transition: height 0.15s;
}
#log-drawer.collapsed { height: 26px; }
#log-drawer.collapsed #log-body { display: none; }
#log-drawer.full { right: 0; }
#log-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px; padding: 2px 12px;
  background: #2c2820; font-size: 14px;
  cursor: pointer;                       /* the whole bar toggles */
}
#log-head button {
  color: #e8e2ce; border: none; background: none;
  font-size: 18px; padding: 0 4px; line-height: 1;
}
#log-title { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis;
             white-space: nowrap; }
#log-body {
  flex: 1; overflow-y: auto; margin: 0; padding: 6px 12px;
  font-family: monospace; font-size: 12px; white-space: pre-wrap;
}

/* ---- chef mascot ---- */
#chef { position: fixed; left: 12px; bottom: 8px; z-index: 16; pointer-events: none;
        transition: bottom 0.15s; }
/* the drawer is dark and the chef is drawn in dark ink -- stand him on top of
   it rather than letting him vanish into it */
body.log-open #chef { bottom: calc(var(--log-h) + 10px); }
body.log-open.log-collapsed #chef { bottom: 34px; }
#chef-bubble {
  position: absolute; left: 84px; bottom: 46px;
  min-width: 160px; max-width: 340px;
  background: #fff; border: 2.5px solid var(--ink);
  border-radius: 14px 18px 15px 17px / 17px 15px 18px 14px;
  padding: 8px 12px; font-size: 14px;
  pointer-events: auto;
}
#chef-bubble::after {
  content: ""; position: absolute; left: -14px; bottom: 10px;
  border: 8px solid transparent; border-right-color: var(--ink);
}

/* ---- lightbox ---- */
#lightbox {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(20, 18, 12, 0.82);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  cursor: zoom-out;
}
#lightbox img {
  max-width: 92%; max-height: 86%;
  border: 4px solid #fff; border-radius: 8px; background: #fff;
}
#lightbox-caption { color: #fff; margin-top: 10px; font-size: 15px; }

/* ---- overlays / modal ---- */
.modal-back {
  position: fixed; inset: 0; z-index: 30; background: rgba(0,0,0,0.35);
  display: flex; align-items: center; justify-content: center;
}
.modal {
  background: var(--panel); border: 3px solid var(--ink);
  border-radius: 12px 16px 13px 15px / 15px 13px 16px 12px;
  padding: 16px 20px; max-width: 720px; max-height: 80vh; overflow: auto;
}
.modal h3 { margin-top: 0; }
.modal table { border-collapse: collapse; font-size: 13px; }
.modal th, .modal td { border-bottom: 1.5px solid #999; padding: 3px 8px; text-align: left; }
.modal .pick { cursor: pointer; }
.modal .pick:hover { background: #f4efdd; }

/* ---- data previews ---- */
.preview-loading {
  font-size: 13px; color: #888; font-style: italic;
  padding: 10px 0;
}
.data-strip { margin: 10px 0 4px; }
.data-strip h4 {
  margin: 8px 0 4px; border-bottom: 2px solid var(--ink); font-size: 16px;
}
.ds-row {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 13px; padding: 1.5px 0;
}
.ds-row span { color: #666; }
.ds-row.ds-key b { color: var(--accent); }
.ds-row.ds-bad b, .ds-bad { color: var(--bad); }
.data-strip .ds-bad { font-size: 13px; padding: 4px 0; }

.chef-tip {
  display: flex; align-items: flex-start; gap: 8px;
  border: 2px solid var(--ink);
  border-radius: 10px 14px 11px 13px / 13px 11px 14px 10px;
  padding: 7px 9px; margin: 6px 0; font-size: 13px;
  background: #fffbe9;
}
.chef-tip.warn { background: #fff3d6; border-color: #a3690b; }
.chef-tip.bad  { background: #ffe9e9; border-color: var(--bad); }
.chef-tip::before { content: "👨‍🍳"; flex: 0 0 auto; }
.chef-tip .tip-text { flex: 1 1 auto; }
.chef-tip .tip-accept {
  flex: 0 0 auto; font-size: 12px; padding: 2px 8px;
  background: #ffd9a0; transform: none;
}
.chef-tip .tip-dismiss {
  flex: 0 0 auto; border: none; background: none;
  font-size: 16px; padding: 0 2px; line-height: 1;
}

.preview-hint { font-size: 12px; color: #777; margin: 8px 0 4px; font-style: italic; }
.preview { position: relative; margin: 4px 0 10px; line-height: 0; }
.preview.loading { opacity: 0.45; }
.preview-img {
  width: 100%; display: block;
  border: 2px solid var(--ink);
  border-radius: 6px 9px 7px 10px / 10px 7px 9px 6px;
  background: #fff;
}
.preview-img.standalone { margin: 6px 0 10px; }
.preview-overlay {
  position: absolute; inset: 0; width: 100%; height: 100%;
  cursor: crosshair; touch-action: none;
}
.preview-hud {
  position: absolute; left: 6px; bottom: 6px;
  background: rgba(253, 252, 247, 0.9);
  border: 1.4px solid var(--ink); border-radius: 5px;
  font-size: 11.5px; line-height: 1.3; padding: 1px 6px;
  pointer-events: none;
}

/* ---- tasting board ---- */
.modal.board { max-width: min(96vw, 1400px); }
.board-grid { display: grid; gap: 16px; margin: 10px 0 16px; }
.board-col h4 { margin: 0 0 2px; font-size: 17px; }
.board-sub { font-size: 12px; color: #777; margin-bottom: 6px; }
.board-col img {
  width: 100%; border: 2px solid var(--ink);
  border-radius: 6px 9px 7px 10px / 10px 7px 9px 6px;
  cursor: zoom-in; background: #fff; margin-bottom: 6px;
}
.board-col table, .modal.board table { width: 100%; }
/* null-control rows: the floor under every score on this board */
.board-sep td {
  padding-top: 8px; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; color: #777; border-top: 1px dashed #bbb;
}
.board-ok { color: #1a7f37; }
.board-bad { color: #b3261e; }
.board-warn {
  border: 2px solid #b3261e; background: #fff4f2; color: #5c1712;
  padding: 9px 12px; margin: 8px 0 4px; font-size: 13px; line-height: 1.45;
  border-radius: 7px 10px 8px 11px / 11px 8px 10px 7px;
}

/* ---- account badge ---- */
#account { margin-left: auto; align-items: center; }
.acct-name { font-size: 15px; font-weight: bold; }
.acct-role {
  font-size: 11.5px; padding: 1px 7px; border: 1.6px solid var(--ink);
  border-radius: 7px 9px 8px 10px / 10px 8px 9px 7px; background: #e8e2ce;
}
.acct-role.admin { background: #ffd0a0; }
.acct-role.editor { background: #cde8c0; }
.acct-role.viewer { background: #dcd8c8; }
#account button { font-size: 15px; padding: 2px 7px; }
body.guest #toolbar button:disabled { opacity: 0.3; }

/* ---- node palette ---- */
.palette {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px; margin-top: 10px; max-width: 700px;
}
.palette-item {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; padding: 7px 10px; transform: none;
}
.palette-item b { font-size: 15px; }
.palette-item span { font-size: 11.5px; color: #666; line-height: 1.3; }
.palette-item:hover { transform: none; background: #f4efdd; }

/* ---- upload ---- */
.modal.wide { max-width: 820px; }
.dropzone {
  display: block; text-align: center; cursor: pointer;
  border: 3px dashed var(--ink); border-radius: 14px 18px 15px 17px / 17px 15px 18px 14px;
  padding: 26px 16px; margin: 12px 0; background: #fffbe9;
}
.dropzone:hover, .dropzone.over { background: #fff3d6; }
.dropzone.busy { opacity: 0.55; pointer-events: none; }
.dropzone span { font-size: 13px; color: #777; }
.pair-row {
  border: 2px solid var(--ink);
  border-radius: 10px 14px 11px 13px / 13px 11px 14px 10px;
  padding: 8px 12px; margin: 10px 0; background: #fffef9;
}
.pair-row .form-row label { flex: 0 0 34%; }

/* ---- live controls ---- */
.slider-row { margin: 8px 0 2px; }
.slider-head { display: flex; justify-content: space-between; font-size: 13px; }
.slider-head b { color: var(--accent); }
.slider-row input[type=range] { width: 100%; accent-color: #b3552e; }
.counts-strip { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 4px; }
.chip {
  font-size: 11.5px; padding: 1px 8px; background: #e8e2ce;
  border: 1.5px solid var(--ink);
  border-radius: 7px 9px 8px 10px / 10px 8px 9px 7px;
}
.chip.pay { background: #f3c9c9; }
.chip.wet { background: #c9d8ea; }
.feas-line { font-size: 12.5px; margin: 4px 0; font-weight: bold; }
.feas-line.ok { color: var(--ok); }
.feas-line.warn { color: #a3690b; }
.feas-line.bad { color: var(--bad); }
.track-wrap { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.interval-track, .sweep-canvas, .blind-canvas {
  border: 2px solid var(--ink);
  border-radius: 5px 8px 6px 9px / 9px 6px 8px 5px;
  background: #fff; max-width: 100%;
}
.sweep-canvas { cursor: crosshair; margin: 4px 0; width: 100%; }
.blind-canvas { width: 100%; }
.form-note .sw {
  display: inline-block; width: 10px; height: 10px; margin: 0 2px 0 6px;
  border: 1px solid var(--ink);
}
.form-note .sw.pay { background: #b02c2c; }
.form-note .sw.wet { background: #2c5e8a; }

/* ---- dialogs ---- */
.dlg-foot { display: flex; gap: 8px; margin-top: 14px; }
.modal table { width: 100%; }
.modal td button { font-size: 13px; padding: 2px 9px; }

/* ---- verdict: a score is never shown without its floor (verdict.js) ---- */
.v-score { font-weight: 700; }
.v-above { color: #1a7f37; font-size: 11px; white-space: nowrap; }
.v-below { color: #b3261e; font-size: 11px; white-space: nowrap; }
.v-unverified { color: #9a6700; font-size: 11px; white-space: nowrap; cursor: help; }
.v-none { color: #999; }
.v-evidence { font-size: 11px; color: #555; white-space: nowrap; }
.v-chip {
  display: inline-block; padding: 1px 7px; font-size: 11px; font-weight: 700;
  border: 2px solid var(--ink);
  border-radius: 6px 9px 7px 10px / 10px 7px 9px 6px;
}
.v-chip-ok  { background: #e8f5ec; color: #1a7f37; }
.v-chip-bad { background: #fdecea; color: #b3261e; }
.v-chip-raw { background: #f0f0f0; color: #777; }

/* ---- The Pass (triage.js) ---- */
.pass-chips { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
              margin: 10px 0 6px; }
.pass-chip { font-size: 12.5px; padding: 2px 9px; }
.pass-chip.on { background: #ffd9a0; font-weight: bold; }
.pass-search { flex: 1 1 180px; min-width: 140px; font-family: inherit;
  font-size: 13px; border: 1.6px solid var(--ink); background: #fff;
  padding: 3px 8px;
  border-radius: 6px 8px 7px 9px / 9px 7px 8px 6px; }
.pass-table { width: 100%; font-size: 12.5px; }
.pass-table td { vertical-align: top; }
.pass-why { font-size: 11px; color: #777; max-width: 260px; }
.pass-cook { padding: 1px 8px; font-size: 15px; }
.t-served { background: #e8f5ec; color: #1a7f37; }
.t-thin   { background: #fff6e0; color: #9a6700; }
.t-sent   { background: #fdecea; color: #b3261e; }
.t-raw    { background: #f0f0f0; color: #777; }
/* signed headroom bar: zero is the floor, left of it is below */
.hb { position: relative; display: inline-block; width: 100px; height: 11px;
      background: #f2efe6; border: 1.4px solid #bbb; vertical-align: middle; }
.hb-zero { position: absolute; left: 50px; top: -2px; width: 1.6px;
           height: 15px; background: var(--ink); }
.hb-fill { position: absolute; top: 0; height: 100%; }
.hb-pos { background: #1a7f37; }
.hb-neg { background: #b3261e; }
.hb-num { font-size: 11px; margin-left: 6px; }

/* ---- The Prep Station (review.js) ---- */
.modal.prep { width: min(1180px, 94vw); }
.prep-cols { display: flex; gap: 14px; align-items: flex-start; }
.prep-queue { flex: 0 0 300px; max-height: 62vh; overflow-y: auto;
              padding-right: 4px; }
.prep-card { flex: 1 1 auto; max-height: 62vh; overflow-y: auto;
             border-left: 2px dashed #ccc; padding-left: 14px; }
.prep-row { border: 1.8px solid var(--ink); background: #fff;
            border-radius: 8px 11px 9px 12px / 12px 9px 11px 8px;
            padding: 6px 9px; margin-bottom: 7px; cursor: pointer; }
.prep-row:hover { background: #fffdf5; }
.prep-row.on { background: #ffd9a0; }
.prep-row b { display: block; font-size: 12px; margin-top: 2px; }
.prep-meta { font-size: 11px; color: #666; margin-top: 2px; }
.prep-meta.bad { color: #b3261e; }
/* upload states — the same colour language as the Pass tiers */
.u-staged { background: #f0f0f0; color: #777; }
.u-busy   { background: #e9f0fb; color: #1c4f9c; }
.u-review { background: #fff6e0; color: #9a6700; }
.u-ready  { background: #e8f5ec; color: #1a7f37; }
.u-done   { background: #eef7ee; color: #4a7f4a; }
.u-bad    { background: #fdecea; color: #b3261e; }
/* the checklist: commit is blocked until every line is ticked */
.prep-checks { margin-top: 10px; }
.prep-check { display: grid; grid-template-columns: 18px 1fr;
              grid-template-areas: "box label" ". note";
              gap: 1px 6px; align-items: baseline; font-size: 13px;
              padding: 4px 6px; margin-bottom: 3px;
              border-left: 4px solid #ddd; }
.prep-check input { grid-area: box; }
.prep-check > span:nth-of-type(1) { grid-area: label; }
.prep-note { grid-area: note; font-size: 11px; color: #666; }
.prep-check.s-ok   { border-left-color: #1a7f37; }
.prep-check.s-warn { border-left-color: #e0a800; background: #fffdf3; }
.prep-check.s-bad  { border-left-color: #b3261e; background: #fdf3f2; }
.prep-check.s-bad .prep-note { color: #b3261e; }
/* count of uploads sitting on somebody, on the toolbar button */
#btn-prep .badge {
  display: inline-block; margin-left: 5px; padding: 0 6px;
  font-size: 11px; font-weight: 700; background: #ffd9a0;
  border: 1.4px solid var(--ink);
  border-radius: 7px 9px 8px 10px / 10px 8px 9px 7px;
}

/* typeahead in front of the dataset picker — a flat select of 300 is a wall */
.ds-find { width: 100%; font-family: inherit; font-size: 13px;
  border: 1.6px solid var(--ink); background: #fff; padding: 3px 8px;
  margin-bottom: 5px;
  border-radius: 6px 8px 7px 9px / 9px 7px 8px 6px; }

/* Tasting Board: the label column stays put while the dishes scroll */
.board table th:first-child, .board table td:first-child {
  position: sticky; left: 0; background: #fffdf7; z-index: 1;
}

/* ---- the corpus view (meta.js) ---- */
.modal.meta { width: min(980px, 94vw); }
.meta-block { margin: 16px 0 4px; border-top: 2px dashed #ddd; padding-top: 10px; }
.meta-block h4 { margin: 0 0 4px; }
.meta-why { display: block; font-size: 11px; color: #777; font-weight: normal; }
.meta-ax { font-size: 10px; fill: #666; font-family: inherit; }
.meta-svg { margin: 6px 0; border: 1.8px solid var(--ink); background: #fffdf7;
  border-radius: 8px 11px 9px 12px / 12px 9px 11px 8px; }
/* funnel: each stage is a subset of the one above, so the bars only shrink */
.meta-funnel-row { display: flex; align-items: center; gap: 8px; margin: 3px 0; }
.meta-funnel-row.empty .meta-funnel-n { color: #b3261e; }
.meta-funnel-n { flex: 0 0 34px; text-align: right; font-weight: 700; }
.meta-funnel-bar { flex: 0 0 190px; height: 13px; background: #f2efe6;
  border: 1.4px solid #bbb; }
.meta-funnel-bar > span { display: block; height: 100%; background: #6aa84f; }
.meta-funnel-label { flex: 1 1 auto; font-size: 12.5px; }
/* histogram: the zero row is a different kind of result, not a small bar */
.meta-hist { margin: 6px 0; }
.meta-hist-row { display: flex; align-items: center; gap: 8px; margin: 2px 0;
  font-size: 12px; }
.meta-hist-row.zero { font-weight: 700; }
.meta-hist-row.zero .meta-hist-bar > span { background: #b3261e; }
.meta-hist-lab { flex: 0 0 110px; text-align: right; }
.meta-hist-bar { flex: 1 1 auto; height: 12px; background: #f2efe6;
  border: 1.4px solid #bbb; }
.meta-hist-bar > span { display: block; height: 100%; background: #6a8caa; }
.meta-hist-n { flex: 0 0 34px; }

/* ---- units and range feedback on every numeric knob (V3-P6) ---- */
/* .form-row is FLEX, so the note needs a full-width basis and wrapping to sit
   on its own line -- as a plain third item it squeezes the input instead. */
.form-row { flex-wrap: wrap; }
.field-unit { flex: 0 0 auto; font-size: 11px; color: #666;
  white-space: nowrap; cursor: help; }
.field-note { flex: 1 0 100%; font-size: 11px; margin: 1px 0 0 0;
  line-height: 1.35; }
.field-note:empty { display: none; }        /* no gap when there is nothing to say */
.field-note.warn { color: #9a6700; }
.field-note.bad  { color: #b3261e; font-weight: 600; }
.form-row.field-warn input, .form-row.field-warn select {
  border-color: #e0a800; background: #fffdf3;
}
.form-row.field-warn label { color: #9a6700; }
/* the keyboard reference in the help dialog */
kbd { font-family: inherit; font-size: 12px; background: #f2efe6;
  border: 1.4px solid var(--ink); padding: 0 5px; white-space: nowrap;
  border-radius: 5px 7px 6px 8px / 8px 6px 7px 5px; }
/* a focus ring that is actually visible on the hand-drawn chrome */
:focus-visible { outline: 3px solid #1c6fd6; outline-offset: 2px; }
.modal:focus { outline: none; }
#btn-undo:disabled { opacity: .45; }

/* ---- the oven queue (queue.js) ---- */
.q-table td { vertical-align: middle; }
.q-table tr.q-running { background: #fffdf3; }
.q-table tr.q-queued  { color: #666; }
/* the sample count travels with the estimate, so a 3-run median and a
   300-run median can never look like the same claim */
.q-basis { font-size: 11px; color: #888; }
.q-noeta { font-size: 11px; color: #9a6700; cursor: help; }
#btn-queue .badge {
  display: inline-block; margin-left: 5px; padding: 0 6px;
  font-size: 11px; font-weight: 700; background: #ffd9a0;
  border: 1.4px solid var(--ink);
  border-radius: 7px 9px 8px 10px / 10px 8px 9px 7px;
}

/* ---- plain-language help: the "?" dot, its bubble, and the glossary ---- */
/* The bubble sits above EVERY other layer (lightbox is 40, modals 30): a
   help popover opened from inside a modal that renders behind it is worse
   than no help at all. */
.help-dot {
  font: inherit; font-size: 11px; line-height: 15px;
  width: 17px; height: 17px; padding: 0; margin-left: 5px;
  border: 1.8px solid #8a8577; border-radius: 50%;
  background: #f3efe2; color: #55503f; cursor: pointer;
  vertical-align: middle; flex: 0 0 auto;
}
.help-dot:hover, .help-dot:focus-visible {
  background: var(--accent); color: #fff; border-color: var(--ink);
}
.help-dot[aria-expanded="true"] {
  background: var(--accent); color: #fff; border-color: var(--ink);
}
/* nothing behind it yet — still reachable, but it does not advertise */
.help-dot-thin { opacity: 0.45; }

.help-pop {
  position: fixed; z-index: 50; max-width: 340px;
  background: var(--panel); color: var(--ink);
  border: 2.5px solid var(--ink);
  border-radius: 11px 14px 12px 13px / 13px 12px 14px 11px;
  padding: 10px 26px 10px 12px;
  box-shadow: 3px 4px 0 rgba(0,0,0,0.18);
  font-size: 13px; line-height: 1.45;
}
.help-pop p { margin: 0 0 7px; }
.help-pop .help-path {
  font-size: 11px; color: #8a8577; margin-bottom: 4px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace; word-break: break-all;
}
.help-pop .help-matters { color: #5a4a2a; }
.help-pop .help-facts {
  font-size: 11.5px; color: #777; border-top: 1.5px dashed #ccc;
  padding-top: 5px; margin-top: 6px;
}
.help-pop .help-why { font-size: 12px; color: #777; margin-top: 5px; }
.help-pop-x {
  position: absolute; top: 3px; right: 5px;
  border: none; background: none; cursor: pointer;
  font: inherit; font-size: 17px; line-height: 1; color: #8a8577; padding: 2px;
}
.help-pop-x:hover { color: var(--bad); }

/* a term that can explain itself: dotted underline, not a colour, so it
   still reads as text rather than as a control */
.jargon {
  font: inherit; padding: 0; border: none; background: none;
  color: inherit; cursor: help;
  border-bottom: 1.6px dotted var(--accent);
}
.jargon:hover, .jargon:focus-visible { background: #fdf0d5; color: var(--accent); }
.jargon-unknown { border-bottom-style: none; cursor: default; }

.modal.glossary { max-width: min(94vw, 760px); }
.gloss-list { margin-top: 10px; }
.gloss-item { margin: 0 0 12px; padding: 6px 8px; border-radius: 6px; }
.gloss-item h5 { margin: 0 0 3px; font-size: 15px; }
.gloss-item p { margin: 0; font-size: 13px; line-height: 1.5; }
.gloss-item .gloss-see { font-size: 12px; color: #777; margin-top: 4px; }
.gloss-hit { background: #fdf0d5; outline: 2px dashed var(--accent); }
.gloss-open {
  font: inherit; font-size: 12px; padding: 1px 8px; cursor: pointer;
  border: 1.8px solid #8a8577; border-radius: 8px 10px 9px 11px / 10px 9px 11px 8px;
  background: #f3efe2; color: #55503f; white-space: nowrap;
}
.gloss-open:hover, .gloss-open:focus-visible {
  background: var(--accent); color: #fff; border-color: var(--ink);
}

/* ---- the horizon picker ---- */
.modal.picker { width: 96vw; height: 92vh; max-width: none; display: flex;
                flex-direction: column; }
.picker-toolbar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
                  margin-bottom: 8px; }
.picker-toolbar select, .picker-toolbar input[type=text] { font: inherit;
  font-size: 13px; padding: 2px 4px; }
.pick-tools { display: inline-flex; gap: 2px; }
.pick-tool[aria-pressed="true"] { background: var(--accent); color: #fff;
                                  border-color: var(--ink); }
.picker-stage { position: relative; flex: 1 1 auto; min-height: 0;
                border: 2.5px solid var(--ink); background: #fff; }
.picker-canvas, .picker-overlay { position: absolute; inset: 0; width: 100%;
                                  height: 100%; }
.picker-overlay { touch-action: none; }
.picker-stage[data-tool="pan"]    .picker-overlay { cursor: grab; }
.picker-stage[data-tool="pick"]   .picker-overlay { cursor: crosshair; }
.picker-stage[data-tool="window"] .picker-overlay { cursor: ns-resize; }
.picker-canvas:focus-visible { outline: 3px solid var(--accent); }
.picker-hud { font-size: 12.5px; color: #555; margin-top: 5px;
              font-variant-numeric: tabular-nums; }
.picker-live { position: absolute; width: 1px; height: 1px; overflow: hidden;
               clip: rect(0 0 0 0); white-space: nowrap; }
.open-picker { font: inherit; font-size: 12.5px; margin: 6px 0; cursor: pointer;
  padding: 2px 10px; border: 1.8px solid var(--ink); background: #f3efe2;
  border-radius: 9px 11px 10px 12px / 11px 10px 12px 9px; }
.open-picker:hover, .open-picker:focus-visible { background: var(--accent);
                                                 color: #fff; }
.hz-paste { width: 100%; font-family: ui-monospace, Menlo, monospace;
            font-size: 12px; margin: 4px 0; }
.hz-shape table { margin: 6px 0; width: 100%; }
.hz-shape select { font: inherit; font-size: 12.5px; }
.yaml-edit { width: 100%; font-family: ui-monospace, Menlo, monospace;
             font-size: 12.5px; line-height: 1.45; }
.modal.manage { max-width: min(94vw, 900px); }
.modal.manage td button { font-size: 12px; padding: 1px 7px; margin-right: 3px; }

/* ---- the interactive well tie ------------------------------------------ */
.modal.picker.tie-panel { gap: 6px; }
.tie-bar { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
  padding: 4px 2px; }
.tie-r { font-size: 1.15rem; }
.tie-r.good { color: #2a7d2a; }
.tie-r.fair { color: #b07000; }
.tie-r.poor, .tie-r.warn { color: #a33; }
.tie-shift { color: #555; }
.tie-status { color: #555; font-size: 0.85rem; flex: 1 1 200px; }
/* the tracks share one row and one time axis; each canvas fills its box */
.tie-strip { display: flex; gap: 4px; flex: 1 1 auto; min-height: 0; }
.tie-track { display: flex; flex-direction: column; min-width: 0;
  border: 1px solid #ccc; border-radius: 3px; background: #fff; }
.tie-track-title { font-size: 0.7rem; text-align: center; padding: 2px 3px;
  color: #333; border-bottom: 1px solid #eee; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.tie-canvas { flex: 1 1 auto; width: 100%; min-height: 0; display: block; }
.tie-canvas.draggable { cursor: ns-resize; touch-action: none; }
.tie-foot { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  padding-top: 4px; }
.tie-btn { font: inherit; padding: 3px 9px; cursor: pointer; }
.tie-btn.primary { font-weight: bold; }
.tie-hint { font-size: 0.8rem; color: #555; }

/* ---- tracker knobs + quality line in the picker ------------------------ */
.pick-knob { display: inline-flex; align-items: center; gap: 4px;
  font-size: 0.8rem; white-space: nowrap; }
.pick-knob input[type=number] { width: 4.5em; font: inherit; }
.pick-knob input[type=range] { width: 90px; }
.pick-rmin-out { min-width: 2.6em; font-variant-numeric: tabular-nums; }
/* reserving the line's height matters more than it looks: the quality text
   is (re)written inside draw(), AFTER the overlay has been painted against
   the current layout -- if this element grows from empty, the whole stage
   shifts and everything just drawn sits ~1 line off its data until the next
   repaint. Measured: an 11 px drift that put a reference horizon visibly
   below the reflector it names. */
.pick-qual { color: #6a5210; min-height: 1.25em; }

.pick-lock-banner { background: #fdf3d8; border: 1px solid #c98a1b;
  border-radius: 4px; padding: 4px 8px; font-size: 0.85rem;
  display: flex; gap: 8px; align-items: center; }
.pick-make-editable { font: inherit; cursor: pointer; }
select.pick-load { max-width: 170px; font: inherit; }

.picker-legend { position: absolute; top: 8px; right: 8px; z-index: 5;
  background: rgba(253,251,244,0.95); border: 1.5px solid #444;
  border-radius: 5px; padding: 6px 10px; font-size: 0.8rem;
  display: flex; flex-direction: column; gap: 3px; max-height: 40%;
  overflow-y: auto; }
.picker-legend-row { display: flex; align-items: center; gap: 6px;
  cursor: pointer; white-space: nowrap; }
.picker-legend-chip { width: 14px; height: 4px; border-radius: 2px;
  display: inline-block; }
.picker-legend-empty { color: #777; font-style: italic; }
.pick-preview[aria-pressed="false"], .pick-refs[aria-pressed="false"] {
  opacity: 0.55; }

/* ---- the pay/wet zone picker -------------------------------------------- */
.picker-stage[data-tool="pay"]   .picker-overlay,
.picker-stage[data-tool="wet"]   .picker-overlay { cursor: ns-resize; }
.picker-stage[data-tool="erase"] .picker-overlay { cursor: not-allowed; }
.zone-del { font: inherit; font-size: 11px; line-height: 1; cursor: pointer;
  border: none; background: none; color: #a33; padding: 0 2px;
  margin-left: auto; }
.zone-del:hover, .zone-del:focus-visible { color: #fff; background: #a33;
  border-radius: 3px; }
.picker-legend-row .picker-legend-chip { flex: 0 0 auto; }

/* ---- the interactive well tie: parameter surface (T6) ---- */
.tie-controls { display: flex; flex-direction: column; gap: 4px;
                margin: 6px 0; padding: 6px 8px; background: #fafafa;
                border: 1px solid #e4e4e4; border-radius: 4px; }
.tie-ctl-main { display: grid; grid-template-columns: repeat(2, 1fr);
                gap: 2px 16px; }
.tie-ctl { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.tie-ctl-label { flex: 0 0 74px; color: #444; }
.tie-ctl-input { flex: 1 1 auto; min-width: 60px; }
.tie-ctl-num { flex: 0 0 62px; font-size: 12px; padding: 1px 3px; }
.tie-ctl-unit { flex: 0 0 auto; color: #777; min-width: 18px; }
.tie-ctl-quick { display: flex; gap: 2px; }
.tie-btn.tiny { padding: 0 4px; font-size: 10px; line-height: 16px; }
/* a knob the current wavelet ignores is visibly inert rather than quietly
   doing nothing when you drag it */
.tie-ctl.dimmed { opacity: .45; }
.tie-ctl.dimmed .tie-ctl-label::after { content: " (ignored)"; font-style: italic; }
.tie-adv { font-size: 12px; }
.tie-adv summary { cursor: pointer; color: #555; padding: 2px 0; }
.tie-ctl-adv { display: grid; grid-template-columns: repeat(2, 1fr);
               gap: 2px 16px; padding-top: 2px; }
/* display-only controls are separated so they cannot read as tie parameters */
.tie-display-only { display: flex; align-items: center; gap: 6px;
                    font-size: 12px; padding-top: 4px; margin-top: 2px;
                    border-top: 1px dashed #d0d0d0; }
.tie-display-note { color: #888; font-size: 11px; }
/* r has no value while the server is re-convolving, and says so */
.tie-r.pending { color: #999; font-style: italic; }
/* the tie-point picker: an armed tool must look armed */
.tie-btn.active { background: #7b3fa0; color: #fff; border-color: #7b3fa0; }
/* a sweep curve computed with parameters that have since changed */
.sweep-canvas.stale { opacity: .4; filter: grayscale(1); }
.chef-tip.stale .tip-accept[disabled] { opacity: .5; cursor: progress; }
/* a control that is shown for reference but will not travel to the cook */
.form-row.inert { opacity: .55; }
.form-row.inert::after { content: " (from the saved recipe)"; font-size: 10px;
                         color: #888; font-style: italic; margin-left: 4px; }
.batch-inert-note { margin: 6px 0; }
/* a node another editor is holding */
.node.held { opacity: .85; }
.node-held { font: 10px sans-serif; fill: #7b3fa0; }
/* who else is here, and where they are pointing */
#presence { display: inline-flex; gap: 3px; align-items: center; }
.avatar { display: inline-flex; align-items: center; justify-content: center;
          width: 22px; height: 22px; border-radius: 50%; color: #fff;
          font-size: 10px; font-weight: bold; text-transform: uppercase; }
.peer-cursor { pointer-events: none; }
.peer-name { font: 11px sans-serif; paint-order: stroke;
             stroke: #fff; stroke-width: 3px; }

/* the run catalogue: what has been cooked against one dataset.
   `aborted` is amber, never green -- the pipeline refusing to finish is not
   a success, and painting it as one is a regression this app has had before. */
.run-ok     { color: var(--good); font-weight: 700; }
.run-warn   { color: #b7950b; font-weight: 700; }
.run-bad    { color: var(--bad); font-weight: 700; }
.run-muted  { color: #888; }
.run-sub    { color: #888; font-size: 11px; margin-left: 4px; }
.pass-runs  { margin-left: 3px; }

/* recipe labels: the readable title under the handle, and its tags */
.rcp-title { font-size: 11.5px; color: #444; font-style: italic; }
.rcp-tag { display: inline-block; font-size: 10.5px; padding: 0 5px;
  border: 1.4px solid var(--ink); border-radius: 7px 5px 8px 6px / 6px 8px 5px 7px;
  margin: 2px 3px 0 0; background: #fff; }
.run-note { width: 100%; }

/* the dataset preview: the seismic, the logs, the map and the audit, before
   any recipe exists. Uses .modal.picker's full-screen flex column, so the
   grid must be the flexing child and scroll on its own. */
/* .modal.picker is a fixed-height flex column, so the content must be the
   flexing child and scroll on its own -- otherwise the audit below the fold
   is simply unreachable, which is exactly what it must never be. */
.dspreview .dsp-scroll { flex: 1 1 auto; min-height: 0; overflow: auto;
  padding-right: 4px; }
.dspreview .dsp-grid {
  display: grid; gap: 10px; margin-top: 8px;
  grid-template-columns: minmax(300px, 1fr) minmax(360px, 1.6fr);
  align-items: start;
}
.dspreview .dsp-col { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 900px) { .dspreview .dsp-grid { grid-template-columns: 1fr; } }
.dsp-pane { border: 1.6px solid var(--ink); background: var(--panel);
  border-radius: 10px 7px 12px 8px / 8px 12px 7px 10px; padding: 6px 8px 8px; }
.dsp-pane-head { display: flex; align-items: center; gap: 8px;
  justify-content: space-between; }
.dsp-pane-head h4 { margin: 2px 0 4px; font-size: 12.5px; }
.dsp-pane-body { min-height: 0; }
.dsp-img { position: relative; }
.dsp-img.loading { opacity: 0.45; }
.dsp-img img { width: 100%; height: auto; display: block; }
.dsp-img-note { font-size: 10.5px; color: #666; margin-top: 2px; }
/* a pane whose PNG draws a refusal rather than data. The routes answer 200
   with a legible error image on purpose, so without this a refusal and a
   plot look alike -- to a reader and to a gate. */
.dsp-img-error img { opacity: 0.55; }
.dsp-img-error .dsp-img-note { color: var(--bad); font-weight: 700; }
.dsp-cap { font-size: 11px; color: #555; margin-top: 3px; }
.dsp-axis { display: inline-flex; gap: 3px; }
.dsp-axis .on { background: var(--accent); }

/* the audit. `unknown` is muted, never green: "could not be measured" must
   not read as "fine", which is the failure this screen exists to avoid. */
.dsp-audit { margin-top: 10px; }
.dsp-qc-row { display: flex; gap: 7px; align-items: baseline;
  padding: 2px 0; border-bottom: 1px dotted #ddd; }
.dsp-qc-icon { flex: 0 0 14px; text-align: center; }
.dsp-qc-text { font-size: 12px; color: var(--ink); }
.dsp-qc-text b { margin-right: 2px; }
.dsp-notchecked { margin-top: 9px; padding: 6px 9px; background: #fbf7ee;
  border: 1.6px dashed var(--ink);
  border-radius: 9px 6px 11px 7px / 7px 11px 6px 9px; }
.dsp-notchecked h5 { margin: 1px 0 4px; font-size: 12px; }
.dsp-notchecked ul { margin: 0; padding-left: 17px; }
.dsp-notchecked li { font-size: 11.5px; color: #444; margin-bottom: 3px; }

/* the typeface switch in the account badge; `on` = the reading face is active */
.acct-font { font-weight: bold; letter-spacing: 0.5px; }
.acct-font.on { background: #ffd9a0; }

/* Node labels in the reading face.
 *
 * The node box is a fixed 150 px and the labels were sized against xkcd
 * Script, which is narrow. A system face at the same nominal size is roughly
 * a tenth wider per character, and "Pantry Labels" / "Plated Dish" spilled
 * straight over the right-hand border -- measured on screen, not guessed.
 *
 * Each label is stepped down rather than all of them being flattened to one
 * size: the title/subtitle/badge hierarchy is what makes a node readable at a
 * glance. A system face at 12.5 px still reads close to xkcd Script at 15 px,
 * because its x-height is taller.
 *
 * 12.5 is measured, not chosen. The longest stage name is "Cutting Board", and
 * its right edge lands at 150/148/146 px for 13.5/13/12.5 px type against a
 * 150 px box -- the label is anchored at x = W/2 + 22 to clear the icon, so it
 * has only ~53 px of room to the right, not 75. My first pass used 13.5 and
 * LOOKED fine in a screenshot; it was grazing the border by a pixel.
 */
html[data-font="pro"] #canvas text.n-title   { font-size: 12.5px; }
html[data-font="pro"] #canvas text.n-sub     { font-size: 10.5px; }
html[data-font="pro"] #canvas text.n-badge   { font-size: 9.5px; }
html[data-font="pro"] #canvas text.n-variant { font-size: 12px; }
