/* BE HUB. An instrument panel, not a web app.
 *
 * The look is borrowed from rack equipment and test gear, because that is what
 * the people using this have in front of them all day: near-black panel, one
 * signal colour, hairline rules, status LEDs, and every measured number in a
 * tabular monospace so columns of figures line up and a digit that changed is
 * obvious. Prose is in a UI sans; anything the instrument MEASURED is mono.
 * That split is the whole typographic rule, and it is functional not styling.
 *
 * Read at 02:00 during a fault: high contrast, no animation that delays
 * information, and colour used only where it carries meaning.
 */

:root{
  /* surfaces, darkest to lightest */
  --bg:#0a0d12; --panel:#11151c; --panel2:#171d26; --panel3:#1e2530;
  --line:#222a36; --line2:#303a49;
  /* ink */
  --ink:#e8edf4; --dim:#98a5b6; --faint:#69748a;
  /* one signal colour, plus semantics */
  --accent:#35c8f2; --accent-ink:#04222c; --accent-soft:rgba(53,200,242,.12);
  --accent-line:rgba(53,200,242,.32);
  --ok:#3ad6a0; --warn:#f5b53f; --bad:#ff5d6c;
  --ok-soft:rgba(58,214,160,.12); --warn-soft:rgba(245,181,63,.12);
  --bad-soft:rgba(255,93,108,.12);
  --radius:10px; --radius-sm:7px;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px rgba(0,0,0,.28);
  --grid:rgba(255,255,255,.022);
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
}
@media (prefers-color-scheme: light){
  :root{
    --bg:#eef1f5; --panel:#fff; --panel2:#f5f7fa; --panel3:#eaeef3;
    --line:#dde3ea; --line2:#c3ccd8;
    --ink:#10161f; --dim:#54606f; --faint:#7d8896;
    --accent:#0479a5; --accent-ink:#fff; --accent-soft:rgba(4,121,165,.09);
    --accent-line:rgba(4,121,165,.3);
    --ok:#0a8f68; --warn:#8f6200; --bad:#c62f3c;
    --ok-soft:rgba(10,143,104,.1); --warn-soft:rgba(143,98,0,.12);
    --bad-soft:rgba(198,47,60,.1);
    --shadow:0 1px 2px rgba(16,22,31,.06),0 6px 18px rgba(16,22,31,.07);
    --grid:rgba(16,22,31,.032);
  }
}

*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  -webkit-font-smoothing:antialiased;
  /* A faint panel grid: present at the edge of vision, never in the way. */
  background-image:linear-gradient(var(--grid) 1px,transparent 1px),
                   linear-gradient(90deg,var(--grid) 1px,transparent 1px);
  background-size:88px 88px,88px 88px;
  background-attachment:fixed}

h1{font-size:1.55rem;margin:0 0 .25rem;letter-spacing:-.015em;font-weight:650}
h3{font-size:.98rem;margin:0 0 .4rem;font-weight:620;letter-spacing:-.005em}
p{margin:.3rem 0}
a{color:var(--accent)}

/* Every measured value is mono AND tabular, so digits sit in columns. */
.mono,.tv,.big,.credits,.wstate,.pm,.cmd{font-family:var(--mono);
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.mono{font-size:.85em;letter-spacing:-.01em}
/* A multicast URL or a blob of JSON parameters is ONE unbreakable token, and
 * left alone it sets the width of whatever contains it. Measured at 390px: a
 * job card came out 1215px wide and a monitor card 896px, and the monitors
 * page forced the phone to lay the whole site out at 908px and shrink it to
 * fit. These fields are addresses and parameters, not prose - they are allowed
 * to break mid-token. */
.sub.mono{overflow-wrap:anywhere}
.muted,.hint,.sub{color:var(--dim)}
.hint,.sub{font-size:.85rem}
.sub{display:block;margin-top:.15rem}

/* ---- header: the front panel ------------------------------------------- */
.top{display:flex;align-items:center;gap:1rem;padding:.6rem 1.25rem;
  background:linear-gradient(180deg,var(--panel2),var(--panel));
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:40;
  /* a hairline of signal colour along the top edge of the panel */
  box-shadow:inset 0 2px 0 -1px var(--accent),0 1px 0 rgba(0,0,0,.3)}
.brand{font-weight:750;letter-spacing:.11em;font-size:.82rem;text-transform:uppercase;
  color:var(--ink);text-decoration:none;display:flex;align-items:center;gap:.5rem;
  white-space:nowrap}
.brand::before{content:"";width:8px;height:8px;border-radius:50%;flex:none;
  background:var(--ok);box-shadow:0 0 0 3px var(--ok-soft),0 0 9px var(--ok)}

nav{display:flex;gap:.1rem;flex:1;flex-wrap:wrap}
nav a{color:var(--dim);text-decoration:none;font-size:.86rem;
  padding:.4rem .7rem;border-radius:var(--radius-sm);position:relative;
  transition:color .12s,background .12s}
nav a:hover{color:var(--ink);background:var(--panel3)}
nav a.here{color:var(--ink);background:var(--panel3)}
nav a.here::after{content:"";position:absolute;left:.7rem;right:.7rem;bottom:-.62rem;
  height:2px;background:var(--accent);border-radius:2px 2px 0 0;
  box-shadow:0 0 8px var(--accent)}

.who{display:flex;align-items:center;gap:.7rem;font-size:.82rem;color:var(--dim);
  white-space:nowrap}
.credits{background:var(--accent-soft);color:var(--accent);
  border:1px solid var(--accent-line);padding:.2rem .55rem;border-radius:999px;
  font-size:.78rem;font-weight:600}

/* ---- the monitors drill-down: two ways to start something --------------- */
.monacts{display:flex;gap:.5rem;flex-wrap:wrap;margin:.7rem 0 .2rem}
.btn.ghost{background:var(--panel3);color:var(--ink);border:1px solid var(--line2)}
.btn.ghost:hover{border-color:var(--accent);color:var(--accent)}
#mon-scan-panel{margin-top:.8rem;padding-top:.8rem;border-top:1px solid var(--line)}
#mon-fill{margin-top:.8rem;padding-top:.8rem;border-top:1px solid var(--line)}
.scanning{padding:.7rem 0}
.scanning strong{color:var(--accent)}
.scanlist{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--radius-sm);overflow:hidden;margin:.6rem 0}
/* ⚠️ A scan row is a <label>, and `label` on this panel is a FORM label:
 * uppercase, letter-spaced, small and dim. Inherited here it shouted the
 * service name back as FRAMECASTIC CH13 and, worse, turned the address into
 * UDP://239.100.1.39:20013 - a thing somebody might copy. This is the second
 * time the global label rule has leaked into content; it is switched off
 * explicitly rather than by hoping the next author notices. */
