:root{
  --ground:#EFF2F3; --surface:#FFF; --surface-2:#E4E9EB; --sunk:#F7F9F9;
  --ink:#171A1C; --ink-2:#4B5459; --ink-3:#7C878D;
  --line:#D5DBDE; --line-2:#BAC3C8;
  --accent:#C24F14; --accent-soft:#FBEBE1; --on-accent:#FFF;
  --ok:#2C6B4F; --ok-bg:#E2EFE8;
  --warn:#8F6714; --warn-bg:#F7EEDA;
  --bad:#99392A; --bad-bg:#F7E8E4;
  --slate:#3E5A67;
  /* The soft edge on a sideways-scrolling table. A token because a themed
     page may be darker than this shadow, and v0.113's themes restate :root
     wholesale -- anything left as a literal here would survive the restate
     and be the one thing that did not follow the theme. */
  --shade:rgba(0,0,0,.13); --shade-0:rgba(0,0,0,0);
  color-scheme:light dark;
  --f-ui:"Archivo","Helvetica Neue",Arial,sans-serif;
  --f-mono:"IBM Plex Mono",ui-monospace,Menlo,monospace;
}
@media (prefers-color-scheme:dark){:root{
  --ground:#101317; --surface:#191D21; --surface-2:#232830; --sunk:#14181C;
  --ink:#E8EBEC; --ink-2:#AAB3B9; --ink-3:#77828A;
  --line:#2C333A; --line-2:#3D464E;
  --accent:#F0793C; --accent-soft:#2C1E16; --on-accent:#2A1408;
  --ok:#6FC79B; --ok-bg:#16281F;
  --warn:#D9AC53; --warn-bg:#2A2213;
  --bad:#DE8272; --bad-bg:#2C1A16;
  --slate:#8FB0BF;
  --shade:rgba(0,0,0,.40); --shade-0:rgba(0,0,0,0);
}}
*{box-sizing:border-box}
body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--f-ui);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
a{color:var(--accent)}
button,input,select{font-family:inherit;font-size:inherit;color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}
.mono,.n{font-family:var(--f-mono);font-variant-numeric:tabular-nums}
.lbl{font-family:var(--f-mono);font-size:10px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3)}

nav{background:var(--surface);border-bottom:1px solid var(--line);padding:11px 22px;display:flex;gap:15px;align-items:center;flex-wrap:wrap}
nav .brand{margin-right:auto;display:flex;align-items:center;text-decoration:none}
nav .brand img{height:26px;width:auto;display:block}
@media (prefers-color-scheme:dark){nav .brand img{filter:brightness(1.35) contrast(1.05)}}
nav a{text-decoration:none;color:var(--ink-2);font-weight:500;padding:4px 2px;border-bottom:2px solid transparent}
nav a:hover{color:var(--accent)}
nav a.on{color:var(--ink);border-bottom-color:var(--accent)}
nav .db{font-family:var(--f-mono);font-size:10.5px;color:var(--ink-3)}
nav .navlinks{display:contents}
nav .navfoot{display:contents}
nav .navtoggle{display:none}
/* A hairline between links. Kevin, 2026-09-20: "The links look closer
   together. its nice, but is there any way to put dividers there? | like
   that?" -- said of a stale stylesheet that was packing them at word spacing.
   So the tighter run is deliberate now (gap 20 -> 15) and the rules make it
   read as a deliberate set rather than a crowd. Drawn as a pseudo-element, so
   nothing moves and no markup carries a character that a screen reader would
   have to read out. Off in the phone sheet, where the rows already have
   borders between them and a vertical bar would sit across them. */
nav .navlinks>a{position:relative}
nav .navlinks>a+a::before{content:"";position:absolute;left:-8px;top:3px;
  bottom:3px;width:1px;background:var(--line-2)}
.vis-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ------------------------------------------------------------ nav, narrow
 * Fourteen links wrapped to four rows on a phone and pushed the page below
 * the fold -- Kevin, 2026-09-19, with a photo of it: "It's not bad on PC but
 * this is rough lol".
 *
 * So below 820px the links become a sheet the brand bar opens, and the bar
 * keeps the name of the page you are on, because a menu that hides where you
 * are costs more than the rows it saves. Above 820px nothing changes: the
 * links are `display:contents`, so they sit in nav's own flex row exactly as
 * they always did and the desktop layout is untouched. */


