:root {
  --paper:#f6f7f4; --surface:#fff; --surface-2:#eef0eb; --ink:#131c1a;
  --ink-soft:#4b5a56; --ink-faint:#7e8c88; --rule:#d6dbd4; --rule-soft:#e5e9e2;
  --copper:#a85b33; --teal:#14524b; --good:#2e6a4c; --warn:#8a6410; --crit:#9b3a31;
  --crit-bg:#f8e9e7; --good-bg:#e8f2ec; --warn-bg:#f8f1de;
  --mono: ui-monospace, "SF Mono", "IBM Plex Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root {
    --paper:#101917; --surface:#17221f; --surface-2:#1e2a27; --ink:#e6ebe7;
    --ink-soft:#a6b3ae; --ink-faint:#7a8783; --rule:#2c3936; --rule-soft:#232f2c;
    --copper:#e0996b; --teal:#6fbdb0; --good:#6fbb90; --warn:#d6a94a; --crit:#e58c81;
    --crit-bg:#2c1a18; --good-bg:#16261e; --warn-bg:#2a2213;
    color-scheme: dark;
  }
}
*{box-sizing:border-box}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--sans);
     font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased}
a{color:var(--copper);text-underline-offset:2px}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
textarea:focus-visible{outline:2px solid var(--copper);outline-offset:2px}

/* header */
header.top{background:var(--surface);border-bottom:1px solid var(--rule);
  position:sticky;top:0;z-index:20}
.topinner{max-width:1400px;margin:0 auto;padding:0 20px;display:flex;
  align-items:center;gap:26px;min-height:54px;flex-wrap:wrap}
.brand{font-weight:700;letter-spacing:-.01em;font-size:15px;text-decoration:none;
  color:var(--ink);display:flex;align-items:center;gap:8px}
.brand .dot{width:9px;height:9px;border-radius:50%;background:var(--copper);flex:none}
/* Wraps. Without this the row of links is 589px wide whatever the window is,
   so every page in the portal was wider than a phone — and once the document
   is wider than the window, everything inside it measures wide too, which is
   what made the fault look like nine separate layout problems. */
nav.main{display:flex;gap:20px;flex:1;flex-wrap:wrap}
nav.main a{font-size:13.5px;text-decoration:none;color:var(--ink-soft);
  padding:16px 0;border-bottom:2px solid transparent}
nav.main a.on{color:var(--ink);border-bottom-color:var(--copper);font-weight:600}
nav.main a:hover{color:var(--ink)}
.whoami{font-family:var(--mono);font-size:11.5px;color:var(--ink-faint)}

main{max-width:1400px;margin:0 auto;padding:26px 20px 80px}
h1{font-size:1.55rem;letter-spacing:-.01em;margin:0 0 4px}
h2{font-size:1.1rem;margin:32px 0 10px}
h3{font-size:.95rem;margin:22px 0 8px}
.sub{color:var(--ink-soft);margin:0 0 22px;font-size:14px}
.muted{color:var(--ink-faint)}
.mono{font-family:var(--mono);font-size:.9em}
.right{text-align:right}
.nowrap{white-space:nowrap}

/* banner */
.banner{border:1px solid var(--rule);border-left:3px solid var(--copper);
  background:var(--surface);padding:12px 16px;margin:0 0 20px;font-size:14px}
.banner.warn{border-left-color:var(--warn);background:var(--warn-bg)}
.banner.crit{border-left-color:var(--crit);background:var(--crit-bg)}
.banner.good{border-left-color:var(--good);background:var(--good-bg)}
.banner strong{font-weight:600}

/* cards */
.cards{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:0 0 24px}
.card{background:var(--surface);padding:14px 16px}
.card .k{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-faint);margin-bottom:5px}
.card .v{font-size:1.7rem;font-weight:700;line-height:1.1;font-variant-numeric:tabular-nums}
.card .n{font-size:12.5px;color:var(--ink-soft);margin-top:3px}
.card .v.crit{color:var(--crit)} .card .v.good{color:var(--good)}
.card .v.warn{color:var(--warn)}
/* A card that is a link behaves like one: the whole tile is the hit target,
   not a five-word phrase underneath it. The old dashboard put the link on the
   caption, which on a phone is a target the width of "emails to approve". */
a.card{text-decoration:none;color:inherit;display:block}
a.card:hover{background:var(--surface-2)}

/* ------------------------------------------------------------- the board
   The sales pipeline, one column per stage, in the order a deal moves.
   Scrolls sideways rather than wrapping: the order is the meaning, and a
   board that reflows into two rows on a laptop reads as two pipelines. */
.board{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(150px,1fr);
  gap:1px;background:var(--rule);border:1px solid var(--rule);
  overflow-x:auto;margin:0 0 6px}
.boardcol{background:var(--surface);padding:12px 14px;text-decoration:none;
  color:inherit;display:block;min-width:150px}
.boardcol:hover{background:var(--surface-2)}
.boardcol .k{font-family:var(--mono);font-size:10px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-faint);margin-bottom:5px}
.boardcol .v{font-size:1.5rem;font-weight:700;line-height:1.1;
  font-variant-numeric:tabular-nums}
.boardcol .num{font-size:12px;color:var(--ink-soft);margin-top:2px}
.boardcol .why{font-size:11.5px;color:var(--ink-faint);margin-top:8px;
  line-height:1.35}

/* Two columns on a wide screen, one on a narrow one. Tasks and the activity
   feed are both short and neither deserves the full width. */
.split{display:grid;gap:26px;margin-top:26px;
  grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}