.scanrow{display:grid;grid-template-columns:auto minmax(0,1fr);
  grid-template-areas:"c n" "c a" "c m";column-gap:.7rem;row-gap:.1rem;
  align-items:baseline;padding:.5rem .7rem;background:var(--panel);
  cursor:pointer;margin:0;text-transform:none;letter-spacing:normal;
  font-weight:400;color:var(--ink);font-size:1rem}
.scanrow:hover{background:var(--panel2)}
.scanrow input{grid-area:c;align-self:center;margin:0;width:16px;height:16px;
  accent-color:var(--accent)}
.scanrow .sn{grid-area:n;font-weight:620;font-size:.92rem}
.scanrow .sa{grid-area:a;font-size:.82rem;color:var(--dim);overflow-wrap:anywhere}
.scanrow .sm{grid-area:m;font-size:.76rem;color:var(--faint)}
/* Announced but silent: shown, because "the plant says this exists and nothing
 * arrived" is information - but not tickable, because there is nothing there
 * to point a monitor at yet. */
.scanrow.quiet{opacity:.62}
.scanrow.quiet .sn::after{content:" - nothing arriving";color:var(--warn);
  font-weight:500;font-size:.8rem}
.scango{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap;margin-top:.6rem}
.scango .rows{flex-basis:100%}

/* ---- a monitor card folds away its evidence ----------------------------- */
.montop{cursor:pointer;user-select:none;margin:0}
.montop h3{display:flex;align-items:center;gap:.45rem;margin:0}
.caret{width:0;height:0;flex:none;border-left:5px solid currentColor;
  border-top:4px solid transparent;border-bottom:4px solid transparent;
  transform:rotate(90deg);transition:transform .12s;color:var(--faint)}
.card.monitor.shut .caret{transform:rotate(0deg)}
.card.monitor.shut .mondetail{display:none}
.montop:hover h3{color:var(--accent)}
.montop:focus-visible{outline:2px solid var(--accent);outline-offset:3px;
  border-radius:var(--radius-sm)}

/* ---- auth visibility ---------------------------------------------------- */
/* `hidden` is a PROPERTY, and the browser's own [hidden]{display:none} has the
 * specificity of a bare attribute selector - so ANY author rule declaring
 * `display` silently beats it and the element stays painted while every DOM
 * assertion reports it hidden. Four shipped that way (.picker, .list, .wall
 * and a bare label) before tests/hidden-sweep.py went looking. This is the
 * one line that makes `el.hidden = true` mean what everybody reads it as.
 * Removing it does not fail a build; run tests/hidden-sweep.py. */
[hidden]{display:none!important}

.auth-only{display:none!important}
/* `:not([hidden])` is load-bearing: these are !important AND out-specify the
 * guard above, so without it an .auth-only element could not be hidden. */
body.authed .auth-only:not([hidden]){display:revert!important}
body.authed nav a.auth-only:not([hidden]){display:block!important}
body.authed .who .auth-only:not([hidden]){display:inline-block!important}
body.authed .anon-only{display:none!important}

/* ---- layout ------------------------------------------------------------- */
/* Full width on purpose: this is an operations console, and the tables, tile
 * banks and monitor walls are all things you want more of on screen at once.
 * The side gutter is deliberately small - enough that nothing touches the
 * edge of the glass, not enough to waste a wide monitor. */
main{max-width:none;margin:0;padding:1.5rem 1.25rem 4.5rem}
.view{display:none}
.view.on{display:block}

.hero{margin-bottom:1.35rem}
.hero h1{font-size:1.9rem}

.grid{display:grid;gap:.9rem;grid-template-columns:repeat(auto-fill,minmax(310px,1fr))}
.list{display:grid;gap:.8rem;margin-top:.8rem;grid-template-columns:minmax(0,1fr)}
/* A grid item's automatic minimum is its MIN-CONTENT width, so one nowrap
 * evidence table inside one card sets the width of the whole column - and the
 * .mini{overflow-x:auto} that was put there to scroll it never gets the
 * chance, because the card around it was widened to fit. Measured at 390px:
 * the monitor list came out 896px wide and the phone laid the ENTIRE site out
 * at 908px and shrank it to fit. */
.list>*,.wall>*{min-width:0}

/* ---- cards -------------------------------------------------------------- */
.card{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:1rem 1.05rem;box-shadow:var(--shadow);
  /* a 1px highlight along the top edge, the way a bevelled panel catches light */
  background-image:linear-gradient(180deg,rgba(255,255,255,.035),transparent 64px)}
.card.wide{grid-column:1/-1}
.err-card{border-color:var(--bad);background:var(--bad-soft)}

/* ---- forms -------------------------------------------------------------- */
label{display:block;margin:.6rem 0;font-size:.72rem;color:var(--dim);
  text-transform:uppercase;letter-spacing:.07em;font-weight:650}
label.check{display:flex;align-items:center;gap:.45rem;text-transform:none;
  letter-spacing:normal;font-size:.85rem;font-weight:400}
label.check input{margin:0;width:auto}
input,select{width:100%;margin-top:.3rem;padding:.5rem .6rem;
  background:var(--panel2);color:var(--ink);
  border:1px solid var(--line2);border-radius:var(--radius-sm);
  font:inherit;font-size:.92rem;transition:border-color .12s,box-shadow .12s}
input:focus,select:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
.fieldhint{display:block;margin-top:.3rem;color:var(--faint);font-size:.75rem;
  text-transform:none;letter-spacing:normal;font-weight:400}

output{display:block;margin-top:.85rem;padding-top:.85rem;
  border-top:1px solid var(--line)}
output strong{font-size:1.5rem;display:block;font-family:var(--mono);
  font-variant-numeric:tabular-nums;letter-spacing:-.025em;font-weight:600}

/* ---- rows: a table whose columns do not wander -------------------------- */
.rows{margin-top:.6rem;display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden}
.row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.75rem;
  align-items:baseline;padding:.45rem .65rem;background:var(--panel);
  font-size:.87rem}
/* Fixed tracks, set by the markup that knows how many fields it has. Flex with
 * space-between put the same field in a different place on every row. */
.row.lead{grid-template-columns:auto minmax(0,1fr)}
.row.r3{grid-template-columns:auto minmax(0,1fr) auto}
.row.r4{grid-template-columns:auto minmax(0,1fr) auto minmax(0,1.5fr)}
.row>span:first-child{color:var(--dim)}
.row .mono{color:var(--dim);white-space:nowrap}
/* state rides a left rail, which reads faster than coloured text */
.row.ok{box-shadow:inset 3px 0 0 var(--ok)}
.row.no{box-shadow:inset 3px 0 0 var(--bad);background:var(--bad-soft)}
#event-list .row{grid-template-columns:auto auto minmax(0,1fr)}
#event-list .row span:nth-child(2){font-weight:650}

/* ---- a result, read under pressure --------------------------------------
 * Scalars share a grid rather than taking a line each - eight facts about a
 * machine used to be eight rows of two words. What the tool could NOT measure
 * gets its own block and is not a footnote: it is the half of the answer that
 * stops somebody acting on a number that was never taken. */