.wrap{max-width:1180px;margin:0 auto;padding:26px 22px 34px}
/* The copyright notice. Quiet on purpose -- it is an assertion of ownership,
   not a thing anybody needs to read twice -- but present on every page of
   both the office and the portal, which is where it does its work if this is
   ever licensed to another contractor. */
footer.legal{max-width:1180px;margin:0 auto;padding:0 22px 34px;
  font-size:11px;color:var(--ink-3)}
h1{font-size:23px;font-weight:700;letter-spacing:-.02em;margin:0 0 4px}
h2{font-size:16px;font-weight:700;margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line)}
h3{font-size:13px;font-weight:700;margin:0 0 8px}
p.sub{color:var(--ink-2);margin:0 0 22px}
section{margin-bottom:30px}

.card{background:var(--surface);border:1px solid var(--line);border-radius:6px;padding:18px 20px}
/* A card you can fold away. Kevin, 2026-09-20: "if the proposals are wrong in
   the craft cat section, i'll keep having to scroll past them to work on the
   cats". Open by default everywhere -- the point is being able to shut a
   block you have already judged, not to hide it from someone who hasn't. */
details.card>summary{cursor:pointer;font-weight:600}
details.card>summary>h3{display:inline;margin:0}
details.card>summary .pill{margin-left:6px}
/* A table wider than its box scrolls sideways -- which on a phone looks
 * exactly like content that has been cut off, because nothing says otherwise.
 * These four layers are the standard shadow trick: two solid "caps" painted
 * over the shadows and pinned to the scroller's own edges, so the shadow at
 * an edge is only visible while there is more table past it. Pure paint, no
 * JS, and it costs nothing on a table that fits. */
.scroller{overflow-x:auto;border:1px solid var(--line);border-radius:6px;background:var(--surface);
  background-image:
    linear-gradient(to right, var(--surface), var(--surface)),
    linear-gradient(to right, var(--surface), var(--surface)),
    linear-gradient(to right, var(--shade), var(--shade-0)),
    linear-gradient(to left,  var(--shade), var(--shade-0));
  background-position:left center, right center, left center, right center;
  background-repeat:no-repeat;
  background-size:22px 100%, 22px 100%, 13px 100%, 13px 100%;
  background-attachment:local, local, scroll, scroll}
table{border-collapse:collapse;width:100%;min-width:640px;font-size:13.5px}
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--line);vertical-align:middle}
thead th{font-family:var(--f-mono);font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);background:var(--surface-2);white-space:nowrap;vertical-align:bottom}
/* A header that is a whole sentence. Kevin, 2026-09-20, on the job-statuses
   table: "perhaps the headers can take a little more space and wrap text, so
   we can read the rows? lol" -- four headers like THE WORK IS FINISHED ENOUGH
   TO BILL could not wrap, so each demanded its full single-line width, the
   table overflowed its 150px columns, and the browser took the space back out
   of the only cells that could give it: the Order and Name inputs, which
   ended up about 30px wide. Opt-in rather than the default, because a short
   header breaking across two lines is worse than a wide one. */
/* NOT `.wrap` -- that is the page container (max-width and 26px/90px of
   padding), and a cell wearing it gets the whole page's padding. v0.113's
   theme table did exactly that and shipped with 116px-tall rows nobody
   looked at closely. Named so it cannot happen again. */
th.cellwrap,td.cellwrap{white-space:normal}
/* A column holding one checkbox. Centred under its header instead of pinned
   to the left of a 150px column, which is the difference between reading a
   row and counting across to it.

   NOT `.tick` -- that is already the 7px round status dot further down this
   file, and a cell wearing it became a 7px inline-block circle: the reason
   four checkboxes huddled together under headers that were 150px wide. The
   second name collision in one sitting, after `.wrap`; hence the test in
   tests/test_nav.py that now checks for the next one. */
th.ckcol,td.ckcol{text-align:center}
/* Once the headers wrap, auto layout has no reason to keep a column holding
   one 16px checkbox any wider than the checkbox -- so the four ticks bunch
   into a corner while their headers sit over empty space. `width` is only a
   hint to auto layout and `min-width` on a cell is barely more than that;
   the thing that actually holds a column where it is put is fixed layout.
   Opt-in, because it also means a column can no longer grow to fit an
   unexpectedly long value. */