/* ------------------------------------------------------- the lead form
   A form that asks one question properly rather than twelve badly. The legal
   form options are radio cards with their consequence written next to them,
   because "sole trader" and "may not be emailed" are the same fact and only
   one of them is obvious. */
.leadform .row{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:4px}
.leadform h2{margin:22px 0 8px;font-size:15px}
.forms{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.formopt{background:var(--surface);padding:10px 14px;display:flex;gap:10px;
  align-items:flex-start;font-size:13px;cursor:pointer;line-height:1.4}
.formopt:hover{background:var(--surface-2)}
.formopt input{margin-top:3px;flex:none}
.formopt .muted{font-size:12px}

.feed{list-style:none;padding:0;margin:0;border:1px solid var(--rule)}
.feed li{padding:9px 14px;border-bottom:1px solid var(--rule-soft);
  font-size:13px;line-height:1.5}
.feed li:last-child{border-bottom:0}

/* tables */
.tablewrap{overflow-x:auto;border:1px solid var(--rule);background:var(--surface);
  margin:0 0 20px}
table{border-collapse:collapse;width:100%;font-size:13.5px}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--rule-soft);
  vertical-align:top}
thead th{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-faint);background:var(--surface-2);
  border-bottom:1px solid var(--rule);white-space:nowrap}

/* A table with no wrapper still has to fit on a phone.
   `.tablewrap` is the usual answer — a bordered box that scrolls sideways —
   but a couple of small tables sit inside other furniture where that border
   would be wrong. Below 720px they become their own scroll box instead, which
   costs nothing on a desktop and stops one narrow table dragging the whole
   page wider than the window. */
@media (max-width:720px){
  .tablewrap{max-width:100%}
  table{display:block;overflow-x:auto;max-width:100%}
}

/* Column headings do not follow the page.
   They used to, and it produced the same visible fault twice: a heading row
   painted through the middle of the data. The cause was not the offset — it
   was `.tablewrap`, which has `overflow-x:auto` so that a wide table can be
   scrolled sideways on a laptop. That makes it a scroll container, and
   `position:sticky` inside one pins to *its* box rather than to the window:
   the heading sat a fixed distance down from the top of the table, leaving a
   blank strip above it and printing over the first rows.

   There is no arrangement of overflow that gives both a sideways-scrolling
   wrapper and a heading that follows the window, so the heading stays where
   it belongs — at the top of its table. `class="stick"` is left on the
   templates that carry it; it simply no longer does anything, which is
   better than a page that looks broken. */
tbody tr:hover{background:var(--surface-2)}
tbody tr:last-child td{border-bottom:0}
td.num,th.num{font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap}

/* pills */
.pill{display:inline-block;font-family:var(--mono);font-size:10px;letter-spacing:.05em;
  text-transform:uppercase;padding:1px 6px;border:1px solid currentColor;border-radius:2px;
  white-space:nowrap}
.pill.good{color:var(--good)} .pill.warn{color:var(--warn)}
.pill.crit{color:var(--crit)} .pill.info{color:var(--teal)}
.pill.off{color:var(--ink-faint)}

/* score bar */
.score{display:flex;align-items:center;gap:7px;font-family:var(--mono);
  font-variant-numeric:tabular-nums}
.score .bar{width:44px;height:5px;background:var(--rule);position:relative}
.score .bar i{position:absolute;inset:0 auto 0 0;background:var(--copper);display:block}

/* forms */
form.filters{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin:0 0 16px}
label.field{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--ink-soft)}
label.field>span{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--ink-faint)}
input[type=text],input[type=email],input[type=number],input[type=password],
select,textarea{
  font:inherit;font-size:14px;padding:7px 9px;border:1px solid var(--rule);
  background:var(--surface);color:var(--ink);border-radius:2px;min-width:0;width:100%}
textarea{font-family:var(--mono);font-size:13px;line-height:1.55;resize:vertical}
button,.btn{font:inherit;font-size:13.5px;padding:7px 14px;border:1px solid var(--rule);
  background:var(--surface);color:var(--ink);border-radius:2px;cursor:pointer;
  text-decoration:none;display:inline-block}
button:hover,.btn:hover{background:var(--surface-2)}
button.primary,.btn.primary{background:var(--copper);border-color:var(--copper);color:#fff}
button.primary:hover,.btn.primary:hover{filter:brightness(1.08)}
button.danger{color:var(--crit);border-color:var(--crit)}
.inline{display:inline}

/* saved views */
.views{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0 0 14px}
.view{display:inline-flex;align-items:center;gap:6px;background:var(--surface);
  border:1px solid var(--rule);border-radius:14px;padding:3px 6px 3px 12px;
  font-size:13px;max-width:100%}
.view.mine{border-color:var(--teal)}
.view a{text-decoration:none;color:var(--ink);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:240px}
.view a:hover{text-decoration:underline}
.view .muted{font-size:11px;font-family:var(--mono)}
.view button.x{border:0;background:none;padding:0 4px;line-height:1;
  font-size:15px;color:var(--ink-faint);cursor:pointer}
.view button.x:hover{color:var(--crit);background:none}
.saveview{display:inline-flex;align-items:center;gap:6px}
.saveview input[type=text]{width:200px;font-size:13px;padding:5px 8px}
.saveview .tick{display:inline-flex;align-items:center;gap:4px;font-size:12px;
  color:var(--ink-soft);white-space:nowrap}
.saveview .tick input{width:auto}
.saveview button{padding:5px 12px;font-size:13px}

/* free space, on the page where the jobs are */
.disk{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin:0 0 16px;
  font-size:13px}
.disk .vol{padding:5px 12px;border:1px solid var(--rule);border-radius:14px;
  background:var(--surface)}
.disk .vol.low{border-color:var(--crit);background:var(--crit-bg);
  color:var(--crit)}

/* charts drawn by the server
   Two series only, and they are the two this page is about — won and lost —
   so the colours are named after the fact rather than taken from a ramp. The
   legend is always present because identity must never be colour alone. */
.chartrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
  gap:18px;margin:0 0 18px}