.kv{margin-top:.6rem;display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;
  grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
.kv>div{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,auto);gap:.75rem;
  align-items:baseline;padding:.4rem .65rem;background:var(--panel);font-size:.87rem}
.kv>div>span:first-child{color:var(--dim);min-width:0;overflow-wrap:anywhere}
/* ⚠️ The value must be allowed to WRAP. `.mono` is nowrap everywhere else, and
 * inherited here it made a CPU model run straight through its own label - two
 * strings painted over each other, which no DOM assertion would have caught. */
.kv>div>:last-child{min-width:0;text-align:right;white-space:normal;
  overflow-wrap:anywhere}
.kv>div.wide{grid-template-columns:1fr;gap:.1rem}
.kv>div.wide>:last-child{text-align:left}
.yes{color:var(--ok);font-weight:650}
.no-txt{color:var(--bad);font-weight:650}
.dim{color:var(--dim)}

.block{margin-top:.75rem}
.block>h4{margin:0 0 .35rem;font-size:.78rem;font-weight:650;letter-spacing:.04em;
  text-transform:uppercase;color:var(--dim)}

.chips{display:flex;flex-wrap:wrap;gap:.35rem}
.chip{padding:.2rem .5rem;border-radius:999px;font-size:.8rem;
  border:1px solid var(--line);background:var(--panel)}
.chip.on{border-color:color-mix(in srgb,var(--ok) 45%,var(--line));
  color:var(--ok)}
.chip.off{border-color:color-mix(in srgb,var(--bad) 45%,var(--line));
  color:var(--bad);background:var(--bad-soft)}
.chip .x{opacity:.75;font-size:.72rem}

.mini{overflow-x:auto;border:1px solid var(--line);border-radius:var(--radius-sm)}
.mini table{border-collapse:collapse;width:100%;font-size:.84rem}
.mini th,.mini td{text-align:left;padding:.35rem .6rem;white-space:nowrap;
  border-bottom:1px solid var(--line)}
.mini thead th{color:var(--dim);font-weight:600;font-size:.76rem;
  letter-spacing:.03em;text-transform:uppercase;background:var(--panel-2,var(--panel))}
.mini tbody tr:last-child th,.mini tbody tr:last-child td{border-bottom:0}
.mini tbody th{color:var(--dim);font-weight:500}
.mini .mono{white-space:nowrap}
.chips+.sub,.mini+.sub{margin-top:.4rem}

/* The tools that DO run, grouped. A heading column and a wrapped list, so
 * sixty-seven names read as seven lines rather than one paragraph. */
.runs{margin-top:.7rem;border-top:1px solid var(--line);padding-top:.55rem}
.runs>h5{margin:0 0 .35rem;font-size:.78rem;font-weight:650;letter-spacing:.04em;
  text-transform:uppercase;color:var(--dim)}
.runs-g{display:grid;grid-template-columns:8.5rem minmax(0,1fr);gap:.6rem;
  padding:.2rem 0;font-size:.84rem;align-items:baseline}
.runs-g .g{color:var(--dim);font-weight:600}
.runs-g .n{color:var(--ink,inherit);overflow-wrap:anywhere}
/* The separator is its own element so it can sit BACK from the names - a
 * divider as loud as the thing it divides adds work rather than removing it.
 * ⚠️ But --line is the BORDER colour (#222a36 on a #11151c panel) and at that
 * contrast the pipe simply was not there. --faint is the register this wants:
 * clearly visible, plainly subordinate to the names. Subordinate is not the
 * same as absent, and the first attempt confused the two. */
.runs-g .sep{font-style:normal;color:var(--faint);padding:0 .5rem;
  user-select:none}
@media (max-width:640px){.runs-g{grid-template-columns:1fr;gap:.1rem}}

.nm{margin-top:.75rem;padding:.55rem .7rem;border:1px solid var(--line);
  border-left:3px solid var(--warn,var(--dim));border-radius:var(--radius-sm);
  background:var(--panel)}
.nm>h4{margin:0 0 .3rem;font-size:.78rem;font-weight:650;letter-spacing:.04em;
  text-transform:uppercase;color:var(--dim)}
.nm ul{margin:0;padding-left:1.1rem}
.nm li{font-size:.85rem;color:var(--dim);margin:.15rem 0}

.promise{margin:.7rem 0 0;padding:.55rem .75rem;font-size:.88rem;
  color:var(--dim);border:1px solid var(--line);border-left:3px solid var(--ok);
  border-radius:var(--radius-sm);background:var(--panel);max-width:62rem}
.promise strong{color:var(--ok)}

/* ---- built, not yet offered: the vote ------------------------------------ */
.vote{display:inline-block;padding:.3rem .65rem;font-size:.8rem;border-radius:999px;
  border:1px solid var(--accent-line);background:var(--accent-soft);
  color:var(--accent);cursor:pointer;font-family:inherit}
.vote:hover{background:var(--accent);color:var(--accent-ink)}
.vote.voted{border-color:color-mix(in srgb,var(--ok) 50%,var(--line));
  background:var(--ok-soft);color:var(--ok)}
.votes{font-size:.78rem;color:var(--dim)}
.votes.dim{color:var(--faint)}

/* ---- the machine check on the front door ---------------------------------
 * Small. It asks one question and shows three lines; the detail belongs on the
 * result page and in the catalogue. An earlier version listed every runnable
 * tool here and buried everything under it. */
.mcheck{margin:1.5rem 0;padding:1.1rem 1.2rem;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--panel)}
.mc-head h2{margin:0 0 .3rem;font-size:1.25rem}
.mc-head .hint{margin:0 0 .9rem;max-width:58rem}
.mc-invite p{margin:0 0 .6rem;font-size:.92rem;color:var(--dim);max-width:58rem}
.mc-invite code{display:block;font-family:var(--mono);font-size:.78rem;
  background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:.45rem .6rem;overflow-x:auto;
  color:var(--ink);margin:0 0 .7rem}
.mc-cta{margin:.7rem 0 0;display:flex;flex-wrap:wrap;gap:.7rem;align-items:center}
.mc-cta .btn{margin-top:0}
.mc-pick{font-size:.85rem;padding:.4rem .5rem;background:var(--panel2);
  color:var(--ink);border:1px solid var(--line);border-radius:var(--radius-sm)}
.mc-what{margin:0;font-size:.95rem}
.mc-spec{color:var(--dim);font-size:.85rem}
.mc-count{margin:.25rem 0 .5rem;font-size:1.35rem;font-weight:650;color:var(--ok)}
.mc-count strong{font-size:1.8rem}
.mc-blocked{margin:0;padding-left:1.1rem;color:var(--dim);font-size:.88rem}
.mc-blocked li{margin:.12rem 0}
.mc-blocked strong{color:var(--ink)}
.mc-all{margin:0;color:var(--ok);font-size:.9rem}

.catpurpose{margin:-.2rem 0 .5rem;color:var(--dim);font-size:.87rem}