table.fixed{table-layout:fixed}
tbody tr:last-child td{border-bottom:none}
td.n,th.n{text-align:right;white-space:nowrap}
td.code{font-family:var(--f-mono);font-weight:600;font-size:12.5px;white-space:nowrap}
tfoot td{font-weight:700;background:var(--sunk);border-top:2px solid var(--line-2)}
tr.craft-head td{background:var(--sunk);font-family:var(--f-mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);font-weight:600}

.pill{font-family:var(--f-mono);font-size:9.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;padding:2px 7px;border-radius:2px;white-space:nowrap;display:inline-block}
.pill.ok{background:var(--ok-bg);color:var(--ok)}
.pill.warn{background:var(--warn-bg);color:var(--warn)}
.pill.bad{background:var(--bad-bg);color:var(--bad)}
.pill.mute{background:var(--surface-2);color:var(--ink-3)}

.btn{display:inline-block;background:var(--sunk);border:1px solid var(--line);border-radius:5px;padding:7px 14px;font-size:13px;font-weight:600;cursor:pointer;text-decoration:none;color:var(--ink)}
.btn:hover{background:var(--surface-2)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.btn.primary:hover{filter:brightness(1.08)}
.btn.lg{padding:11px 22px;font-size:15px}
.btn.sm{padding:3px 9px;font-size:11.5px;font-weight:600;border-radius:4px}
.btn.danger{border-color:var(--bad);color:var(--bad)}
.btn.danger:hover{background:var(--bad-bg)}

/* `input:not([type])` earns its place: an `<input name="zip">` with no type
 * attribute is a text input to the browser and invisible to `input[type=text]`.
 * Nineteen fields across `job.html` and `settings.html` are written that way
 * and had never been styled at all -- no background, no border, and no
 * `width:100%`, so each sat at its intrinsic ~375px. That is what pushed the
 * job page's edit panel to 521px inside a 390px phone. Found 2026-09-20.
 * `input[type=date]` was missing the same way until 2026-09-21: nine date
 * boxes drawn as the browser's bare default, white on the dark themes. */
input:not([type]),input[type=text],input[type=search],input[type=number],input[type=file],input[type=date],select{background:var(--sunk);border:1px solid var(--line);border-radius:5px;padding:7px 10px;width:100%}
input[type=number]{text-align:right;font-family:var(--f-mono);max-width:110px}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.grid{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);border-radius:6px;overflow:hidden;grid-template-columns:repeat(auto-fit,minmax(158px,1fr))}
.tile{background:var(--surface);padding:14px 16px}
.tile .v{font-family:var(--f-mono);font-size:20px;font-weight:600;letter-spacing:-.02em;margin-top:4px;font-variant-numeric:tabular-nums}
.tile .s{font-size:11.5px;color:var(--ink-3);margin-top:3px}
.tile.hero{background:var(--accent-soft)} .tile.hero .v{color:var(--accent);font-size:24px}
.tile.good .v{color:var(--ok)} .tile.bad .v{color:var(--bad)}

.note{border:1px solid var(--line);border-left:4px solid var(--slate);background:var(--surface);border-radius:5px;padding:13px 16px;margin:14px 0;font-size:13.5px;line-height:1.6;color:var(--ink-2)}
.note.warn{border-left-color:var(--warn);background:var(--warn-bg);color:var(--warn)}
.note.bad{border-left-color:var(--bad);background:var(--bad-bg);color:var(--bad)}
.note.ok{border-left-color:var(--ok);background:var(--ok-bg);color:var(--ok)}
.note b{color:inherit;font-weight:700}
.note ul{margin:7px 0 0;padding-left:18px}