figure.chart{margin:0;background:var(--surface);border:1px solid var(--rule);
  padding:12px 14px}
figure.chart figcaption{font-family:var(--mono);font-size:10.5px;
  letter-spacing:.07em;text-transform:uppercase;color:var(--ink-faint);
  display:flex;gap:12px;align-items:center;margin-bottom:6px}
figure.chart svg{width:100%;height:auto;display:block}
.chart .key{display:inline-flex;gap:10px;align-items:center;
  text-transform:none;letter-spacing:0;font-size:11px;color:var(--ink-soft)}
.chart .key i{width:9px;height:9px;display:inline-block;margin-right:3px;
  border-radius:1px;vertical-align:-1px}
.chart .key i.s1,.chart rect.s1{background:var(--teal);fill:var(--teal)}
.chart .key i.s2,.chart rect.s2{background:var(--copper);fill:var(--copper)}
.chart .grid{stroke:var(--rule-soft);stroke-width:1}
.chart .axis{stroke:var(--rule);stroke-width:1}
.chart .tick{font-size:9.5px;fill:var(--ink-faint);font-family:var(--mono)}

/* how far deals get */
.funnel{display:flex;flex-direction:column;gap:6px;margin:0 0 24px;
  background:var(--surface);border:1px solid var(--rule);padding:14px 16px}
.funnel .step{display:grid;grid-template-columns:150px 1fr 60px;gap:12px;
  align-items:center;font-size:13.5px}
.funnel .track{height:14px;background:var(--surface-2);position:relative}
.funnel .track i{position:absolute;inset:0 auto 0 0;background:var(--teal);
  display:block;border-radius:0 2px 2px 0}
.funnel .n{text-align:right;font-family:var(--mono);font-size:13px}

/* tabs on one record */
/* `wrap`, because Settings has sixteen of these and a row that cannot
   wrap widens the document instead — which takes every page on a phone
   with it, exactly as `nav.main` did. */
nav.tabs{display:flex;flex-wrap:wrap;gap:4px;margin:0 0 16px;
  border-bottom:1px solid var(--rule)}
nav.tabs button{border:1px solid transparent;border-bottom:0;background:none;
  padding:8px 16px;font-size:14px;border-radius:2px 2px 0 0;color:var(--ink-soft);
  margin-bottom:-1px}
nav.tabs button:hover{background:var(--surface-2)}
nav.tabs button.on{background:var(--surface);border-color:var(--rule);
  color:var(--ink);font-weight:600;
  box-shadow:inset 0 2px 0 var(--copper)}

/* when to come back */
fieldset.followup{border:1px solid var(--rule);background:var(--surface-2);
  padding:10px 14px 12px;margin:2px 0 0}
fieldset.followup legend{font-family:var(--mono);font-size:10.5px;
  letter-spacing:.07em;text-transform:uppercase;color:var(--ink-faint);
  padding:0 6px}
.whens{display:flex;gap:6px;flex-wrap:wrap}
.when{position:relative}
.when input{position:absolute;opacity:0;pointer-events:none}
.when span{display:inline-block;padding:5px 11px;font-size:13px;
  border:1px solid var(--rule);border-radius:14px;background:var(--surface);
  cursor:pointer;user-select:none}
.when input:checked + span{background:var(--copper);border-color:var(--copper);
  color:#fff}
.when input:focus-visible + span{outline:2px solid var(--teal);outline-offset:2px}

/* a row that will not be acted on — the import dry run */
tbody tr.off td{opacity:.55}

/* per-business identity */
.idents{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:14px;margin:0 0 18px}
.ident{border:1px solid var(--rule);display:flex;flex-direction:column;gap:10px}
.ident.warn{border-left:3px solid var(--warn)}
.identhead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding-bottom:6px;border-bottom:1px solid var(--rule-soft)}
.identhead .mono{font-size:11px}

/* bulk actions */
.bulkbar{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;
  background:var(--surface);border:1px solid var(--rule);padding:10px 14px;
  margin:0 0 14px;opacity:.6}
.bulkbar.on{opacity:1;border-color:var(--copper);background:var(--surface-2)}
.bulkbar .count{font-size:13px;align-self:center;white-space:nowrap}
.bulkbar .grow{flex:1}
.bulkbar select,.bulkbar input[type=text]{font-size:13px;padding:5px 8px}
th.pick,td.pick{width:28px;padding-left:10px;padding-right:0}
td.pick input,th.pick input{width:auto;cursor:pointer}

/* settings */
.setgrid{display:grid;grid-template-columns:minmax(220px,320px) minmax(0,1fr);
  gap:0;border:1px solid var(--rule);background:var(--surface);margin:0 0 26px}
.setrow{display:contents}
.setrow .lab{padding:14px 16px;border-bottom:1px solid var(--rule-soft)}
.setrow .val{padding:14px 16px;border-bottom:1px solid var(--rule-soft);
  border-left:1px solid var(--rule-soft)}
.setrow:last-child .lab,.setrow:last-child .val{border-bottom:0}
.setrow .lab b{display:block;font-size:14px;font-weight:600;margin-bottom:3px}
.setrow .lab .help{font-size:12.5px;color:var(--ink-soft);line-height:1.45}
.setrow .lab .key{font-family:var(--mono);font-size:10.5px;color:var(--ink-faint);
  margin-top:5px;display:block}
