/* ONE STYLESHEET, ONE SET OF TOKENS.
   The system this replaces injected its light theme as a second sheet with
   !important on every property. It silently won every argument, which made
   button states impossible to reason about — a class that looked applied
   did nothing. Both themes here are the same rules with different values,
   so what you read is what renders. */
/* THE SHOP'S COLOURS, AND DARK IS THE DEFAULT.
   Anthony: "Add the same greens, purples and oranges etc we had. Auto load
   to dark theme when it opens." So these are the exact values off the
   existing system rather than something close to them -- two products from
   one shop that are nearly the same colour look like a mistake, where
   plainly different ones look like a choice.

   DARK LIVES ON :root, not inside a media query. The old system did the
   same and it is the right way round here: this is a shop-floor tool, read
   on a phone in a driveway and on a screen in a bay, and it opens dark
   because that is what it is for. Light is a deliberate switch, remembered
   per person.

   The surfaces are spaced by perceptual lightness rather than by eye --
   under about 4 in L* reads as one flat sheet, which is why a card has to
   look like it SITS ON the page rather than being painted onto it. */
:root{
  color-scheme:dark;
  --bg:#141920; --bg2:#1f2630; --bg3:#2b3440; --bg4:#3b4655;
  --border:rgba(255,255,255,0.09); --border2:rgba(255,255,255,0.16);
  --green:#3AAB22; --green2:#63de46;
  --purple:#964AD9; --purple2:#c68cff;
  --red:#ff7a7a; --amber:#fbbf24; --blue:#60a5fa; --yellow:#eab308;
  --text:#e9ebee; --text2:#b2bbc7; --text3:#95a0ae;
  --shadow:0 1px 2px rgba(0,0,0,.35),0 4px 14px rgba(0,0,0,.28);

  /* Names this build already uses, pointed at the shop's palette. Aliases
     rather than a rename so there is ONE place a colour is decided. */
  --card:var(--bg2); --line:var(--border); --line2:var(--bg3);
  --ink:var(--text); --ink2:var(--text2); --dim:var(--text3);
  --ok:var(--green2);
  --radius:13px; --pad:18px;
  --font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;
  --font-mono:ui-monospace,Menlo,Consolas,monospace;
}

/* Light, when somebody asks for it. Same hues, darkened enough to carry
   their own weight on a pale surface -- a green that reads well on
   near-black is unreadable on white, so these are not the same numbers. */
:root[data-theme="light"]{
  color-scheme:light;
  --bg:#eef3ec; --bg2:#ffffff; --bg3:#e0e9dd; --bg4:#cdd9c8;
  --border:rgba(22,64,14,0.13); --border2:rgba(22,64,14,0.22);
  --green:#1f7d10; --green2:#166209;
  --purple:#7c35c2; --purple2:#5e18a8;
  --red:#b02a1d; --amber:#a34a00; --blue:#1d4ed8; --yellow:#7a5c00;
  --text:#1c231d; --text2:#3a4740; --text3:#5d6b62;
  --shadow:0 1px 2px rgba(22,64,14,.06),0 4px 14px rgba(22,64,14,.05);
  --ok:var(--green);
}

*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.6 var(--font);
  -webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
h1{font-size:20px;margin:0 0 2px;letter-spacing:.01em}
code{font-family:var(--font-mono);font-size:.92em}

/* bar */
/* `hidden` is an attribute, and the browser implements it as a very weak
   display:none. An id selector outranks it, so `#bar{display:flex}` quietly
   un-hid the header and put an empty white strip above the sign-in card.
   Anything given display by id has to say what hidden means for itself. */
#bar[hidden]{display:none}
#bar{display:flex;align-items:center;gap:20px;padding:0 20px;height:56px;background:var(--card);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:10;flex-wrap:wrap}
#bar .brand{font-weight:700;letter-spacing:.02em}
#bar .stage{font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.09em;
  color:var(--purple);border:1px solid currentColor;border-radius:5px;padding:1px 6px;
  margin-left:7px;vertical-align:2px}
#bar nav{display:flex;gap:4px;flex:1}
#bar nav a{padding:7px 13px;border-radius:8px;font-size:14px;color:var(--ink2)}
#bar nav a:hover{background:var(--line2)}
#bar nav a.on{background:var(--line2);color:var(--ink);font-weight:600}
#bar .me{display:flex;align-items:center;gap:11px;font-size:13px;color:var(--dim)}
#bar .me i{font-style:normal;display:block;font-size:11px;text-transform:uppercase;letter-spacing:.07em}

/* layout */
.page{max-width:920px;margin:0 auto;padding:26px 20px 60px}
/* THE CLASS THAT WAS NEVER WRITTEN.
   The technician's wizard mounts into `.wrap techwrap` and there has never
   been a `.wrap` rule in this file, so it had no padding at all -- not at
   the sides, not at the top, and, the one that cost a day's work, not at
   the bottom. On a phone the page ends flush with its last button and the
   fixed tab bar covers it, so Save and Next could not be reached however
   far you scrolled. Robert lost the Lexus on it. */
.wrap{max-width:920px;margin:0 auto;padding:26px 20px 60px}
.page-head{margin-bottom:18px}
.page-head p{margin:0;color:var(--dim);font-size:14px}
.back{font-size:13px;color:var(--dim);display:inline-block;margin-bottom:8px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;margin-bottom:16px}
.card.pad{padding:var(--pad)}
.card-head{padding:13px var(--pad);border-bottom:1px solid var(--line2);font-size:12px;
  font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dim)}
/* ─── A CARD THAT FOLDS ────────────────────────────────────────────────
   Anthony: "the areas beneath the six tile function ... are very spread
   out and hard to get to. We'd like less scroll."

   The head becomes the control. Nothing else about the card changes, so
   everything that queries inside one still finds what it is looking for
   -- a shut card's contents are hidden, not removed. The count stays in
   the head, which is the point: "What they authorized · 2" answers the
   commonest question without anybody opening it. */
.card.foldable > .card-head{padding:0;border-bottom:1px solid var(--line2)}
.card.foldable.shut > .card-head{border-bottom:0}
.card.foldable.shut > *:not(.card-head){display:none}
.foldbtn{display:flex;width:100%;align-items:center;justify-content:space-between;
  gap:12px;font:inherit;font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:.08em;color:var(--dim);cursor:pointer;text-align:left;
  padding:13px var(--pad);min-height:46px;
  background:transparent;border:0;border-radius:inherit}
.foldbtn:hover{color:var(--text2)}
/* The head can be long. It shortens rather than pushing the arrow off. */
.foldlab{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.foldbtn:focus-visible{outline:2px solid var(--green);outline-offset:-2px}
.foldarrow{flex:0 0 auto;width:9px;height:9px;border-right:2px solid currentColor;
  border-bottom:2px solid currentColor;transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .14s ease}
.shut .foldarrow{transform:rotate(-45deg) translate(-1px,1px)}
@media (prefers-reduced-motion:reduce){.foldarrow{transition:none}}

/* Archive on a tile. Pinned to the foot beside the money, the same place
   the row keeps it, so switching view does not move it. */
.tilearch{margin-top:0;margin-left:auto;font-size:11px;min-height:28px;padding:4px 8px}

/* ─── @SOMEBODY ────────────────────────────────────────────────────────
   Anthony: "use the notes area, but let users tag other users and give
   them a notification in our notification bell window about it."

   The tag reads as a name, not as a link: tapping it should do nothing,
   because the person is not a page. It is coloured so the eye finds who a
   note was aimed at without reading the sentence. */
.notetag{font-weight:650;color:var(--green);font-style:normal}

/* The picker, over the box it is filling in. Absolute so a long shop does
   not push the Add note button off a phone screen while you type. */
.noteadd{position:relative}
.tagpick{position:absolute;left:0;right:0;top:100%;z-index:40;margin-top:4px;
  max-height:15rem;overflow-y:auto;border:1px solid var(--border);
  border-radius:10px;background:var(--card);box-shadow:0 10px 30px rgba(0,0,0,.28)}
.tagopt{display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;
  font:inherit;font-size:14px;cursor:pointer;padding:9px 12px;min-height:42px;
  background:transparent;border:0;border-bottom:1px solid var(--line2);color:var(--text)}
.tagopt:last-child{border-bottom:0}
.tagopt.on,.tagopt:hover{background:color-mix(in srgb,var(--green) 12%,transparent)}
.tagopt b{font-weight:600}
.tagopt i{font-style:normal;font-size:12.5px;color:var(--green)}
.tagrole{margin-left:auto;font-size:11px;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text3)}
.notehint{margin-top:7px;font-size:12px;color:var(--text3)}
.notehint b{color:var(--green)}

/* ─── A PAYMENT, AND ONE THAT WAS TAKEN OFF ────────────────────────────
   Anthony: "now the job 1645 has 150 recorded twice and the customer only
   paid 150 total."

   A voided row stays on the page struck through with its reason, because a
   shop with a BAR licence does not delete money records. It has to read as
   plainly NOT money at a glance, hence the strike and the drop in weight,
   and the reason sits under it in full rather than behind a tooltip. */
.payrow{display:grid;grid-template-columns:1fr auto;gap:2px 12px;align-items:baseline;
  padding:8px 0;border-bottom:1px solid var(--line2);font-size:14px}
.payrow:last-child{border-bottom:0}
.paywhen{display:block;font-style:normal;font-size:11.5px;color:var(--text3);margin-top:2px}
.payoff{grid-column:1/-1;justify-self:start;font-size:12px}
.payrow.void{opacity:.62}
.payrow.void > span:first-child,.payrow.void b{text-decoration:line-through}
.payvoid{grid-column:1/-1;font-size:12px;line-height:1.45;color:var(--amber);margin-top:2px}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:700px){.grid2{grid-template-columns:1fr}}

/* tiles */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:11px;margin-bottom:18px}
.tile{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:15px 16px}
.tile:hover{border-color:var(--green2)}
.tile-n{font-size:26px;font-weight:700;line-height:1.1}
.tile-l{font-size:12.5px;color:var(--dim);margin-top:2px}
.tile.warn .tile-n{color:var(--amber)}

/* rows */
.row{display:flex;gap:14px;padding:13px var(--pad);border-bottom:1px solid var(--line2);align-items:flex-start}
.row:last-child{border-bottom:0}
.row:hover{background:var(--line2)}
.row-main{flex:1;min-width:0}
.row-title{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.num{font-weight:650;font-variant-numeric:tabular-nums}
.row-sub{font-size:13.5px;color:var(--ink2);margin-top:1px}
.row-note{font-size:13px;color:var(--dim);margin-top:2px}
.dot{margin:0 6px;color:var(--dim)}
.row-side{text-align:right;flex:none}
.amount{font-weight:650;font-variant-numeric:tabular-nums}
.when{font-size:12px;color:var(--dim)}

.pill{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  padding:2px 8px;border-radius:99px;background:var(--line2);color:var(--ink2)}
.s-estimate{background:color-mix(in srgb,var(--purple) 16%,transparent);color:var(--purple)}
.s-scheduled,.s-in_progress{background:color-mix(in srgb,var(--green) 17%,transparent);color:var(--green)}
.s-paid,.s-invoiced{background:color-mix(in srgb,var(--ok) 17%,transparent);color:var(--ok)}
.s-cancelled{background:color-mix(in srgb,var(--red) 14%,transparent);color:var(--red)}

/* WHERE A QUOTE IS, on the card. Not sent is deliberately the quiet one:
   it is the pile that needs a person, not the pile that needs a colour.
   Sent is the one somebody is hunting for, so it reads like an action that
   has happened rather than one that is owed. */
.q-not_sent{background:var(--line2);color:var(--ink2)}
.q-sent{background:color-mix(in srgb,var(--purple) 16%,transparent);color:var(--purple)}
.q-changes{background:color-mix(in srgb,var(--amber) 20%,transparent);color:var(--amber)}
.q-approved{background:color-mix(in srgb,var(--ok) 17%,transparent);color:var(--ok)}
.q-cancelled{background:color-mix(in srgb,var(--red) 14%,transparent);color:var(--red)}

/* job detail */
.kv{display:flex;justify-content:space-between;gap:14px;padding:6px 0;font-size:14px}
.kv span{color:var(--dim)}
.big{font-size:30px;font-weight:700;letter-spacing:-.01em;margin-bottom:6px}
.auth{margin-top:12px;padding:11px 13px;border-radius:9px;font-size:13.5px;line-height:1.5}
.auth.ok{background:color-mix(in srgb,var(--ok) 12%,transparent)}
.auth.none{background:color-mix(in srgb,var(--red) 11%,transparent)}
.auth .fine{color:var(--dim);font-size:12.5px;margin-top:3px}
.auth .when{color:var(--dim);font-size:12px}
table.lines{width:100%;border-collapse:collapse;font-size:13.5px}
table.lines th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.07em;
  color:var(--dim);padding:9px var(--pad);border-bottom:1px solid var(--line2);font-weight:700}
table.lines td{padding:10px var(--pad);border-bottom:1px solid var(--line2);vertical-align:top}
table.lines tr:last-child td{border-bottom:0}
.n{text-align:right;font-variant-numeric:tabular-nums}
.b{font-weight:650}
.k{color:var(--dim);font-size:12.5px}
.pn{display:block;font-size:11.5px;color:var(--dim);font-family:var(--font-mono)}
.timeline{list-style:none;margin:0;padding:0}
.timeline li{padding:12px var(--pad);border-bottom:1px solid var(--line2);font-size:13.5px}
.timeline li:last-child{border-bottom:0}
.timeline .who{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--dim);margin-left:8px}
.timeline .when{margin-left:8px}
.timeline .detail{color:var(--ink2);margin-top:3px}

/* the door */
.gate{min-height:100vh;display:grid;place-items:center;padding:24px}
.gate-card{width:100%;max-width:24rem;padding:28px 26px}
.sub{color:var(--dim);font-size:14px;margin:0 0 18px}
.sub.small{font-size:12.5px;margin-top:12px}
.tabs{display:flex;gap:4px;background:var(--line2);padding:4px;border-radius:10px;margin-bottom:18px}
.tab{flex:1;border:0;background:none;padding:8px;border-radius:7px;font:inherit;font-size:13.5px;
  color:var(--dim);cursor:pointer}
.tab.on{background:var(--card);color:var(--ink);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.07)}
.field{display:block;margin-bottom:13px}
.field span{display:block;font-size:12.5px;color:var(--dim);margin-bottom:4px}
.field input{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:9px;
  background:var(--bg);color:var(--ink);font:inherit;font-size:14.5px}
.field input:focus{outline:2px solid var(--green2);outline-offset:-1px;border-color:transparent}
.btn{border:1px solid var(--line);background:var(--card);color:var(--ink);font:inherit;font-size:14px;
  padding:9px 15px;border-radius:9px;cursor:pointer}
.btn:hover{border-color:var(--green2)}
.btn.primary{background:var(--green);border-color:var(--green);color:#fff;font-weight:600}
.btn.primary:hover{background:var(--green2);border-color:var(--green2)}
.btn.primary:disabled{opacity:.6;cursor:default}
.btn.ghost{background:none}
.btn.sm{padding:6px 11px;font-size:13px}
.btn.wide{width:100%;margin-top:6px}
.msg{font-size:13px;margin:12px 0 0;padding:9px 11px;border-radius:8px}
.msg.bad{background:color-mix(in srgb,var(--red) 13%,transparent)}
.msg.good{background:color-mix(in srgb,var(--ok) 14%,transparent)}

.empty{padding:34px 20px;text-align:center;color:var(--dim);font-size:14px}
.empty.bad{color:var(--red)}
.loading{padding:30px 20px;text-align:center;color:var(--dim);font-size:13.5px}

/* ── the railway ─────────────────────────────────────────────────────
   Stages read left to right and each one carries two numbers: what is
   sitting there, and how much of it has been sitting too long. A funnel
   that only shows the first number always looks healthy, because every
   row is somewhere. */
.rail{display:flex;align-items:stretch;gap:0;flex-wrap:wrap}
.rail-stage{flex:1 1 96px;min-width:96px;text-align:center;padding:12px 8px;border-radius:10px;
  border:1px solid transparent}
.rail-stage:hover{background:var(--line2)}
.rail-stage.hot{border-color:color-mix(in srgb,var(--amber) 55%,transparent);
  background:color-mix(in srgb,var(--amber) 9%,transparent)}
.rail-n{font-size:24px;font-weight:700;line-height:1.15;font-variant-numeric:tabular-nums}
.rail-l{font-size:12px;color:var(--dim);margin-top:1px}
.rail-stuck{font-size:11.5px;font-weight:650;color:var(--amber);margin-top:4px}
.rail-ok{font-size:11.5px;color:var(--dim);opacity:.55;margin-top:4px}
.rail-link{flex:0 0 14px;align-self:center;height:2px;background:var(--line);border-radius:2px}
@media(max-width:640px){.rail-link{display:none}.rail-stage{flex-basis:31%}}
.rail-notes{margin-top:14px;padding-top:13px;border-top:1px solid var(--line2);
  font-size:13px;color:var(--ink2);display:grid;gap:5px}
.rail-notes.clear{color:var(--dim)}
.rail-notes b{color:var(--ink)}
.fell-l{font-size:13.5px;color:var(--ink2)}
.num.bad{color:var(--red)}

/* ── search and paging ───────────────────────────────────────────── */
.searchbar{margin-bottom:14px}
.searchbar input{width:100%;padding:11px 14px;border:1px solid var(--line);border-radius:11px;
  background:var(--card);color:var(--ink);font:inherit;font-size:15px}
.searchbar input:focus{outline:2px solid var(--green2);outline-offset:-1px;border-color:transparent}
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;
  font-size:13px;color:var(--dim);padding:0 2px}
.pager .btn:disabled{opacity:.4;cursor:default}
.tabs.wide{max-width:none}
.card-sub{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  color:var(--dim);margin-bottom:8px}
.quiet{color:var(--dim);font-size:13.5px}
.pad-sm{padding:0 var(--pad) 10px}
.card-head.bad{color:var(--red)}
.warnbox{border-color:color-mix(in srgb,var(--red) 45%,transparent)}
.row.cold{background:color-mix(in srgb,var(--amber) 7%,transparent)}
.amount.bad{color:var(--red)}
.slot{flex:0 0 66px;font-size:13px;font-variant-numeric:tabular-nums;color:var(--ink2);padding-top:1px}

/* ── the track everybody watches ──────────────────────────────────── */
.track{display:flex;align-items:flex-start;gap:0;flex-wrap:wrap;margin-bottom:16px}
.stop{flex:1 1 68px;min-width:68px;text-align:center;position:relative;padding-top:20px}
.stop::before{content:"";position:absolute;top:6px;left:50%;right:-50%;height:2px;background:var(--line)}
.stop:last-child::before{display:none}
.stop .dotm{position:absolute;top:0;left:50%;transform:translateX(-50%);width:14px;height:14px;
  border-radius:50%;background:var(--card);border:2px solid var(--line);font-size:9px;line-height:11px;
  color:transparent;z-index:1}
.stop-l{font-size:11px;color:var(--dim);letter-spacing:.01em}
.stop.past::before{background:var(--green)}
.stop.past .dotm{background:var(--green);border-color:var(--green);color:#fff}
.stop.past .stop-l{color:var(--ink2)}
.stop.now .dotm{background:var(--card);border-color:var(--green);box-shadow:0 0 0 3px color-mix(in srgb,var(--green) 22%,transparent)}
.stop.now .stop-l{color:var(--ink);font-weight:650}
.stop.halted .dotm{border-color:var(--red)}
.stop.halted.now .dotm{box-shadow:0 0 0 3px color-mix(in srgb,var(--red) 20%,transparent)}
/* Each stop draws its connector 50% past its own right edge, and
   `:last-child` only hides the last one in the whole track — not the last
   one on a WRAPPED row. So on a phone the fourth stop's line ran off into
   nothing and gave the card a horizontal scrollbar. The lines are
   decoration; clipping them is the honest fix. */
.track{overflow:hidden}
@media(max-width:560px){.stop{flex-basis:25%}.stop-l{font-size:10px}}

.next{border-radius:10px;padding:13px 15px;background:color-mix(in srgb,var(--green) 10%,transparent)}
.next.done{background:color-mix(in srgb,var(--ok) 11%,transparent)}
.next.halted{background:color-mix(in srgb,var(--red) 10%,transparent)}
.next-h{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dim)}
.next-a{font-size:16.5px;font-weight:650;margin-top:2px}
.next-w{font-size:13px;color:var(--ink2);margin-top:3px;line-height:1.5}
#bar nav a.more{margin-left:auto;color:var(--dim)}
.row.soon{opacity:.5}
.mono{font-family:var(--font-mono);font-size:12px}

/* the one form this build has, so far */
.authform{margin-top:14px;padding:14px 15px;border-radius:10px;
  background:color-mix(in srgb,var(--purple) 9%,transparent)}
.authform .fields{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px}
.authform .field{margin:0}
.authform .field.wide{grid-column:1/-1}
.authform select{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:9px;
  background:var(--bg);color:var(--ink);font:inherit;font-size:14.5px}
.af-row{display:flex;gap:9px;align-items:center;margin-top:12px}
.authform .fine{font-size:12px;color:var(--dim);margin-top:10px;line-height:1.5}
@media(max-width:560px){.authform .fields{grid-template-columns:1fr}}

/* the theme switch */
#bar .themebtn{border:1px solid var(--border);background:none;color:var(--text3);
  font:inherit;font-size:15px;line-height:1;padding:6px 9px;border-radius:8px;cursor:pointer}