dl.kv{display:grid;grid-template-columns:auto 1fr;gap:5px 16px;margin:0;font-size:13px}
dl.kv dt{font-family:var(--f-mono);font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);padding-top:2px}
dl.kv dd{margin:0;color:var(--ink-2)}
.cols{display:grid;grid-template-columns:1fr 1fr;gap:22px}
.fields{display:grid;grid-template-columns:auto minmax(0,1fr);gap:7px 14px;align-items:center;font-size:13px}
.fields .lbl{padding-top:0;white-space:nowrap}
.fields .ro{color:var(--ink-2);font-size:13px}
.fields .triple{display:flex;gap:7px}
.req{font-family:var(--f-mono);font-size:9px;letter-spacing:.07em;color:var(--bad);margin-left:5px}
td.ro{color:var(--ink-3)}
#lines td{padding:4px 6px}
#lines input{padding:4px 7px;font-size:12.5px}
#lines input.n{text-align:right}
#lines input.code{font-family:var(--f-mono)}
@media(max-width:760px){.cols{grid-template-columns:1fr}}
.empty{padding:40px 20px;text-align:center;color:var(--ink-3)}
.drop{border:2px dashed var(--line-2);border-radius:8px;padding:30px;text-align:center;background:var(--surface)}
.drop input[type=file]{max-width:420px;margin:14px auto 0}

.inv-meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px 22px;margin-bottom:18px}
.inv-meta .v{font-family:var(--f-mono);font-weight:600;margin-top:3px}
.frame{width:100%;height:1080px;border:1px solid var(--line);border-radius:6px;background:#fff}
.setgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px 22px}
@media(max-width:760px){.setgrid{grid-template-columns:1fr}}
.setgrid label{display:block;margin-bottom:12px}
.setgrid .lbl{display:block;margin-bottom:4px}
textarea{width:100%;background:var(--sunk);border:1px solid var(--line);border-radius:5px;padding:7px 10px;font-family:var(--f-ui);font-size:13px;min-height:86px;color:var(--ink)}

tr.done td{color:var(--ink-3);background:var(--sunk)}

.who{margin-left:14px;font-size:11.5px;color:var(--ink-3)}
.who a{color:var(--ink-3)}

/* ---------------------------------------------------------------- rate sheet builder
   A console you tune on the left, the document it produces on the right. The
   proof is re-rendered by the server on every change, so what is on screen is
   always what the platform would actually generate. */
.bld{display:grid;grid-template-columns:400px minmax(0,1fr);gap:0;
     margin:-26px -22px -90px;min-height:calc(100vh - 52px)}
.bld-console{background:var(--surface);border-right:1px solid var(--line);
     display:flex;flex-direction:column;max-height:calc(100vh - 52px);overflow-y:auto}
.bld-head{padding:22px 22px 16px;border-bottom:1px solid var(--line)}
.bld-head h1{font-size:20px;margin:0 0 6px}
.bld-head p{margin:0;font-size:13px;color:var(--ink-3);line-height:1.5}
.bld-console .note{margin:14px 22px}

.stg{padding:20px 22px;border-bottom:1px solid var(--line)}
.stg-h{display:flex;align-items:baseline;gap:10px}
.stg-n{font-family:var(--f-mono);font-size:11px;font-weight:600;color:var(--accent);letter-spacing:.1em}
.stg h2{font-size:16px;margin:0}
.stg-p{font-size:12.5px;color:var(--ink-3);margin:5px 0 14px;line-height:1.5}
.f{display:block;margin-bottom:11px}
.f .lbl{display:block;margin-bottom:4px}
/* The hand-entry form on a job. Labels above fields in a grid that reflows,
   rather than a wrapping row of label/field pairs -- that left the date box
   stranded on a line of its own with its label on the line before. */
.entrygrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:4px 14px;margin-top:12px}
.entrygrid .wide{grid-column:span 2}
.entrygrid .f{margin-bottom:6px}
.entrygrid select,.entrygrid input{width:100%;max-width:none}
@media(max-width:560px){.entrygrid .wide{grid-column:auto}}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:end}
.sub-h{display:flex;align-items:center;justify-content:space-between;margin:16px 0 8px}
.stack{display:flex;flex-direction:column;gap:8px}