/* ---- three steps --------------------------------------------------------- */
.steps{margin:1.8rem 0}
.steps>h2{margin:0 0 .8rem;font-size:1.15rem}
.steplist{list-style:none;counter-reset:s;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(17rem,1fr));gap:.9rem}
.steplist>li{counter-increment:s;position:relative;padding:.9rem 1rem .9rem 2.8rem;
  border:1px solid var(--line);border-radius:var(--radius);background:var(--panel)}
.steplist>li::before{content:counter(s);position:absolute;left:.95rem;top:.9rem;
  width:1.4rem;height:1.4rem;border-radius:50%;display:grid;place-items:center;
  font-size:.8rem;font-weight:700;background:var(--accent-soft);
  color:var(--accent);border:1px solid var(--accent-line)}
.steplist h3{margin:0 0 .3rem;font-size:.98rem}
.steplist p{margin:0 0 .5rem;font-size:.87rem;color:var(--dim)}
.steplist code{display:block;font-family:var(--mono);font-size:.76rem;
  background:var(--panel2);border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:.4rem .5rem;overflow-x:auto;color:var(--ink)}

/* ---- buttons ------------------------------------------------------------ */
.btn{display:inline-block;margin-top:.8rem;padding:.5rem .95rem;
  background:var(--accent);color:var(--accent-ink);border:1px solid transparent;
  border-radius:var(--radius-sm);font:inherit;font-size:.88rem;font-weight:650;
  cursor:pointer;text-decoration:none;transition:filter .12s,transform .06s}
.btn:hover{filter:brightness(1.12)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
.btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.monitor.btn{background:transparent;color:var(--ink);border-color:var(--line2)}
.monitor.btn:hover{border-color:var(--accent);color:var(--accent);filter:none}
.monitor.live{color:var(--ok);border-color:var(--ok)}
.quick{display:flex;flex-wrap:wrap;gap:.5rem}
.quick .btn{margin-top:0}
.link{background:none;border:none;color:var(--accent);cursor:pointer;
  font:inherit;font-size:.85rem;padding:0;text-decoration:underline;
  text-underline-offset:2px}
.link.danger{color:var(--bad);margin-top:.5rem}
.chan-acts{display:flex;gap:.9rem;margin-top:.6rem}
.chan-acts .link{font-size:.82rem}

/* ---- workers ------------------------------------------------------------ */
.wtop{display:flex;justify-content:space-between;align-items:center;gap:.6rem}
.wname{font-weight:640}
.mods{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.7rem}
.chip{font-size:.75rem;padding:.2rem .5rem;border-radius:999px;
  border:1px solid var(--line2);background:var(--panel2);color:var(--dim);
  cursor:pointer;font-family:var(--mono);transition:border-color .12s,color .12s}
.chip:hover{border-color:var(--accent);color:var(--ink)}
.chip.on{background:var(--accent-soft);border-color:var(--accent-line);color:var(--accent)}
.chip.blocked{opacity:.4;cursor:not-allowed}

/* status pills carry an LED, the way a device front panel does */
.pill{font-size:.72rem;padding:.18rem .5rem .18rem .45rem;border-radius:999px;
  border:1px solid var(--line2);color:var(--dim);display:inline-flex;
  align-items:center;gap:.35rem;text-transform:uppercase;letter-spacing:.05em;
  font-weight:650;white-space:nowrap}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;flex:none;
  background:currentColor;box-shadow:0 0 7px currentColor}
.pill.online{color:var(--ok);border-color:rgba(58,214,160,.35);background:var(--ok-soft)}
.pill.offline{color:var(--bad);border-color:rgba(255,93,108,.35);background:var(--bad-soft)}
.pill.never{color:var(--faint)}
.pill.revoked{color:var(--faint);text-decoration:line-through}

.cmd{background:var(--bg);border:1px solid var(--line2);border-radius:var(--radius-sm);
  padding:.7rem .8rem;font-size:.78rem;overflow-x:auto;color:var(--ok);
  white-space:pre-wrap;word-break:break-all}
pre{margin:.5rem 0}

/* ---- the alerts badge --------------------------------------------------- */
.badge{display:inline-block;margin-left:.4rem;min-width:1.2rem;padding:0 .32rem;
  border-radius:999px;background:var(--bad);color:#fff;font-size:.68rem;
  font-weight:700;text-align:center;line-height:1.2rem;font-family:var(--mono);
  box-shadow:0 0 10px rgba(255,93,108,.5)}

/* ---- dashboard tiles: the meter bank ------------------------------------ */
.tiles{display:grid;gap:.7rem;
  grid-template-columns:repeat(auto-fit,minmax(148px,1fr));margin-bottom:1rem}
.tile{border:1px solid var(--line);border-radius:var(--radius);
  background:var(--panel);padding:.75rem .85rem .75rem .95rem;display:flex;
  flex-direction:column;gap:.1rem;position:relative;overflow:hidden;
  box-shadow:var(--shadow)}
/* the left rail is the state; the number itself never changes for it */
.tile::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--line2)}
.tile.warn::before{background:var(--warn);box-shadow:0 0 12px var(--warn)}
.tile.bad::before{background:var(--bad);box-shadow:0 0 12px var(--bad)}
.tv{font-size:1.65rem;font-weight:620;line-height:1.15;letter-spacing:-.025em}
.tile.warn .tv{color:var(--warn)}
.tile.bad .tv{color:var(--bad)}
.tl{font-size:.68rem;color:var(--dim);text-transform:uppercase;
  letter-spacing:.09em;font-weight:650}
.ts{font-size:.72rem;color:var(--faint);font-family:var(--mono)}

/* ---- the monitor wall --------------------------------------------------- */
.wall{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fill,minmax(236px,1fr))}
.wtile{border-radius:var(--radius);padding:.7rem .8rem .7rem .9rem;
  border:1px solid var(--line);background:var(--panel);display:flex;
  flex-direction:column;gap:.22rem;position:relative;overflow:hidden;
  box-shadow:var(--shadow)}
.wtile::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--line2)}
.wtile.ok::before{background:var(--ok);box-shadow:0 0 12px var(--ok)}
.wtile.warn::before{background:var(--warn);box-shadow:0 0 12px var(--warn)}
.wtile.crit::before{background:var(--bad);box-shadow:0 0 12px var(--bad)}
.wtile.stale::before{background:var(--faint)}
.wstate{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;
  font-weight:700;color:var(--dim)}
.wtile.ok .wstate{color:var(--ok)}
.wtile.warn .wstate{color:var(--warn)}
.wtile.crit .wstate{color:var(--bad)}
.wtile.stale .wstate{color:var(--faint)}
.wsub{font-size:.78rem;color:var(--dim)}

/* ---- the list/wall segmented control ------------------------------------ */
.seg{display:inline-flex;border:1px solid var(--line2);border-radius:var(--radius-sm);
  overflow:hidden;background:var(--panel2)}