#bar .themebtn:hover{color:var(--text);border-color:var(--green)}
.btn.primary{color:#08140a}
:root[data-theme="light"] .btn.primary{color:#fff}

/* Icons: one rule, because they are all stroked and all take their colour
   from whatever they sit in. Nothing per-icon, ever. */
svg{fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
#bar nav a{display:inline-flex;align-items:center;gap:7px}
#bar nav a svg{width:16px;height:16px;opacity:.75}
#bar nav a.on svg{opacity:1;color:var(--green2)}
.rowicon{flex:0 0 20px;color:var(--text3);padding-top:2px}
.rowicon svg{width:19px;height:19px}
@media(max-width:820px){#bar nav a span{display:none}#bar nav a{padding:7px 10px}}

/* ═══════════════════════════════════════════════════════════════════
   THE CALENDAR — it had no styles at all until now, which is why it
   rendered as forty-two stacked divs and read as "overlap everywhere".
   Markup shipped without its stylesheet is not a small mistake.
   ═══════════════════════════════════════════════════════════════════ */
.cal-head{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap}
.cal-head b{font-size:15.5px;min-width:9.5rem}
.cal-dow{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin-bottom:5px}
.cal-dow div{font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text3);text-align:center;padding-bottom:2px}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:5px}
.cal-cell{min-height:86px;border:1px solid var(--border);border-radius:9px;padding:5px;
  background:var(--bg);display:flex;flex-direction:column;gap:3px;overflow:hidden}
.cal-cell.other{opacity:.38}
.cal-cell.today{border-color:var(--green);box-shadow:inset 0 0 0 1px var(--green)}
.cal-cell.has{background:var(--bg2)}
.cal-date{font-size:11.5px;color:var(--text3);font-variant-numeric:tabular-nums;line-height:1.2}
.cal-cell.today .cal-date{color:var(--green2);font-weight:700}
.cal-chip{display:block;font-size:10.5px;line-height:1.35;padding:2px 5px;border-radius:5px;
  background:var(--bg3);color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-chip b{font-variant-numeric:tabular-nums;font-weight:650}
.cal-chip:hover{background:var(--bg4);color:var(--text)}
.cal-more{font-size:10px;color:var(--text3);padding-left:5px}

/* A seven-column grid cannot be read on a phone. Below 720 the month
   becomes a list of the days that actually have work on them, which is
   the only part anybody reads on a small screen anyway. */
@media(max-width:720px){
  .cal-dow{display:none}
  .cal-grid{grid-template-columns:1fr;gap:6px}
  .cal-cell{min-height:0;flex-direction:row;align-items:flex-start;flex-wrap:wrap;gap:6px;padding:9px 11px}
  .cal-cell:not(.has){display:none}
  .cal-cell.other{display:none}
  /* A day already gone is not what you opened the schedule to see. It is
     still on the desk grid, dimmed; on a phone the agenda starts where you
     are standing. Walk back to a past month and every day of it returns,
     because then the past IS what you asked for. */
  .cal-cell.gone{display:none}
  .cal-date{flex:0 0 2rem;font-size:14px;font-weight:700;color:var(--text)}
  .cal-chip{font-size:12px;padding:3px 8px}
  .cal-head b{min-width:0;flex:1}
}

/* ═══════════════════════════════════════════════════════════════════
   THE PICKER  ‹ Newest first ›
   ═══════════════════════════════════════════════════════════════════ */
.picker{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.picker-cap{font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--text3)}
.picker-ctl{position:relative;display:inline-flex;align-items:stretch;
  border:1px solid var(--border);border-radius:9px;background:var(--bg2);overflow:visible}
.picker-arrow{border:0;background:none;color:var(--text3);font:inherit;font-size:17px;line-height:1;
  padding:7px 11px;cursor:pointer}
.picker-arrow:hover{color:var(--green2);background:var(--bg3)}
.picker-arrow:first-child{border-radius:8px 0 0 8px}
.picker-now{border:0;border-left:1px solid var(--border);border-right:1px solid var(--border);
  background:none;color:var(--text);font:inherit;font-size:13.5px;font-weight:600;
  padding:7px 13px;cursor:pointer;min-width:9rem}
.picker-now:hover{background:var(--bg3)}
.picker-menu{position:absolute;top:calc(100% + 5px);left:0;right:0;z-index:30;
  background:var(--bg2);border:1px solid var(--border2);border-radius:10px;
  box-shadow:var(--shadow);padding:4px;min-width:11rem}
.picker-opt{display:block;width:100%;text-align:left;border:0;background:none;color:var(--text2);
  font:inherit;font-size:13.5px;padding:8px 11px;border-radius:7px;cursor:pointer}
.picker-opt:hover{background:var(--bg3);color:var(--text)}
.picker-opt.on{color:var(--green2);font-weight:650}
.listbar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:13px;flex-wrap:wrap}

/* ═══════════════════════════════════════════════════════════════════
   RESPONSIVE SWEEP
   The bar carried height:56px AND flex-wrap:wrap, which is a
   contradiction: the moment it wrapped, the second row rendered outside
   the box and sat on top of the page. Fixed height and wrapping cannot
   both be true.
   ═══════════════════════════════════════════════════════════════════ */
#bar{height:auto;min-height:56px;padding:8px 16px;gap:12px;row-gap:6px}
#bar nav{flex:1 1 100%;order:3;overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;gap:2px}
#bar nav::-webkit-scrollbar{display:none}
#bar .brand{order:1;flex:1}
#bar .me{order:2}
@media(min-width:900px){
  #bar{padding:0 20px;row-gap:0}
  #bar nav{flex:1 1 auto;order:2;overflow:visible}
  #bar .me{order:3}
}
@media(max-width:520px){
  /* Direct child only. The flyout menu lives inside .me, so a descendant
     selector here blanks every label in it — the same mistake twice, in
     two media blocks, which is what a duplicated rule is for. */
  #bar .me > span{display:none}
  #bar .brand{font-size:15px}
  .page{padding:18px 14px 56px}
  .card.pad{padding:14px}
  .card-head{padding:11px 14px}
  .row{padding:11px 14px;gap:10px}
  h1{font-size:18px}
}

/* Rows: the side column was free to grow and squeeze the name to nothing. */
.row-side{min-width:5.5rem}
.row-title{gap:6px}
.row-sub{overflow-wrap:anywhere}

/* A table cannot reflow, so it scrolls inside its own card rather than
   pushing the page sideways. */
.card{overflow-x:auto}
table.lines{min-width:30rem}

/* The two-up grid was still two-up inside a phone-width card. */
@media(max-width:640px){
  .grid2{grid-template-columns:1fr;gap:12px}
  .tiles{grid-template-columns:repeat(auto-fit,minmax(96px,1fr))}
  .pager{font-size:12px}
  .picker-now{min-width:7.5rem;font-size:13px}
  .picker-cap{display:none}
}

/* Tablet: six nav items and a name fit, the railway needs two rows. */
@media(min-width:641px) and (max-width:900px){
  .rail-stage{flex-basis:30%}
  .rail-link{display:none}
}

.pager-note{font-size:12.5px;color:var(--text3)}

/* ═══════════════════════════════════════════════════════════════════
   LEADS AS CARDS
   A job row is a record you scan. A lead is a person you are about to
   ring, and the number, the want, the place and the wait do not fit on
   one line without turning into a wall of small grey text.
   ═══════════════════════════════════════════════════════════════════ */
.leadgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:12px}
.lead{text-align:left;border:1px solid var(--border);background:var(--bg2);border-radius:var(--radius);
  padding:14px 15px;cursor:pointer;font:inherit;color:var(--text);display:flex;flex-direction:column;
  gap:9px;min-height:132px}
.lead:hover{border-color:var(--green);transform:translateY(-1px)}
.lead.hot{border-color:color-mix(in srgb,var(--green) 55%,transparent)}
.lead.cold{border-color:color-mix(in srgb,var(--amber) 50%,transparent);
  background:color-mix(in srgb,var(--amber) 6%,var(--bg2))}
.lead-top{display:flex;align-items:center;gap:10px}
.avatar{flex:0 0 34px;height:34px;border-radius:50%;display:grid;place-items:center;
  background:var(--bg3);color:var(--text2);font-size:12.5px;font-weight:700;letter-spacing:.02em}
.avatar.big{flex-basis:44px;height:44px;font-size:15px}
.lead-who{min-width:0}
.lead-name{font-weight:650;font-size:14.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lead-age{font-size:12px;color:var(--text3)}
.lead.cold .lead-age{color:var(--amber)}
.lead-want{font-size:13px;color:var(--text2);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.lead-foot{margin-top:auto;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.lead-tel{font-size:12.5px;color:var(--green2);font-variant-numeric:tabular-nums}
.lead-tel.none{color:var(--text3)}
.tabs .tab i{font-style:normal;font-size:11px;opacity:.65;margin-left:5px}

/* the panel it pops out into */
/* ── the sheet ────────────────────────────────────────────────────────
   A panel taller than the screen used to be UNREACHABLE at the top. It
   was centred with `place-items:center` inside a scrolling backdrop, and
   centring overflowing content pushes its top edge above the scroll
   origin — so the person's name, and on the team panel the first fields,
   simply could not be scrolled to. The team editor grew long enough for
   that to bite.

   `margin:auto` in a flex container centres a panel that FITS and pins a
   panel that does not to the top, which is the behaviour wanted in both
   cases. The panel then caps its own height and scrolls internally, with
   the head and the buttons pinned so Save is never the thing off screen.

   dvh, not vh: on a phone vh is the tallest the viewport ever gets, so a
   sheet sized in vh hides its bottom behind the browser's own chrome —
   the other half of "goes too high or low". */
.sheet-back{position:fixed;inset:0;z-index:100;background:rgba(0,0,0,.55);
  display:flex;justify-content:center;padding:20px;overflow-y:auto;
  overscroll-behavior:contain}
.sheet{width:100%;max-width:30rem;margin:auto;background:var(--bg2);
  border:1px solid var(--border2);border-radius:16px;box-shadow:var(--shadow);
  display:flex;flex-direction:column;max-height:calc(100dvh - 40px);overflow:hidden}
.sheet-head{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding:18px 18px 14px}
.sheet-head h2{margin:0;font-size:17.5px}
.sheet-x{margin-left:auto;border:0;background:none;color:var(--text3);font-size:26px;line-height:1;
  cursor:pointer;padding:0 4px}
.sheet-x:hover{color:var(--text)}
.sheet-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:0 18px 4px;-webkit-overflow-scrolling:touch}
/* A shadow only while there is more below, so it is a cue rather than
   decoration. */
.sheet-body{scrollbar-width:thin}
.aged-note{margin:0 18px 12px;padding:10px 12px;border-radius:9px;font-size:13px;line-height:1.5;
  background:color-mix(in srgb,var(--amber) 12%,transparent);color:var(--text2)}
.sheet-acts{flex:0 0 auto;display:flex;gap:8px;flex-wrap:wrap;padding:14px 18px 18px;
  border-top:1px solid var(--border);background:var(--bg2)}
.sheet-acts .btn{flex:1 1 auto;text-align:center}
.msg-slot:empty{display:none}
.msg-slot{padding:0 18px 16px}
.sheet > .msg-slot{flex:0 0 auto}
@media(max-width:560px){
  .leadgrid{grid-template-columns:1fr}
  /* A bottom sheet, but one that stops short of the top of the screen so
     it never looks like it has been cut off, and never covers the whole
     display. `margin-top:auto` pins it down without centring. */
  .sheet-back{align-items:flex-end;padding:0}
  .sheet{max-width:none;margin:auto auto 0;border-radius:16px 16px 0 0;border-bottom:0;
    max-height:92dvh}
  .sheet-head{padding:15px 15px 11px}
  .sheet-body{padding:0 15px 4px}
  .sheet-acts{padding:12px 15px 16px}
}

/* ── click the thing, change the thing ────────────────────────────── */
.ed{display:inline-block;min-width:2.5rem;border-radius:6px;padding:1px 5px;margin:-1px -5px;
  cursor:text;border:1px solid transparent;transition:background .1s,border-color .1s}
.ed:hover,.ed:focus{background:var(--bg3);border-color:var(--border);outline:none}
.ed i{color:var(--text3);font-style:normal}
.ed.locked{cursor:default;border-color:transparent}
.ed.locked:hover{background:none;border-color:transparent}
.ed.saving{opacity:.55}
.ed.saved{background:color-mix(in srgb,var(--green) 20%,transparent);border-color:var(--green)}
.ed.failed{background:color-mix(in srgb,var(--red) 18%,transparent);border-color:var(--red)}
.ed-input{width:100%;min-width:8rem;font:inherit;font-size:inherit;color:var(--text);
  background:var(--bg);border:1px solid var(--green);border-radius:6px;padding:3px 6px;outline:none}
textarea.ed-input{resize:vertical;line-height:1.5}
h1 .ed{min-width:6rem}
.kv b .ed{font-weight:inherit}

/* ── the five stars ───────────────────────────────────────────────────
   The headline on a job card: readable from across a room, before
   anybody has read a word of the detail underneath. */
/* THE STARS LINE UP ACROSS, and the labels hang below them.

   This was align-items:flex-end, which lines up the BOTTOMS -- so a step
   whose label wraps to two lines pushed its star up while "Paid" on one
   line sat low, and the row read as wonky rather than as a railway. The
   icons are the thing the eye tracks left to right, so they are what has
   to be level. */
.stars{display:flex;align-items:flex-start;gap:4px;margin-bottom:16px;flex-wrap:wrap}
.star{flex:1 1 54px;min-width:54px;text-align:center;padding:6px 2px;border-radius:9px}
.star svg{width:26px;height:26px;stroke:var(--border2);fill:none;stroke-width:1.4;display:block;margin:0 auto}
.star span{display:block;font-size:10.5px;color:var(--text3);margin-top:3px;letter-spacing:.01em}
.star.on svg{stroke:var(--green2);fill:var(--green2)}
.star.on span{color:var(--text2);font-weight:600}
.star-count{margin-left:auto;font-size:22px;font-weight:700;color:var(--green2);
  font-variant-numeric:tabular-nums;padding-bottom:2px;align-self:center}
.star-count i{font-style:normal;font-size:13px;color:var(--text3);font-weight:600}
@media(max-width:520px){
  /* ONE ROW, WHATEVER THE STEP COUNT.

     This was flex-basis:19%, which is 5 x 19% = 95% and fits exactly five
     stars. Adding "Work started" made six, 114%, and Paid wrapped onto a
     row of its own -- the same ragged-row bug the job tabs had, from the
     same cause: a magic percentage that silently encodes how many things
     there are.

     flex:1 1 0 with nowrap divides the line evenly among however many
     stars exist. A seventh step would still sit on one row. */
  .stars{flex-wrap:nowrap}
  .star{flex:1 1 0;min-width:0;padding:4px 0}
  .star svg{width:22px;height:22px}
  .star span{font-size:9.5px;overflow-wrap:anywhere}
  .star-count{display:none}
}

/* ═══════════════════════════════════════════════════════════════════
   THE BOTTOM BAR
   A thumb reaches the bottom of a phone and does not reach the top of
   one. This is a tool used one-handed next to a car, so on a phone the
   destinations sit where the hand already is and the top bar keeps only
   the shop name and the way out.
   ═══════════════════════════════════════════════════════════════════ */
#tabbar{display:none}
@media(max-width:820px){
  #tabbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:50;
    background:var(--bg2);border-top:1px solid var(--border);
    padding:6px 4px calc(6px + env(safe-area-inset-bottom,0px));
    box-shadow:0 -2px 14px rgba(0,0,0,.22)}
  #tabbar a{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
    padding:6px 2px;border-radius:10px;color:var(--text3);min-width:0}
  #tabbar a svg{width:23px;height:23px}
  #tabbar a span{font-size:10px;letter-spacing:.01em;white-space:nowrap;
    overflow:hidden;text-overflow:ellipsis;max-width:100%}
  #tabbar a.on{color:var(--green2)}
  #tabbar a.on svg{stroke-width:2}
  /* The top bar drops its nav entirely — two navs on one screen is the
     button overload this whole build exists to avoid. */
  #bar nav{display:none}
  #bar{min-height:52px;padding:8px 14px}
  /* Every full-page container, not just .page -- see the note on .wrap.
     88px is the bar plus room for a thumb under the last button. */
  .page,.wrap{padding-bottom:88px}
  .sheet-back{padding-bottom:76px}
}

/* ── icons were too small to hit ──────────────────────────────────── */
@media(max-width:820px){
  .rowicon{flex-basis:26px}
  .rowicon svg{width:24px;height:24px}
  #bar .themebtn{font-size:18px;padding:8px 11px}
}

/* ── editing on a phone ───────────────────────────────────────────────
   A tap target the size of the text is a tap target you miss. Fields
   become full-width rows with room around them, the label sits above
   rather than beside, and the input is 16px because anything smaller
   makes iOS zoom the page on focus and never zoom back. */
@media(max-width:640px){
  .kv{flex-direction:column;align-items:stretch;gap:3px;padding:9px 0;
    border-bottom:1px solid var(--line2)}
  .kv:last-child{border-bottom:0}
  .kv span{font-size:11.5px;text-transform:uppercase;letter-spacing:.06em}
  .kv b{font-weight:600;font-size:15px}
  .ed{display:block;min-height:38px;padding:7px 9px;margin:0;
    border:1px dashed var(--border);border-radius:9px}
  .ed:hover,.ed:focus{border-style:solid}
  .ed.locked{border-color:transparent;padding-left:0}
  .ed-input{font-size:16px;padding:8px 10px;min-height:38px}
  h1 .ed{font-size:19px;padding:6px 9px}
}

/* ── the schedule stepper ─────────────────────────────────────────── */
.stepper{display:flex;gap:6px;align-items:center}
.stepper .btn{padding:7px 12px}
@media(max-width:640px){
  .listbar{gap:8px}
  .stepper{flex:1;justify-content:flex-end}
}
/* Month cells are buttons now, not divs. */
button.cal-cell{font:inherit;color:inherit;text-align:left;cursor:pointer}
button.cal-cell:hover{border-color:var(--green)}
.cal-chip{pointer-events:none}

/* ── purple on the railway and the stars ──────────────────────────────
   Asked for directly. It also separates two things that were both green
   and meant different things: the railway and the stars are STATE, while
   green stays the colour of an action you can take.

   Stuck stays amber on purpose. It is a warning rather than a state, and
   a warning that wears the same colour as everything around it is not one. */
.rail-stage{border:1px solid transparent}
.rail-stage:hover{background:color-mix(in srgb,var(--purple) 10%,transparent);
  border-color:color-mix(in srgb,var(--purple) 40%,transparent)}
.rail-n{color:var(--purple2)}
.rail-stage.hot .rail-n{color:var(--amber)}
.stop.past::before{background:var(--purple)}
.stop.past .dotm{background:var(--purple);border-color:var(--purple);color:#fff}
.stop.now .dotm{border-color:var(--purple2);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--purple) 26%,transparent)}

.star.on svg{stroke:var(--purple2);fill:var(--purple2)}
.star-count{color:var(--purple2)}

/* ── mobile or shop ───────────────────────────────────────────────────
   Two shapes nobody confuses at a glance. Not said is deliberately dim
   rather than hidden: it is a real state and worth noticing. */
.wherebadge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:650;
  padding:2px 9px 2px 7px;border-radius:99px;letter-spacing:.01em;
  background:color-mix(in srgb,var(--blue) 15%,transparent);color:var(--blue)}
.wherebadge svg{width:14px;height:14px}
.wherebadge.shop{background:color-mix(in srgb,var(--purple) 16%,transparent);color:var(--purple2)}
.wherebadge.unset{background:var(--bg3);color:var(--text3);font-weight:500}
.wherebadge.sm{font-size:10.5px;padding:1px 7px 1px 5px}
.wherebadge.sm svg{width:12px;height:12px}
.choice{display:inline-flex;gap:6px;flex-wrap:wrap}
.chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);
  background:var(--bg);color:var(--text3);font:inherit;font-size:13px;font-weight:600;
  padding:7px 12px;border-radius:9px;cursor:pointer}
.chip svg{width:16px;height:16px}
.chip:hover{border-color:var(--purple2);color:var(--text)}
.chip.on{border-color:var(--purple2);color:var(--purple2);
  background:color-mix(in srgb,var(--purple) 13%,transparent)}

/* ── the centred menu ─────────────────────────────────────────────────
   Desk is not item one of seven, it is the place you come back to between
   the other six. Centring it means the same thumb travel home from either
   side of a phone, and on a wide screen it reads as a hub. */
#bar nav{justify-content:center}
#bar nav a.home svg{color:var(--purple2)}
#bar nav a.home.on{background:color-mix(in srgb,var(--purple) 16%,transparent)}
#tabbar a.home svg{width:27px;height:27px;color:var(--purple2)}
#tabbar a.home.on{color:var(--purple2)}
#tabbar a.home.on svg{color:var(--purple2)}

/* ── the railway, scoped and clickable ────────────────────────────── */
.railhead{display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-bottom:14px;flex-wrap:wrap}
.railhead .picker-cap{display:none}
@media(max-width:640px){.railhead{gap:8px}}

/* ── the bar, actually centred ────────────────────────────────────────
   justify-content:center did nothing while the brand had flex:1 and the
   name beside it did not — the nav was centred inside whatever space was
   left over, which is not the middle of anything. Both flanks now take
   an equal share, so the middle is the middle. */
@media(min-width:900px){
  #bar .brand{flex:1 1 0;min-width:0}
  #bar .me{flex:1 1 0;min-width:0;justify-content:flex-end}
  #bar nav{flex:0 0 auto;justify-content:center}
}

/* the stage picker on a job */
.stagepick{display:flex;gap:6px;flex-wrap:wrap;margin-top:2px}
.stagepick .chip{padding:6px 11px;font-size:12.5px}
.appt input{width:100%;font:inherit;font-size:15px;color:var(--text);background:var(--bg);
  border:1px solid var(--border);border-radius:9px;padding:8px 10px}
.appt input:focus{outline:2px solid var(--purple2);outline-offset:-1px;border-color:transparent}
.appt .btn{margin-top:8px}
.appt-row{display:flex;gap:8px}
.appt-row input{flex:1;min-width:0}