.pct-row{display:flex;align-items:center;gap:16px;margin-bottom:10px}
.pct-big{font-family:var(--f-mono);font-size:44px;font-weight:600;line-height:1;
     letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.pct-big i{font-style:normal;font-size:20px;color:var(--accent);margin-left:3px}
.pct-big input{font-family:var(--f-mono);font-size:40px;font-weight:600;width:150px;
     max-width:150px;text-align:left;padding:0 4px;background:transparent;border:0;
     border-bottom:2px solid var(--line);border-radius:0;letter-spacing:-.02em}
.pct-big input:focus{border-bottom-color:var(--accent)}
.pct-side{font-size:12.5px;color:var(--ink-2);line-height:1.45}
.slider{width:100%;accent-color:var(--accent);margin:0}
.ticks{display:flex;justify-content:space-between;font-family:var(--f-mono);
     font-size:10px;color:var(--ink-3);margin-top:3px}

.chk{display:flex;gap:9px;align-items:flex-start;font-size:13px;line-height:1.45;margin-bottom:6px}
.chk input{margin-top:3px;flex:0 0 auto;width:auto}

.ex{border:1px solid var(--line);border-radius:5px;padding:9px 10px;background:var(--sunk)}
.ex-top{display:flex;gap:7px;align-items:center}
.ex-top .code{font-family:var(--f-mono);font-weight:600;font-size:12.5px;flex:1}
.ex-top input[type=text],.ex-top .gname{flex:1}
.ex-top .gpct,.ex-top .cval{max-width:82px}
.ex-top .cmode{max-width:118px}
.ex textarea{width:100%;margin-top:7px;background:var(--surface);border:1px solid var(--line);
     border-radius:5px;padding:6px 8px;font-family:var(--f-mono);font-size:11.5px;resize:vertical}

/* craft-group quick-add — the ready-made groups a sheet used to offer */
.craftpicks{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 14px}
.craftbtn{font-family:var(--f-mono);font-size:11px}
.craftbtn.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent);cursor:default}

/* codes picked for a group — clicking beats typing them out */
.chips{display:flex;flex-wrap:wrap;gap:5px;margin:7px 0 0}
.chip{display:inline-flex;align-items:center;gap:4px;background:var(--surface);
     border:1px solid var(--line);border-radius:99px;padding:2px 3px 2px 9px;
     font-family:var(--f-mono);font-size:11px}
.chip .chipx{cursor:pointer;color:var(--ink-3);width:15px;height:15px;border-radius:50%;
     display:inline-flex;align-items:center;justify-content:center;line-height:1;text-decoration:none}
.chip .chipx:hover{background:var(--bad-bg);color:var(--bad)}
.codepick{margin-top:7px}
.codepick summary{cursor:pointer;font-size:11.5px;color:var(--accent);font-weight:600}
.codepick input[type=search]{width:100%;margin:7px 0}
.picklist{max-height:190px;overflow-y:auto;border:1px solid var(--line);
     border-radius:5px;background:var(--surface);padding:3px}
.pickrow{display:flex;gap:8px;align-items:baseline;font-size:12px;padding:4px 6px;
     border-radius:3px;cursor:pointer}
.pickrow:hover{background:var(--sunk)}
.pickrow input{margin:0 0 0 1px;flex:0 0 auto;width:auto}
.pickrow .pc-code{font-family:var(--f-mono);font-weight:600;flex:0 0 auto}
.pickrow .pc-desc{color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.bld-actions{padding:18px 22px 26px;margin-top:auto;
     border-top:1px solid var(--line);background:var(--sunk);position:sticky;bottom:0}
.bld-actions .btn.primary{width:100%;text-align:center}
.bld-actions .btnrow{display:flex;gap:8px;margin:10px 0 12px}
.bld-actions p{margin:0}

.bld-proof{display:flex;flex-direction:column;min-width:0;background:var(--ground)}
.bld-bar{display:flex;align-items:center;gap:12px;padding:11px 22px;
     border-bottom:1px solid var(--line);background:var(--surface)}
.bld-bar .legend{display:flex;gap:14px;font-size:11.5px;color:var(--ink-3)}
#proof{padding:20px 22px 40px;overflow-y:auto}

.pf-summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
     gap:1px;background:var(--line);border:1px solid var(--line);border-radius:6px;
     overflow:hidden;margin-bottom:16px}
.pf-stat{background:var(--surface);padding:13px 16px 15px}
.pf-stat.pf-hero{background:var(--accent-soft)}
.pf-n{font-family:var(--f-mono);font-size:27px;font-weight:600;line-height:1.1;
     margin-top:6px;font-variant-numeric:tabular-nums}
.pf-n i{font-style:normal;font-size:14px;color:var(--ink-3);margin-left:1px}
.pf-n.pf-sm{font-size:16px;padding-top:7px}
.pf-n.pf-none{color:var(--ink-3)}
.pf-s{font-size:11.5px;color:var(--ink-3);line-height:1.45;margin-top:4px;display:block}