.segb{background:none;border:none;color:var(--dim);font:inherit;font-size:.82rem;
  padding:.35rem .8rem;cursor:pointer;transition:background .12s,color .12s}
.segb.on{background:var(--accent);color:var(--accent-ink);font-weight:620}
.segb:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* ---- the glossary ------------------------------------------------------- */
.term{border-bottom:1px dotted var(--faint);cursor:help}
.term:focus{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
.qwrap{display:inline-block;margin-left:.3rem;vertical-align:middle}
.qwrap .term{border:1px solid var(--line2);border-radius:50%;width:15px;height:15px;
  display:inline-flex;align-items:center;justify-content:center;font-size:.62rem;
  font-weight:700;color:var(--faint);line-height:1;cursor:help}
.qwrap .term:hover{border-color:var(--accent);color:var(--accent)}
.pn .term,.fname .term,.row .term{border:none;display:inline-flex;align-items:center}
.pn .term:hover,.fname .term:hover,.row .term:hover{color:var(--accent)}
.tip{pointer-events:none;position:fixed;z-index:50;max-width:330px;
  padding:.7rem .8rem;background:var(--panel2);border:1px solid var(--line2);
  border-radius:var(--radius-sm);box-shadow:var(--shadow);
  display:flex;flex-direction:column;gap:.3rem;font-size:.83rem;line-height:1.45}
.tip strong{font-size:.9rem}
.tip span{color:var(--dim)}
.tipmore{color:var(--faint);font-size:.78rem;border-top:1px solid var(--line);
  padding-top:.35rem}

/* ---- the tool picker ---------------------------------------------------- */
.picker{margin-top:.6rem;display:grid;gap:1.15rem}
.pgroup h4{margin:0 0 .5rem;font-size:.68rem;text-transform:uppercase;
  letter-spacing:.11em;color:var(--faint);font-weight:700;
  padding-bottom:.32rem;border-bottom:1px solid var(--line)}
.pgroup{display:grid;gap:.4rem;
  grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}
.pgroup h4{grid-column:1/-1}
/* Icon down the left, the three text rows beside it. Named areas rather than
 * a row span, so a card with no price line still puts the glyph in the right
 * place instead of collapsing the column. */
.pcard{display:grid;grid-template-columns:auto minmax(0,1fr);
  grid-template-areas:"i n" "i s" "i m";column-gap:.75rem;row-gap:.18rem;
  text-align:left;padding:.7rem .8rem;
  background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-sm);cursor:pointer;font:inherit;color:var(--ink);
  transition:border-color .12s,background .12s,transform .06s}
.pn{grid-area:n} .ps{grid-area:s} .pm{grid-area:m}
/* The glyph tile. One signal colour, as everywhere else on the panel: the
 * drawing carries the meaning, not a hue per group. */
.picon{grid-area:i;align-self:start;width:34px;height:34px;flex:none;
  display:grid;place-items:center;border-radius:8px;margin-top:.1rem;
  background:var(--accent-soft);border:1px solid var(--accent-line);
  color:var(--accent);transition:background .12s,border-color .12s}
.picon svg{width:19px;height:19px;display:block}
.pcard:hover .picon{background:var(--accent);color:var(--accent-ink);
  border-color:var(--accent)}
/* Not enabled yet, or the machine cannot run it: the glyph goes quiet too,
 * otherwise a card that is off still reads as the brightest thing in the row. */
.pcard.off .picon,.pcard.blocked .picon{background:var(--panel3);
  border-color:var(--line2);color:var(--faint)}
.pcard.blocked:hover .picon{background:var(--panel3);border-color:var(--line2);
  color:var(--faint)}
.pcard:hover{border-color:var(--accent);background:var(--panel2)}
.pcard:active{transform:translateY(1px)}
.pcard:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.pcard.blocked{opacity:.5;cursor:not-allowed}
.pcard.blocked:hover{border-color:var(--line);background:var(--panel)}
.pn{font-weight:620;font-size:.93rem}
/* .ps is the tool's DESCRIPTION - prose, so a UI sans. .pm is its price and
 * state - a measured value, so mono. Getting these the wrong way round set
 * every description in monospace, which read as terminal output. */
.ps{font-size:.82rem;color:var(--dim);line-height:1.4}
.pm{font-size:.73rem;color:var(--faint)}
.fname{display:block;font-weight:620}
.fname b{font-weight:700}

/* ---- results ------------------------------------------------------------ */
.verdict{font-size:1rem;margin:.2rem 0 .5rem}
.verdict.big{font-size:1.3rem;font-weight:650;letter-spacing:-.012em}
.result.good{color:var(--ok)}
.result.bad{color:var(--bad)}
.ok-txt{color:var(--ok)}
.neg{color:var(--bad)}
.err{color:var(--bad);font-size:.88rem}
#run-result{margin-top:1rem}
.quote{margin-top:.8rem;padding:.6rem .75rem;border-radius:var(--radius-sm);
  background:var(--panel2);border:1px solid var(--line);font-size:.85rem}
.quote strong{font-family:var(--mono);font-variant-numeric:tabular-nums}
.quote.warn{border-color:var(--warn);background:var(--warn-soft)}
.note{font-size:.8rem;color:var(--faint);margin-top:.5rem}
.needs{font-size:.8rem;color:var(--warn);margin-top:.4rem}

/* ---- onboarding, empties, warnings -------------------------------------- */
.onboard{border:1px solid var(--accent-line);border-radius:var(--radius);
  padding:1rem 1.1rem;margin-bottom:1.2rem;background:var(--accent-soft)}
.steps{margin:.7rem 0 .4rem;padding-left:1.25rem;display:grid;gap:.75rem;
  font-size:.9rem}
.steps strong{display:block}
.steps .btn{margin-top:.45rem}
.empty{text-align:center;padding:2rem 1rem;color:var(--dim)}
.empty strong{display:block;font-size:1.05rem;color:var(--ink);margin-bottom:.35rem}
.warnbox{border-left:3px solid var(--bad);background:var(--bad-soft);
  padding:.7rem .9rem;border-radius:var(--radius-sm);margin-bottom:1rem;
  font-size:.9rem}
.warnbox a{color:var(--bad)}
.warn{color:var(--warn)}
.upsell{border:1px dashed var(--line2);border-radius:var(--radius);
  padding:1rem;margin-top:1rem}
.upsell h3{margin-top:0}
.big{font-size:1.6rem;font-weight:620;margin:.2rem 0;letter-spacing:-.025em}

details{margin-top:1rem}
details summary{cursor:pointer;color:var(--dim);font-size:.87rem;
  padding:.3rem 0;list-style:none}
details summary::-webkit-details-marker{display:none}
details summary::before{content:"\25B8  ";color:var(--faint)}
details[open] summary::before{content:"\25BE  "}
details summary:hover{color:var(--ink)}
details#run-history{margin-top:1.4rem}
summary{user-select:none}