/* ── money on a job ───────────────────────────────────────────────── */
.card .pad{padding:var(--pad)}
.owed{color:var(--amber)}
.settled{color:var(--green2)}
.paylist{margin:10px 0 4px;border-top:1px solid var(--line2);padding-top:8px}
.payrow{display:flex;justify-content:space-between;gap:12px;font-size:13.5px;padding:4px 0;color:var(--text2)}
.paytake{margin-top:14px;padding-top:14px;border-top:1px solid var(--line2)}
.payin{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.payin input{flex:1 1 8rem;min-width:0;font:inherit;font-size:15px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:9px 11px}
.payin input:focus{outline:2px solid var(--green2);outline-offset:-1px;border-color:transparent}
.payin .btn{flex:0 0 auto}
.paytake .fine{font-size:12px;color:var(--text3);margin-top:9px;line-height:1.5}
.settledbox{margin-top:12px;padding:10px 13px;border-radius:9px;font-size:13.5px;font-weight:600;
  color:var(--green2);background:color-mix(in srgb,var(--green) 12%,transparent)}
@media(max-width:560px){.payin input{flex-basis:100%}.payin .btn{width:100%}}

/* The span control sits to the RIGHT, on every screen that has one.
   Asked for, and it is the better place: the eye starts at the heading on
   the left and the control is a modifier, not the subject. */
/* THE DATE RANGE on Booked and Lost. Two boxes and a sentence saying what
   they are filtering, because a range with no stated column is a filter
   people guess at. Wraps to its own row on a phone rather than squeezing
   two date inputs into half a screen. */
.rangebar{flex-wrap:wrap}
.rangepick{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rangepick label{display:flex;align-items:center;gap:6px;font-size:12.5px;
  color:var(--text3);white-space:nowrap}
.rangepick input[type=date]{font:inherit;font-size:13px;padding:5px 8px;
  border:1px solid var(--border);border-radius:8px;
  background:var(--card);color:var(--text);min-height:34px}
@media (max-width:560px){
  .rangebar .pager-note{flex:1 1 100%}
  .rangepick{width:100%}
  .rangepick label{flex:1}
  .rangepick input[type=date]{flex:1;min-width:0}
}

/* ─── CLASSES THAT WERE WRITTEN AND NEVER STYLED ──────────────────────
   Found by tools/smoke.mjs, which flags a class used exactly once that
   app.css has never heard of. That is the shape of the `.wrap` bug: the
   markup says a thing is special, the stylesheet does not agree, and the
   screen renders wrong without anything failing. Nine of them. */

/* The approve-or-refuse box on the desk. Its input had class="input",
   which does not exist, so it rendered as a raw browser field beside two
   styled buttons. */
.desk-decide{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;align-items:center}
.desk-decide .input{flex:1 1 240px;min-width:0}
.input{font:inherit;font-size:14px;padding:9px 11px;min-height:40px;
  border:1px solid var(--border);border-radius:9px;
  background:var(--card);color:var(--text)}
.input:focus{outline:2px solid var(--green);outline-offset:1px}

/* A warranty job, on the job card. It is a kv row saying the shop is not
   being paid, which should not look like every other row. */
.kv.nocharge b{color:var(--amber)}
.kv.nocharge span{color:var(--amber);opacity:.85}

/* The parts-deposit block sat flush against the money above it. */
.deposit{margin-top:14px;padding-top:12px;border-top:1px solid var(--line2)}

/* The warning above Send the portal link: this sends the link, not the
   price. It was a bare paragraph, so the one sentence that stops somebody
   sending an unsent quote read like body copy. */
.cp-warn{margin:8px 0 0;padding:9px 11px;font-size:13px;line-height:1.5;
  color:var(--text2);border-radius:8px;
  border:1px solid color-mix(in srgb,var(--amber) 45%,transparent);
  background:color-mix(in srgb,var(--amber) 8%,transparent)}

/* A text in the conversation strip. Long messages ran to the edge with no
   wrapping rule of their own. */
.talk-t{font-size:13.5px;line-height:1.5;overflow-wrap:anywhere;white-space:pre-wrap}

/* The role picker when adding somebody to the team. */
.roles{display:flex;flex-direction:column;gap:8px}

/* A tyre on the inspection sheet, and an invoice row in the terminal.
   Both are hooks with no styling of their own, and both are correct as
   they are -- named here so the sweep stops asking. */
.ipt.tire,.row.tinv{}

.listbar.right{justify-content:flex-end}
.listbar.right .pager-note,.listbar.right .stepper{margin-right:auto}
@media(max-width:560px){
  .listbar.right{justify-content:space-between}
  .listbar.right .pager-note{margin-right:0;flex:1 1 100%;order:2}
}

/* ── the quote, edited in place ───────────────────────────────────────
   Cost and customer price are two columns for two audiences, and only one
   of them is ever on an invoice. */
.lineedit table.lines{min-width:38rem}
.lineedit th i{font-style:normal;color:var(--text3);cursor:help;font-size:11px}
input.li,select.li{width:100%;font:inherit;font-size:13.5px;color:var(--text);background:var(--bg);
  border:1px solid transparent;border-radius:7px;padding:6px 8px}
input.li:hover,select.li:hover{border-color:var(--border)}
input.li:focus,select.li:focus{outline:2px solid var(--purple2);outline-offset:-1px;background:var(--bg2)}
input.li.n{text-align:right;font-variant-numeric:tabular-nums}
.li-desc{min-width:12rem}
.li-cost{color:var(--text3)}
.li-x{border:0;background:none;color:var(--text3);font-size:19px;line-height:1;cursor:pointer;padding:2px 6px}
.li-x:hover{color:var(--red)}
.lineadd{display:flex;gap:8px;align-items:center;padding:12px var(--pad);flex-wrap:wrap;
  border-top:1px solid var(--line2)}
.grandtotal{margin-left:auto;font-size:19px;font-weight:700;font-variant-numeric:tabular-nums}
.lineedit .fine{padding:0 var(--pad) 14px;font-size:12px;color:var(--text3);line-height:1.55}
td{position:relative}
.sugg{position:absolute;right:8px;bottom:-7px;font:inherit;font-size:10.5px;font-weight:650;
  border:1px solid var(--purple2);color:var(--purple2);background:var(--bg2);
  border-radius:99px;padding:1px 7px;cursor:pointer;white-space:nowrap;z-index:2}
.sugg:hover{background:color-mix(in srgb,var(--purple) 18%,transparent)}

/* ── the profile menu ─────────────────────────────────────────────────
   On a phone the bottom bar is six places you GO. Everything else hangs
   off who you are, which is where a person already looks for it. */
.avatarbtn{display:none;width:34px;height:34px;border-radius:50%;border:1px solid var(--border);
  background:var(--bg3);color:var(--text2);font:inherit;font-size:12.5px;font-weight:700;
  cursor:pointer;letter-spacing:.02em}
.avatarbtn:hover{border-color:var(--green);color:var(--text)}
/* THE MENU HAS TO END SOMEWHERE YOU CAN REACH.
   Anthony: "on the top right nav menu on my phone I can't scroll to the
   bottom right of it or reach the bottom of the menu."

   It had no height limit, so on a phone it grew past the bottom of the
   screen and the last items -- which include Sign out -- were simply not
   reachable. The page behind it does not scroll while a fixed menu is
   over it, so there was no way down at all.

   Scrolling beats folding here. A fold hides the same items behind one
   more press and makes somebody guess which fold they are in; a scroll
   keeps every label visible and is the gesture a thumb is already making.
   The cap is the viewport minus the bar and a margin, so the menu always
   ends above the bottom edge. */
.profmenu{position:absolute;top:calc(100% + 6px);right:12px;z-index:60;min-width:14rem;
  max-height:calc(100vh - var(--header-h,56px) - 24px);overflow-y:auto;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  background:var(--bg2);border:1px solid var(--border2);border-radius:12px;
  box-shadow:var(--shadow);padding:6px;display:none}
.profmenu-who{padding:9px 11px 8px;font-size:14px;font-weight:650;border-bottom:1px solid var(--line2);
  margin-bottom:5px}
.profmenu-who i{display:block;font-style:normal;font-size:10.5px;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text3);font-weight:600;margin-top:1px}
.profmenu a,.profmenu button{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  border:0;background:none;color:var(--text2);font:inherit;font-size:14px;padding:9px 11px;
  border-radius:8px;cursor:pointer}
.profmenu a:hover,.profmenu button:hover{background:var(--bg3);color:var(--text)}
.profmenu svg{width:18px;height:18px;flex:none}
.profmenu-sep{height:1px;background:var(--line2);margin:5px 0}

@media(max-width:820px){
  #bar{position:sticky;overflow:visible}
  .avatarbtn{display:grid;place-items:center}
  /* `dvh` follows the browser chrome as it hides and shows, which `vh`
     does not -- on iOS a `vh` menu is still cut off once the address bar
     slides away. The `vh` line above stays as the fallback. */
  .profmenu{max-height:calc(100dvh - 76px);left:12px;right:12px;min-width:0}
  .profmenu:not([hidden]){display:block}
  /* The name, the theme button and Sign out all live inside the menu on a
     phone, so the bar keeps only the brand and the avatar.

     The CHILD combinator matters. Written as `#bar .me span` this also
     matched every label inside the flyout — the menu is inside .me — so
     the whole menu rendered as a column of unlabelled icons. */
  #bar .me > span,#bar .themebtn,#bar #signout{display:none}
}

/* ── the comms ledger ─────────────────────────────────────────────────
   A call and a text do not look the same on purpose. A call has a length
   and sometimes a recording; a text has words. Rendering both as a
   generic "event" throws away the thing that tells you which is which
   from across the room. */
.commlist{max-height:none}
.comm{display:flex;gap:12px;padding:12px var(--pad);border-bottom:1px solid var(--line2);
  align-items:flex-start}
.comm:last-child{border-bottom:0}
.comm-ic{flex:0 0 20px;padding-top:2px;color:var(--text3)}
.comm-ic svg{width:18px;height:18px}
.comm.out .comm-ic{color:var(--green2)}
.comm.in .comm-ic{color:var(--blue)}
.comm-main{flex:1;min-width:0}
.comm-head{display:flex;align-items:center;gap:7px;flex-wrap:wrap;font-size:13.5px}
.comm-what{color:var(--text3)}
.comm-sub{font-size:13.5px;font-weight:600;margin-top:2px}
.comm-body{font-size:13.5px;color:var(--text2);margin-top:3px;line-height:1.5;
  overflow-wrap:anywhere;white-space:pre-wrap}
.comm-rec{display:inline-block;margin-top:5px;font-size:12.5px;color:var(--purple2);font-weight:600}
.comm-when{flex:none;font-size:12px;color:var(--text3);text-align:right;min-width:4.5rem}
@media(max-width:560px){
  .comm{padding:11px 14px;gap:9px}
  .comm-when{min-width:3.4rem;font-size:11px}
}

/* ── the protocol gate ────────────────────────────────────────────────
   Not an error. The system doing what it is for, and saying what is
   missing in the shop's own words. */
.pgate{margin-top:12px;padding:13px 15px;border-radius:10px;
  background:color-mix(in srgb,var(--amber) 11%,transparent)}
.pgate-h{font-size:14.5px;font-weight:650;margin-bottom:6px}
.pgate-list{margin:0 0 8px;padding-left:20px}
.pgate-list li{font-size:13.5px;color:var(--text2);line-height:1.55;padding:2px 0}
.pgate .fine{font-size:12.5px;color:var(--text3);line-height:1.5}
.pgate-act{display:flex;gap:8px;margin-top:11px;flex-wrap:wrap}
.pgate-act input{flex:1 1 14rem;min-width:0;font:inherit;font-size:14px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:9px 11px}
.pgate-act input:focus{outline:2px solid var(--amber);outline-offset:-1px;border-color:transparent}
@media(max-width:560px){.pgate-act input,.pgate-act .btn{flex-basis:100%}}


/* == WALKTHROUGHS =====================================================
   Three tiles, then a numbered spine. Tinted per role so a technician
   learns "mine is the amber one" in a day and stops reading headings. */
.wt-tiles{display:grid;gap:12px;grid-template-columns:repeat(3,1fr);margin-bottom:14px}
/* On a phone three full tiles push the first step off the screen, and the
   first step is the point. Same tile, laid on its side. */
@media(max-width:760px){
  .wt-tiles{grid-template-columns:1fr;gap:9px}
  .wt-tile{display:grid;grid-template-columns:auto 1fr auto;align-items:center;
    column-gap:13px;padding:13px 15px}
  .wt-ico{width:38px;height:38px;margin:0;grid-row:span 2}
  .wt-role{grid-column:2;align-self:end}
  .wt-who{grid-column:2;grid-row:2;align-self:start;margin-top:1px}
  .wt-n{grid-column:3;grid-row:1/span 2;margin:0;white-space:nowrap}
}
.wt-tile{display:block;padding:20px 18px 17px;border-radius:14px;text-decoration:none;
  background:var(--bg2);border:1px solid var(--border);transition:transform .12s,border-color .12s}
.wt-tile:hover{transform:translateY(-2px);border-color:var(--tint)}
.wt-tile.purple{--tint:var(--purple)} .wt-tile.green{--tint:var(--green)}
.wt-tile.amber{--tint:var(--amber)}
.wt-ico{display:grid;place-items:center;width:42px;height:42px;border-radius:11px;
  margin-bottom:12px;color:var(--tint);
  background:color-mix(in srgb,var(--tint) 15%,transparent)}
.wt-ico svg{width:22px;height:22px}
.wt-role{font-size:17px;font-weight:670;color:var(--text)}
.wt-who{font-size:13.5px;color:var(--text2);margin-top:3px;line-height:1.5}
.wt-n{font-size:11.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--tint);margin-top:11px}

.wt-steps{list-style:none;margin:0 0 14px;padding:0;
  display:flex;flex-direction:column;gap:2px}
.wt-steps.purple{--tint:var(--purple)} .wt-steps.green{--tint:var(--green)}
.wt-steps.amber{--tint:var(--amber)}
.wt-steps li{position:relative;display:flex;gap:14px;padding:15px 17px;
  background:var(--bg2);border:1px solid var(--border)}
.wt-steps li:first-child{border-radius:13px 13px 0 0}
.wt-steps li:last-child{border-radius:0 0 13px 13px}
.wt-steps li+li{border-top:0}
.wt-num{flex:0 0 auto;width:27px;height:27px;border-radius:50%;display:grid;place-items:center;
  font-size:13px;font-weight:700;color:var(--tint);
  background:color-mix(in srgb,var(--tint) 15%,transparent)}
.wt-body{min-width:0;flex:1}
.wt-do{font-size:15px;font-weight:600;color:var(--text);line-height:1.45}
.wt-why{font-size:13.5px;color:var(--text2);line-height:1.6;margin-top:5px}
.wt-go{display:inline-block;margin-top:9px;font-size:13px;font-weight:640;
  color:var(--tint);text-decoration:none}
.wt-go:hover{text-decoration:underline}
.wt-trap{font-size:13.5px;color:var(--text2);line-height:1.6;padding:11px 15px}
.wt-trap+.wt-trap{border-top:1px solid var(--border)}
@media(max-width:560px){
  .wt-steps li{padding:14px 13px;gap:11px}
  .wt-do{font-size:14.5px}
}


/* the car, asked for at the moment it is cheapest to ask */
.tocar{padding:2px 0 4px;border-top:1px solid var(--border);margin-top:12px;padding-top:13px}
.tocar-h{font-size:12px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text3);margin-bottom:5px}
.tocar .quiet{margin:0 0 10px;font-size:13px;line-height:1.5}
.tocar-row{display:grid;gap:8px;grid-template-columns:5rem 1fr 1fr}
.tocar-row input{font:inherit;font-size:15px;color:var(--text);min-width:0;
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px 11px}
.tocar-row input:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
@media(max-width:460px){.tocar-row{grid-template-columns:5rem 1fr}}


/* == THE FOLDED AUTHORIZATION ========================================
   A missing yes is the loudest thing on a job, and it was also the
   tallest: four fields and two buttons above the stars, the track and the
   next step. Folded to one line on a phone, still amber, still first. */
.authwrap{margin-top:13px;border-radius:11px;overflow:hidden;
  background:color-mix(in srgb,var(--amber) 11%,transparent)}
.authwrap>summary{display:flex;align-items:center;gap:10px;cursor:pointer;
  padding:12px 14px;list-style:none;font-size:14.5px;font-weight:600;color:var(--text)}
.authwrap>summary::-webkit-details-marker{display:none}
.aw-dot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;background:var(--amber)}
.aw-t{flex:1;min-width:0}
.aw-go{flex:0 0 auto;font-size:12.5px;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--amber)}
.authwrap[open]>summary .aw-go{opacity:.55}
.authwrap[open]>summary{padding-bottom:2px}
.authwrap #authhost{padding:0 14px 13px}
.authwrap .authform{margin:0}
@media(max-width:560px){
  .aw-t{font-size:14px}
  .aw-go{font-size:11.5px}
}


/* == TILES OR ROWS ====================================================
   The toggle, and the tile it toggles to. */
.viewtog{display:inline-flex;gap:2px;padding:2px;border-radius:9px;
  background:var(--bg3);border:1px solid var(--border);margin-left:auto}
.viewtog button{display:grid;place-items:center;width:30px;height:26px;padding:0;
  border:0;border-radius:7px;background:none;color:var(--text3);cursor:pointer}
.viewtog button svg{width:15px;height:15px}
.viewtog button.on{background:var(--bg2);color:var(--text);box-shadow:0 1px 2px rgba(0,0,0,.25)}
.viewtog button:hover:not(.on){color:var(--text2)}

/* Four facts, a thumb-sized target, and as many across as the glass has
   room for. auto-fill with a minimum means a Surface Pro gets five and a
   phone gets one without a single breakpoint. */
.jtiles{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr))}
.jtile{display:flex;flex-direction:column;gap:3px;padding:13px 14px 11px;border-radius:12px;
  text-decoration:none;background:var(--bg2);border:1px solid var(--border);
  border-left:3px solid var(--stage,var(--border2));transition:border-color .12s,transform .12s}