.pf-table table{min-width:700px;table-layout:auto}
.pf-table td:nth-child(2){min-width:190px}
/* The reason is the point of the column — let it wrap rather than clip, so a
   sheet can be read without scrolling sideways to find out why a rate moved. */
.pf-why{font-size:12px;color:var(--ink-3);white-space:normal;min-width:150px}
.tick{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;vertical-align:middle}
/* v0.151. A stack of tick boxes answering one question -- which companies
   hold this Design Area. The whole ROW is the tap target rather than the
   16px box, because this gets read on a phone in a truck and a miss means
   handing the work to the wrong company.

   NOT `.tick`, directly above: that is the 7px round status dot, and a
   label wearing it becomes a 7px circle. Third time that name has nearly
   been taken; `tests/test_nav.py` catches it on table cells and would not
   have caught it here. */
/* THE SHORT NAME, AND THE WHOLE SENTENCE ONE TAP BEHIND IT. v0.155, and
   Kevin asked for it as the man who used to be in the field: "i really just
   wanted a way for anyone to be able to pull up the FULL verbiage... please
   just show me what i'm actually getting paid to do here??"

   The short name SWAPS FOR the long one rather than pushing it down the
   page, which is what he drew. A row that grows by six lines under the one
   you tapped moves every row beneath it, and in a list of 168 codes that
   loses your place.

   `::after` rather than two spans: one element whose text comes from the
   open state cannot disagree with itself about which way the fold is. */
.verb > summary{list-style:none;cursor:pointer;display:block}
.verb > summary::-webkit-details-marker{display:none}
.verb[open] .short{display:none}
.xpand{font-size:11px;color:var(--accent);margin-left:8px;white-space:nowrap}
.xpand::after{content:"expand"}
.verb[open] .xpand{margin-left:0}
.verb[open] .xpand::after{content:"show less"}
.tickrows{display:flex;flex-direction:column}
.tickrow{display:flex;align-items:center;gap:11px;padding:11px 10px;
  border-radius:6px;cursor:pointer}
.tickrow+.tickrow{border-top:1px solid var(--line)}
.tickrow:hover{background:var(--sunk)}
.tickrow input{margin:0;flex:none}
.tk-code{background:var(--accent)}
.tk-group{background:var(--slate)}
.tk-hold{background:var(--warn)}
tr.lv-code td,tr.lv-group td,tr.lv-hold td{background:var(--sunk)}
tr.lv-code td:nth-child(5){color:var(--accent)}

@media (max-width:900px){
  .bld{grid-template-columns:1fr;margin:-26px -14px -90px}
  .bld-console{max-height:none;border-right:0;border-bottom:1px solid var(--line)}
  .bld-actions{position:static}
}

/* Shown on every page when the database is not enforcing row-level security.
   Deliberately loud: the alternative is a log line nobody reads, and the
   failure it warns about is one company seeing another's rates. */
.noiso{background:var(--bad);color:#fff;padding:7px 22px;font-size:12.5px;
  display:flex;gap:14px;align-items:baseline;flex-wrap:wrap}
.noiso b{font-family:var(--f-mono);font-size:11px;letter-spacing:.1em;white-space:nowrap}
.noiso span{opacity:.9}

/* A disabled control has to LOOK disabled. The section-cutting screens ship a
   submit button that is disabled until a box is drawn, and with no rule here it
   rendered as a solid, inviting orange that did nothing -- found by looking at
   a screenshot, which is the only way this kind of thing is ever found. */
.btn:disabled,button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}

/* `hidden` HAS TO WIN. The attribute's own rule is `display:none` at the very
   bottom of the cascade, so any class that sets `display` beats it -- and this
   sheet has `.row{display:flex}` on nearly every form line. A `<div class="row"
   hidden>` therefore rendered in full, which is how the rate-card screen came
   to show both of its "Link it to" rows at once with the JS working perfectly.
   Nothing in 1,530 tests could see it; a screenshot could. */
[hidden]{display:none !important}

/* The please-wait overlay. Kevin, 2026-09-06 (relayed by CS): "no one likes a
   frozen-looking screen after pressing a button and that's all we have."
   Driven from base.html's own script; see it for when this shows and clears.
   `[hidden]` above already wins over this `display:flex` when the attribute
   is set, which is the whole reason that rule is `!important`. */