.setrow.locked .val{background:var(--surface-2)}
@media (max-width:720px){.setgrid{grid-template-columns:minmax(0,1fr)}
  .setrow .val{border-left:0}}
.sticky-save{position:sticky;bottom:0;background:var(--surface);
  border-top:1px solid var(--rule);padding:12px 16px;display:flex;gap:12px;
  align-items:center;margin-top:-1px;z-index:10}

/* thread */
.msg{border:1px solid var(--rule);background:var(--surface);margin:0 0 12px}
.msg .head{display:flex;gap:12px;flex-wrap:wrap;align-items:center;
  padding:9px 14px;border-bottom:1px solid var(--rule-soft);background:var(--surface-2);
  font-size:12.5px}
.msg .head .who{font-weight:600}
.msg .head .when{font-family:var(--mono);font-size:11px;color:var(--ink-faint);
  margin-left:auto}
.msg .body{padding:12px 14px;white-space:pre-wrap;font-family:var(--mono);
  font-size:12.5px;line-height:1.6;max-height:340px;overflow:auto}
.msg.inbound{border-left:3px solid var(--teal)}
.msg.outbound{border-left:3px solid var(--copper)}

.cols2{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:26px}
/* `minmax(0,1fr)`, not `1fr`. A bare `1fr` is `minmax(auto,1fr)`, and an auto
   minimum refuses to go below the content's own minimum width — so on a phone
   the column stayed as wide as its widest table and took the page with it. */
@media (max-width:980px){.cols2{grid-template-columns:minmax(0,1fr)}}
/* When a tab leaves only one column with anything in it, that column takes
   the width. Two tracks with one of them empty is what put the Finance tab in
   a narrow strip down the right-hand side. */
.cols2.one{grid-template-columns:minmax(0,1fr)}

/* `hidden` has to win.
   The attribute's style comes from the browser's own sheet, which any inline
   `style="display:flex"` beats — and several blocks on the company record
   carry exactly that. The tab script hid them and they stayed on screen,
   under the wrong tab. */
[hidden]{display:none !important}
dl.kv{display:grid;grid-template-columns:auto 1fr;gap:5px 14px;margin:0;font-size:13.5px}
dl.kv dt{font-family:var(--mono);font-size:11px;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-faint);padding-top:2px}
dl.kv dd{margin:0}
.pager{display:flex;gap:14px;align-items:center;font-size:13px;flex-wrap:wrap;
  padding:10px 0 4px}
.pager .pagenav{display:flex;gap:4px;align-items:center}
.pager .btn{padding:5px 10px;min-width:34px;text-align:center}
.pager .btn.off{opacity:.35;pointer-events:none}
.pager form.jump{display:flex;gap:7px;align-items:center}
.pager input[type=number],.pager select{padding:5px 7px;font-size:13px;width:auto}
.empty{padding:34px 16px;text-align:center;color:var(--ink-faint);
  border:1px dashed var(--rule);background:var(--surface)}

/* processing page — the job log tail, and a running row that catches the eye */
pre.mono{font-family:var(--mono);border:1px solid var(--rule);border-radius:2px}
.banner.info{border-left-color:var(--teal);background:var(--surface-2)}

/* Secondary detail inside a table cell — a reason, a confidence, a caveat.
   Small enough to skim past, large enough to read when it matters. */
.tiny{font-size:11.5px;line-height:1.45}

/* ---------------------------------------------------------- the pipeline
   One block per job: its name, its numbers, its state and its button, in
   pipeline order. This replaced three separate lists that named the same
   work three different ways — a stat card, a job row and a run row — and
   left the reader holding a private translation table.

   The numbers under each job are legs of a partition, colour-coded by what
   kind of number each is rather than by whether it is large: done reads as
   progress, left as work, excluded as a decision somebody made. A big number
   in the excluded column is not a fault, and it must not look like one. */
.stages{display:flex;flex-direction:column;gap:1px;background:var(--rule);
  border:1px solid var(--rule);margin:0 0 22px}
.stage{background:var(--surface);padding:13px 16px 14px}
.stage.running{border-left:3px solid var(--good);padding-left:13px}
.stage-head{display:flex;gap:14px;align-items:flex-start;flex-wrap:wrap}
.stage-head > div:first-child{flex:1 1 210px;min-width:0}
.stage h3{margin:0;font-size:15.5px;font-weight:650;letter-spacing:-.01em}
.stage-state{flex:0 0 auto;text-align:right}
.stage-do{flex:0 0 auto;margin-left:auto}

/* The legs. `tabular-nums` so the columns line up down the page — the whole
   point is being able to run an eye down "left to search" without reading. */
ul.legs{list-style:none;display:flex;flex-wrap:wrap;gap:0 26px;
  margin:10px 0 0;padding:0}
.leg{font-size:13px;line-height:1.3;padding:2px 0}
.leg .leg-v{display:block;font-size:1.15rem;font-weight:650;
  font-variant-numeric:tabular-nums}
.leg .leg-w{display:block;color:var(--ink-soft);font-size:12px}
.leg.done .leg-v{color:var(--good)}
.leg.left .leg-v{color:var(--ink)}
.leg.excluded .leg-v,.leg.other .leg-v{color:var(--ink-faint);font-weight:600}
.leg.excluded .leg-w,.leg.other .leg-w{color:var(--ink-faint)}
.leg.total{margin-left:auto;text-align:right}
.leg.total .leg-v{font-weight:500;color:var(--ink-soft)}