@media (max-width:520px){
  main{padding:1.1rem .75rem 3.5rem}
  .top{padding:.55rem .75rem}
  .top{padding:.55rem .8rem;gap:.6rem}
  .hero h1{font-size:1.5rem}
  .tv{font-size:1.4rem}
  .row,.row.lead,.row.r3,.row.r4,#event-list .row{grid-template-columns:minmax(0,1fr);gap:.15rem}
  .row .mono{white-space:normal}
  /* The header is three nowrap flex items - brand, tabs, account. On a phone
   * they do not fit, and .who will not shrink, so the tabs were squeezed to
   * their widest word (94px, one word per line, painted beside the account
   * chip) and the DOCUMENT came out 485px wide inside a 390px screen: the
   * whole page scrolled sideways. Measured in a browser at 390px, and it was
   * already true at eight tabs - the ninth only made it one row taller.
   * Let the header wrap: brand and account on the first line, the tabs across
   * the full width below them, every tab still on the screen. */
  .top{flex-wrap:wrap;row-gap:.2rem}
  .who{margin-left:auto;min-width:0}
  /* 6rem, not more: at 390px the brand, the credit chip, the address and the
   * sign-out link have 290px between them, and an address allowed to run to
   * 8.5rem pushed .who onto a line of its own and made the sticky header
   * 183px tall. The full address is on the Account tab. */
  #whoami{display:inline-block;max-width:6rem;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
  #nav{order:3;flex-basis:100%;padding-bottom:.3rem}
  #nav a{white-space:nowrap}
}

/* ---- the glossary: an encyclopaedia, not a card wall -------------------
 * Cards made 137 definitions into 137 boxes to scan. A reference wants an
 * index you can steer by and entries that flow: term, expansion, definition,
 * separated by a hairline and nothing else. */
.glossary{margin-top:1rem}
.ency{display:grid;grid-template-columns:232px minmax(0,1fr);gap:2.2rem;
  align-items:start}
.encynav{position:sticky;top:4.2rem;max-height:calc(100vh - 6rem);
  overflow-y:auto;padding-right:.4rem}
.encyaz{display:flex;flex-wrap:wrap;gap:.1rem;margin-bottom:.9rem;
  padding-bottom:.7rem;border-bottom:1px solid var(--line)}
.encyaz a,.encyaz span{font-family:var(--mono);font-size:.72rem;
  width:1.25rem;height:1.25rem;display:inline-flex;align-items:center;
  justify-content:center;border-radius:3px}
.encyaz a{color:var(--accent);text-decoration:none;background:var(--accent-soft)}
.encyaz a:hover{background:var(--accent);color:var(--accent-ink)}
.encyaz span{color:var(--line2)}
.encynav nav{display:grid;gap:.05rem}
.encynav nav a{display:flex;justify-content:space-between;align-items:baseline;
  gap:.5rem;color:var(--dim);text-decoration:none;font-size:.82rem;
  padding:.3rem .5rem;border-radius:var(--radius-sm);border-left:2px solid transparent}
.encynav nav a:hover{color:var(--ink);background:var(--panel2);
  border-left-color:var(--accent)}
.encynav nav a span{font-family:var(--mono);font-size:.7rem;color:var(--faint)}

.encybody{min-width:0}
.gcat{margin-bottom:2.2rem}
.gcat h3{font-size:.72rem;text-transform:uppercase;letter-spacing:.11em;
  color:var(--accent);font-weight:700;margin:0 0 .15rem;scroll-margin-top:4.5rem}
.gcat>.hint{margin:.3rem 0 .9rem}
/* The entry itself: a definition list, hanging term, hairline between. */
.entries{margin:0;display:grid;grid-template-columns:minmax(150px,210px) minmax(0,1fr);
  gap:0 1.4rem}
.entries dt{grid-column:1;font-weight:650;font-size:.92rem;
  padding:.7rem 0 .7rem;border-top:1px solid var(--line);
  scroll-margin-top:4.6rem;display:flex;flex-direction:column;gap:.12rem}
.entries dd{grid-column:2;margin:0;padding:.7rem 0;
  border-top:1px solid var(--line)}
.entries dt:first-of-type,.entries dd:first-of-type{border-top:none;padding-top:.2rem}
.galso{font-size:.72rem;font-weight:400;color:var(--faint);
  font-family:var(--mono);line-height:1.35}
.entries dd p{margin:0;font-size:.88rem;color:var(--dim);line-height:1.55;
  max-width:78ch}
.gmore{margin-top:.5rem!important;color:var(--faint)!important;
  font-size:.81rem!important;border-left:2px solid var(--line);
  padding-left:.7rem}
#gl-count{margin:.5rem 0 0}

@media (max-width:1000px){
  .ency{grid-template-columns:minmax(0,1fr);gap:1rem}
  .encynav{position:static;max-height:none;
    border-bottom:1px solid var(--line);padding-bottom:.8rem}
  .encynav nav{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
}
@media (max-width:640px){
  .entries{grid-template-columns:minmax(0,1fr);gap:0}
  .entries dt{padding-bottom:.15rem}
  .entries dd{grid-column:1;border-top:none;padding-top:0;padding-bottom:.8rem}
  .entries dt:first-of-type{padding-top:.2rem}
}

/* ---- the calculators: a list on the left, one calculator on the right ----
 * Eighteen of them. A grid of eighteen cards is a wall; a list you steer by
 * and one answer at a time is a tool. Every card still COMPUTES on load - only
 * its visibility is switched - so nothing is ever a blank form. */
.calcs{display:grid;grid-template-columns:258px minmax(0,1fr);gap:1.4rem;
  align-items:start}

/* the picker */
.calcnav{position:sticky;top:4.2rem;max-height:calc(100vh - 6rem);
  overflow-y:auto;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:.85rem .8rem;box-shadow:var(--shadow)}
.calcnav label{margin:0 0 .1rem;font-size:.65rem}
.calcnav input{margin-top:.25rem;font-size:.85rem;padding:.4rem .55rem}
.calcnav nav{display:grid;gap:.05rem;margin-top:.8rem}
.calcnav nav h4{margin:.85rem 0 .25rem;font-size:.62rem;text-transform:uppercase;
  letter-spacing:.11em;color:var(--faint);font-weight:700;
  padding-bottom:.25rem;border-bottom:1px solid var(--line)}
.calcnav nav h4:first-child{margin-top:.2rem}
.calcnav nav a{display:block;color:var(--dim);text-decoration:none;
  font-size:.83rem;padding:.34rem .5rem;border-radius:var(--radius-sm);
  border-left:2px solid transparent;transition:background .12s,color .12s}
.calcnav nav a:hover{color:var(--ink);background:var(--panel2)}
.calcnav nav a.on{color:var(--accent);background:var(--accent-soft);
  border-left-color:var(--accent);font-weight:620}

/* the calculator itself */
.calcpane{min-width:0}
.calcpane .card{padding:1.2rem 1.35rem 1.35rem}
.calcpane .card h3{font-size:1.2rem;margin-bottom:.15rem}
.calcpane .card>.hint{margin:0 0 1rem;font-size:.88rem}
/* Fields in their own block, two-up, so the form reads as the question and the
 * output below reads as the answer. */
.calcfields{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
  gap:0 1.1rem}
.calcpane .card label{margin:.35rem 0 .55rem;font-size:.68rem}
.calcpane .card label input,.calcpane .card label select{font-size:.92rem}
/* The answer gets its own surface: a calculator is a question and an answer,
 * and they should not look like one continuous form. */
.calcpane .card>output{margin-top:1.1rem;padding:.95rem 1.05rem;
  border-top:none;background:var(--panel2);border:1px solid var(--line);
  border-left:3px solid var(--accent);border-radius:var(--radius-sm)}
.calcpane .card>output strong{font-size:1.85rem;letter-spacing:-.03em;
  color:var(--ink)}
.calcpane .card>output .sub{margin-top:.25rem}
.calcpane .card>output .rows{margin-top:.6rem}
#view-tools .hero{margin-bottom:1.1rem}

@media (max-width:900px){
  .calcs{grid-template-columns:minmax(0,1fr);gap:1rem}
  .calcnav{position:static;max-height:none}
  .calcnav nav{grid-template-columns:repeat(auto-fill,minmax(170px,1fr))}
  .calcnav nav h4{grid-column:1/-1}
}

/* ---- calculator visualisations ------------------------------------------
 * A number says what; a picture says what SHAPE. Only where the shape is the
 * point - a 4:3 frame inside 16:9, a GOP landing mid-segment, the headroom
 * above white that narrow range exists for. */
.viz{display:block;margin-top:.8rem;max-width:100%;
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg)}
.vizcap{display:block;margin-top:.3rem;font-size:.74rem;color:var(--faint)}
.vizkey{display:inline-block;width:.6rem;height:.6rem;border-radius:2px;
  margin-right:.3rem;vertical-align:baseline}