#overlay{position:fixed;inset:0;z-index:9999;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;text-align:center;
  padding:24px;background:rgba(8,10,12,.6)}
#overlay .ov-spin{width:34px;height:34px;border-radius:50%;
  border:3px solid rgba(255,255,255,.28);border-top-color:var(--accent);
  animation:ov-spin .8s linear infinite}
@keyframes ov-spin{to{transform:rotate(360deg)}}
#overlay .ov-msg{color:#fff;font-weight:600;font-size:14.5px}
#overlay .ov-slow{color:rgba(255,255,255,.82);font-size:12.5px;max-width:320px}
#overlay .ov-dismiss{color:#fff;font-size:12.5px;text-decoration:underline;
  background:none;border:0;cursor:pointer;padding:0;font-family:inherit}

/* ------------------------------------------------------------ dispatch board
   Crews down the side, dates across, and an ORDERED LIST inside each cell --
   never a timeline. claude/dispatch-model.md has the reasoning; the short of
   it is that a bar drawn from a duration nobody has is a guess displayed at
   pixel precision, and the inputs to this decision (oldest, closest, due) hold
   no clock at all. So a job chip has a position in a list and no width. */
.btn.xs{padding:1px 5px;font-size:10.5px;font-weight:600;border-radius:3px;line-height:1.5}
table.board td{vertical-align:top}
table.board th.today,table.board td.today{background:var(--sunk)}
table.board td.cell{min-width:190px;padding:6px}
.daybox{border:1px solid var(--line);border-radius:5px;padding:6px 7px;margin-bottom:6px;
  background:var(--surface-2)}
.daybox:last-child{margin-bottom:0}
.jobchip{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:4px 0;
  border-top:1px solid var(--line)}
.jobchip:first-of-type{border-top:0}
.jobchip .chipacts{margin-left:auto;display:flex;gap:2px;white-space:nowrap}

/* ------------------------------------------------------------------ phone
 * Kevin, 2026-09-20, after a week of testing from his phone: "take a look at
 * things and see what you can spruce up". Measured at 390x844 across eleven
 * office pages rather than guessed at. Everything here is inside the narrow
 * query on purpose -- the desktop density is what he works in all day and he
 * has said twice it is fine as it is.
 *
 * AND IT LIVES AT THE END OF THIS FILE ON PURPOSE. It was written near the
 * top first, above the rules it overrides, where `.fields` at its own equal
 * specificity simply won and the phone layout silently did nothing. A
 * narrow-screen override block belongs after everything it overrides. */