.stage-note{margin:8px 0 0;font-size:12px;color:var(--ink-faint);
  max-width:70ch;line-height:1.45}
/* A remainder that should always be zero. Loud on purpose. */
.stray{margin:8px 0 0;font-size:12.5px;color:var(--crit);
  background:var(--crit-bg);border-left:3px solid var(--crit);padding:7px 10px}
pre.stage-log{font-size:11px;background:var(--surface-2);padding:8px 10px;
  margin:10px 0 0;max-height:150px;overflow:auto;white-space:pre-wrap}

@media (max-width:640px){
  ul.legs{gap:0 18px}
  .leg.total{margin-left:0;text-align:left;width:100%;
    border-top:1px solid var(--rule-soft);margin-top:6px;padding-top:6px}
  .leg.total .leg-v{display:inline;font-size:13px}
  .leg.total .leg-w{display:inline}
  .stage-do{width:100%;margin-left:0}
  .stage-state{text-align:left}
}
/* Why the number is what it is, printed under it rather than hidden behind a
   hover: a tooltip is unreachable on a phone, and the superscript "?" that
   used to summon it read as punctuation — "never scored?" is a question, and
   the page was not asking one. */
.leg .leg-d{display:block;font-size:11px;color:var(--ink-faint);
  margin-top:3px;max-width:32ch;line-height:1.4}

/* A button that cannot work says so by looking unavailable, rather than by
   accepting the click and changing nothing. */
button:disabled,.btn:disabled{opacity:.45;cursor:not-allowed}
button:disabled:hover{background:var(--surface)}

/* A row for somebody who has left. Dimmed, not hidden: they are still part of
   the company's history and sometimes the reason a lead exists at all. */
tr.gone td{color:var(--ink-faint)}
tr.gone td a{color:var(--ink-soft)}

/* A switch says what it is, and says when what you are looking at is not yet
   what is stored. "Off" on its own beside a tick box reads as the name of the
   box as readily as its state — and, rendered once from the saved value, it
   went on saying "Off" after somebody ticked it. */
.toggle{display:flex;gap:9px;align-items:center;cursor:pointer}
.toggle input:disabled{cursor:not-allowed}
.state{font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;
  text-transform:uppercase;padding:2px 7px;border:1px solid var(--rule);
  border-radius:2px;white-space:nowrap}
.state.on{color:var(--good);border-color:var(--good)}
.state.off{color:var(--ink-faint)}
.state.unsaved{color:var(--warn);border-color:var(--warn);
  background:var(--warn-bg);text-transform:none;letter-spacing:0;
  font-family:var(--sans);font-size:11.5px}

/* `why` — the pipeline for one company, stage by stage. */
ul.whylines{margin:5px 0 0 16px;padding:0;font-size:12.5px;color:var(--ink-soft)}
ul.whylines li{margin:1px 0}
.whyfix{margin-top:5px;font-size:12.5px;color:var(--ink-soft);
  border-left:2px solid var(--copper);padding-left:9px;max-width:78ch}

/* The bulk-load history. Narrow, dense, and read as a column of days rather
   than as a table of runs — the eye wants to run down the share and spot the
   day that is different, so the numbers are tabular and the rows are tight. */
table.bulk{margin-top:8px;font-size:12px}
table.bulk th,table.bulk td{padding:4px 10px 4px 0;border-bottom:0;
  vertical-align:baseline}
table.bulk thead th{background:none;font-size:9.5px;padding-bottom:6px}
table.bulk tbody tr:hover{background:none}
table.bulk tbody td{color:var(--ink-soft)}
table.bulk tbody td:first-child{color:var(--ink);white-space:nowrap}

/* ---------------------------------------------------------- four businesses
   The header takes its colour from the business you are in. `--brand` is set
   inline on <header> from the tenant's accent, and falls back to the house
   copper for a page with no business resolved — the login screen, or an
   account nobody has given access to yet. */
header.top{--brand:var(--copper);box-shadow:inset 0 3px 0 var(--brand)}
.brand{display:inline-flex;align-items:center;gap:9px;text-decoration:none;
  color:var(--ink);font-weight:600}
.brand .mark{display:inline-flex;align-items:center;justify-content:center;
  min-width:34px;height:22px;padding:0 6px;border-radius:3px;
  background:var(--brand);color:#fff;font-family:var(--mono);font-size:11px;
  letter-spacing:.08em;font-weight:600}
.brand .logo{height:22px;width:auto;display:block}
.brand .brandname{font-size:14px;letter-spacing:-.01em}
.switcher{margin-left:12px;display:inline-block}
.switcher select{font-size:12px;padding:3px 6px;width:auto;max-width:210px}

/* The admin menu. Everything about running the machine, behind one item, so
   the pipeline pages are not competing with Processing and Suppression for
   the same row of attention. Hover and focus-within together: a menu that
   only opens on hover is one nobody can reach from a keyboard. */
.menu{position:relative;display:inline-block}
/* The Admin menu sat a line above everything else in the navigation.
   Perry: *"Why is Admin menu above the line of other menus please?"*

   `nav.main` is a flex row, so its direct children stretch to the full height
   of the bar and each link's 16px of vertical padding centres its text in it.
   The Admin item is not a link, though — it is a `span` wrapping one — so the
   span stretched and the link inside it sat at the *top* of that span rather
   than in the middle. The active underline went with it, which is the stray
   copper line to the right of the word.

   Making the wrapper stretch its own child fixes both: the link becomes a
   full-height box like its siblings, and its padding and underline land where
   theirs do. */