/* A proportional bar, in flex rather than SVG - see vizBar(). */
.vizbar{display:flex;margin-top:.8rem;height:30px;border-radius:var(--radius-sm);
  overflow:hidden;border:1px solid var(--line);background:var(--bg)}
.vizseg{display:flex;align-items:center;justify-content:center;min-width:2px;
  font-family:var(--mono);font-size:.68rem;color:#06121a;font-weight:700;
  white-space:nowrap;overflow:hidden}
.vizsep{display:inline-block;width:.85rem}

/* ---- "How it fits together": the arrangements ---------------------------
 * Chains in HTML, not SVG: they wrap on a phone, read in order to a screen
 * reader, and scale with the page text. */
.setup{margin:0 0 2rem;padding-bottom:1.4rem;border-bottom:1px solid var(--line)}
.setup:last-child{border-bottom:none}
.setup h4{margin:0 0 .3rem;font-size:1.02rem;font-weight:650}
.setupblurb{margin:0 0 .9rem;font-size:.88rem;color:var(--dim);
  line-height:1.55;max-width:80ch}
.setupextra{margin:.9rem 0 .4rem;font-size:.75rem;color:var(--faint);
  text-transform:uppercase;letter-spacing:.08em;font-weight:650}
.chain{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;
  gap:.5rem;align-items:stretch}
.stage{position:relative;flex:1 1 160px;min-width:150px;max-width:260px;
  background:var(--panel2);border:1px solid var(--line2);
  border-radius:var(--radius-sm);padding:.55rem .65rem;
  display:flex;flex-direction:column;gap:.15rem}
/* The arrow between stages. Drawn on the box, so wrapping cannot orphan one. */
.stage::after{content:"";position:absolute;right:-.5rem;top:50%;
  width:.5rem;height:1px;background:var(--line2)}
.stage:last-child::after{display:none}
.stn{font-size:.83rem;font-weight:620;color:var(--ink)}
.sts{font-size:.73rem;color:var(--faint);line-height:1.4}
.watch{margin-top:1rem;padding:.75rem .9rem;background:var(--warn-soft);
  border-left:3px solid var(--warn);border-radius:var(--radius-sm)}
.watch h5{margin:0 0 .4rem;font-size:.68rem;text-transform:uppercase;
  letter-spacing:.09em;color:var(--warn);font-weight:700}
.watch ul{margin:0;padding-left:1.1rem;display:grid;gap:.4rem}
.watch li{font-size:.84rem;color:var(--dim);line-height:1.5}
@media (max-width:640px){
  .stage{flex:1 1 100%;max-width:none}
  .stage::after{display:none}
}

/* ---- orientation strip on the landing page ------------------------------
 * What you can use right now, what an account buys, and what each is FOR -
 * at the top, where somebody arriving actually is. It used to be one
 * paragraph at the BOTTOM of the calculators, which nobody scrolls to. */
.orient{display:grid;gap:.8rem;margin-bottom:1.6rem;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.ocard{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);padding:.95rem 1.05rem;box-shadow:var(--shadow);
  display:flex;flex-direction:column;gap:.35rem}
.ocard.ofree{border-top:2px solid var(--ok)}
.ocard.opaid{border-top:2px solid var(--accent);background:var(--panel2)}
.obadge{font-size:.62rem;text-transform:uppercase;letter-spacing:.09em;
  font-weight:700;color:var(--faint)}
.ocard.ofree .obadge{color:var(--ok)}
.ocard.opaid .obadge{color:var(--accent)}
.ocard h3{margin:0;font-size:1.1rem}
.ocard p{margin:0;font-size:.86rem;color:var(--dim);line-height:1.5}
.ocard .ohint{font-size:.79rem;color:var(--faint)}
.ocard .btn{margin-top:auto;align-self:flex-start}
.secttl{font-size:1.15rem;margin:0 0 .15rem;font-weight:650}
.secsub{margin:0 0 .9rem}

/* ---- the front door -----------------------------------------------------
 * Three doors, and two of them need nothing. Big enough to be the whole
 * screen's business, because on arrival that IS the whole business: which of
 * these three is the thing you came for. */
.doors{display:grid;gap:1rem;
  grid-template-columns:repeat(auto-fit,minmax(310px,1fr));align-items:stretch}
.door{display:flex;flex-direction:column;gap:.45rem;text-decoration:none;
  background:var(--panel);border:1px solid var(--line);border-top:2px solid var(--ok);
  border-radius:var(--radius);padding:1.2rem 1.25rem 1.1rem;box-shadow:var(--shadow);
  color:var(--ink);transition:border-color .12s,transform .08s,background .12s}
.door:hover{border-color:var(--accent);background:var(--panel2);
  transform:translateY(-2px)}
.door:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.door.doorpaid{border-top-color:var(--accent);background:var(--panel2)}
.door.doorpaid:hover{background:var(--panel3)}
.door h2{margin:.1rem 0 .1rem;font-size:1.35rem;letter-spacing:-.02em}
.door p{margin:0;font-size:.88rem;color:var(--dim);line-height:1.55}
.doorgo{margin-top:auto;padding-top:.7rem;color:var(--accent);font-weight:650;
  font-size:.88rem}