.jtile:hover{transform:translateY(-1px);border-color:var(--border2)}
.jtile.s-estimate{--stage:var(--amber)}
.jtile.s-scheduled{--stage:var(--purple)}
.jtile.s-in_progress{--stage:#60a5fa}
.jtile.s-completed{--stage:var(--green)}
.jtile.s-invoiced{--stage:var(--amber)}
.jtile.s-paid{--stage:var(--green)}
.jtile.s-cancelled{--stage:var(--text3)}
.jtile-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.jtile-top .num{font-size:12.5px;font-weight:700;color:var(--text3);letter-spacing:.02em}
.jtile-who{font-size:15.5px;font-weight:650;color:var(--text);line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jtile-car{font-size:13px;color:var(--text2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jtile-foot{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  margin-top:7px;padding-top:8px;border-top:1px solid var(--line2)}
.jtile-foot .amount{font-size:17px;font-weight:700;color:var(--text)}
.jtile-foot .when{font-size:12px;color:var(--text3);white-space:nowrap}
@media(max-width:520px){
  .jtiles{grid-template-columns:1fr;gap:8px}
  .jtile{padding:12px 13px 10px}
}


/* == REPORTS ==========================================================
   Big number, small label, one line of what it is made of. A figure with
   no provenance is a figure somebody argues with. */
.mtiles{display:grid;gap:11px;grid-template-columns:repeat(auto-fill,minmax(13.5rem,1fr))}
.mtile{padding:15px 16px 13px;border-radius:12px;background:var(--bg2);
  border:1px solid var(--border);border-top:3px solid var(--k,var(--border2))}
.mtile.money{--k:var(--green)}
.mtile.rate{--k:var(--purple)}
.mtile.count{--k:#60a5fa}
.mtile.unknown{--k:var(--border2)}
.mtile-l{font-size:11.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text3)}
.mtile-v{font-size:29px;font-weight:700;color:var(--text);line-height:1.15;margin-top:5px;
  font-variant-numeric:tabular-nums}
.mtile.unknown .mtile-v{color:var(--text3)}
.mtile-n{font-size:12.5px;color:var(--text2);line-height:1.45;margin-top:5px}
@media(max-width:520px){
  .mtiles{grid-template-columns:1fr 1fr;gap:8px}
  .mtile{padding:12px 12px 10px}
  .mtile-v{font-size:23px}
  .mtile-n{font-size:11.5px}
}

/* who finished the work */
.bar-row{display:grid;grid-template-columns:8.5rem 1fr 2.5rem;align-items:center;gap:11px;
  padding:6px 0}
.bar-name{font-size:13.5px;color:var(--text2);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.bar-track{height:9px;border-radius:5px;background:var(--bg3);overflow:hidden}
.bar-fill{display:block;height:100%;border-radius:5px;
  background:linear-gradient(90deg,var(--purple),var(--green))}
.bar-n{font-size:13px;font-weight:700;color:var(--text);text-align:right;
  font-variant-numeric:tabular-nums}
@media(max-width:520px){.bar-row{grid-template-columns:6.5rem 1fr 2rem;gap:8px}}


/* ═══════════════════════════════════════════════════════════════════
   TOUCH
   ═══════════════════════════════════════════════════════════════════
   Anthony: "techs and service advisors are using a surface pro, tablet,
   ipad and a bunch of devices to access. Check mobile responsiveness for
   all buttons and that they can access."

   KEYED ON THE POINTER, NOT THE WIDTH, and that distinction is the whole
   point. A Surface Pro is 1368px wide and is a touch screen. Sizing
   controls by screen width would have given the shop's main machine the
   same 20-pixel arrows as a mouse-driven desktop, which is exactly the
   device the complaint is about.

   `pointer: coarse` asks the right question — is the primary input a
   finger — so a phone, an iPad, and a Surface Pro in tablet mode all get
   targets a hand can hit, and a desk with a mouse keeps its tighter
   layout. A finger pad is about 10mm; every guideline lands near 44px,
   and measured against the real stylesheet these were 20x21.

   Nothing here changes a colour, a font or a position. It changes what
   you can hit. */
@media (pointer: coarse) {
  /* The sort and span control — two arrows and a label, and on a tablet
     the arrows were the smallest thing on the screen. */
  .picker-arrow{padding:12px 16px;font-size:19px}
  .picker-now{padding:12px 15px;font-size:14.5px}
  .picker-opt{padding:12px 13px;font-size:14.5px}

  /* Tiles or rows. */
  .viewtog button{width:42px;height:38px}
  .viewtog button svg{width:17px;height:17px}

  /* Back out of a screen — a 21px-tall link is a miss, and a miss here
     means opening the wrong record. */
  .back{display:inline-flex;align-items:center;min-height:44px;font-size:14px}

  /* Anything that changes a stage, a filter or a where. */
  .chip{padding:11px 15px;font-size:14px}
  .stagepick .chip{padding:10px 14px;font-size:13.5px}
  .tab{padding:12px 10px;font-size:14px}
  #bar .themebtn{padding:10px 13px;font-size:17px}

  /* The profile flyout is how a technician reaches everything that is not
     on the bottom bar. */
  .profmenu a,.profmenu button{padding:12px 12px;font-size:15px}
  .profmenu svg{width:20px;height:20px}

  /* Rows are targets too. A job row that is 44px tall is a job you open
     on the first try. */
  .row{min-height:56px}
  .viewtog,.picker-ctl{touch-action:manipulation}

  /* iOS zooms the page when a focused input is under 16px. That zoom does
     not undo itself, and the technician is left on a magnified form in a
     car park wondering what happened. */
  input,select,textarea{font-size:16px}
}


/* == THE PEOPLE =======================================================
   A card per human, opening a real editor. */
.pcards{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(15.5rem,1fr))}
/* ALIGN-CONTENT, NOT JUST ALIGN-ITEMS.
   The outer .pcards grid stretches every card in a row to the tallest one.
   Without this the slack is handed to the auto rows, so a card with less in
   it spreads its name, tags and phone down the card and NOTHING lines up
   across a row. `align-items:start` only pins each item inside its own row;
   `align-content:start` pins the ROWS to the top and leaves the slack at
   the bottom where nobody reads it.
   It mattered the moment some cards grew a completion score and others did
   not, which is when Anthony saw it. */
.pcard{display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto auto auto;
  column-gap:12px;row-gap:2px;align-items:start;align-content:start;
  width:100%;text-align:left;cursor:pointer;
  padding:14px 15px;border-radius:12px;background:var(--bg2);border:1px solid var(--border);
  font:inherit;transition:transform .12s,border-color .12s}
.pcard:hover{transform:translateY(-1px);border-color:var(--green)}
.pcard.off{opacity:.55}
.pcard-av{grid-row:span 3;width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
  background:var(--bg3);color:var(--text2);font-size:14px;font-weight:700}
.pcard-name{font-size:15.5px;font-weight:650;color:var(--text)}
.pcard-tags{display:flex;flex-wrap:wrap;gap:4px;margin:3px 0 1px}
.pcard-sub{font-size:12.5px;color:var(--text3);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.pill.r-admin{background:color-mix(in srgb,var(--purple) 20%,transparent);color:var(--purple)}
.pill.r-supervisor{background:color-mix(in srgb,var(--purple) 16%,transparent);color:var(--purple)}
.pill.r-advisor{background:color-mix(in srgb,var(--green) 17%,transparent);color:var(--green)}
.pill.r-technician{background:color-mix(in srgb,var(--amber) 17%,transparent);color:var(--amber)}
.pill.r-viewer{background:var(--bg3);color:var(--text3)}

/* the editor — a real window, not a squeezed row */
.sheet.wide{max-width:34rem}
.ed-sec{font-size:11.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text3);margin:16px 0 9px;padding-top:13px;border-top:1px solid var(--line2)}
.sheet-body > .ed-sec:first-child{margin-top:0;padding-top:0;border-top:0}
.rolepick{display:grid;gap:6px;margin-bottom:14px}
.rolebtn{display:block;width:100%;text-align:left;padding:11px 13px;border-radius:10px;
  border:1px solid var(--border);background:var(--bg);color:var(--text2);font:inherit;cursor:pointer}
.rolebtn b{display:block;font-size:14.5px;font-weight:650;color:var(--text)}
.rolebtn i{display:block;font-style:normal;font-size:12.5px;color:var(--text3);
  line-height:1.45;margin-top:2px}
.rolebtn.on{border-color:var(--green);background:color-mix(in srgb,var(--green) 9%,transparent)}
.rolebtn.on b{color:var(--green)}
.check{display:flex;align-items:center;gap:9px;font-size:14.5px;color:var(--text);
  cursor:pointer;padding:6px 0}
.check input{width:18px;height:18px;accent-color:var(--green);flex:none}
.quiet.small{font-size:12.5px;line-height:1.55;margin:2px 0 0}
.linkbtn{border:0;background:none;padding:0;font:inherit;font-size:inherit;
  color:var(--green);text-decoration:underline;cursor:pointer}
@media(pointer:coarse){
  .rolebtn{padding:13px 14px}
  .check input{width:22px;height:22px}
}
@media(max-width:520px){.pcards{grid-template-columns:1fr}}


/* the quiet-lead note, when newest-first buries an old one */
.quietnote{padding:11px 14px;border-radius:10px;margin-bottom:12px;font-size:13.5px;
  line-height:1.55;color:var(--text2);
  background:color-mix(in srgb,var(--amber) 10%,transparent)}
.quietnote .linkbtn{color:var(--amber)}


/* == THE WORKFLOW MAP =================================================
   A diagram that scrolls sideways rather than shrinking to nothing, and
   the same steps written out underneath with the photos. */
.wt-tile.wide{margin-bottom:12px}
.wt-tile.wide .wt-who{white-space:normal}

.wfdiag{overflow-x:auto;-webkit-overflow-scrolling:touch;margin-bottom:10px;
  padding:6px 2px 10px;scrollbar-width:thin}
.wfdiag svg{display:block;min-width:940px;width:100%;height:auto}
.wflegend{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:22px;font-size:12.5px;
  color:var(--text3)}
.wflegend span{display:inline-flex;align-items:center;gap:6px}
.wflegend i{width:11px;height:11px;border-radius:3px;display:inline-block}
.wflegend i.dash{background:none;border-top:2px dashed var(--purple);border-radius:0;height:0;
  width:16px}

.wfsteps{display:flex;flex-direction:column;gap:12px;margin-bottom:16px}
.wfstep{padding:15px 17px 14px;border-radius:12px;background:var(--bg2);
  border:1px solid var(--border);border-left:3px solid var(--who,var(--border2))}
.wfstep-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:7px}
.wfstep-n{width:25px;height:25px;border-radius:50%;display:grid;place-items:center;flex:none;
  font-size:12px;font-weight:700;color:var(--who);
  background:color-mix(in srgb,var(--who) 18%,transparent)}
.wfstep-t{font-size:16px;font-weight:660;color:var(--text);flex:1;min-width:0}
.wfstep-w{font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--who)}
.wfstep-b{margin:0;font-size:14px;line-height:1.65;color:var(--text2)}
.wfstep-rule,.wfstep-add{margin-top:10px;padding:9px 12px;border-radius:9px;
  font-size:13px;line-height:1.55;color:var(--text2)}
.wfstep-rule{background:color-mix(in srgb,var(--green) 10%,transparent)}
.wfstep-rule b{color:var(--green)}
.wfstep-add{background:color-mix(in srgb,var(--purple) 11%,transparent)}
.wfstep-add b{color:var(--purple)}
.wfstep-photo{margin:12px 0 0}
.wfstep-photo img{display:block;width:100%;max-width:19rem;height:auto;border-radius:10px;
  border:1px solid var(--border)}
.wfstep-photo figcaption{font-size:12.5px;color:var(--text3);margin-top:6px;line-height:1.5}
@media(max-width:560px){
  .wfstep{padding:13px 14px 12px}
  .wfstep-t{font-size:15px}
  .wfstep-photo img{max-width:100%}
}

.addedmark{color:var(--purple)}


/* == PHOTOS ===========================================================
   A grid that fills whatever glass it is on, and one image large. */
.phgrid{display:grid;gap:7px;grid-template-columns:repeat(auto-fill,minmax(7rem,1fr))}
.phgrid.big{grid-template-columns:repeat(auto-fill,minmax(10rem,1fr));gap:9px}
.ph{position:relative;display:block;padding:0;border:1px solid var(--border);border-radius:10px;
  overflow:hidden;background:var(--bg3);cursor:pointer;aspect-ratio:4/3}
.ph img{display:block;width:100%;height:100%;object-fit:cover}
.ph-k{position:absolute;top:5px;left:5px;font-size:10px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;padding:3px 6px;border-radius:5px;
  background:rgba(8,12,17,.72);color:#e9edf2}
.ph-c{position:absolute;left:0;right:0;bottom:0;padding:14px 7px 5px;font-size:11px;
  color:#eef1f5;text-align:left;line-height:1.35;
  background:linear-gradient(180deg,transparent,rgba(8,12,17,.85));
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ph-kinds{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:11px}
/* WHAT THE PHOTO IS OF, typed at the car. Optional, so it reads as a
   note rather than a required field, and 16px so iOS does not zoom in on
   it the moment somebody taps it. */
.ph-note{width:100%;font:inherit;font-size:16px;color:var(--text);background:var(--bg);
  border:1px solid var(--border);border-radius:10px;padding:11px 12px;margin-bottom:9px}
.ph-note::placeholder{color:var(--text3)}
.ph-note:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
/* Camera and gallery, side by side and equal — the gallery is not a
   lesser way to add a photo, it is the only way to add one taken before
   the job was opened. Both stay thumb-sized on a phone. */
.ph-add{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ph-add .btn{width:100%;text-align:center;justify-content:center;min-height:44px}
@media (max-width:380px){.ph-add{grid-template-columns:1fr}}
.ph-status{min-height:20px;margin:8px 0 4px;font-size:13px}

.lightbox{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:12px;padding:18px;
  background:rgba(6,9,13,.92)}
.lightbox img{max-width:100%;max-height:72vh;border-radius:12px;object-fit:contain}
.lb-x{position:absolute;top:12px;right:14px;width:42px;height:42px;border:0;border-radius:50%;
  background:rgba(255,255,255,.10);color:#fff;font-size:24px;line-height:1;cursor:pointer}
.lb-bar{display:flex;align-items:center;gap:9px;flex-wrap:wrap;justify-content:center;
  max-width:40rem;width:100%}
.lb-bar input{flex:1 1 12rem;min-width:0;font:inherit;font-size:14px;color:var(--text);
  background:var(--bg2);border:1px solid var(--border);border-radius:9px;padding:9px 11px}
@media(max-width:520px){
  .phgrid{grid-template-columns:repeat(auto-fill,minmax(5.5rem,1fr))}
  .lb-bar input{flex-basis:100%}
}
@media(pointer:coarse){.lb-x{width:48px;height:48px}}


/* == PAY ==============================================================
   The period control, and a row where the number that matters is owed. */
.period{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}
.period-now{font-size:13.5px;font-weight:650;color:var(--text);
  font-variant-numeric:tabular-nums}
.payrow .amount.owed{color:var(--amber)}
.payrow .amount.clear{color:var(--text3)}
.mtiles + .card{margin-top:14px}
#paybody > .btn.primary{margin-bottom:13px}
@media(max-width:520px){
  .period{width:100%;justify-content:space-between}
  .period-now{order:-1;width:100%;text-align:center;margin-bottom:4px}
}


/* the markup calculator — a worked number beats a paragraph */
.tryrow{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap}
.tryrow .field{margin-bottom:0;flex:0 1 12rem}
.tryout{display:flex;flex-direction:column;gap:3px}
.tryout span{font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text3)}
.tryout b{font-size:26px;font-weight:700;color:var(--green);font-variant-numeric:tabular-nums}


/* certifications — the colour is the deadline */
.row.cert{border-left:3px solid transparent}
.row.cert.gone{border-left-color:var(--amber);
  background:color-mix(in srgb,var(--amber) 7%,transparent)}
.row.cert.soon{border-left-color:var(--purple)}
.row.cert.ok{border-left-color:var(--green)}


/* == BOOK TIME ========================================================
   A suggestion with the money shown next to it, so the advisor is
   choosing a price rather than a number of hours. */
.booktime{margin-top:12px;padding:13px 15px;border-radius:11px;
  background:color-mix(in srgb,#60a5fa 10%,transparent)}
.bt-h{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:#60a5fa;margin-bottom:9px}
.bt-row{display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:11px;
  padding:8px 0;cursor:pointer;border-top:1px solid var(--line2)}
.bt-row:first-of-type{border-top:0}
.bt-row input{width:18px;height:18px;accent-color:var(--green);flex:none}
.bt-task{font-size:14px;color:var(--text);min-width:0}
.bt-hours{font-size:13.5px;font-weight:650;color:var(--text2);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.bt-money{font-size:14px;font-weight:700;color:var(--green);white-space:nowrap;
  font-variant-numeric:tabular-nums;min-width:4.5rem;text-align:right}
.bt-note{font-size:13px;color:var(--text2);line-height:1.55;margin-top:8px}
.bt-act{display:flex;gap:8px;margin:12px 0 6px;flex-wrap:wrap}
@media(pointer:coarse){.bt-row input{width:22px;height:22px}}
@media(max-width:520px){
  .bt-row{grid-template-columns:auto 1fr auto;row-gap:2px}
  .bt-money{grid-column:2/4;text-align:left;min-width:0}
}


/* what a VIN says the car is */
.vinout{margin:8px 0 4px;padding:11px 13px;border-radius:10px;
  background:color-mix(in srgb,#60a5fa 10%,transparent)}
.vin-h{font-size:15px;font-weight:660;color:var(--text)}
.vin-bits{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.vin-bits span{font-size:12px;padding:3px 8px;border-radius:6px;
  background:var(--bg3);color:var(--text2)}


/* == NAVIGATE =========================================================
   A mobile job is an address somebody has to drive to. The maps link is
   universal on purpose — it opens Apple Maps on an iPhone, Google Maps on
   Android, and a browser tab on a desktop, without us guessing. */
.navlink{display:inline-block;margin-left:9px;font-size:12.5px;font-weight:650;
  color:var(--green);text-decoration:none;padding:3px 9px;border-radius:7px;
  background:color-mix(in srgb,var(--green) 13%,transparent);white-space:nowrap}
.navlink:hover{background:color-mix(in srgb,var(--green) 22%,transparent)}
@media(pointer:coarse){.navlink{padding:8px 13px;font-size:13.5px;margin-left:8px}}

/* a card that is a link to somewhere else */
.rowlink{display:flex;align-items:center;gap:12px;text-decoration:none}
.rowlink-t{font-size:15.5px;font-weight:650;color:var(--text)}
.rowlink-s{flex:1;font-size:13px;color:var(--text3)}
.rowlink-go{font-size:18px;color:var(--text3)}
@media(max-width:560px){
  .rowlink{flex-wrap:wrap;gap:3px}
  .rowlink-t{width:100%}
  .rowlink-s{flex:1 1 auto}
}

/* == THE INSPECTION SHEET ============================================= */
.iprog{padding:14px 16px;border-radius:12px;background:var(--bg2);
  border:1px solid var(--border);margin-bottom:13px}
.iprog-n{font-size:14px;color:var(--text2)}
.iprog-n b{font-size:22px;font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums}
.iprog-bar{height:7px;border-radius:4px;background:var(--bg3);overflow:hidden;margin-top:9px}
.iprog-bar i{display:block;height:100%;border-radius:4px;
  background:linear-gradient(90deg,var(--purple),var(--green));transition:width .2s}
.iprog-warn{margin-top:9px;font-size:13px;line-height:1.5;color:var(--amber)}
.imeta{display:grid;gap:12px;grid-template-columns:10rem 1fr}
.imeta .field{margin-bottom:0}
@media(max-width:560px){.imeta{grid-template-columns:1fr}}

.isecs{display:flex;flex-direction:column;gap:9px;margin-bottom:14px}
.isec{border-radius:12px;background:var(--bg2);border:1px solid var(--border);overflow:hidden}
.isec-h{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:11px;
  width:100%;padding:14px 15px;border:0;background:none;font:inherit;cursor:pointer;
  text-align:left;color:var(--text)}
.isec-i{font-size:20px;line-height:1}
.isec-t{font-size:15.5px;font-weight:650;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.isec-c{font-size:13px;font-weight:700;color:var(--text3);
  font-variant-numeric:tabular-nums}
.isec-c.full{color:var(--green)}
.isec-bar{grid-column:1/-1;height:4px;border-radius:3px;background:var(--bg3);overflow:hidden}
.isec-bar i{display:block;height:100%;background:var(--green);transition:width .2s}
.isec-b{display:none;padding:2px 15px 13px}
.isec.open .isec-b{display:block}

.ipt{padding:11px 0;border-top:1px solid var(--line2)}
.ipt:first-child{border-top:0}
.ipt-n{font-size:14.5px;color:var(--text);margin-bottom:8px}
.ipt-v{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.vbtn{padding:10px 4px;border-radius:9px;border:1px solid var(--border);background:var(--bg);
  color:var(--text3);font:inherit;font-size:12px;font-weight:700;letter-spacing:.03em;
  cursor:pointer}
.vbtn.on{color:#0b0f14}
.vbtn.v-good.on{background:var(--green);border-color:var(--green)}
.vbtn.v-watch.on{background:var(--amber);border-color:var(--amber)}
.vbtn.v-now.on{background:var(--red,#f26d6d);border-color:var(--red,#f26d6d);color:#fff}
.vbtn.v-na.on{background:var(--bg3);border-color:var(--border2);color:var(--text2)}
.ipt-why{margin-top:8px}
.ipt-note{width:100%;font:inherit;font-size:14px;color:var(--text);background:var(--bg);
  border:1px solid var(--amber);border-radius:9px;padding:10px 11px}
.tire-in{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.ipt-tread{width:5rem;font:inherit;font-size:16px;text-align:center;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px}
.tire-u{font-size:13px;color:var(--text3)}
.tire-say{font-size:13px;font-weight:650}
.tire-say.v-good{color:var(--green)}
.tire-say.v-watch{color:var(--amber)}
.tire-say.v-now{color:var(--red,#f26d6d)}
.ifin{display:flex;gap:9px;flex-wrap:wrap;margin-bottom:8px}
@media(max-width:420px){
  .ipt-v{grid-template-columns:repeat(2,1fr)}
  .ifin .btn{flex:1 1 100%}
}
@media(pointer:coarse){.vbtn{padding:13px 4px;font-size:13px}}


/* == THE QUOTE, LAID OUT LIKE A QUOTE ================================
   Anthony: "line items for labor and parts look like shit."

   Eight identical bordered cards with a KIND dropdown under every one,
   saying "part" eight times in a column. The kind is a HEADING now, with
   its own subtotal, and the dropdown lives in a panel that opens when
   asked. What stays on the line is what gets scanned and typed: what it
   is, how many, the price, and what the line comes to.

   Still tall rather than wide. A phone must never scroll sideways to
   reach the price box. */
.lrows{display:flex;flex-direction:column;gap:16px;padding:2px var(--pad) 4px}

.lgroup{display:flex;flex-direction:column}
.lgroup-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:0 2px 6px;border-bottom:1px solid var(--line2);margin-bottom:2px}
.lgroup-head span{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text3)}
.lgroup-head b{font-size:13.5px;color:var(--text2);font-variant-numeric:tabular-nums}

.lrow{padding:9px 4px 8px;border-bottom:1px solid var(--line2)}
.lrow:last-child{border-bottom:0}
.lrow-top{display:flex;align-items:center;gap:8px}
.lrow-top .li-desc{flex:1 1 auto;min-width:0;font:inherit;font-size:15px;font-weight:600;
  color:var(--text);background:none;border:0;border-bottom:1px dashed transparent;padding:6px 0}
.lrow-top .li-desc:hover{border-bottom-color:var(--border2)}
.lrow-top .li-desc:focus{outline:none;border-bottom-color:var(--green)}

/* HOW MANY, next to what it is, because "12 quarts" is part of the
   sentence and was living in a labelled box on a second row. */
.lrow-qty{display:flex;align-items:center;gap:3px;flex:0 0 auto}
.lrow-qty .li-qty{width:3.4rem;font:inherit;font-size:14px;text-align:right;color:var(--text2);
  background:none;border:1px solid transparent;border-radius:7px;padding:6px 5px;
  font-variant-numeric:tabular-nums}
.lrow-qty .li-qty:hover{border-color:var(--border)}
.lrow-qty .li-qty:focus{outline:2px solid var(--green);outline-offset:-1px;background:var(--bg2)}
.lrow-unit{font-size:12px;color:var(--text3);width:1.1rem}
.lrow-qty-off{flex:0 0 auto;width:4.5rem}

.lrow-money{display:flex;align-items:center;gap:1px;flex:0 0 auto}
.lrow-cur{font-size:13px;color:var(--text3)}
.lrow-money .li-price{width:5.4rem;font:inherit;font-size:15px;font-weight:600;text-align:right;
  color:var(--text);background:var(--bg2);border:1px solid var(--border);border-radius:8px;
  padding:7px 8px;font-variant-numeric:tabular-nums}
.lrow-money .li-price:focus{outline:2px solid var(--green);outline-offset:-1px;
  border-color:transparent}

/* WHAT THE LINE COMES TO, on the line. It used to sit at the far right of
   a second row, disconnected from the price that produced it. */
.lrow-total{flex:0 0 auto;width:5.6rem;text-align:right;font-size:15px;color:var(--text);
  font-variant-numeric:tabular-nums}

.lrow-open,.li-x{flex:0 0 auto;width:30px;height:30px;border:0;border-radius:8px;background:none;
  color:var(--text3);font-size:17px;line-height:1;cursor:pointer}
.lrow-open{font-size:15px;letter-spacing:1px;padding-bottom:5px}
.lrow-open:hover{background:var(--bg2);color:var(--text)}
.li-x{font-size:21px}
.li-x:hover{background:color-mix(in srgb,var(--amber) 18%,transparent);color:var(--text)}

/* THE SHOP'S OWN COST, under the part it belongs to. Readable at a
   glance, editable on touch, and never looking like a form. */
.lrow-part{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:2px 0 2px 2px;
  font-size:12.5px;color:var(--text3)}
.lrow-part label{display:flex;align-items:center;gap:3px}
.lrow-part input{font:inherit;font-size:12.5px;color:var(--text2);background:none;
  border:0;border-bottom:1px dashed var(--line2);padding:2px 1px;width:5rem}
.lrow-part .li-cost{text-align:right;font-variant-numeric:tabular-nums;width:4.4rem}
.lrow-part .li-partno{width:7rem}
.lrow-part input::placeholder{color:var(--text3);opacity:.5}
.lrow-part input:hover{border-bottom-color:var(--border2)}
.lrow-part input:focus{outline:none;border-bottom-color:var(--green);color:var(--text)}

.lrow-more{display:none;align-items:flex-end;gap:10px;flex-wrap:wrap;margin-top:8px;
  padding:9px 2px 3px;border-top:1px solid var(--line2)}
.lrow.open .lrow-more{display:flex}
.lbit{display:flex;flex-direction:column;gap:3px;min-width:0}
.lbit > span{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--text3)}
.lbit select{font:inherit;font-size:14px;color:var(--text);background:var(--bg2);
  border:1px solid var(--border);border-radius:8px;padding:7px 9px;width:7rem}
.lbit select:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
.li-copy{font:inherit;font-size:13px;color:var(--text2);background:var(--bg2);cursor:pointer;
  border:1px solid var(--border);border-radius:8px;padding:8px 11px}
.li-copy:hover{border-color:var(--border2);color:var(--text)}

/* ON A PHONE THE NAME GETS ITS OWN LINE and the numbers get the next
   one, in the order they are read: how many, at what, comes to. Everything
   has to fit inside 375px without wrapping again — a delete button alone
   on a third row is what this looked like on the first attempt. */
@media(max-width:560px){
  .lrow{padding:8px 2px 7px}
  .lrow-top{flex-wrap:wrap;gap:6px}
  .lrow-top .li-desc{flex:1 1 100%;font-size:15px;padding:3px 0}
  .lrow-qty{margin-right:auto}
  .lrow-qty .li-qty{width:2.9rem;padding:5px 4px}
  .lrow-money .li-price{width:4.6rem;padding:6px 6px}
  .lrow-total{width:auto;min-width:4.3rem;font-size:14.5px}
  .lrow-open,.li-x{width:30px;height:30px}
  .lrow-part{gap:11px;font-size:12px}
  .lrow-part input{width:4rem}
  .lrow-part .li-partno{width:5.4rem}
}
@media(pointer:coarse){
  .lrow-open,.li-x{width:36px;height:36px}
  .lrow-part input{padding:6px 2px}
}
@media(pointer:coarse) and (max-width:560px){
  .lrow-open,.li-x{width:32px;height:32px}
}
  .lrow-money .li-price{padding:11px 9px}
}


/* On a phone the description gets its own line. Squeezed beside a price
   box it clipped at nineteen characters, and 'Replace front brake' is not
   a line item. The price stays with it, just underneath. */
@media(max-width:560px){
  .lrow-top{flex-wrap:wrap;row-gap:7px}
  .lrow-top .li-desc{flex:1 1 100%;order:1;font-size:15.5px}
  .lrow-money{order:2;margin-left:auto}
  .li-x{order:3}
}


/* == FIND, ON EVERY LIST ==============================================
   Over the top of the page, not tucked into a toolbar — it is the first
   thing a busy shop reaches for. */
.findbar{display:flex;align-items:center;gap:9px;margin-bottom:12px;padding:0 13px;
  border-radius:11px;background:var(--bg2);border:1px solid var(--border)}
.findbar:focus-within{border-color:var(--green)}
.findbar-i{display:grid;place-items:center;color:var(--text3);flex:none}
.findbar-i svg{width:17px;height:17px}
.findbar-q{flex:1;min-width:0;font:inherit;font-size:15px;color:var(--text);
  background:none;border:0;padding:12px 0}
.findbar-q:focus{outline:none}
.findbar-q::-webkit-search-cancel-button{filter:invert(.55)}
.findbar-n{flex:none;font-size:12px;font-weight:650;color:var(--text3);white-space:nowrap}
@media(pointer:coarse){.findbar-q{padding:14px 0;font-size:16px}}

/* a login link, ready to be sent however the shop already talks */
.linkout{margin-top:11px;padding:12px 13px;border-radius:11px;
  background:color-mix(in srgb,var(--green) 10%,transparent)}
.linkout-h{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--green);margin-bottom:7px}
.linkout-v{width:100%;font:inherit;font-size:12.5px;font-family:ui-monospace,monospace;
  color:var(--text2);background:var(--bg);border:1px solid var(--border);border-radius:8px;
  padding:9px 10px;resize:vertical;word-break:break-all}
.linkout-act{display:flex;gap:8px;margin:9px 0 6px;flex-wrap:wrap}


/* set a password, right there on the person's card */
.pwrow{display:flex;gap:9px;flex-wrap:wrap;margin-bottom:6px}
@media(max-width:520px){.pwrow .btn{flex:1 1 100%}}


/* ── the sheet on a phone, said last so it wins ───────────────────────
   The tab-bar rule at 820px adds 76px of bottom padding so a CENTRED
   sheet clears the nav. Below 560px the sheet is a bottom sheet instead
   and that padding leaves it floating with a gap under it. A modal is
   allowed to cover the nav — it is a modal — so it does, and the height
   stops accounting for a bar it is now on top of. */
@media(max-width:560px){
  .sheet-back{padding-bottom:0}
  .sheet{max-height:92dvh;padding-bottom:env(safe-area-inset-bottom,0px)}
  .sheet-acts{position:sticky;bottom:0}

  /* The team panel is the longest one in the app: a login, five roles, a
     technician record and a password. The role descriptions are what a
     person reads ONCE while learning; after that they are five lines of
     scrolling between them and Save. */
  .rolebtn i{display:none}
  .rolebtn{padding:12px 13px}
  .rolebtn.on i{display:block}
  .ed-sec{margin:13px 0 8px;padding-top:11px}
  .field{margin-bottom:11px}
}

/* Above the phone breakpoint the sheet is centred and must still clear
   the tab bar, which the 820px rule handles — but its own height has to
   know about that padding or the bottom of a tall panel sits under the
   nav. */
@media(min-width:561px) and (max-width:820px){
  .sheet{max-height:calc(100dvh - 116px)}
}


/* The inspection badge says what is WRONG, not how much is answered —
   every point starts good, so a progress bar would be full on arrival. */
.isec-c.fault{color:var(--amber)}
.iprog-sub{font-size:12.5px;color:var(--text3);line-height:1.5;margin-top:5px}
.isec-bar{display:none}


/* a text, typed by a person */
.txtbox{margin:9px 0 4px;padding:11px 12px;border-radius:11px;
  background:color-mix(in srgb,var(--green) 9%,transparent)}
.txtbox textarea{width:100%;font:inherit;font-size:15px;color:var(--text);background:var(--bg);
  border:1px solid var(--border);border-radius:9px;padding:10px 11px;resize:vertical}
.txtbox textarea:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
.txt-act{display:flex;align-items:center;gap:10px;margin-top:9px;flex-wrap:wrap}
.txt-act .fine{flex:1;min-width:0}
button.navlink{border:0;cursor:pointer;font:inherit}


/* the review ask, at the end of a paid job */
.reviewask{border-left:3px solid var(--green)}
.reviewask .quiet{margin:5px 0 11px;line-height:1.55}

/* people who have left, folded away */
.pcard.showgone{border-style:dashed;opacity:.85}
.pcard.showgone .pcard-av{background:var(--bg2);color:var(--text3);font-size:13px}
.goneline{grid-column:1/-1;font-size:11.5px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--text3);margin:14px 0 2px}


/* deleting your own login — typed, not tapped */
.delrow{display:flex;gap:9px;flex-wrap:wrap;margin-top:11px}
.delrow input{flex:1 1 12rem;min-width:0;font:inherit;font-size:15px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px 11px}
.delrow input:focus{outline:2px solid var(--amber);outline-offset:-1px;border-color:transparent}
@media(max-width:520px){.delrow input,.delrow .btn{flex-basis:100%}}

.txtbox input{width:100%;font:inherit;font-size:15px;color:var(--text);background:var(--bg);
  border:1px solid var(--border);border-radius:9px;padding:10px 11px;margin-bottom:8px}
.txtbox input:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}

.hoursrow{width:100%;text-align:left;border:0;background:none;font:inherit;cursor:pointer}
.hoursrow:hover{background:var(--bg3)}

/* ── THE CONTACT LIST ───────────────────────────────────────────────────
   A row is a person and three things you can do to reach them. On a phone
   the actions drop under the name rather than squeezing beside it, because
   a call button you have to aim at is a call button you miss. */
.contacts{background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.contact{display:flex;align-items:center;gap:12px;padding:11px 14px;border-bottom:1px solid var(--line)}
.contact:last-child{border-bottom:0}
.contact-main{min-width:0;flex:1}
.contact-name{display:flex;align-items:center;gap:7px;font-weight:600;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.contact-name a{color:inherit;text-decoration:none}
.contact-name a:hover{text-decoration:underline}
.contact-sub{font-size:12.5px;color:var(--text3);margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.contact-acts{display:flex;gap:6px;flex-shrink:0}
.contact-acts .navlink{margin-left:0}
.pill.c-customer{background:color-mix(in srgb,var(--green) 17%,transparent);color:var(--green)}
.pill.c-team{background:color-mix(in srgb,var(--purple) 18%,transparent);color:var(--purple)}
.pill.c-supplier{background:color-mix(in srgb,var(--amber) 17%,transparent);color:var(--amber)}
@media(max-width:560px){
  .contact{flex-direction:column;align-items:stretch;gap:8px}
  .contact-acts{width:100%}
  .contact-acts .navlink{flex:1;text-align:center}
}

/* ── CERTIFICATION TRACKS ───────────────────────────────────────────────
   Thirteen cards, and the ones somebody already holds carry a name so the
   grid answers "who can do this" without opening anything. */
.ctracks{display:grid;gap:10px;padding:12px 14px;
  grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.ctrack{border:1px solid var(--line);border-radius:10px;padding:11px 12px;background:var(--bg2)}
.ctrack.held{border-color:color-mix(in srgb,var(--green) 45%,var(--line));
  background:color-mix(in srgb,var(--green) 7%,var(--bg2))}
.ctrack-t{font-weight:650;font-size:14px;display:flex;align-items:baseline;gap:7px}
.ctrack-code{font-size:11px;font-weight:800;letter-spacing:.07em;color:var(--text3)}
.ctrack.held .ctrack-code{color:var(--green)}
.ctrack-w{font-size:12.5px;color:var(--text3);margin-top:4px;line-height:1.4}
.ctrack-who{font-size:12.5px;font-weight:600;color:var(--green);margin-top:7px}
.ctrack-none{font-size:12.5px;color:var(--text3);margin-top:7px;opacity:.75}
.ctrack-go{display:inline-block;margin-top:8px;font-size:12.5px;font-weight:650;
  color:var(--green);text-decoration:none}
.ctrack-go:hover{text-decoration:underline}

/* ── SOMEBODY'S OWN PAGE ────────────────────────────────────────────────
   Reads like a person, not a form: their name and headline at the top,
   then the four things an advisor actually wants before handing them a
   job. Labels sit beside the answers on a desk and above them on a
   phone, because a two-column grid at 360px is two columns of nothing. */
.mehead{display:flex;gap:14px;align-items:center;padding:16px 16px 4px}
.mehead h2{margin:0;font-size:19px}
.meav{width:52px;height:52px;border-radius:50%;flex-shrink:0;
  display:grid;place-items:center;font-weight:700;font-size:18px;
  background:color-mix(in srgb,var(--purple) 22%,transparent);color:var(--purple)}
.megrid{display:grid;grid-template-columns:170px 1fr;gap:6px 16px;padding:12px 16px 16px}
.melabel{font-size:12.5px;color:var(--text3);font-weight:600;padding-top:1px}
.meval{font-size:14px;line-height:1.5;white-space:pre-wrap}
@media(max-width:560px){
  .megrid{grid-template-columns:1fr;gap:2px}
  .melabel{margin-top:10px}
}

/* ── THE BUTTON THAT MOVES THE JOB ──────────────────────────────────────
   Sits inside "What is next", because a person who has just read what to
   do should not then have to go and find where to do it. Full width on a
   phone: this is pressed with a thumb, often with a glove on. */
.next-do{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.next-do .btn{min-height:42px;padding-left:18px;padding-right:18px}
@media(max-width:560px),(pointer:coarse){
  .next-do{flex-direction:column}
  .next-do .btn{width:100%;min-height:48px;font-size:15.5px}
}

/* A row drawn outside the day board carries its date above the time, so
   "9:00" cannot be misread as this morning. */
.slot i{display:block;font-style:normal;font-size:12px;color:var(--text3)}

/* ── THE CAR, ON A LEAD ─────────────────────────────────────────────────
   Three unlabelled boxes jammed into a row was the old shape, and on a
   phone the year and the make touched. Every field is labelled now and
   has room to breathe, and the VIN sits above the rest because one paste
   fills them in. */
.tocar-f{display:block;margin-top:10px}
.tocar-f>span{display:block;font-size:12px;font-weight:600;color:var(--text3);
  margin-bottom:5px;letter-spacing:.01em}
.tocar-f input{width:100%;font:inherit;font-size:15px;color:var(--text);min-width:0;
  padding:10px 12px;border-radius:9px;border:1px solid var(--border);background:var(--bg2)}
.tocar-f input:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
.tocar-vin{display:flex;gap:8px;align-items:stretch}
.tocar-vin input{flex:1;letter-spacing:.06em;text-transform:uppercase}
.tocar-vin input::placeholder{text-transform:none;letter-spacing:0}
.tocar-vin .btn{flex:0 0 auto;padding:0 16px;white-space:nowrap}
.tocar-row{display:grid;gap:10px;grid-template-columns:5.5rem 1fr 1fr;
  align-items:end;margin-top:0}
.tocar-row .tocar-f{margin-top:10px}
#c-vinout:not(:empty){margin-top:10px}
@media(max-width:460px){
  .tocar-row{grid-template-columns:5.5rem 1fr}
  .tocar-row .tocar-f:last-child{grid-column:1 / -1}
  .tocar-vin{flex-direction:column}
  .tocar-vin .btn{width:100%;min-height:44px}
}

/* ═══════════════════════════════════════════════════════════════════
   TOUCH — one place, rather than fifteen
   ═══════════════════════════════════════════════════════════════════
   Anthony: "buttons can be polished a little better in regards to design
   and where they sit."

   The base button was 14px type in 9px padding, which lands at 38px tall.
   Apple's own guidance is 44, Google's is 48, and both of them are about
   the same thing: a thumb is not a mouse pointer, and a gloved thumb in a
   bay in August is not a thumb. Half the controls in here were built to
   desk sizes and then patched one at a time as somebody noticed — so
   `.rolebtn`, `.li-x`, `.lb-x`, `.navlink` and the rest each grew their
   own coarse-pointer rule and everything else stayed small.

   This is the floor, applied once. It keys on pointer rather than width,
   because a Surface Pro is a wide screen you touch and a phone in
   landscape is a narrow one — the question is what is doing the pressing,
   not how big the glass is.

   `min-height` and not more padding: padding pushes the text off centre
   on anything that already had some, and every one of these is either a
   flex or a grid container already, so the label stays where it is. */
@media(pointer:coarse){
  .btn{min-height:44px;padding-left:17px;padding-right:17px;font-size:14.5px;
    display:inline-flex;align-items:center;justify-content:center}
  .btn.sm{min-height:38px;padding:8px 13px;font-size:13.5px}
  .chip{min-height:40px;padding:9px 14px;font-size:13.5px}
  .tab{min-height:42px;font-size:14px}
  .stepper .btn{min-height:42px;padding:9px 15px}
  .viewtog button{width:42px;height:38px}
  .viewtog svg{width:19px;height:19px}
  .sheet-x{width:44px;height:44px;display:grid;place-items:center;padding:0}

  /* An underlined word inside a sentence cannot grow without wrecking the
     sentence, so it gets reach instead of size: the tap area extends past
     the text without moving anything around it. */
  .linkbtn{position:relative}
  .linkbtn:after{content:'';position:absolute;left:-6px;right:-6px;top:-11px;bottom:-11px}
}

/* THE ROW OF ACTIONS AT THE FOOT OF A SHEET.
   `flex:1 1 auto` with wrap sorts five buttons into whatever ragged shape
   the words happen to make — on a lead, "Raise a quote" and "Draft it for
   me" took a line each and "Mark lost" sat alone at half width looking
   like a mistake. On a phone it is a two-column grid with the primary
   across the top, so the important one is always the widest thing there
   and the rest line up under it. */
@media(max-width:560px){
  .sheet-acts{display:grid;grid-template-columns:1fr 1fr;gap:9px}
  .sheet-acts .btn{width:100%;min-height:46px}
  .sheet-acts .btn.primary{grid-column:1 / -1}
  /* An only child has nothing to sit beside, so it takes the width. */
  .sheet-acts .btn:only-child{grid-column:1 / -1}
}

/* THE BAR OVER A LIST. Three controls that each want the right-hand end.
   Below 560 they get a line of their own: the arrows and Today first,
   then the range picker and the view toggle sharing the second. Wrapping
   them freely put a lone chevron on its own row. */
@media(max-width:560px){
  .listbar{display:grid;grid-template-columns:1fr auto;gap:9px 8px;align-items:center}
  .listbar .stepper{grid-column:1 / -1;width:100%}
  .listbar .stepper .btn{flex:1}
  .listbar .viewtog{margin-left:0;justify-self:end}
}

/* Days behind us, on a desk-sized month. Dimmed rather than hidden: the
   grid has to keep its shape or the dates stop lining up under the
   weekday headings. */
.cal-cell.gone:not(.today){opacity:.5}

/* ═══════════════════════════════════════════════════════════════════
   ON A HOME SCREEN
   ═══════════════════════════════════════════════════════════════════
   Anthony: "I added it to home to use as a mobile app and it's not
   great. Needs to be more stable."

   Three separate things were making it drift, and only one of them was
   the manifest.

   THE STATUS BAR. `black-translucent` plus `viewport-fit=cover` means the
   page owns the whole screen including the strip behind the clock — which
   is right, and means the top bar has to move itself out from under it.
   Without this the shop name sits under the carrier and the battery.

   THE ZOOM THAT NEVER CAME BACK. iOS zooms the entire page when you focus
   an input whose text is smaller than 16px, and it does not zoom out
   again. One tap on a search box and every screen after it is offset by a
   few per cent — which is exactly what "screen drift" looks like from the
   outside. Every field is 16px on a touch device. `maximum-scale` in the
   viewport is the belt; this is the braces, and it is the one that
   matters, because taking pinch-zoom away from somebody who needs it is a
   last resort rather than a fix.

   THE RUBBER BAND. Dragging past the end of a list bounced the whole app
   and, in standalone mode, could pull the page away from the top and
   leave it there. Contained. */
@supports(padding:env(safe-area-inset-top)){
  #bar{padding-top:env(safe-area-inset-top,0px)}
  @media(max-width:820px){
    #bar{padding-top:calc(8px + env(safe-area-inset-top,0px))}
  }
}

@media(pointer:coarse){
  input,select,textarea,
  .appt input,.payin input,input.li,select.li,.pgate-act input,
  .tocar-row input,.tocar-f input,.lb-bar input,.lbit input,.lbit select,
  .findbar-q{font-size:16px}
}

html,body{overscroll-behavior-y:contain}

/* In standalone there is no browser chrome to scroll away, so the app owns
   the full height and only the lists inside it move. */
@media(display-mode:standalone){
  html,body{height:100%;overflow:hidden}
  #app{height:calc(100dvh - 56px);overflow-y:auto;-webkit-overflow-scrolling:touch}
  @media(max-width:820px){
    #app{height:calc(100dvh - 56px - env(safe-area-inset-top,0px));
      padding-bottom:calc(64px + env(safe-area-inset-bottom,0px))}
  }
}

/* The add-a-car form, on a job that arrived without one. Same shape as
   the lead sheet's, so it is one thing to learn rather than two. */
.addcar{margin-top:10px;padding:12px;border-radius:10px;background:var(--bg2);
  border:1px solid var(--line)}
.addcar .next-do{margin-top:12px}

/* The pay-this-invoice block on the customer's own page. */
.gpay{margin-top:14px;padding:14px;border-radius:12px;background:var(--bg3);
  border:1px solid var(--line)}

/* The card form drops in under the invoice it belongs to, so there is no
   doubt which one is being paid. */
.tpay:not(:empty){padding:14px 16px 18px;background:var(--bg3);
  border-bottom:1px solid var(--line)}

/* Resending a bill that never arrived. Sits under the payment box because
   that is where the invoice already is. */
.resend{margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}
.resend .choice{margin:8px 0 10px}

/* WHO IS ON THE JOB. The lead is marked because somebody has to be the
   one the desk rings, and on a crew of one it is obvious and says so
   quietly rather than shouting. */
.crew{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center}
.crew-one{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;
  background:var(--bg3);font-size:13px;font-weight:600}
.crew-one.lead{background:color-mix(in srgb,var(--green) 16%,transparent);color:var(--green)}
.crew-one i{font-style:normal;font-size:10.5px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;opacity:.8}
.crewpick{margin-top:10px;padding:12px;border-radius:10px;background:var(--bg2);
  border:1px solid var(--line)}
.crewpick .check{justify-content:flex-start}
.crewpick .check span{flex:1}
.crew-lead{font-size:12px;opacity:.55}
.crew-lead.on{opacity:1;font-weight:700}

/* The rest of a conversation, folded. The newest message is the one that
   matters; the other fifty-nine are history you go looking for. */
.commmore{border-top:1px solid var(--line)}
.commmore>summary{list-style:none;cursor:pointer;padding:11px 16px;font-size:13.5px;
  font-weight:600;color:var(--green);user-select:none}
.commmore>summary::-webkit-details-marker{display:none}
.commmore>summary:hover{background:var(--bg3)}
.commmore>summary:before{content:"\25BE";display:inline-block;margin-right:7px;
  transition:transform .15s}
.commmore[open]>summary:before{transform:rotate(180deg)}
@media(pointer:coarse){.commmore>summary{padding:14px 16px;font-size:14.5px}}

/* THE PHOTO TILE, SHUT UNTIL THERE IS WORK. A camera plus four category
   chips plus a caption box on a quote nobody has started is a third of a
   phone screen spent saying "nothing here yet". Shut it is one line, and
   the count on the summary means nobody opens it to find out it is empty.
   The row is deliberately big enough to hit with a thumb in a glove. */
.phfold>summary{list-style:none;cursor:pointer;user-select:none;
  display:flex;align-items:center;gap:8px}
.phfold>summary::-webkit-details-marker{display:none}
.phfold>summary:after{content:"▾";margin-left:auto;color:var(--dim);
  font-size:12px;transition:transform .15s}
.phfold[open]>summary:after{transform:rotate(180deg)}
.phfold>summary:hover{background:var(--bg3)}
.phn{font-style:normal;font-weight:650;font-size:12px;color:var(--dim);
  font-variant-numeric:tabular-nums}
.phn:not(:empty){padding:1px 7px;border-radius:99px;background:var(--line2)}
@media(pointer:coarse){.phfold>summary{padding-top:14px;padding-bottom:14px}}

/* On a lead it is a card of its own at the foot of the sheet. */
.leadphotos{margin-top:14px;border:1px solid var(--line);border-radius:12px;
  overflow:hidden;background:var(--bg2)}

/* WHAT IS IN THE WAY. Inside "what is next", because being stuck is part
   of what is next rather than a separate complaint. */
.stuck{margin-top:11px;padding:10px 12px;border-radius:9px;
  background:color-mix(in srgb,var(--amber) 13%,transparent)}
.stuck-h{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;
  color:var(--amber)}
.stuck ul{margin:6px 0 0;padding-left:18px}
.stuck li{font-size:13.5px;line-height:1.55;margin-top:3px}

/* TEXTED TWICE, NOTHING BACK. Red, and it says what to do rather than
   only that something is wrong — a colour on its own is a puzzle. */
.lead.callnow{border-color:var(--red);
  box-shadow:0 0 0 1px color-mix(in srgb,var(--red) 45%,transparent)}
.lead-call{margin-top:10px;padding:7px 11px;border-radius:8px;font-size:13px;
  font-weight:700;line-height:1.4;
  background:color-mix(in srgb,var(--red) 18%,transparent);color:var(--red)}
/* ORANGE IS ONE OF OURS WAITING ON US, which is a different problem from
   nobody having spoken to a stranger. Same shape, different colour, so
   the two are told apart at a glance without being read. */
.lead-wait{margin:8px 0 0;padding:7px 9px;border-radius:8px;font-size:12.5px;font-weight:600;
  color:var(--amber,#f0a742);background:color-mix(in srgb,var(--amber,#f0a742) 13%,transparent)}
.lead.waitback{border-color:color-mix(in srgb,var(--amber,#f0a742) 45%,var(--border))}

/* ═══════════════════════════════════════════════════════════════════
   THE JOB CARD'S TABS
   ═══════════════════════════════════════════════════════════════════
   Anthony: "We like the left to right tabs that changed from like
   communication, notes, details, etc we had in the old one."

   What sits ABOVE them is the band nobody should have to go looking for:
   where the job is, what to do next, what is in the way. Everything else
   is a drawer, and which drawer opens depends on where the job is.

   On a phone the bar scrolls sideways rather than wrapping to two rows —
   a tab strip that changes height when you pick a tab moves everything
   under it, and the thing under it is what you were reaching for. */
/* THREE ACROSS, THEN TWO UNDER THEM. Anthony: no left to right scroll.
   A sideways strip hides tabs off the edge, and a tab you cannot see is a
   tab nobody presses — the whole point of putting them there was that all
   five are in front of you. Five does not divide by three, so the last
   two take half the width each and the row reads as a row rather than as
   a gap. */
/* TWO ACROSS, THREE DOWN, EVEN.

   This was six columns with every tab spanning two and a leftover rule
   giving the fourth and fifth a span of three. That was right for five
   tabs. A sixth arrived with the Vehicle tab and the arithmetic came out
   3 / 2 / 1 -- History alone on the last row against four columns of
   empty space, which is what "aligned wrong" looks like.

   Two real columns instead of six imaginary ones, and no spans at all, so
   the rows stay even whatever the tab count becomes. */
.jtabs{display:grid;grid-template-columns:repeat(2,1fr);gap:4px;margin:14px 0 12px;
  padding:3px;border-radius:11px;background:var(--bg3);border:1px solid var(--border);
  /* Nothing scrolls sideways, at any width. A nowrap label in a narrow
     cell will happily push its container wider than the screen, and the
     whole point of wrapping the tabs was that none of them hide. */
  overflow:hidden}
.jtab{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  border:0;background:none;font:inherit;font-size:13.5px;font-weight:600;color:var(--text3);
  padding:9px 10px;border-radius:9px;cursor:pointer;min-width:0;
  /* The label gives way before the layout does: it clips rather than
     forcing the row wide. min-width:0 is what lets a grid cell shrink at
     all — without it the content floor wins and the container overflows. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.jtab:hover{color:var(--text2)}
.jtab.on{background:var(--card);color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.09)}
.jtab i{font-style:normal;font-size:11px;font-weight:700;padding:1px 6px;border-radius:20px;
  background:var(--bg3);color:var(--text3)}
.jtab.on i{background:color-mix(in srgb,var(--green) 18%,transparent);color:var(--green)}
.jtab i.owed{background:color-mix(in srgb,var(--red) 18%,transparent);color:var(--red)}
/* ── ON YOU, AND WHO OWES US AN ANSWER ──────────────────────────────────
   Anthony: "put the Quotes waiting on a yes right under on you on the desk
   feed. Side by side is find too on desktop only."

   Stacked on a phone because two half-width cards on a 375px screen are
   two unreadable cards. Two columns from 900px, where there is room for
   both without either getting narrow.

   align-items:start because a grid row stretches its children to the
   tallest by default, and a short card padded out to match a long one is
   exactly the alignment bug that took two passes to find on the team
   page.

   An empty #mine collapses rather than holding a blank column: somebody
   with nothing assigned should see one full-width card, not a gap. */
/* ONE PERSON, MORE THAN ONE ROW. Four calls in six minutes made four
   lead cards; they are one card now and this says how many rows it stands
   for, so the fold is visible rather than silent. */
.lead-dupes{font-style:normal;font-size:11px;font-weight:700;padding:1px 6px;
  border-radius:20px;background:var(--line2);color:var(--dim);vertical-align:middle}

/* GET CERTIFIED. Shown to anybody whose certificate list is empty, which
   is most of the shop. Inviting rather than scolding: a technician with no
   ticket is not doing anything wrong. */
.certgo{border:1px solid color-mix(in srgb,var(--green) 34%,transparent)}
.certgo-h{font-weight:700;font-size:15px;margin-bottom:6px}
.certgo p{margin:0 0 8px}
.certgo .btn{margin-top:4px}

/* THE PAY SUM, WRITTEN OUT UNDER THE ROW. Quiet, because it is the
   working rather than the answer -- but present, because a technician who
   can check their own commission against the jobs they did is one who
   never has to ask. */
.paymath{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}

/* THE BAR REGISTRATION on a customer's estimate. Quiet: it is a fact the
   law wants available, not a badge the shop is selling with. */
.gbar{margin-bottom:4px;letter-spacing:.02em}

/* THE BILL AND THE LINES HAVE COME APART. Amber rather than red: it is
   usually a line added after the invoice went, which is an ordinary thing
   to do and not a fault. It says what the customer sees, because that is
   the fact that decides whether it matters. */
.billdrift{margin:6px 0 10px;padding:9px 11px;border-radius:9px;font-size:13px;line-height:1.5;
  background:color-mix(in srgb,var(--amber) 13%,transparent)}
.billdrift .btn{margin-top:7px}

/* Sending the invoice sits under the balance, where somebody has just
   read the number they are about to send. */
.invsend{margin-top:12px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}

/* THEIR PAGE, at the foot of the job. The link is shown in full rather
   than hidden behind a button: somebody checking why a customer says the
   link does not work needs to see the link. */
.custpage .cp-link{font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  color:var(--dim);background:var(--bg3);border:1px solid var(--line);
  border-radius:9px;padding:9px 11px;margin:10px 0 12px;
  word-break:break-all;line-height:1.5}
.custpage .cp-acts{display:flex;gap:9px;flex-wrap:wrap}

.deskpair{display:grid;gap:14px}
#mine:empty{display:none}
@media(min-width:900px){
  .deskpair{grid-template-columns:1fr 1fr;align-items:start}
}
/* The unsent tail on the quotes card. It is a count, not a job, so it
   reads quieter than the rows above it. */
.quiet-row .row-sub{color:var(--dim)}

.jpanel[hidden]{display:none}
.jpanel>*+*{margin-top:12px}
@media(pointer:coarse){
  .jtab{min-height:44px;font-size:14.5px;padding:10px 16px}
}

/* ── THE QUOTE EDITOR ───────────────────────────────────────────────────
   Adding a line is what this screen is for, so the buttons that do it are
   the biggest thing on it rather than three small ones tucked beside a
   total. */
.lineadd{display:flex;flex-wrap:wrap;gap:8px;padding:12px 14px}
.lineadd .btn{flex:1 1 auto;min-height:44px}
@media(max-width:560px){
  .lineadd{flex-direction:column}
  .lineadd .btn{width:100%;min-height:48px}
}

/* What it adds up to, split the way a customer reads a bill: the first
   question after "how much" is always "how much of that is parts". */
.ltotals{padding:4px 14px 14px;display:grid;gap:5px}
.ltot{display:flex;justify-content:space-between;align-items:baseline;font-size:14px}
.ltot span{color:var(--text3)}
.ltot b{font-variant-numeric:tabular-nums}
.ltot.grand{margin-top:6px;padding-top:9px;border-top:1px solid var(--line);font-size:16px}
.ltot.grand b{font-size:21px;font-weight:700}

/* Another one like this. Four brake parts differ by a word and a price. */
.li-copy{border:0;background:none;color:var(--text3);font-size:20px;line-height:1;
  cursor:pointer;padding:0 6px;border-radius:7px}
.li-copy:hover{color:var(--green);background:var(--bg3)}
@media(pointer:coarse){.li-copy{width:42px;height:42px}}

/* What the quote says, and where to send it. */
.quotesays .card-sub i{font-style:normal;font-weight:400;font-size:12px;color:var(--text3);
  margin-left:8px}
.sendquote{display:flex;flex-wrap:wrap;align-items:center;gap:12px}
.sendquote-l{flex:1 1 16rem;min-width:0}
.sendquote .msg-slot{flex:1 1 100%}
.gquote{font-size:14.5px;line-height:1.65;white-space:pre-wrap}
.gquote-x{font-size:13px;color:var(--text3);margin:10px 0 0}
.gfine{margin-top:12px;font-size:12.5px}
.gfine a{color:var(--text3)}

/* Start a job. Work that walks in the door rather than announcing itself
   through a webhook, which is half of it. */
.newjob{margin-top:12px}
@media(max-width:560px){.newjob{width:100%;min-height:48px}}
.page-head .newjob{align-self:flex-start}

/* ── THE DRAFTED QUOTE ──────────────────────────────────────────────────
   Shown with its own numbers and a button. Nothing reaches the job until
   somebody has read it: an estimator that writes straight into the quote
   is an estimator whose mistakes arrive as facts. */
.draftbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:0 14px 12px}
.draftbar .btn{min-height:44px}
.draftbar .quiet{flex:1 1 14rem;min-width:0}
.draft{margin:0 14px 14px;padding:14px;border-radius:11px;background:var(--bg2);
  border:1px solid color-mix(in srgb,var(--purple) 38%,var(--line))}
.draft-h{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;
  color:var(--purple2)}
.draft-seen{margin:8px 0 0;font-size:13.5px;line-height:1.55;padding:8px 11px;border-radius:8px;
  background:color-mix(in srgb,var(--amber) 13%,transparent)}
.draft-sec{margin-top:12px;font-size:12px;font-weight:700;color:var(--text3);
  text-transform:uppercase;letter-spacing:.06em}
.draft-row{display:flex;justify-content:space-between;gap:12px;font-size:14px;
  padding:4px 0;border-bottom:1px solid var(--line)}
.draft-row:last-of-type{border-bottom:0}
.draft-row i{font-style:normal;font-size:11px;color:var(--text3);
  background:var(--bg3);padding:1px 6px;border-radius:20px;margin-left:5px}
.draft-row b{font-variant-numeric:tabular-nums;white-space:nowrap}
.draft-note{margin:6px 0 0;font-size:14px;line-height:1.6}
.draft-used{margin:10px 0 0;font-size:12.5px;color:var(--text3);line-height:1.5}
@media(max-width:560px){.draftbar{flex-direction:column;align-items:stretch}
  .draftbar .btn{width:100%}}

/* ── THE SIGNATURE ON FILE ───────────────────────────────────────────────
   Anthony: "notes needs to have a record of signature or record somewhere
   on the card." This is the file copy of what a customer agreed to, so the
   drawing is rendered at a readable size rather than as a thumbnail: it is
   looked at exactly once, by somebody who is being told the work was never
   approved, and a postage stamp settles nothing. */
.authrec{padding:12px 14px;border-top:1px solid var(--line)}
.authrec:first-of-type{border-top:0}
.authrec-h{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.authrec-h b{font-size:17px}
.authrec-w{margin-top:2px;font-size:14px;color:var(--text2)}
.authrec-w .when{color:var(--text3);margin-left:6px;font-size:12.5px}
.authrec-sig{display:block;margin-top:8px;max-width:100%;height:auto;background:#fff;
  border:1px solid var(--line);border-radius:8px;padding:4px}
.authrec.dec{opacity:.7}
.auth-see{margin-left:8px;font:inherit;font-size:12.5px;color:inherit;background:none;
  border:0;padding:0;text-decoration:underline;cursor:pointer}

/* ── WHAT THE SHOP CHARGES, ON THE SCREEN ────────────────────────────────
   The hourly rate in force and the shop's fixed prices, above the buttons
   that add a line. Anthony's three flat prices and the after-hours rate
   were being typed from memory onto every quote, which is how the same job
   goes out at two prices in one day. */
.pricebar{margin:0 0 10px}
.pricebar:empty{display:none}
.pb-rate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  font-size:13.5px;color:var(--text2);margin:0 0 8px}
.pb-rate b{color:var(--text);font-size:15px}
.pb-switch{border:1px solid var(--border);background:var(--bg3);color:var(--text2);
  font:inherit;font-size:12.5px;font-weight:600;padding:5px 10px;border-radius:8px;cursor:pointer}
.pb-switch:hover{border-color:var(--green);color:var(--text)}
.pb-when{font-size:12px;color:var(--text3)}
.pb-flat{display:flex;gap:6px;flex-wrap:wrap}
.pb-fixed{display:inline-flex;align-items:center;gap:6px;border:1px dashed var(--border);
  background:none;color:var(--text2);font:inherit;font-size:13px;padding:7px 11px;
  border-radius:9px;cursor:pointer}
.pb-fixed b{color:var(--text)}
.pb-fixed:hover{border-style:solid;border-color:var(--green);color:var(--text)}
.pb-fixed[disabled]{opacity:.5;cursor:default}

/* The first-time concession reads as money OFF, not another thing to add. */
.pb-fixed.off{border-color:var(--amber,#b45309);color:var(--amber,#b45309)}
.pb-fixed.off b{color:inherit}
.pb-fixed.off:hover{border-color:var(--amber,#b45309)}

/* ── DUPLICATES ──────────────────────────────────────────────────────────
   Two jobs of one screen: show what each copy is holding, and show why the
   pair is on the list. Both exist so nobody presses Merge on faith. */
.duprow{display:grid;grid-template-columns:1.2fr 1.4fr auto;gap:14px;align-items:center;
  padding:13px 14px;border-top:1px solid var(--line)}
.duprow:first-of-type{border-top:0}
.duprow.keeper{background:rgba(95,211,154,.055)}
.duprow-who b{font-size:16px}
.duprow-who a{color:inherit;text-decoration:none}
.duprow-who a:hover b{text-decoration:underline}
.duprow-has{display:flex;flex-wrap:wrap;gap:6px}
.duprow-has span{font-size:12.5px;color:var(--text3);background:var(--bg3);
  border:1px solid var(--border);border-radius:7px;padding:3px 8px}
.duprow-has span b{color:var(--text)}
.duprow-has span.paid{border-color:var(--green);color:var(--green)}
.duprow-has span.paid b{color:var(--green)}
.duprow-act{text-align:right}

/* The pair, side by side, so which one survives is never a guess. */
.duppair{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:14px}
.dupside{display:block;padding:13px 15px;border:1px solid var(--line);border-radius:12px;
  text-decoration:none;color:inherit}
.dupside.keeper{border-color:var(--green);background:rgba(95,211,154,.06)}
.dupside.dropping{border-style:dashed;opacity:.86}
.dupside-t{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--text3);margin-bottom:7px}
.dupside.keeper .dupside-t{color:var(--green)}
.dupside-n{font-size:16px;font-weight:700;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.dupside-d{margin-top:5px;font-size:13.5px;color:var(--text2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dupside-m{margin-top:5px;font-size:12.5px;color:var(--text3)}

.dupwhy{display:flex;flex-wrap:wrap;gap:6px;padding:0 14px 14px}
.dupwhy span{font-size:12.5px;color:var(--text2);background:var(--bg3);
  border:1px solid var(--border);border-radius:999px;padding:4px 11px}

@media (max-width: 700px){
  .duprow{grid-template-columns:1fr;gap:8px}
  .duprow-act{text-align:left}
  .duppair{grid-template-columns:1fr}
}


/* A chip on the calendar opens its job, so it has to look like it will.
   It sits inside the day button, which is why the pointer is set here
   rather than inherited. */
.cal-chip[data-job]{cursor:pointer}
.cal-chip[data-job]:hover{outline:1px solid var(--green);outline-offset:1px}

/* ── ON YOU ──────────────────────────────────────────────────────────────
   The band a technician came to the schedule for. A calendar answers
   "what is happening on this day"; this answers "what is on me", which
   is a different question and had no answer anywhere until now. */
/* WORK THAT IS FINISHED STAYS ON ITS DAY and stops shouting. A day with
   two jobs left and three already paid was reading as five jobs left. */
.row.settled .row-title .num,.row.settled .row-sub,.row.settled .slot{opacity:.55}
.row.settled .amount{opacity:.7}

.minecard{border-color:var(--green);box-shadow:0 0 0 1px rgba(99,222,70,.10)}
.minecard .card-head{color:var(--green)}
.minecard .row.late{background:rgba(240,167,66,.07)}
.pill.late{border-color:var(--amber,#b45309);color:var(--amber,#b45309)}

/* ── THE THREE THINGS YOU DO WITH A QUOTE ────────────────────────────────
   Draft it, send it, bin it. They used to be in three different places on
   the one screen whose entire purpose is the quote. */
.quoteacts{display:flex;flex-wrap:wrap;align-items:center;gap:14px}
.quoteacts-l{flex:1 1 16rem;min-width:0}
.quoteacts-b{display:flex;gap:8px;flex-wrap:wrap}
.quoteacts .msg-slot{flex:1 1 100%}
.btn.danger{color:var(--red,#e06c6c);border-color:rgba(224,108,108,.34)}
.btn.danger:hover{border-color:var(--red,#e06c6c);background:rgba(224,108,108,.08)}
.headacts{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
@media(max-width:560px){
  .quoteacts-b{width:100%}
  .quoteacts-b .btn{flex:1 1 auto;min-height:44px}
  .headacts .btn{flex:1 1 auto;min-height:46px;margin-top:0}
}

/* Whose work is on the board. Sits with the day/week/month picker, because
   they are two halves of the same question. */
.whopick{font:inherit;font-size:13px;padding:7px 10px;border-radius:9px;
  border:1px solid var(--border);background:var(--bg3);color:var(--text2);
  max-width:11rem;min-height:36px}
.whopick:focus{outline:0;border-color:var(--green);color:var(--text)}
@media(max-width:560px){.whopick{max-width:none;flex:1 1 100%;min-height:44px}}

/* ── PARTS WE HAVE FITTED ────────────────────────────────────────────────
   The shop's own history as a catalogue, because parts_catalog holds
   nothing and always will. */
.pspart{display:grid;grid-template-columns:1fr auto;gap:2px 12px;width:100%;text-align:left;
  font:inherit;background:none;border:0;border-top:1px solid var(--line);
  padding:11px 2px;cursor:pointer;color:inherit}
.pspart:first-child{border-top:0}
.pspart:hover{background:var(--bg3)}
.pspart-n{font-size:14.5px;font-weight:600;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.pspart-s{grid-column:1;font-size:12px;color:var(--text3)}
.pspart-p{grid-row:1/3;align-self:center;font-size:16px;font-weight:700;white-space:nowrap}
.pspart.on{background:rgba(99,222,70,.10)}
.pspart.on .pspart-p:after{content:" ✓";color:var(--green)}
.pspart[disabled]{opacity:.5}

/* ── WHOSE WORK IS ON THE BOARD ──────────────────────────────────────────
   The filter and the legend are one control, because choosing whose day to
   look at and reading whose job is whose are the same question. It replaced
   a small grey dropdown wedged between three other controls, which looked
   like furniture and removed itself whenever anything threw. */
.crewbar{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 13px}
.crewpill{display:inline-flex;align-items:center;gap:7px;font:inherit;font-size:13px;
  font-weight:600;padding:6px 12px;border-radius:999px;cursor:pointer;
  border:1px solid var(--border);background:var(--bg3);color:var(--text3)}
.crewcount{align-self:center;font-size:11.5px;color:var(--text3);margin-left:4px}
.crewpill:hover{color:var(--text);border-color:var(--text3)}
.crewpill.on{color:var(--text);background:var(--bg2);
  border-color:hsl(var(--crew,140) 70% 55%);
  box-shadow:inset 0 0 0 1px hsl(var(--crew,140) 70% 55% / .45)}
.crewdot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;
  background:hsl(var(--crew,140) 70% 55%)}

/* The calendar chip wears its technician's colour. Status still shows
   through the left edge, because "booked" and "in progress" are different
   things whoever is doing them. */
.cal-chip.crewed{background:hsl(var(--crew) 70% 55% / .18);
  border-left:3px solid hsl(var(--crew) 70% 55%)}

/* And the list says the name outright, because a colour with no key is a
   riddle when you are reading one row rather than a month. */
.whochip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  padding:2px 8px;border-radius:999px;white-space:nowrap;
  color:hsl(var(--crew,140) 65% 62%);
  background:hsl(var(--crew,140) 70% 55% / .13);
  border:1px solid hsl(var(--crew,140) 70% 55% / .34)}
.whochip.none{color:var(--text3);background:var(--bg3);border-color:var(--border)}
/* WHOSE WORK IS SHOWING, lit or dimmed rather than lit or absent.
   Anthony: "selected ones are lit up unselected are greyed out a bit so
   you can tell the difference." An unselected name has to stay readable,
   because it is how you add the second technician. */
.crewpill{opacity:.5;transition:opacity .12s}
.crewpill:hover{opacity:.85}
.crewpill.on{opacity:1}

/* ONE BAR, LEFT TO RIGHT, ON EVERY SCREEN.
   Anthony: "for the left right and month/day/week functions and the list
   vs calendar view. Make this one left to right bar where they do their
   correct action once clicked. But one bar left to right."

   It wrapped to two rows on a phone -- arrows and the range on the first,
   the calendar/list toggle stranded in the middle of the second -- which
   reads as three separate controls rather than one bar. Nothing wraps now:
   back, forward, how much, and how to look at it, in the order they are
   used. The sizes below are what makes all four fit inside 375px. */
.schedbar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;width:100%}
/* The steppers are one unit and must never wrap inside themselves: with
   only `display:flex` on .schsteps the two arrows stacked vertically the
   moment the bar got tight, which is worse than the wrapping it replaced. */
.schedbar .schsteps{flex:0 0 auto;flex-wrap:nowrap}
.schedbar .schstep{flex:0 0 auto}
.schedbar .viewtog{margin-left:0}
.schedbar .viewtog.wide{flex:1 1 auto;min-width:0;justify-content:center}
.schedbar .viewtog.wide button{flex:1 1 auto;min-width:0;white-space:nowrap}
.schedbar .viewtog.icons{flex:0 0 auto;margin-left:auto}

@media(max-width:560px){.crewpill{font-size:12.5px;padding:7px 11px}
  .schedbar{gap:6px}
  .schsteps{gap:4px;margin-right:0}
  .schstep{width:32px;height:32px;font-size:13px}
  .viewtog.wide button{padding:7px 8px;font-size:12.5px}
  .schedbar .viewtog.icons button{width:28px;height:26px}}
@media(pointer:coarse) and (max-width:560px){.schstep{width:34px;height:34px}}

/* The counters, and everything else folded behind them. O'Reilly, AutoZone
   and NAPA are minutes away and will hold a part for a van this morning;
   a marketplace four dollars cheaper and three days out is not a better
   answer for a mobile mechanic. */
.pshead{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text3);padding:12px 2px 4px}
.pspart.counter .pspart-s b{color:var(--green)}
.pspart.counter{border-left:3px solid var(--green);padding-left:9px}
.psweb{margin-top:10px;border-top:1px solid var(--line)}
.psweb summary{cursor:pointer;font-size:12.5px;color:var(--text3);padding:11px 2px}
.psweb summary:hover{color:var(--text2)}

/* "On you", under the railway. The railway answers where the SHOP is
   stuck; this answers where YOU are, and they get asked in the same
   breath by the same person. */
.minesum{display:flex;flex-wrap:wrap;gap:8px 16px;padding:11px var(--pad) 12px;
  border-bottom:1px solid var(--line2);font-size:13.5px;color:var(--text3)}
.minesum b{color:var(--text);font-size:15px}
.minesum .bad b,.minesum .bad{color:var(--amber,#f0a742)}
/* THE REST, BEHIND A BUTTON. The count is on the button rather than left
   to be discovered: a list that quietly stops short is how work goes
   missing, which is the whole reason this box exists. */
.minemore{display:block;width:100%;font:inherit;font-size:13px;color:var(--text3);
  background:none;border:0;border-top:1px solid var(--line2);padding:12px var(--pad);
  text-align:left;cursor:pointer}
.minemore:hover{color:var(--text);background:var(--bg2)}
.minecard .row.late{background:rgba(240,167,66,.07)}

/* Closing one of these out from the board rather than opening the job. */
.lateline{border-top:1px solid var(--line)}
.lateline:first-of-type{border-top:0}
.lateline .row{border-top:0}
.lateacts{display:flex;gap:8px;justify-content:flex-end;padding:0 var(--pad) 11px}
.btn.sm{font-size:12.5px;padding:6px 12px}

/* == THE FRONT DESK FEED ==============================================
   Who is waiting on a person, in the top right. Red is nobody has spoken
   to them and the machine cannot; amber is one of ours waiting on an
   answer. The bell carries the colour so the difference survives without
   opening it. */
.feedwrap{position:relative;display:flex;align-items:center}
.feedbell{position:relative;display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;border:0;border-radius:10px;background:none;color:var(--text3);
  cursor:pointer}
.feedbell:hover{background:var(--bg2);color:var(--text)}
.feedbell.amber{color:var(--amber,#f0a742)}
.feedbell.red{color:var(--red,#f26d6d)}
.feedn{position:absolute;top:1px;right:0;min-width:16px;height:16px;padding:0 4px;
  border-radius:9px;background:var(--text3);color:var(--bg);font-size:10.5px;font-weight:800;
  font-style:normal;line-height:16px;text-align:center}
.feedbell.amber .feedn{background:var(--amber,#f0a742)}
.feedbell.red .feedn{background:var(--red,#f26d6d);color:#fff}

.feedpanel{position:absolute;top:44px;right:0;z-index:60;width:min(23rem,calc(100vw - 24px));
  max-height:min(30rem,70vh);overflow-y:auto;background:var(--bg);border:1px solid var(--border);
  border-radius:13px;box-shadow:0 14px 40px rgba(0,0,0,.34)}
.feedpanel-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:12px 14px 10px;font-size:11px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--text3);border-bottom:1px solid var(--line2)}
.feedpanel-head b{font-size:11px;letter-spacing:.04em}
.feedempty{padding:18px 14px;font-size:13px;color:var(--text3);line-height:1.55}

.feedrow{display:flex;align-items:flex-start;gap:9px;padding:11px 12px 11px 10px;
  border-bottom:1px solid var(--line2);text-decoration:none;color:inherit}
.feedrow:last-child{border-bottom:0}
.feedrow:hover{background:var(--bg2)}
.feeddot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;margin-top:6px;
  background:var(--text3)}
.feedrow.red .feeddot{background:var(--red,#f26d6d)}
.feedrow.orange .feeddot{background:var(--amber,#f0a742)}
.feedmain{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.feedtitle{font-size:14px;font-weight:600;color:var(--text)}
.feeddetail{font-size:12.5px;color:var(--text2);overflow:hidden;text-overflow:ellipsis;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.feedwhen{font-size:11.5px;color:var(--text3)}
.feeddone{flex:0 0 auto;width:28px;height:28px;border:1px solid var(--border);border-radius:8px;
  background:none;color:var(--text3);font-size:14px;line-height:1;cursor:pointer}
.feeddone:hover{border-color:var(--green);color:var(--green)}

@media(max-width:560px){
  .feedpanel{position:fixed;top:56px;right:10px;left:10px;width:auto}
}
@media(pointer:coarse){.feeddone{width:34px;height:34px}}

/* RED, ACROSS THE TOP, UNASKED. Only red: a strip that is permanently
   there stops being read, and orange is one of ours waiting rather than a
   stranger about to ring the next shop. */
.urgent{display:flex;align-items:center;gap:11px;padding:10px 16px;text-decoration:none;
  color:var(--text);background:color-mix(in srgb,var(--red,#f26d6d) 15%,var(--bg));
  border-bottom:1px solid color-mix(in srgb,var(--red,#f26d6d) 40%,transparent)}
.urgent:hover{background:color-mix(in srgb,var(--red,#f26d6d) 22%,var(--bg))}
.urgent-dot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;background:var(--red,#f26d6d)}
.urgent-main{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.urgent-main b{font-size:14px;font-weight:700}
.urgent-main i{font-style:normal;font-size:12.5px;color:var(--text2)}
.urgent-go{flex:0 0 auto;font-size:12px;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--red,#f26d6d)}
@media(max-width:560px){
  .urgent{padding:9px 12px;gap:9px}
  .urgent-main b{font-size:13.5px}
  .urgent-go{font-size:11px}
}

/* == THE SCHEDULE BAR, SIMPLIFIED =====================================
   It had a stepper whose middle button said "Today" beside a dropdown
   whose value also said "Today". The arrows now sit next to the TITLE,
   which is the thing they move; the ranges are three buttons rather than
   a three-item menu; and Today only exists when you are not on today. */
.schwhen{display:flex;align-items:center;gap:8px;margin-top:2px;flex-wrap:wrap}
.schwhen p{margin:0}
.schstep{width:30px;height:30px;border:1px solid var(--border);border-radius:8px;
  background:none;color:var(--text3);font-size:14px;line-height:1;cursor:pointer}
.schstep:hover{border-color:var(--border2);color:var(--text)}
.schhome{font:inherit;font-size:12.5px;color:var(--text2);background:var(--bg2);cursor:pointer;
  border:1px solid var(--border);border-radius:8px;padding:5px 11px;margin-left:4px}
.schhome:hover{border-color:var(--green);color:var(--text)}
.viewtog.wide button{padding:7px 13px;font-size:13px;width:auto}
@media(pointer:coarse){.schstep{width:38px;height:38px}}

/* == WHO IS ON THIS JOB ===============================================
   The two questions asked most often about a job, at the top, in the same
   hues the calendar and the tiles use. A colour code that changes between
   screens is decoration; this is the same crewHue everywhere. */
.whobar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:10px var(--pad);margin-bottom:10px;background:var(--bg);
  border:1px solid var(--border);border-radius:12px}
.whob-lab{font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--text3)}
.whob-sep{width:1px;align-self:stretch;background:var(--line2);margin:0 4px}
.whob,.whob-pick{display:inline-flex;align-items:center;gap:6px;padding:3px 10px 3px 3px;
  border-radius:999px;background:hsl(var(--crew,210) 60% 50% / .13);
  border:1px solid hsl(var(--crew,210) 60% 55% / .38)}
.whob-dot{display:flex;align-items:center;justify-content:center;width:24px;height:24px;
  border-radius:50%;font-style:normal;font-size:10.5px;font-weight:800;color:#fff;
  background:hsl(var(--crew,210) 58% 45%)}
.whob-n{font-size:13px;font-weight:600;color:var(--text)}
.whob.none{background:var(--bg2);border-color:var(--border)}
.whob.none .whob-dot{background:var(--text3)}
.whob.none .whob-n{color:var(--text3);font-weight:500}
.whob-pick select{font:inherit;font-size:13px;font-weight:600;color:var(--text);
  background:none;border:0;padding:2px 2px 2px 0;cursor:pointer;max-width:11rem}
.whob-pick select:focus{outline:2px solid var(--green);outline-offset:2px;border-radius:4px}
.whob-edit{font:inherit;font-size:12px;color:var(--text3);background:none;cursor:pointer;
  border:1px solid var(--border);border-radius:8px;padding:4px 9px}
.whob-edit:hover{border-color:var(--border2);color:var(--text)}
@media(max-width:560px){
  .whobar{padding:9px 11px;gap:7px}
  .whob-sep{display:none}
  .whob-n{font-size:12.5px}
}

/* == SUGGESTIONS UNDER THE BOX ========================================
   On the body rather than beside the input, because the line editor rows
   have their own overflow and a list clipped by its own row is a list
   nobody can read past the second entry. */
.tahead{position:absolute;z-index:80;max-height:17rem;overflow-y:auto;
  background:var(--bg);border:1px solid var(--border2);border-radius:11px;
  box-shadow:0 12px 34px rgba(0,0,0,.32)}
.ta-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  width:100%;font:inherit;text-align:left;background:none;border:0;cursor:pointer;
  padding:9px 12px;border-bottom:1px solid var(--line2)}
.ta-row:last-child{border-bottom:0}
.ta-row:hover,.ta-row.on{background:var(--bg2)}
.ta-l{font-size:14px;color:var(--text);font-weight:500}
.ta-r{flex:0 0 auto;font-size:11.5px;color:var(--text3);white-space:nowrap}
@media(pointer:coarse){.ta-row{padding:12px}}

/* == THE DRAFT, EDITABLE BEFORE IT LANDS ==============================
   It was a read-only list with one button that took all of it, so an
   estimator right about four lines in five was all-or-nothing. Every field
   here is a field; nothing reaches the job until Put it on the quote. */
.draft-row{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  padding:7px 0;border-bottom:1px solid var(--line2)}
.draft-row:last-of-type{border-bottom:0}
.draft-t{flex:1 1 11rem;min-width:0;font:inherit;font-size:13.5px;color:var(--text);
  background:none;border:0;border-bottom:1px dashed transparent;padding:4px 0}
.draft-t:hover{border-bottom-color:var(--border2)}
.draft-t:focus{outline:none;border-bottom-color:var(--green)}
.draft-q{display:inline-flex;align-items:center;gap:2px;flex:0 0 auto}
.draft-q i{font-style:normal;font-size:11.5px;color:var(--text3)}
.draft-q input{font:inherit;font-size:13px;text-align:right;color:var(--text2);
  background:var(--bg2);border:1px solid var(--border);border-radius:7px;padding:5px 6px;
  width:3.6rem;font-variant-numeric:tabular-nums}
.draft-q input:focus{outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}
.draft-pn{flex:0 0 6.5rem;font:inherit;font-size:12px;color:var(--text3);background:none;
  border:0;border-bottom:1px dashed var(--line2);padding:4px 2px}
.draft-pn:focus{outline:none;border-bottom-color:var(--green);color:var(--text)}
.draft-x{flex:0 0 auto;width:26px;height:26px;border:0;border-radius:7px;background:none;
  color:var(--text3);font-size:18px;line-height:1;cursor:pointer}
.draft-x:hover{background:color-mix(in srgb,var(--amber) 18%,transparent);color:var(--text)}
textarea.draft-note{width:100%;font:inherit;font-size:13px;color:var(--text2);
  background:var(--bg2);border:1px solid var(--border);border-radius:9px;padding:9px 10px;
  resize:vertical;line-height:1.5}
textarea.draft-note:focus{outline:2px solid var(--green);outline-offset:-1px;
  border-color:transparent}
@media(max-width:560px){
  .draft-t{flex:1 1 100%}
  .draft-pn{flex:1 1 6rem}
}
@media(pointer:coarse){.draft-x{width:34px;height:34px}}

/* == THE LEAD TILE: WHERE THEY ARE AND WHAT WAS SAID ==================
   The card used to say "not contacted" beside a red tag saying we had
   texted twice, because the two came from different places. Both come off
   the conversation now. The stage pill colours by whose move it is: grey
   while nothing has gone out, blue once we have texted, green when they
   have written back, amber when they are waiting on us. */
.lead-said{margin:8px 0 0;padding:8px 10px;border-radius:9px;font-size:12.5px;
  line-height:1.45;color:var(--text2);background:var(--bg2);
  border-left:3px solid var(--border2)}
.lead-said i{font-style:normal;font-weight:700;font-size:10.5px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text3);margin-right:6px}
.lead-said.them{border-left-color:var(--green);color:var(--text)}

.pill.stage-new{background:var(--bg2);color:var(--text3)}
.pill.stage-opener,.pill.stage-nudged{background:color-mix(in srgb,var(--purple2,#8b7cf6) 18%,transparent);
  color:var(--purple2,#8b7cf6)}
.pill.stage-replied{background:color-mix(in srgb,var(--amber,#f0a742) 18%,transparent);
  color:var(--amber,#f0a742)}
.pill.stage-talking,.pill.stage-called{background:color-mix(in srgb,var(--green) 16%,transparent);
  color:var(--green)}
.pill.stage-quoted{background:color-mix(in srgb,var(--green) 22%,transparent);color:var(--green)}

/* The all-time balance under a pay row. Quieter than the period figure,
   because it is the sanity check rather than the number being worked. */
.when.alltime{font-size:11px;color:var(--text3);margin-top:1px}

/* A payment with no period is filed on the day it was handed over, which
   is right for ordinary pay and wrong for a catch-up. Said in amber rather
   than left as grey text, because it is the thing that made four people
   read as overpaid. */
.noperiod{color:var(--amber,#f0a742)}

/* FINISHED WORK ON THE ON YOU CARD. Green and under a divider, not mixed
   in: the question the card answers is "what is left", and that answer
   must not be something you read past four green rows to find. The rows
   reuse .row.settled, which already dims done work on the schedule. */
.minedone{border-top:1px solid var(--line2);margin-top:4px}
.minedone-h{padding:9px var(--pad) 5px;font-size:10.5px;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;color:var(--green)}
.minedone .row .num{color:var(--green)}
.minedone .row .pill{background:color-mix(in srgb,var(--green) 16%,transparent);
  color:var(--green)}
.minesum .good{color:var(--green)}
.minesum .good b{color:var(--green)}

/* HOW SOMEBODY IS DOING, on their team card. Admin and supervisor only.
   The COUNT is the headline and the percentage is small and last: leading
   with a rate tells a crew to take fewer jobs, because two out of two
   beats seven out of nine on any percentage. */
/* IT HAS TO SAY WHICH COLUMN IT IS IN.
   `.pcard` is a three-row grid with the avatar spanning all three, so a
   fourth child creates a fourth row and lands in the avatar's column --
   which shoved the name, the tags and the email out of line on every card
   on the page. It spans both columns and reads as a footer. */
.pcard-score{grid-column:1 / -1;display:flex;align-items:baseline;gap:5px;flex-wrap:wrap;
  margin-top:7px;padding-top:7px;border-top:1px solid var(--line2);
  font-size:12px;color:var(--text3)}
.pcard-score b{font-size:14px;color:var(--green);font-weight:700}
.pcard-score i{font-style:normal}
.pcard-score i.bad{color:var(--amber,#f0a742)}
.pcard-score em{margin-left:auto;font-style:normal;font-size:11.5px;font-weight:700;
  color:var(--text2);background:var(--bg2);border-radius:999px;padding:2px 8px}

/* == THE RUNBOOK ======================================================
   Cards you read one of, not a document you read through. Each one starts
   with the thing that happened, because that is what somebody is holding
   when they open this. */
.rbsecs{display:flex;flex-direction:column;gap:26px}
.rbsec > h2{font-size:12px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text3);margin:0 0 10px;padding-bottom:7px;border-bottom:1px solid var(--line2)}
.rb{background:var(--bg);border:1px solid var(--border);border-radius:13px;
  padding:15px 16px;margin-bottom:12px}
.rb > h3{margin:0 0 7px;font-size:16px;font-weight:700;color:var(--text);line-height:1.35}
.rb-means{margin:0 0 11px;font-size:13.5px;color:var(--text2);line-height:1.6}
.rb-do{margin:0;padding-left:19px;display:flex;flex-direction:column;gap:6px}
.rb-do li{font-size:13.5px;color:var(--text);line-height:1.55}
.rb-not{margin:12px 0 0;padding:9px 11px;border-radius:9px;font-size:13px;line-height:1.55;
  color:var(--text2);background:color-mix(in srgb,var(--amber,#f0a742) 11%,transparent);
  border-left:3px solid var(--amber,#f0a742)}
.rb-not b{color:var(--amber,#f0a742)}
.rb-shot{margin:13px 0 0}
.rb-shot img{display:block;width:100%;height:auto;border-radius:10px;
  border:1px solid var(--border)}
.rb-shot figcaption{margin-top:6px;font-size:12px;color:var(--text3);line-height:1.5}
.rb-go{display:inline-block;margin-top:12px;font-size:13px;font-weight:600;
  color:var(--green);text-decoration:none}
.rb-go:hover{text-decoration:underline}
@media(max-width:560px){
  .rb{padding:13px 13px}
  .rb > h3{font-size:15px}
}

/* A picture on a walkthrough step. Narrower than the runbook's, because
   here it is illustrating a sentence rather than being the point. */
/* A PICTURE THE SIZE OF THE SCREEN IT IS OF.
   The phone shot is 375 wide. Stretched to a laptop it is a blurry poster
   of a phone, and it stops looking like the thing in the trainee's hand.
   So each device gets the width it was taken at, capped by the column. */
.wt-shot{margin:10px 0 2px}
.wt-shot-m img{max-width:23.5rem}
.wt-shot-t img{max-width:30rem}
.wt-shot-d img{max-width:34rem}
.wt-shot img{display:block;width:100%;max-width:34rem;height:auto;border-radius:10px;
  border:1px solid var(--border)}

/* A QUIET TALLY OF THE DAY. No target, no bar, no colour that changes
   when it is low: a counter that judges is one people learn to game. It
   does not draw at all until there is something to say. */
.tally{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  padding:11px var(--pad);margin-bottom:10px;border-radius:12px;
  background:var(--bg);border:1px solid var(--border)}
.tally-n{font-size:22px;font-weight:800;color:var(--green);
  font-variant-numeric:tabular-nums;line-height:1}
.tally-w{font-size:13.5px;color:var(--text2);font-weight:600}
.tally-of{margin-left:auto;font-size:11.5px;color:var(--text3)}
@media(max-width:560px){
  .tally-of{margin-left:0;flex:1 1 100%}
}

/* A LANDLINE IS THE PRIORITY CALLBACK. Two things turn a lead red and only
   one of them can never answer a text -- flattening those into one colour
   is how the urgent one gets worked third. */
.lead-landline{display:inline-block;font-size:10px;font-weight:800;letter-spacing:.09em;
  padding:2px 6px;border-radius:5px;margin-right:6px;
  background:var(--red,#f26d6d);color:#fff}
.lead.landline{border-color:var(--red,#f26d6d)}
.lead.landline .lead-call{color:var(--red,#f26d6d);
  background:color-mix(in srgb,var(--red,#f26d6d) 13%,transparent)}

/* ── working in another shop ─────────────────────────────────────────
   The banner has to be impossible to miss: while it is on screen, every
   other page in the app is about somebody else's shop, and a person who
   forgets that will read an empty Jobs list as data loss. Amber rather
   than red -- nothing is wrong, you are just somewhere else. */
.awaybar{border-color:var(--amber);box-shadow:inset 0 0 0 1px var(--amber)}
.awaybar .card-head{color:var(--amber)}
.row.rowhere{background:rgba(255,255,255,.035)}
.row-side .btn{margin-top:6px;font-size:13px;padding:7px 11px}

/* ── a price nobody sent ─────────────────────────────────────────────
   The list sorts newest first and folds anything over a week behind a
   button, so the quotes that have been ignored longest are the hardest to
   reach. These two make neglect visible without reordering the screen
   under somebody mid-scroll.

   Amber, not red: nothing has failed, it is simply the shop's move and
   nobody has made it. The edge is on the left because a thumb scrolling a
   phone tracks that side, and it is 3px because 1px reads as a hairline
   border rather than a flag. */
.row.coldquote{border-left:3px solid var(--amber);padding-left:calc(var(--pad) - 3px)}
.row.coldquote .when{color:var(--amber)}

/* A POSSIBLE DUPLICATE, and the way off the board.

   The flag is a statement, not an accusation — two open quotes for one
   car is usually two people being helpful on the same morning. So it
   reads in the same amber the cold-quote strip uses rather than in red,
   and the archive button beside it is quiet until it is armed. */
.row.twinquote{border-left:3px solid var(--amber);padding-left:calc(var(--pad) - 3px)}
.pill.warn{background:var(--amber-bg,rgba(232,163,60,.14));color:var(--amber);
  border:1px solid var(--amber)}
/* Start the walk straight off the list. Green because it is the way
   forward, and quiet because most rows are not the technician's. */
.rowwalk{display:inline-block;margin-top:6px;margin-right:6px;font-size:12px;
  line-height:1;padding:7px 11px;min-height:32px;border-radius:8px;
  text-decoration:none;color:#fff;background:var(--green);border:1px solid var(--green)}
.rowwalk:hover{filter:brightness(1.06)}
.rowarch{margin-top:6px;font:inherit;font-size:12px;line-height:1;cursor:pointer;
  padding:6px 9px;min-height:32px;border-radius:8px;color:var(--text3);
  background:transparent;border:1px solid var(--border)}
.rowarch:hover{color:var(--text);border-color:var(--text3)}
.rowarch.armed{color:#fff;background:var(--amber);border-color:var(--amber)}
.rowarch:disabled{opacity:.6;cursor:default}

.coldbar{display:flex;align-items:center;gap:12px;width:100%;margin-top:10px;
  padding:12px 14px;border:1px solid var(--amber);border-radius:var(--radius);
  background:color-mix(in srgb,var(--amber) 9%,transparent);
  color:var(--ink);font:inherit;text-align:left;cursor:pointer;
  transition:background 180ms ease,transform 120ms ease}
.coldbar:hover{background:color-mix(in srgb,var(--amber) 15%,transparent)}
.coldbar:active{transform:translateY(1px)}
.coldbar:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.coldbar .coldn{font-size:27px;font-weight:800;line-height:1;color:var(--amber);
  font-variant-numeric:tabular-nums}
.coldbar i{font-style:normal;color:var(--dim);font-size:12.5px}
.coldbar b{font-variant-numeric:tabular-nums}

/* ── is it on, or is it off ──────────────────────────────────────────
   The switch card marked its state with `.pill good` and `.pill bad`.
   Neither class was ever written, so an automation that was ON and one
   that was OFF rendered the same grey pill: the only indicator on the
   screen did not indicate anything, and the word had to be read to know.

   Read at arm's length in a bay, so it is colour AND weight AND a dot,
   not colour alone -- about one man in twelve cannot separate the red
   from the green, and this screen decides whether customers get answered. */
.onoff{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:800;
  letter-spacing:.08em;padding:3px 9px 3px 7px;border-radius:999px;
  font-variant-numeric:tabular-nums}
.onoff::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
.onoff.is-on{color:var(--green2);background:color-mix(in srgb,var(--green) 18%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--green) 40%,transparent)}
.onoff.is-off{color:var(--text3);background:transparent;
  box-shadow:inset 0 0 0 1px var(--border2)}
.onoff.is-off::before{background:transparent;box-shadow:inset 0 0 0 1.5px currentColor}

/* A switch that is off is not an error, so the button that turns it back
   on is the primary one and the button that turns it off is the quiet one.
   Both need a visible focus ring: this screen is reachable by keyboard on
   the desktop in the office. */
.btn:focus-visible{outline:2px solid var(--purple2);outline-offset:2px}
.btn[disabled]{opacity:.55;cursor:progress}

/* ── the automations screen ──────────────────────────────────────────
   A row that is switched off is not an error and must not read like one.
   It is dimmed rather than coloured, so the eye finds the ON rows first
   and an OFF row still reads clearly when you go looking for it. */
.autosum{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.autosum .fine{margin-left:auto}
.row.isoff .row-sub,.row.isoff .row-note{opacity:.62}
.row.isoff .num{opacity:.78}

/* ══ FEEL ═══════════════════════════════════════════════════════════════
   Anthony: "figured at least it would have rounded buttons and improved
   some of the UI."

   He is right, and the stylesheet said so: in 2,654 lines there was ONE
   :active rule, ten transitions, and a --shadow token used three times.
   Nothing moved when you pressed it and nothing sat above the page. On a
   desktop that reads as plain; on a phone with a glove on it reads as
   broken, because a tap with no acknowledgement is a tap you make twice.

   What this is NOT: no glass, no grain, no parallax, no motion for its own
   sake. This is a tool used one-handed in a bay in daylight. Every rule
   below either tells you something happened or helps you hit the thing. */

/* ── buttons ──────────────────────────────────────────────────────── */
.btn{
  border-radius:11px;
  font-weight:550;
  transition:background 160ms ease,border-color 160ms ease,
             transform 90ms ease,box-shadow 160ms ease;
}
.btn:hover{background:color-mix(in srgb,var(--bg3) 60%,var(--card))}
/* The press. 1px is enough to feel and small enough never to reflow. */
.btn:active{transform:translateY(1px)}
.btn.primary{
  box-shadow:0 1px 0 color-mix(in srgb,#000 22%,transparent),
             0 2px 8px color-mix(in srgb,var(--green) 26%,transparent);
}
.btn.primary:hover{box-shadow:0 1px 0 color-mix(in srgb,#000 22%,transparent),
             0 3px 12px color-mix(in srgb,var(--green) 34%,transparent)}
.btn.primary:active{box-shadow:0 1px 0 color-mix(in srgb,#000 22%,transparent)}
.btn.wide{padding-top:11px;padding-bottom:11px}

/* ── cards sit on the page rather than being painted onto it ──────── */
.card{
  box-shadow:0 1px 2px color-mix(in srgb,#000 30%,transparent),
             0 6px 18px color-mix(in srgb,#000 18%,transparent);
  border-radius:15px;
}
/* Tinted to the surface rather than pure black, so the shadow belongs to
   this page instead of looking like a sticker dropped on it. */

/* ── a row you can tap says so ────────────────────────────────────── */
a.row,button.row{transition:background 140ms ease,transform 90ms ease}
a.row:active,button.row:active{transform:scale(.995);
  background:color-mix(in srgb,var(--bg3) 80%,transparent)}

/* ── the thumb ────────────────────────────────────────────────────── */
@media(pointer:coarse){
  /* 44px is the smallest target a thumb hits reliably, and this app is
     used standing up next to a car. */
  .btn{min-height:44px;padding-top:11px;padding-bottom:11px}
  .btn.sm{min-height:40px}
  a.row,button.row{padding-top:15px;padding-bottom:15px}
}

/* ── somebody who does not want movement does not get it ──────────── */
@media(prefers-reduced-motion:reduce){
  .btn,a.row,button.row,.coldbar{transition:none}
  .btn:active,a.row:active,button.row:active,.coldbar:active{transform:none}
}

/* ══ WHAT THIS JOB IS ═══════════════════════════════════════════════════
   The shop's one-line answer to the customer's complaint, above the tabs.
   Anthony: "we used to have a really good auto made summary of the repair
   we were going to do on top."

   Not a card. A card says "here is a section"; this is a statement, and
   giving it a border and a heading would make it compete with the six
   tiles directly beneath it for the same attention. It reads as part of
   the title block instead. */
.jsum{padding:2px 2px 0;margin:2px 0 4px}
.jsum-what{font-size:19px;font-weight:650;line-height:1.3;color:var(--ink);
  letter-spacing:-.01em;text-wrap:pretty}
.jsum.unpriced .jsum-what{font-weight:500}
.jsum-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 0;margin-top:5px;
  font-size:13.5px;color:var(--dim);font-variant-numeric:tabular-nums}
.jsum-meta > span + span::before{content:"·";margin:0 8px;opacity:.55}
.jsum-car{color:var(--text2);font-weight:550}
.jsum-money{color:var(--green2);font-weight:700}

/* ── the six tiles ───────────────────────────────────────────────────
   Three rows of two on a phone, which is what Anthony asked for and what
   fits without a label being clipped. The changes here are about which
   one is ON: it was a background swap and a 3px shadow, which on a bright
   screen in a bay is not enough to find at a glance. */
.jtab{transition:background 140ms ease,color 140ms ease}
.jtab.on{box-shadow:0 1px 3px rgba(0,0,0,.28),inset 0 0 0 1px var(--border2);
  font-weight:700}
.jtab:active{transform:translateY(1px)}
.jtab.on i{background:var(--purple);color:#fff}
.jtab i.owed{background:var(--amber);color:#231a06}
@media(pointer:coarse){.jtab{min-height:44px;font-size:14px}}

/* ── the customer block ──────────────────────────────────────────────
   Every field was a kv row of equal weight, so the person's name read
   exactly like the word "Email". The name is the thing you are looking
   at; the rest is how to reach them. */
.jpanel[data-panel="details"] .kv:first-child b{font-size:17px;font-weight:650}
.jpanel[data-panel="details"] .kv b a[href^="tel:"]{color:var(--green2);font-weight:600}
.navlink{margin-left:8px;font-size:12.5px;color:var(--purple2);text-decoration:underline}

/* ══ THE CONVERSATION ON A LEAD CARD ════════════════════════════════════
   Texts read as a thread, calls read as a record. They are different
   things and drawing them the same way is why a card full of "Rang again"
   was so hard to read: nothing on it looked like anybody talking. */
.leadtalk{margin-top:16px}
.talk-h{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  color:var(--dim);margin-bottom:9px;display:flex;gap:10px;align-items:baseline}
.talk-h .quiet{text-transform:none;letter-spacing:0;font-weight:500;font-size:12.5px}
.talk{display:flex;flex-direction:column;gap:8px}
.talk-b{max-width:82%;padding:9px 12px;border-radius:14px;font-size:15px;line-height:1.42}
.talk-b.theirs{align-self:flex-start;background:var(--bg3);border-bottom-left-radius:5px}
.talk-b.mine{align-self:flex-end;background:color-mix(in srgb,var(--green) 20%,var(--bg3));
  border-bottom-right-radius:5px}
.talk-when{font-size:11.5px;color:var(--text3);margin-top:4px}
.talk-call{border:1px solid var(--border);border-radius:12px;padding:11px 13px;
  background:color-mix(in srgb,var(--purple) 7%,transparent)}
.talk-call-h{display:flex;flex-wrap:wrap;gap:9px;align-items:baseline;font-size:12.5px;
  color:var(--dim)}
.talk-call-h b{font-size:14px;color:var(--ink)}
.talk-score{color:var(--green2);font-weight:700;font-variant-numeric:tabular-nums}
.talk-sum{margin-top:7px;font-size:14.5px;line-height:1.45;color:var(--text2);text-wrap:pretty}
.talk-tx{margin-top:8px}
.talk-txt{white-space:pre-wrap;font-size:13.5px;line-height:1.5;color:var(--text2);
  max-height:340px;overflow:auto;padding:10px 12px;border-radius:9px;background:var(--bg);
  border:1px solid var(--border)}

/* ── what the technician found ───────────────────────────────────────
   Sits directly above the button that consumes it, because the two are
   one action. Deliberately plain: this is a scratch box, not a field, and
   anything that looks like a saved field will be treated as one. */
.found{display:block;margin:14px 0 10px}
.found > span:first-child{display:block;font-size:12.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;color:var(--dim);margin-bottom:6px}
.found > span:first-child i{font-style:normal;font-weight:500;text-transform:none;
  letter-spacing:0;color:var(--text3);margin-left:6px}
.found textarea{width:100%;font:inherit;font-size:15px;line-height:1.45;color:var(--ink);
  background:var(--bg);border:1px solid var(--border);border-radius:11px;padding:11px 13px;
  resize:vertical;min-height:76px}
.found textarea:focus{outline:none;border-color:var(--purple2);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--purple) 22%,transparent)}
.found textarea::placeholder{color:var(--text3);opacity:.75}
.found-fine{display:block;margin-top:6px;font-size:12px;color:var(--text3)}

/* ── a rehearsal is not "on" ─────────────────────────────────────────
   Amber, not green: it IS running, and it is not reaching anybody. Green
   would say strangers are being answered, which is the one thing this
   state means they are not. */
.onoff.is-reh{color:var(--amber);background:color-mix(in srgb,var(--amber) 16%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--amber) 45%,transparent)}
.rehsay{color:var(--amber);font-weight:600}


/* ── THE TECHNICIAN'S PATH ─────────────────────────────────────────────
   Built for a phone held in one hand, in a bay, possibly in gloves. One
   thing on screen, targets no smaller than a thumb, and the panel wide
   open on a small screen rather than boxed in the middle of it. */
/* CENTRED. Anthony: "tile when you get wizard open should be centered."
   The wrap inherits the app's left aligned column, which is right for a
   list and wrong for a single panel somebody is working through — it sat
   hard against the left of a wide screen with nothing beside it. */
.techwrap{max-width:640px;margin-inline:auto}

/* THE WAY IN IS A CARD, NOT A BUTTON.
   Anthony: "Auto wizard looking like the start job button is just messy."
   It was a btn primary sitting beside other btn primaries, so the one
   thing on the screen that opens a different KIND of surface looked like
   one more action among five. A card with its own label reads as a door. */
.next-walk{display:flex;flex-direction:column;gap:9px;align-items:stretch}

/* THE MECHANIC'S STRIP, ABOVE THE TABS.
   Two steps in the order they happen, not two choices. A booked job leads
   with On my way and shows the walk quietly underneath; pressing it adds
   .told and they swap. A job already under way never shows On my way at
   all, because they have arrived. */
.mechstrip{display:flex;flex-direction:column;gap:10px;margin:0 0 14px}
.mechstrip .mech-omw{min-height:48px;font-size:16px;justify-content:center}

/* Booked: the walk is the faint next thing -- but only while there IS a
   louder thing. Two doors dimmed against nothing is just a dim card. */
.mechstrip.heading:has(.mech-omw) .walkcard{opacity:.62}
.mechstrip.heading .walkcard:hover{opacity:1}

/* Told them, or already under way: the walk is the action. */
.mechstrip.told .mech-omw,
.mechstrip.running .mech-omw{order:2;opacity:.6;min-height:40px;font-size:14px}
.mechstrip.told .walkcard,
.mechstrip.running .walkcard{opacity:1;order:1}

@media (min-width:620px){
  .mechstrip{flex-direction:row;align-items:stretch}
  .mechstrip .mech-omw{flex:0 0 auto;padding-inline:22px}
  .mechstrip .walkcard{flex:1}
}
/* TWO DOORS, TWO COLOURS, AND A GOLD FRAME ROUND THE PAIR.
   Anthony: "Green is a good background for the technician service track and
   we can use Blue for the Service Advisor track and gold for an owner or
   supervisor."

   The colour is the track, not the person: green always means the car and
   blue always means the desk, on every screen, so a shop with a real desk
   and a one-man shop are learning the same two colours. Gold is not a third
   track. It is the frame that appears when both doors belong to the same
   person, which is Matt Bolen and most shops on this system. */
.walkcard{--walk:var(--green);--walkrgb:63,191,63;
  display:grid;grid-template-columns:1fr auto;align-items:center;
  gap:4px 14px;text-decoration:none;color:inherit;padding:14px 16px;
  border:1px solid var(--walk);border-radius:13px;
  background:linear-gradient(180deg,rgba(var(--walkrgb),.08),rgba(var(--walkrgb),.03))}
.walkcard:hover{background:rgba(var(--walkrgb),.13)}
.walkcard.advisor{--walk:#3d8bff;--walkrgb:61,139,255}
.walkcard-k{grid-column:1;font-size:10.5px;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:var(--walk)}
.walkcard-t{grid-column:1;font-size:17px;font-weight:700;line-height:1.2}
.walkcard-s{grid-column:1;font-size:12.5px;line-height:1.45;color:var(--text3)}
.walkcard-go{grid-column:2;grid-row:1/4;font-size:22px;color:var(--walk)}

/* The owner-operator's frame. Faint on purpose: it is a label on the pair,
   not a third thing competing with the two doors inside it. */
.mechstrip.both{border:1px solid rgba(212,168,60,.45);border-radius:15px;
  background:rgba(212,168,60,.05);padding:11px;gap:9px}
.mechstrip-k{font-size:10.5px;font-weight:800;letter-spacing:.13em;
  text-transform:uppercase;color:#b3892c}
@media (min-width:620px){.mechstrip.both{flex-wrap:wrap}
  .mechstrip.both .mechstrip-k{flex:0 0 100%}}

/* FIVE NAMED STAGES. Ten anonymous pips said how far, never what next. */
.techrail{display:flex;gap:4px;margin-bottom:16px}
.techstage{flex:1;display:flex;flex-direction:column;gap:5px;min-width:0}
.techstage i{display:block;height:4px;border-radius:3px;background:var(--border)}
.techstage b{font-size:10.5px;font-weight:650;letter-spacing:.02em;color:var(--text3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.techstage.done i{background:var(--green)}
.techstage.on i{background:var(--amber)}
.techstage.on b{color:var(--amber);font-weight:800}

/* Section headings inside a details card, so ten rows read as three groups. */
.card-sub.sep{margin-top:18px;padding-top:14px;border-top:1px solid var(--border)}

/* The camera, where it gates the start rather than following it. */
.techshoot{margin-top:16px;padding-top:14px;border-top:1px solid var(--border)}
.techshoot-t{font-size:13px;font-weight:650;color:var(--text2);margin-bottom:8px}

/* ── TESTS, NOT PROSE ────────────────────────────────────────────────
   A card per test, coloured by its verdict, so a glance at the step says
   what has been eliminated and what is still open. */
.ttests{display:grid;gap:10px;margin-bottom:14px}
.ttest{border:1px solid var(--border);border-left:3px solid var(--border);
  border-radius:11px;padding:12px 14px}
.ttest.pass{border-left-color:var(--green)}
.ttest.fail{border-left-color:var(--amber)}
.ttest-h{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.ttest-n{font-size:11px;font-weight:700;letter-spacing:.09em;color:var(--text3)}
.ttest-meta{font-size:12px;color:var(--text3)}
.ttest-name{font-size:16px;font-weight:650;margin:4px 0 6px;line-height:1.3}
.ttest-rules{font-size:13.5px;line-height:1.45;color:var(--text2);
  background:var(--bg);border-radius:8px;padding:8px 10px;margin-bottom:8px}
.ttest-rules b{display:block;font-size:11px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--text3);margin-bottom:2px}
.ttest-note{font-size:13.5px;line-height:1.45;color:var(--amber);margin-bottom:8px}
.ttest-acts{display:flex;gap:7px;align-items:center}
.tv{flex:1;font:inherit;font-size:13.5px;min-height:40px;border-radius:9px;
  cursor:pointer;color:var(--text2);background:transparent;
  border:1px solid var(--border)}
.tv:hover{color:var(--text);border-color:var(--text3)}
.ttest.pass .tv[data-v="pass"].on{background:var(--green);border-color:var(--green);color:#fff}
.ttest.fail .tv[data-v="fail"].on{background:var(--amber);border-color:var(--amber);color:#fff}
.tx{width:40px;min-height:40px;font-size:18px;line-height:1;cursor:pointer;
  color:var(--text3);background:transparent;border:1px solid var(--border);
  border-radius:9px}
.tx:hover{color:var(--red,#e06c6c)}
.tadd{border:1px dashed var(--border);border-radius:11px;padding:0 14px}
.tadd summary{cursor:pointer;padding:12px 0;font-size:14.5px;font-weight:650}
.tadd[open] summary{margin-bottom:6px}
.tadd-row{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.tadd .btn{margin-bottom:14px}

/* The proposed plan. Offered, never asserted — it sits beside "add a
   test" rather than above it, because a technician who already knows what
   to check should not have to dismiss a suggestion first. */
.btn.plan{display:block;width:100%;text-align:center;line-height:1.3;
  min-height:52px;margin-top:12px}
.btn.plan i{display:block;font-style:normal;font-size:12px;font-weight:400;
  opacity:.75;margin-top:3px}
.techhead{display:flex;align-items:baseline;justify-content:space-between;
  gap:12px;margin-bottom:10px}
.techback{font-size:14px;color:var(--text3);text-decoration:none}
.techback:hover{color:var(--text)}
.techcar{font-size:14px;font-weight:650;color:var(--text2)}

/* The funnel, as a row of pips. Progress you can read without reading. */
.techrail{display:flex;gap:6px;margin-bottom:14px}
.techpip{flex:1;height:5px;border-radius:3px;background:var(--border)}
.techpip.done{background:var(--green)}
.techpip.on{background:var(--amber)}

.techpanel{padding:20px}
.techstep{font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--amber);margin-bottom:6px}
.techtitle{font-size:26px;line-height:1.15;margin:0 0 6px;letter-spacing:-.01em}
.techsub{font-size:16px;line-height:1.45;color:var(--text2);margin:0 0 18px}
.techfine{font-size:13.5px;line-height:1.5;color:var(--text3);margin:10px 0 0}

.techfield{display:block;margin:0 0 14px}
.techfield>span{display:block;font-size:13px;font-weight:650;color:var(--text2);
  margin-bottom:6px}
.techfield input,.techfield textarea,.techfield select{width:100%;font:inherit;
  font-size:16px;color:var(--text);background:var(--bg);border:1px solid var(--border);
  border-radius:10px;padding:12px;min-height:48px}
.techfield textarea{min-height:120px;line-height:1.5;resize:vertical}
.techfield input:focus,.techfield textarea:focus,.techfield select:focus{
  outline:2px solid var(--green);outline-offset:-1px;border-color:transparent}

.techok,.techstop{border-radius:12px;padding:14px 16px;margin-bottom:16px}
.techok{background:rgba(63,191,63,.10);border:1px solid var(--green)}
.techstop{background:rgba(232,163,60,.12);border:1px solid var(--amber)}
.techok b,.techstop b{display:block;font-size:16px;margin-bottom:4px}
.techok p,.techstop p{margin:0;font-size:14.5px;line-height:1.5;color:var(--text2)}

.techsums{display:grid;gap:10px;margin-bottom:14px}
.techsums>div{display:flex;align-items:baseline;justify-content:space-between;
  padding:12px 14px;border:1px solid var(--border);border-radius:10px}
.techsums span{font-size:14px;color:var(--text3)}
.techsums b{font-size:20px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}

.techneed{font-size:14px;color:var(--amber);margin:10px 0 0}
.techhave{font-size:14px;color:var(--text3);margin:10px 0 0}

/* Buttons stack on a phone and sit in a row from tablet up. Every one is
   at least 48px tall because the alternative is a mis-tap on a step that
   changes a job's status. */
.techacts{display:flex;flex-direction:column;gap:10px;margin-top:20px}
.techacts .btn{min-height:48px;font-size:16px;justify-content:center;text-align:center}
@media (min-width:560px){
  .techacts{flex-direction:row;flex-wrap:wrap}
  .techacts .btn{flex:1;min-width:150px}
  .techacts .btn.wide{flex-basis:100%}
}

/* Two shares side by side, because labour and parts are one decision. */
.ed-two{display:grid;grid-template-columns:1fr 1fr;gap:12px}

/* ── NOTES ────────────────────────────────────────────────────────────
   One card on three screens — the job's History panel, the lead sheet and
   a customer's record — so a note looks and behaves the same wherever
   somebody writes one. It reads as a conversation rather than a table:
   who, when, what they said, and the photographs under it. */
.noteadd{padding:12px var(--pad) 14px;border-bottom:1px solid var(--line2)}
.noteadd textarea,textarea.noteedit{width:100%;font:inherit;font-size:14.5px;line-height:1.5;
  color:var(--text);background:var(--bg);border:1px solid var(--line);border-radius:9px;
  padding:10px 12px;resize:vertical}
.noteadd textarea:focus,textarea.noteedit:focus{outline:2px solid var(--green);
  outline-offset:-1px;border-color:transparent}
.noteadd-acts{display:flex;align-items:center;gap:10px;margin-top:8px;flex-wrap:wrap}

.notelist{list-style:none;margin:0;padding:0}
.note{padding:13px var(--pad);border-bottom:1px solid var(--line2)}
.note:last-child{border-bottom:0}
.note-w{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;font-size:12.5px}
.note-w b{font-size:13.5px}
.note-w .when{color:var(--dim)}
/* An edit is stated, never implied. It is not a warning though, so it
   does not get a warning colour -- it sits under the words it changed,
   in the same voice as everything else that is merely true. */
.note-ed{margin-top:5px;color:var(--dim);font-size:11.5px}
/* Under 68 characters a line. A note runs the width of a card on a
   desktop otherwise, and a paragraph that wide is read twice or not at
   all. */
.note-b{margin-top:5px;max-width:68ch;font-size:14px;line-height:1.55;color:var(--ink);
  white-space:pre-wrap;overflow-wrap:anywhere}
.note-acts{margin-top:8px;display:flex;align-items:center;gap:4px;max-width:68ch}
/* Three underlined links under every note turned the card into a page of
   links with some words between them. The underline is what tells you a
   thing is clickable, so it arrives when you are pointing at it and stays
   away the rest of the time. */
.note-acts .navlink{margin-left:0;text-decoration:none}
.note-acts .navlink:hover,.note-acts .navlink:focus-visible{text-decoration:underline}
/* DELETE IS NOT A PEER OF EDIT. It sits at the far end, in the quiet
   grey, and only turns red once it has asked whether you meant it --
   which is when the colour is telling you something rather than
   decorating a row of three identical buttons. */
.note-acts .navlink.quiet{margin-left:auto;color:var(--dim);font-weight:500}
.note-acts .navlink.quiet:hover,.note-acts .navlink.quiet:focus-visible{color:var(--red);
  background:color-mix(in srgb,var(--red) 16%,transparent)}
.note-acts .navlink.quiet[data-sure="1"]{color:var(--red);font-weight:650}

.noteshots{display:flex;flex-wrap:wrap;gap:7px;margin-top:9px}
.noteshot{display:block;width:78px;height:78px;border-radius:9px;overflow:hidden;
  background:var(--bg3);border:1px solid var(--line)}
.noteshot img{width:100%;height:100%;object-fit:cover;display:block}
@media(pointer:coarse){.noteshot{width:92px;height:92px}}

/* Said once, above the results: this answer came from outside the screen
   you are looking at. */
.findnote{padding:9px var(--pad);font-size:12.5px;color:var(--dim);
  border-bottom:1px solid var(--line2)}

/* The lifetime figure beside the window's one. Grey and small on purpose:
   the number you can act on this morning is the headline, and the total is
   the context that says whether it is getting better. */
.fell-ever{margin-left:9px;font-size:11.5px;color:var(--text3)}