nav.main .menu{display:flex;align-items:stretch}
.menu .menuitems{display:none;position:absolute;top:100%;left:0;z-index:40;
  min-width:170px;padding:6px 0;background:var(--surface);
  border:1px solid var(--rule);border-radius:3px;
  box-shadow:0 6px 18px rgba(0,0,0,.10)}
.menu:hover .menuitems,.menu:focus-within .menuitems{display:block}
.menu .menuitems a{display:block;padding:6px 14px;white-space:nowrap}

/* ------------------------------------------------------------------ history
   Small multiples: one panel per counter, each on its own scale. Every colour
   comes from the same tokens as the rest of the portal, so the charts follow
   light and dark without a second palette to keep in step — a chart with its
   own colours stops matching the page the first time somebody adjusts the
   theme.

   One series per panel, so: one hue, no legend (the caption names it), and a
   grid recessive enough to read the line over. */
.ranges{display:flex;gap:2px;margin:-8px 0 20px;flex-wrap:wrap}
.ranges a{font-size:12.5px;padding:5px 12px;text-decoration:none;
  color:var(--ink-soft);border:1px solid var(--rule);border-radius:2px;
  background:var(--surface)}
.ranges a:hover{background:var(--surface-2);color:var(--ink)}
.ranges a.on{background:var(--copper);border-color:var(--copper);color:#fff}

.panels{display:grid;gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.panel{margin:0;padding:12px 14px 10px;background:var(--surface);
  border:1px solid var(--rule);border-radius:3px}
.panel figcaption{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  margin-bottom:2px}
/* Text wears text tokens, never the series colour: the line carries identity,
   the numbers stay in ink. */
.panel .pname{font-size:12.5px;color:var(--ink-soft)}
.panel .pnow{font-size:19px;font-weight:600;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;margin-left:auto}
.panel .pdelta{font-family:var(--mono);font-size:11px}
.panel .pdelta.up{color:var(--good)}
.panel .pdelta.down{color:var(--crit)}
.panel .pdelta.flat{color:var(--ink-faint)}
.panel .phelp{font-size:11.5px;color:var(--ink-faint);margin-top:2px}

svg.spark{display:block;width:100%;height:auto;overflow:visible}
.sparkline{fill:none;stroke:var(--copper);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round}
/* A wash rather than a fill: it gives the line a body to sit on without
   competing with it, and at this opacity it survives both themes. */
.sparkarea{fill:var(--copper);opacity:.10;stroke:none}
/* A 2px surface ring, so the marker reads as on top of the line rather than
   part of it. */
.sparknow{fill:var(--copper);stroke:var(--surface);stroke-width:2}
.sparkaxis{font-family:var(--mono);font-size:9px;fill:var(--ink-faint)}
.sparkempty{font-size:11px;fill:var(--ink-faint)}
/* Invisible, and much bigger than the mark: a 2px line is not something
   anybody can point at with a mouse. */
.sparkhit{fill:transparent;stroke:none}
.sparkhit:hover{fill:var(--copper);opacity:.18}

.numbers{margin-top:20px}
.numbers summary{cursor:pointer;font-size:13px;color:var(--ink-soft);
  padding:6px 0}
.numbers table{font-size:12px}

/* ------------------------------------------------------------------ quotes
   A quote is a form with a table inside it, which is the one layout the rest
   of this stylesheet never had to draw. Two rules do most of the work: the
   inputs inside the line table fill their cell rather than carrying a browser
   default width, and the whole thing is inside `.tablewrap` so a narrow
   window scrolls the table instead of the page. */
.quoteform{margin-top:10px}
.quoteform .row{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;
  margin-bottom:10px}
.quoteform label{display:block;font-size:12.5px;color:var(--ink-soft)}
/* A label given `flex:2` stretches; the input inside it does not, unless it
   is told to. Without this the heading box on a quote was forty pixels wide
   next to two thirds of a screen of empty label. */
/* A floor under each field, so a row of seven wraps onto two lines rather
   than giving every one of them a sliver. Perry reads these on a phone. */
.quoteform .row label{flex:1;min-width:150px}
.quoteform .row input,.quoteform .row select{width:100%;min-width:0;font:inherit;
  padding:8px 10px;border:1px solid var(--line);border-radius:8px;
  background:var(--surface);color:var(--ink)}
.quoteform textarea{width:100%;font:inherit;padding:8px 10px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface);
  color:var(--ink)}
/* A white box on a white row is invisible, and a box nobody can see is one
   nobody knows they may type in. The line inputs sit on the tinted surface so
   the editable part of the table reads as editable at a glance. */
table.lines input{width:100%;min-width:0;font:inherit;padding:6px 8px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface-2);
  color:var(--ink)}
table.lines input:focus{background:var(--surface);outline:2px solid var(--brand);
  outline-offset:-1px}
table.lines input.num{text-align:right;font-variant-numeric:tabular-nums}
/* The description is the column that deserves the room; the three numeric
   ones are as narrow as their contents allow. */
table.lines th:first-child,table.lines td:first-child{width:55%}

/* The stored document, shown as it was stored. An iframe rather than the
   markup inlined into this page, because the bytes were hashed and anything
   that re-flows them through the portal's own stylesheet is no longer the
   thing that was signed. */
.docframe{width:100%;height:60vh;min-height:400px;border:1px solid var(--line);
  border-radius:10px;background:#fff;margin-top:8px}
.copyable{font-family:var(--mono);font-size:12.5px;padding:8px 10px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface-2);
  color:var(--ink)}