@media (max-width:820px){
  .wrap{padding:18px 14px 22px}
  footer.legal{padding:0 14px 26px}

  /* Label above field. Two columns of 132px + 375px is 521px of grid in a
     390px screen, which is what pushed `/job`'s edit panel off the side. */
  .fields{grid-template-columns:1fr;gap:3px 0;font-size:14px}
  .fields .lbl{margin-top:9px;white-space:normal}
  .fields .triple{flex-wrap:wrap}
  .setgrid{gap:12px 0}

  /* A FINGER IS ABOUT 44px. `/crews` had 147 controls under 38px tall and
     `/job` 98 -- fine with a mouse, a lottery with a thumb. Inputs also go to
     16px: below that, iOS Safari zooms the whole page the moment one takes
     focus, and then the crew is pinching to get back. */
  input:not([type]),input[type=text],input[type=search],input[type=number],
  input[type=file],input[type=date],input[type=email],input[type=password],
  select,textarea{font-size:16px;min-height:42px}
  input[type=number]{max-width:none}
  .btn,button{min-height:42px;padding:10px 14px}
  .btn.sm,.btn.small,button.small{min-height:38px}
  /* A checkbox drawn at 44px looks absurd, so the box grows a little and the
     cell padding above does the rest: a 24px box in a 9px-padded cell is a
     42px row to aim at. */
  input[type=checkbox],input[type=radio]{width:24px;height:24px}

  /* Tables stay tables -- they are the one thing a phone genuinely cannot
     reflow without lying about what lines up with what -- but they get room
     to breathe and a scrollbar worth seeing. */
  table{font-size:13px}
  th,td{padding:9px 10px}
  /* LET THE COLUMNS BREATHE AND THE TABLE SCROLL, rather than squeezing a
     job name into an 80px column and reading it one word per line. Measured
     on `/approvals`: an average row goes from 156px tall to 52px, so three
     times as many rows fit on a screen. The table gets wider -- 640px to
     978px -- but it was already scrolling at 640, so the width costs nothing
     that was not already being paid. `.scroller`'s edge shadows say there is
     more to the right. */
  .scroller td,.scroller th{white-space:nowrap}
  /* Except where a cell is meant to be prose. */
  .scroller td.cellwrap,.scroller th.cellwrap,.scroller td .hint,.scroller td .s{white-space:normal}
  .scroller{-webkit-overflow-scrolling:touch}
  .scroller::-webkit-scrollbar{height:9px}
  .scroller::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:5px}

  h1{font-size:24px;line-height:1.2}
  h2{font-size:18px}
  .row{flex-wrap:wrap}

  nav{padding:9px 14px;gap:10px;flex-wrap:wrap;align-items:center}
  nav .brand{margin-right:auto}
  nav .brand img{height:22px}
  nav .navtoggle{display:inline-flex;align-items:center;gap:9px;cursor:pointer;
    background:var(--surface);color:var(--ink);border:1px solid var(--line);
    border-radius:9px;padding:7px 11px;font:600 14px/1 inherit}
  nav .navtoggle-bars{width:16px;height:2px;background:currentColor;
    position:relative;display:block;border-radius:2px}
  nav .navtoggle-bars::before,nav .navtoggle-bars::after{content:"";position:absolute;
    left:0;width:16px;height:2px;background:currentColor;border-radius:2px}
  nav .navtoggle-bars::before{top:-5px}
  nav .navtoggle-bars::after{top:5px}
  nav.open .navtoggle{border-color:var(--accent);color:var(--accent)}

  nav .navlinks>a+a::before{display:none}
  nav .navlinks{display:none}
  nav.open .navlinks{display:flex;flex-direction:column;flex-basis:100%;gap:0;
    margin-top:8px;border-top:1px solid var(--line)}
  nav.open .navlinks a{padding:11px 4px;border-bottom:1px solid var(--line);
    border-left:3px solid transparent;padding-left:9px}
  nav.open .navlinks a.on{border-bottom-color:var(--line);
    border-left-color:var(--accent);color:var(--ink);background:var(--surface-2,transparent)}
  nav .navfoot{display:none}
  nav.open .navfoot{display:flex;flex-direction:column;gap:3px;padding:9px 4px 2px}
  /* The connection string is a developer's aid and a phone has no room for
     it. Still there once the menu is open, for the times it is the answer. */
  nav .db{display:none}
  nav.open .db{display:block;word-break:break-all}
}

/* The Design Area status picker on an audit. v0.170. Colour carries the one
   thing somebody scans a list of forty DAs for -- what is finished and what
   is waiting -- and the <select> keeps its own shape so it still reads as a
   control rather than as a label somebody cannot change. */
select.st-on-hold{background:var(--warn-bg);color:var(--warn)}
select.st-complete{background:var(--ok-bg);color:var(--ok)}
select.st-billed{background:var(--accent-soft);color:var(--accent);font-weight:600}

/* Previous / next MST. v0.181, and the whole of the design is where there is
   room: the body is 1180px wide, so below 1500px there is no "just outside
   it" to sit in and these are hidden rather than floated over the form. The
   same two links live at the foot of the page, which is what a narrow window
   and a phone get. */
.mstskip-a{position:fixed;top:50%;transform:translateY(-50%);z-index:40;
  display:flex;align-items:center;justify-content:center;
  width:44px;height:64px;border-radius:12px;
  background:var(--surface);border:1px solid var(--line);
  color:var(--ink-2);font-size:26px;line-height:1;text-decoration:none;
  box-shadow:0 2px 10px var(--shade)}
.mstskip-a:hover{color:var(--ink);border-color:var(--ink-3);
  box-shadow:0 3px 14px var(--shade)}
.mstskip-l{left:max(10px, calc(50vw - 660px))}
.mstskip-r{right:max(10px, calc(50vw - 660px))}
@media (max-width:1500px){.mstskip{display:none}}