.door .ohint{font-size:.78rem;color:var(--faint)}

/* ---- the public tool catalogue ------------------------------------------ */
.catalogue{margin-top:1rem}
.catgroup{margin-bottom:1.6rem}
.catgroup h3{font-size:.7rem;text-transform:uppercase;letter-spacing:.11em;
  color:var(--accent);font-weight:700;margin:0 0 .6rem;
  padding-bottom:.3rem;border-bottom:1px solid var(--line)}
.catlist{display:grid;gap:.6rem;
  grid-template-columns:repeat(auto-fill,minmax(310px,1fr));align-items:start}
.cattool{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:.75rem .85rem}
.cattool h4{margin:0 0 .3rem;font-size:.95rem;font-weight:650}
.cattool p{margin:0;font-size:.85rem;color:var(--dim);line-height:1.5}
.catmeta{margin-top:.5rem!important;display:flex;flex-wrap:wrap;gap:.45rem;
  font-family:var(--mono);font-size:.71rem}
.catcost{color:var(--accent);background:var(--accent-soft);
  border:1px solid var(--accent-line);padding:.1rem .4rem;border-radius:999px}
.catkind,.catneeds{color:var(--faint);border:1px solid var(--line2);
  padding:.1rem .4rem;border-radius:999px}
.catcta{margin-top:1.4rem;padding:1rem 1.15rem;background:var(--panel2);
  border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:var(--radius)}
.catcta p{margin:0 0 .3rem;font-size:.88rem;color:var(--dim);max-width:70ch}

/* ---- what it costs ------------------------------------------------------
 * On the front door, below the three ways in. Everything here is a behaviour
 * that is implemented and tested; the one number nobody has decided is absent
 * on purpose and the page says so, which is the only honest way to have a
 * pricing section before there is a price. */
.pricing{margin-top:2rem;padding-top:1.6rem;border-top:1px solid var(--line)}
.pricing h2{margin:0 0 .3rem;font-size:1.25rem;letter-spacing:-.02em}
.pricelede{margin:0 0 1.1rem;font-size:.95rem;color:var(--dim);max-width:78ch;
  line-height:1.55}
.pricegrid{display:grid;gap:.75rem;
  grid-template-columns:repeat(auto-fit,minmax(272px,1fr))}
.priceitem{background:var(--panel);border:1px solid var(--line);
  border-left:2px solid var(--ok);border-radius:var(--radius-sm);
  padding:.8rem .9rem}
.priceitem h3{margin:0 0 .25rem;font-size:.92rem;font-weight:650}
.priceitem p{margin:0;font-size:.84rem;color:var(--dim);line-height:1.5}
.pricenote{margin-top:1rem;padding:.8rem .95rem;background:var(--warn-soft);
  border-left:3px solid var(--warn);border-radius:var(--radius-sm);
  font-size:.86rem;color:var(--dim);line-height:1.55;max-width:80ch}
.pricenote strong{color:var(--ink)}

/* ---- quick search -------------------------------------------------------
 * One box over calculators, tools, terms and setups. Its four indexes are
 * derived from what the product actually has, so nothing can be added to the
 * product and left unsearchable. */
.quick-search{position:relative;margin:0 0 1.6rem;max-width:760px}
.quick-search label{margin:0 0 .2rem}
.quick-search input{font-size:1rem;padding:.65rem .8rem}
.qhint{margin:.35rem 0 0}
.qresults{position:absolute;left:0;right:0;top:100%;margin-top:.3rem;z-index:30;
  background:var(--panel);border:1px solid var(--line2);
  border-radius:var(--radius-sm);box-shadow:var(--shadow);
  max-height:22rem;overflow-y:auto;padding:.25rem}
.qhit{display:grid;grid-template-columns:5.2rem minmax(0,1fr) auto;gap:.6rem;
  align-items:baseline;width:100%;text-align:left;background:none;border:none;
  color:var(--ink);font:inherit;padding:.45rem .55rem;cursor:pointer;
  border-radius:var(--radius-sm)}
.qhit:hover,.qhit.on{background:var(--panel3)}
.qhit.on{outline:1px solid var(--accent)}
.qkind{font-family:var(--mono);font-size:.66rem;text-transform:uppercase;
  letter-spacing:.06em;color:var(--accent);background:var(--accent-soft);
  border-radius:999px;padding:.1rem .35rem;text-align:center}
.qlabel{font-size:.9rem;font-weight:600}
.qsub{font-size:.74rem;color:var(--faint);font-family:var(--mono);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:16rem}
.qempty{margin:0;padding:.6rem .7rem;font-size:.86rem;color:var(--dim)}

/* ---- the IP stream multiviewer ------------------------------------------
 * The only monitor that can show a picture, and only when the operator asked
 * for it. The note under the wall says which of the two it is, every time:
 * "state only" and "stills are being sent through BE HUB" are different
 * promises and a wall must never leave you guessing which one you are under. */
.wtile.wide{grid-column:1/-1}
.mvwall{display:grid;gap:.5rem;margin-top:.6rem;
  grid-template-columns:repeat(auto-fill,minmax(190px,1fr))}
.mvtile{position:relative;border:1px solid var(--line2);border-radius:6px;
  overflow:hidden;background:var(--bg);display:flex;flex-direction:column}
.mvtile.ok{border-color:rgba(58,214,160,.45)}
.mvtile.crit{border-color:rgba(255,93,108,.5)}
.mvpic{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;
  background:#05070a}
.mvnopic{display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:.68rem;color:var(--faint);text-align:center;
  padding:.3rem}
.mvname{font-size:.68rem;color:var(--dim);padding:.3rem .4rem 0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mvstat{display:flex;align-items:center;gap:.3rem;padding:.1rem .4rem .35rem;
  font-size:.7rem;font-family:var(--mono);color:var(--dim)}
.mvdot{width:6px;height:6px;border-radius:50%;background:var(--faint);flex:none}
.mvtile.ok .mvdot{background:var(--ok);box-shadow:0 0 6px var(--ok)}
.mvtile.crit .mvdot{background:var(--bad);box-shadow:0 0 6px var(--bad)}
.mvtile.crit .mvstat{color:var(--bad)}
.mvtile.warn{border-color:rgba(245,181,63,.5)}
.mvtile.warn .mvdot{background:var(--warn)}
/* BLACK / FROZEN sits ON the picture: a still that shows the black is the
   evidence, and the word is what somebody reads from across the room. */
.mvbadge{position:absolute;top:.35rem;left:.35rem;padding:.05rem .35rem;
  border-radius:4px;background:var(--bad);color:#fff;font-family:var(--mono);
  font-size:.68rem;font-weight:600;letter-spacing:.02em}
.mvnote{margin:.55rem 0 0;font-size:.73rem;color:var(--faint)}
.mvnote.mvmedia{color:var(--warn)}