/* ------------------------------------------------------------------- diary
   Seven columns that become one on a phone, which is where a diary is read
   more often than not. `minmax(0,1fr)` rather than a bare `1fr`: a bare one
   is `minmax(auto,1fr)` and refuses to shrink below its content, which is how
   a week of long company names pushes the page off the side of the screen. */
.week{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:10px;
  margin-top:18px}
.dayblock{background:var(--surface);border:1px solid var(--line);
  border-radius:10px;padding:10px;min-width:0}
.dayblock.today{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand)}
.dayblock h3{margin:0 0 8px;font-size:12.5px;display:flex;
  justify-content:space-between;gap:6px;align-items:baseline}
.dayblock h3 span{font-size:11px;font-variant-numeric:tabular-nums}
/* One line, not the page-wide dashed panel `.empty` draws elsewhere. Five
   free days in a week is five of these, and at 34px of padding each they
   turn an empty week into a page of scrolling past nothing. */
.dayblock .empty{color:var(--ink-faint);font-size:11.5px;margin:6px 0 0;
  padding:0;border:0;background:none;text-align:left}

.entry{border-top:1px solid var(--line);padding:8px 0;font-size:12.5px}
.entry:first-of-type{border-top:0}
.entry .time{font-family:var(--mono);font-size:11.5px;display:flex;gap:6px;
  flex-wrap:wrap;align-items:baseline}
.entry .body strong{display:block;margin-top:2px}
.entry .muted{font-size:11px;word-break:break-word}
/* Cancelled entries stay — "they cancelled twice" is the useful part — but
   they must not read as things somebody still has to attend. */
.entry.cancelled{opacity:.55;text-decoration:line-through}
.entry.cancelled .pill{text-decoration:none}
.entry.done{opacity:.8}
.rearrange summary{cursor:pointer;color:var(--ink-faint);font-size:11px;
  margin-top:4px}
.rearrange form{display:flex;gap:4px;flex-wrap:wrap;margin-top:4px}
.rearrange input{font:inherit;font-size:11.5px;padding:3px 5px;
  border:1px solid var(--line);border-radius:5px;background:var(--surface-2);
  color:var(--ink)}

/* The debt, above the week rather than below it. */
.unmarked{display:flex;gap:12px;align-items:center;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);border-left:3px solid
  var(--warn,#b45309);border-radius:8px;padding:10px 12px;margin-bottom:8px}
.unmarked .when{font-family:var(--mono);font-size:12px;min-width:110px;
  display:flex;gap:8px}
.unmarked .what{flex:2;min-width:180px}
.unmarked .mark{display:flex;gap:6px;flex:2;min-width:220px}
.unmarked .mark input{flex:1;min-width:0;font:inherit;padding:6px 8px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface-2);
  color:var(--ink)}

@media (max-width:900px){
  .week{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:560px){
  .week{grid-template-columns:minmax(0,1fr)}
}

/* The header on a phone.
   `nav.main a` carries 16px of padding top and bottom so the links line up
   with a 54px bar on a desktop. Wrapped onto seven rows on a 400px screen
   that is 350px of navigation above the first word of every page — and the
   row has grown by two items since it was written. Narrow screens get the
   navigation as its own tight full-width row instead. */
@media (max-width:700px){
  .topinner{gap:10px;padding:8px 14px;min-height:0}
  nav.main{flex-basis:100%;order:3;gap:14px}
  nav.main a{padding:5px 0}
  .whoami{order:4;flex-basis:100%}
  .switcher{margin-left:auto}
}

/* A list of sentences rather than a list of data. Used on the retention page,
   where each line is a thing that will or will not happen and needs room to
   say why — a table would compress them into a column and lose the reason. */
ul.plain{margin:0;padding-left:18px;font-size:13.5px;color:var(--ink-soft)}
ul.plain li{margin:6px 0}
ul.plain strong{color:var(--ink)}

/* ----------------------------------------------------- ready to send
   Three states, drawn as three. A check that could not run gets its own
   colour and its own mark, because "the lookup timed out" and "there is no
   record" call for completely different afternoons — and painting the first
   of them green is the whole fault this page exists to avoid. */
.checks{display:flex;flex-direction:column;gap:8px;margin-top:10px}
.check{display:flex;gap:12px;align-items:flex-start;background:var(--surface);
  border:1px solid var(--line);border-left:3px solid var(--line);
  border-radius:8px;padding:11px 13px}
.check .mark{font-family:var(--mono);font-size:15px;line-height:1.3;
  width:18px;flex:none;text-align:center}
.check .what{flex:1;min-width:0}
.check .detail{font-size:12.5px;color:var(--ink-soft);margin-top:2px;
  word-break:break-word}
.check .fix{font-size:12px;color:var(--ink-faint);margin-top:4px}
.check .btn{flex:none}
.check.yes{border-left-color:var(--good)}
.check.yes .mark{color:var(--good)}
.check.no{border-left-color:var(--crit)}
.check.no .mark{color:var(--crit)}
.check.dunno{border-left-color:var(--warn)}
.check.dunno .mark{color:var(--warn)}
@media (max-width:560px){
  .check{flex-wrap:wrap}
  .check .btn{margin-left:30px}
}

/* The live-checks button and the sentence that says what it costs. Side by
   side on a desktop; on a phone the sentence wraps under it rather than
   squeezing the button to two characters wide. */
.deepcheck{display:flex;gap:12px;align-items:flex-start;margin-top:22px;
  flex-wrap:wrap}
.deepcheck .btn{flex:none}
.deepcheck .sub{flex:1;min-width:220px}

/* The lines of a quote, tucked under its row on the account page. A total
   answers "how much" and nothing else — what was offered, and whether a
   cheaper second quote is a discount or a smaller job, is in the lines.
   Rows of flex rather than a nested table: this block sits in the narrow side
   column of the account page, where a second table with its own columns has
   nowhere to put them and pushes the money off the edge. Drawn quietly — it
   is supporting detail under a row, not a table competing with the one
   above. */
tr.quotelines > td{padding-top:0;padding-bottom:10px;border-top:none}
.quotelines .lines{border-left:2px solid var(--line);padding-left:10px;
  font-size:12.5px;color:var(--ink-soft)}
.quotelines .line{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;
  padding:1px 0}
/* Packed to the left, not spread across the row. This table is wider than
   the column it sits in and scrolls sideways, so anything pushed to the far
   right — the one place the eye looks for money — is off the edge until
   somebody scrolls, which nobody does to read a sub-line. */
.quotelines .line .what{flex:0 1 auto;min-width:0}
.quotelines .line .qty{white-space:nowrap;color:var(--ink-faint)}
.quotelines .line .amt{white-space:nowrap;font-variant-numeric:tabular-nums}
.quotelines .by{color:var(--ink-faint);padding-top:2px}

/* The identity card gained three things it did not have: the business's own
   Microsoft 365, its client secret, and its logo. Subheadings inside the card
   because without them it is fourteen boxes in a column and nothing says
   where the footer details end and the mail credentials begin. */
.identsub{font-size:12px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-faint);
  margin:14px 0 2px;padding-top:10px;border-top:1px solid var(--line)}
.identsub .muted{font-weight:400;text-transform:none;letter-spacing:0}
.logopreview{max-height:44px;max-width:100%;margin:4px 0 6px;
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:6px;padding:4px}
.logoupload{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  margin-top:6px;font-size:12.5px}
.logoupload input[type=file]{flex:1;min-width:0}

/* The foot of every page: release, copyright, classification. Quiet — it is a
   marking, not a message — but always there, including on a printed page,
   which is the one place a classification actually does work. */
footer.pagefoot{display:flex;gap:16px;flex-wrap:wrap;justify-content:center;
  align-items:baseline;padding:18px 16px 26px;margin-top:28px;
  border-top:1px solid var(--line);color:var(--ink-faint);font-size:12px}
footer.pagefoot .classification{font-weight:600;letter-spacing:.03em}
@media print{
  footer.pagefoot{position:fixed;bottom:0;left:0;right:0;margin:0;
    border-top:1px solid #999;background:#fff}
}

/* The line suggestions on a quote.
   Fixed on the body rather than absolute inside the table: the lines sit in a
   `.tablewrap`, which scrolls sideways and is therefore a containing block, and
   a dropdown inside one is clipped by it — the same trap that cost this project
   its sticky table headings twice. */
.suggestbox{position:fixed;z-index:60;max-width:min(560px,92vw);
  background:var(--surface);border:1px solid var(--line);border-radius:8px;
  box-shadow:0 8px 24px rgba(0,0,0,.12);overflow:hidden;font-size:13px}
.suggestion{display:flex;gap:10px;align-items:baseline;padding:7px 11px;
  cursor:pointer;border-bottom:1px solid var(--line)}
.suggestion:last-child{border-bottom:none}
.suggestion:hover,.suggestion.on{background:var(--surface-2)}
.suggestion .what{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.suggestion .price{white-space:nowrap;font-variant-numeric:tabular-nums}
/* Which list it came from, on every row. Picking one is agreeing to that price
   on the customer's behalf, so it says so rather than leaving somebody to
   remember. */
.suggestion .from{white-space:nowrap;font-size:11px;letter-spacing:.04em;
  text-transform:uppercase;padding:1px 6px;border-radius:999px;
  border:1px solid var(--line);color:var(--ink-faint)}
.suggestion .from.customer{color:var(--good);border-color:var(--good)}

/* The letterhead designer: controls on the left, the document itself on the
   right. A list of field names is not a design tool — nobody can judge whether
   a logo is too big from a number in a box. */
.designer{display:grid;grid-template-columns:minmax(320px,420px) minmax(0,1fr);
  gap:20px;align-items:start;margin-top:14px}
.designform h2{font-size:14px;margin:18px 0 8px;padding-top:12px;
  border-top:1px solid var(--line)}
.designform h2:first-of-type{border-top:none;padding-top:0}
.designform textarea{width:100%;font:inherit;padding:7px 9px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface-2)}
.designform input[type=color]{padding:2px;height:34px}
.preview{position:sticky;top:12px;border:1px solid var(--line);
  border-radius:10px;overflow:hidden;background:var(--surface)}
.previewhead{padding:8px 12px;border-bottom:1px solid var(--line);
  font-size:12.5px;font-weight:600;background:var(--surface-2)}
.preview iframe{display:block;width:100%;height:78vh;border:0;background:#fff}
/* On a phone the two columns become one and the preview follows the controls,
   which is the right order: you change something, then scroll to look. */
@media (max-width:880px){
  .designer{grid-template-columns:minmax(0,1fr)}
  .preview{position:static}
  .preview iframe{height:60vh}
}

/* An appointment mirrored from somebody's Outlook calendar. Drawn quietly and
   differently from the diary's own: it is here so the day is honest about how
   full it is, and nothing about it can be changed from this page. */
.entry.fromoutlook{background:repeating-linear-gradient(135deg,
  var(--surface) 0 8px, var(--surface-2) 8px 16px);
  border-left-style:dashed}
.entry.fromoutlook .body strong{font-weight:600;color:var(--ink-soft)}
nav.main .navc{color:var(--copper);font-weight:600;font-variant-numeric:tabular-nums}
