:root{--brand:#2B802C;--brand-d:#16501A;--brand-dd:#0F3A14;--brand-text:var(--brand-d);--mint:#B8F2BB;--mint-soft:#EDF6EC;
--cream:#F7F6F2;--card:#FFF;--ink:#20241F;--muted:#6B7568;--yellow:#C9932E;--yellow-soft:#FAF0DC;
--coral:#C24E2E;--coral-soft:#FBEAE3;--teal:#00877C;--teal-soft:#DCEEEC;--line:#E7E7E1;
--card-muted:#F2F3EF;--muted-line:#A6AFA2;
--status-amber-border:#E9CE8F;--status-amber-text:#8A6217;
--status-red-border:#EBBBA4;--status-red-text:#96331A;
--heat-bg:#FDE9D0;--heat-text:#8A4B08;--heat-text-strong:#B4680C;
--yellow-line:#E9D3A4;--yellow-text:#6B5300;--coral-text:#7A2415;
--shadow-sm:0 1px 2px rgba(20,35,20,.06);--shadow-md:0 8px 24px rgba(20,35,20,.12);--shadow-lg:0 -8px 32px rgba(20,35,20,.16);
--radius-card:18px;--radius-lg:20px}
:root[data-theme="dark"]{
  --cream:#14171B;--card:#1E2228;--ink:#EDEEEA;--muted:#94A0A8;--line:#2E333B;
  --yellow-soft:#3A2E14;--coral-soft:#3A1D15;--teal-soft:#12302D;
  --card-muted:#242A30;--muted-line:#454C55;
  --status-amber-border:#5C4A22;--status-amber-text:#E0B563;
  --status-red-border:#5C2E22;--status-red-text:#E08066;
  --heat-bg:#3A2E14;--heat-text:#E0A857;--heat-text-strong:#F0B968;
  --yellow-line:#5C4A22;--yellow-text:#E0B563;--coral-text:#F0A088;
  --shadow-sm:0 1px 2px rgba(0,0,0,.5);--shadow-md:0 8px 24px rgba(0,0,0,.5);--shadow-lg:0 -8px 32px rgba(0,0,0,.55);
}
*{box-sizing:border-box}
body{margin:0;font-family:'Poppins',system-ui,sans-serif;color:var(--ink);background:var(--cream);line-height:1.45;-webkit-font-smoothing:antialiased;padding-bottom:calc(24px + env(safe-area-inset-bottom))}
.wrap{max-width:640px;margin:0 auto;padding:calc(16px + env(safe-area-inset-top)) 14px 16px}
@media (min-width:900px){.wrap{max-width:1100px;margin-left:248px;margin-right:auto}}
.brandbar{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.mark{width:38px;height:38px;border-radius:9px;background:var(--card);display:grid;place-items:center;overflow:hidden;box-shadow:var(--shadow-sm);border:1px solid var(--line)}
.mark img{width:100%;height:100%;object-fit:contain}
.mark.wordmark-fallback{width:auto;min-width:0;padding:0 2px 0 0;background:none;border:none;box-shadow:none;border-radius:0}
.mark.wordmark-fallback img{width:auto;height:24px;object-fit:contain}
.brandbar h1{font-size:16px;font-weight:700;margin:0;letter-spacing:-.1px}
.brandbar p{margin:0;font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px}
.saving{margin-left:auto;font-size:11px;color:var(--muted)}
.gearbtn{border:0;background:none;color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:10px;width:44px;height:44px;flex:none}
.gearbtn:hover,.gearbtn:active{background:var(--mint-soft);color:var(--brand-text)}
.gearbtn svg{width:20px;height:20px}

/* custom type-search dropdown (client/catalog search) — positioned via JS with
   getBoundingClientRect so it works under whatever input it's attached to. Rows
   styled as a stack of .catrow-style cards, matching Catalog/Jobs/Clients lists
   elsewhere in the app, rather than a generic flat browser-style list. */
.typeahead{position:fixed;z-index:120;max-height:264px;overflow:auto;padding:2px}
.taRow{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 12px;margin-bottom:6px;box-shadow:var(--shadow-sm);cursor:pointer}
.taRow:last-child{margin-bottom:0}
.taRow:hover,.taRow:active{background:var(--mint-soft);border-color:var(--brand)}
.taRow .n{font-weight:600;font-size:13.5px;color:var(--ink)}
.taRow .d{font-size:11.5px;color:var(--muted);margin-top:2px}

.navwrap{display:flex}

/* off-canvas nav — a slide-in drawer on phone (dimmed backdrop, tap-out
   to close), the same #sideNav rendered statically on desktop instead
   (see the min-width:900px block below). */
.sidenav-backdrop{position:fixed;inset:0;background:rgba(15,30,15,.4);z-index:79;display:none;opacity:0;transition:opacity .18s}
.sidenav-backdrop.open{display:block;opacity:1}

.sidenav{
  display:flex;flex-direction:column;gap:2px;
  position:fixed;top:0;left:0;bottom:0;width:280px;max-width:82vw;
  background:var(--card);border-right:1px solid var(--line);box-shadow:var(--shadow-lg);
  padding:calc(20px + env(safe-area-inset-top)) 14px 20px;
  overflow-y:auto;z-index:80;
  transform:translateX(-100%);transition:transform .22s ease;
}
.sidenav.open{transform:translateX(0)}
.sidebrand{display:flex;align-items:center;gap:10px;padding:4px 10px 18px}
.sidebrand h1{font-size:14.5px;font-weight:700;margin:0;letter-spacing:-.1px}
.sidebrand p{margin:0;font-size:9.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px}
.sidenav button{display:flex;align-items:center;gap:10px;width:100%;border:0;background:none;text-align:left;padding:10px 12px;border-radius:9px;font-family:inherit;font-weight:600;font-size:13.5px;color:var(--ink);cursor:pointer}
.sidenav button svg{width:18px;height:18px;color:var(--muted);flex:none}
.sidenav button.on{background:var(--mint-soft);color:var(--brand-text)}
.sidenav button.on svg{color:var(--brand-text)}
.sidegroup-toggle .chev{width:14px;height:14px;margin-left:auto;padding:6px;box-sizing:content-box;margin-right:-6px;border-radius:6px;transition:transform .15s;cursor:pointer}
.sidegroup-toggle .chev:hover{background:rgba(0,0,0,.06)}
.sidegroup.expanded .sidegroup-toggle .chev{transform:rotate(90deg)}
.sidegroup-children{display:none;flex-direction:column;gap:2px}
.sidegroup.expanded .sidegroup-children{display:flex}
.sidegroup-children button{padding-left:34px;font-size:13px}
.sidegroup-children button svg{width:16px;height:16px}
@media (min-width:900px){
  .navwrap{display:none}
  .sidenav-backdrop{display:none!important}
  .sidenav{transform:none;box-shadow:none;width:228px;max-width:228px}
}
.view{display:none}.view.on{display:block}
.subview,.subtabpanel{display:none}.subview.on,.subtabpanel.on{display:block}

.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius-card);padding:18px;margin-bottom:14px;box-shadow:var(--shadow-sm)}
.mktlist{margin:0;padding-left:18px;font-size:13px;line-height:1.6;color:var(--ink)}
.mktlist li{margin-bottom:8px}
.mktlist li:last-child{margin-bottom:0}
.eyebrow{font-size:10.5px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--brand-text);margin:0 0 12px;display:flex;justify-content:space-between;align-items:center}
.row{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;padding:9px 0;border-top:1px solid var(--line)}
.row:first-of-type{border-top:0}
.row label{font-size:13.5px;font-weight:500}
.hint{display:block;font-size:11px;color:var(--muted);font-weight:400}
.field{display:flex;align-items:center;border:1.5px solid var(--line);border-radius:9px;overflow:hidden;background:var(--card)}
.field:focus-within{border-color:var(--brand)}
.field .pre,.field .suf{padding:0 8px;font-size:12px;color:var(--muted);font-family:'IBM Plex Mono',monospace;background:var(--cream);align-self:stretch;display:flex;align-items:center}
.field input{border:0;outline:0;width:74px;padding:8px;font-family:'IBM Plex Mono',monospace;font-size:16px;font-weight:600;text-align:right;background:var(--card);color:var(--ink)}
.field.wide input{width:100%;text-align:left}
/* iOS (Safari and Chrome alike — both run on WebKit there) auto-zooms the whole page
   in whenever a focused text input's font-size is under 16px, and often doesn't zoom
   back out cleanly once the keyboard closes. 16px on every real text field is the
   standard fix — keeps the page at 1:1 zoom on open and after typing, without disabling
   the user's own ability to pinch-zoom (unlike a maximum-scale viewport hack). See the
   2026-08-12 report. */
select,input[type=text],input[type=tel],input[type=email],input[type=date],input[type=time],input[type=password],input[type=number],textarea{font-family:inherit;font-size:16px;font-weight:500;padding:9px 10px;border:1.5px solid var(--line);border-radius:9px;background:var(--card);color:var(--ink);width:100%}
select:focus,input:focus,textarea:focus{outline:0;border-color:var(--brand)}
.togwrap{display:flex;gap:8px;align-items:center}
.pagehead{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.pagehead .eyebrow{flex:1;margin:0}
.backbtn{border:0;background:none;color:var(--brand-text);font-family:inherit;font-weight:600;font-size:13.5px;padding:8px 4px;cursor:pointer;flex:none}
.backbtn:hover{opacity:.75}
.linerow{background:var(--cream);border:1px solid var(--line);border-radius:10px;padding:9px 10px;margin-bottom:8px}
.lnTop{display:flex;gap:8px;align-items:center;margin-bottom:7px}
.lnTop .lnDesc{flex:1;text-overflow:ellipsis}
.lnBottom{display:flex;gap:4px;align-items:center}
.lnBottom .lnDate{flex:none;width:112px;font-size:16px;padding:7px 3px}
.lnBottom .lnQty{flex:none;width:40px;text-align:center;font-size:16px;padding:7px 2px}
.lnRateField{flex:none}
.lnRateField .pre{padding:0 4px}
.lnRateField input{width:60px;font-size:16px;padding:7px 4px}
.lnAmt{flex:1;min-width:0;text-align:right;font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:12px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tkTop{display:flex;gap:8px;align-items:center;margin-bottom:7px}
.tkTop .tkTitle{flex:1;text-overflow:ellipsis}
.tkRow2{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tkDoneLabel{display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--muted);flex:none}
.tkDoneLabel input{width:auto}
.tkAssignee{flex:1;min-width:110px;font-size:16px;padding:7px 6px}
.tkNotes{margin-top:7px;font-size:12px}
.toggle{position:relative;width:40px;height:24px;flex:none}
.toggle input{opacity:0;width:0;height:0;position:absolute}
.track{position:absolute;inset:0;background:#D5D9D1;border-radius:20px;transition:.15s;cursor:pointer}
.track::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;background:#fff;border-radius:50%;transition:.15s;box-shadow:0 1px 2px rgba(0,0,0,.15)}
.toggle input:checked + .track{background:var(--brand)}
.toggle input:checked + .track::after{transform:translateX(16px)}
.dim{opacity:.4;pointer-events:none}

.tabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
.tabs button{border:1px solid var(--line);background:var(--card);color:var(--brand-text);padding:8px 11px;border-radius:9px;font-family:inherit;font-weight:600;font-size:12px;cursor:pointer}
.tabs button[aria-selected="true"]{background:var(--brand);color:#fff;border-color:var(--brand)}
/* Quote calculator (#v-quote) — scoped so the shared .tabs/.row rules above stay
   untouched for the client/invoice/status filter tab bars elsewhere in the app. */
#v-quote .tabs{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;padding-bottom:2px;margin-bottom:16px}
#v-quote .tabs::-webkit-scrollbar{display:none}
#v-quote .tabs button{flex:none}
/* Sunken sub-panel for optional modifiers (toggles, waste add-ons) within a
   service panel — visually demotes them below the primary quantity/rate
   fields above, instead of every row reading with identical weight. */
.optgroup{background:var(--cream);border-radius:12px;padding:1px 10px;margin-top:8px}
.optgroup .row:first-of-type{border-top:0}

.addbar{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.liveprice{font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:19px}
.liveprice small{display:block;font-size:11px;color:var(--muted);font-weight:500}
.btn{border:0;background:var(--brand);color:#fff;font-family:inherit;font-weight:600;font-size:13.5px;padding:11px 16px;border-radius:9px;cursor:pointer;box-shadow:var(--shadow-sm);transition:filter .1s,transform .1s}
.btn:active{transform:translateY(1px);filter:brightness(.96)}
.btn.yellow{background:var(--yellow);color:#fff;width:100%}
.btn.ghost{background:var(--card);color:var(--brand-text);border:1.5px solid var(--line);box-shadow:none}
.btn.sm{padding:7px 11px;font-size:12px}
.btn.coral{background:var(--coral)}
.btn.teal{background:var(--teal)}
.btn:disabled{opacity:.5;pointer-events:none}
.btn.block{width:100%}

.jline{display:flex;justify-content:space-between;gap:8px;padding:8px 0;border-top:1px solid var(--line);font-size:13px}
.jline:first-of-type{border-top:0}
.jline .v,.cart .v{font-family:'IBM Plex Mono',monospace;font-weight:600}
.jline.bd{color:var(--muted)}
.jline.bd .v{font-weight:600}
.rm{border:0;background:var(--mint-soft);color:var(--brand-text);width:22px;height:22px;border-radius:6px;cursor:pointer}
.tot{font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:27px}
.tot small{font-size:12px;color:var(--brand);font-weight:600}

.jobcard{background:var(--card);border:1px solid transparent;border-left:4px solid var(--brand);border-radius:var(--radius-card);padding:16px;margin-bottom:12px;box-shadow:var(--shadow-sm);transition:transform .1s}
.jobcard:active{transform:scale(.99)}
.jobcard.st-quoted{background:var(--yellow-soft);border-left-color:var(--yellow)}
.jobcard.st-booked{background:var(--mint-soft);border-left-color:var(--brand)}
.jobcard.st-done{background:var(--card-muted);border-left-color:var(--muted-line);opacity:.85}
.jobcard.st-draft{background:var(--card-muted);border-left-color:var(--muted-line)}
.jobcard.st-sent{background:var(--yellow-soft);border-left-color:var(--yellow)}
.jobcard.st-partial{background:var(--yellow-soft);border-left-color:var(--yellow)}
.jobcard.st-paid{background:var(--mint-soft);border-left-color:var(--brand)}
.jobcard.st-overdue{background:var(--coral-soft);border-left-color:var(--coral)}
.jobcard.st-accepted{background:var(--mint-soft);border-left-color:var(--brand)}
.jobcard.st-expired{background:var(--coral-soft);border-left-color:var(--coral)}
.jobcard.st-lost{background:var(--coral-soft);border-left-color:var(--coral)}
.jobcard.st-planning{background:var(--yellow-soft);border-left-color:var(--yellow)}
.jobcard.st-active{background:var(--mint-soft);border-left-color:var(--brand)}
/* Quote appointments are a site visit to price a job, not the job itself — this
   overrides the status-driven background/border above (same specificity, wins on
   source order) so they read as visually distinct from every job status at a
   glance, on both the Schedule list and (via .dot.appt) the calendar. */
.jobcard.kind-quoteappt{background:var(--teal-soft);border-left-color:var(--teal)}
.jobcard.kind-quoteappt.st-done{opacity:.85}
.jobtop{display:flex;justify-content:space-between;gap:8px;align-items:flex-start}
.jobtop .n{font-weight:700;font-size:15.5px;letter-spacing:-.1px}
.jobtop .p{font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:16px;white-space:nowrap}
.jobmeta{font-size:12px;color:var(--muted);margin-top:3px}
.jobsvc{font-size:12px;margin-top:7px;color:var(--brand-text)}
.jobact{display:flex;gap:6px;margin-top:11px;flex-wrap:wrap}
/* Solid white chip + colored border/text, not a soft-tint fill — job/invoice
   cards now use the soft tints as their own background (see .jobcard.st-*),
   so a badge needs to read as a distinct label sitting on top, not blend in. */
.badge{background:var(--card);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;padding:4px 8px;border-radius:7px;border:1px solid transparent}
.badge.quoted{border-color:var(--status-amber-border);color:var(--status-amber-text)}
.badge.booked{border-color:var(--mint);color:var(--brand-text)}
.badge.done{border-color:var(--line);color:var(--muted)}
.badge.draft{border-color:var(--line);color:var(--muted)}
.badge.sent{border-color:var(--status-amber-border);color:var(--status-amber-text)}
.badge.partial{border-color:var(--status-amber-border);color:var(--status-amber-text)}
.badge.paid{border-color:var(--mint);color:var(--brand-text)}
.badge.overdue{border-color:var(--status-red-border);color:var(--status-red-text)}
.badge.duesoon{border-color:var(--status-amber-border);color:var(--status-amber-text)}
.badge.accepted{border-color:var(--mint);color:var(--brand-text)}
.badge.expired{border-color:var(--status-red-border);color:var(--status-red-text)}
.badge.lost{border-color:var(--status-red-border);color:var(--status-red-text)}
.badge.planning{border-color:var(--status-amber-border);color:var(--status-amber-text)}
.badge.active{border-color:var(--mint);color:var(--brand-text)}
.badge.quoteappt{border-color:var(--teal);color:var(--teal)}

.dayhead{font-size:18px;font-weight:800;letter-spacing:-.2px;color:var(--brand-text);margin:20px 0 8px;display:flex;justify-content:space-between;align-items:baseline}
.dayhead .sum{font-family:'IBM Plex Mono',monospace;color:var(--muted);font-weight:600;font-size:13px;text-transform:none;letter-spacing:0}
.dayhead .guidenote{font-weight:500;color:var(--muted);opacity:.75;font-size:10.5px}
/* Scheduled tab only — the date already reads big and clear in .dayhead above each
   day's group, so these cards can run leaner than the full jobcard used everywhere
   else (unscheduled Jobs, Ready to invoice, Quotes, Invoices) without losing anything
   the day header already said. */
.jobcard-sched{padding:11px 14px;margin-bottom:8px}
.jobcard-sched .jobtop .n{font-size:14px}
.jobcard-sched .jobsvc{font-size:11.5px;margin-top:4px}
.jobcard-sched .jobmeta{margin-top:2px}
.jobcard-sched .jobact{margin-top:8px}
.wxwarn{color:var(--coral);font-weight:600;font-size:11px;margin-left:6px;text-transform:none;letter-spacing:0}
.wxwarn.hot{color:var(--heat-text-strong)}
.tempnote{color:var(--muted);font-weight:600;font-size:11px;margin-left:6px;text-transform:none;letter-spacing:0}
.wxwidget{display:flex;align-items:center;gap:14px;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-card);padding:18px;box-shadow:var(--shadow-sm)}
.wxwidget .wxicon{font-size:40px;line-height:1}
.wxwidget .wxtemp{font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:22px;color:var(--ink)}
.wxwidget .wxsub{font-size:12.5px;color:var(--muted);margin-top:2px}
.wxheatbanner{margin-top:8px;background:var(--heat-bg);color:var(--heat-text);font-size:12.5px;font-weight:600;border-radius:10px;padding:9px 12px}
.linkbtn{background:none;border:0;padding:0;margin-left:8px;font-size:11px;font-weight:600;color:var(--brand-text);text-decoration:underline;cursor:pointer;text-transform:none;letter-spacing:0}
.linkbtn:hover{opacity:.75}
.slot{display:flex;gap:10px;background:var(--card);border:1px solid var(--line);border-radius:16px;padding:13px 14px;margin-bottom:10px;align-items:flex-start;box-shadow:var(--shadow-sm)}
.slot .t{margin-top:1px}
.badge.first{border-color:var(--mint);color:var(--brand-text)}
.reqline{font-size:11.5px;color:var(--coral);margin-top:3px;display:flex;gap:4px}
.slot .t{font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:13px;color:var(--brand);min-width:52px}
.slot .n{font-weight:600;font-size:13.5px}
.slot .d{font-size:11.5px;color:var(--muted)}
.slot .p{margin-left:auto;font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:13px}
.empty{color:var(--muted);font-size:13px;padding:22px 14px;text-align:center;background:var(--card);border:1.5px dashed var(--line);border-radius:16px}
.statgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px}
/* Jobs view's stat row has a 4th tile (Potential $) — narrow phones wrap it to
   a 2x2 grid rather than squeezing 4 across, which crowds the dollar figures. */
#v-jobs .statgrid{grid-template-columns:repeat(2,1fr)}
@media (min-width:480px){ #v-jobs .statgrid{grid-template-columns:repeat(4,1fr)} }
.stat{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px 13px;box-shadow:var(--shadow-sm)}
.stat .k{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;font-weight:600}
.stat .v{font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:17px;margin-top:2px}
.formgrid{display:grid;gap:9px}
dialog{border:0;border-radius:16px;padding:18px;max-width:420px;width:calc(100% - 32px);max-height:calc(100vh - 64px);overflow:auto;box-shadow:var(--shadow-md);background:var(--card);color:var(--ink)}
dialog::backdrop{background:rgba(15,30,15,.4)}
/* Tour steps that navigate to a real page (see openHelpDetail) — a much lighter
   backdrop and a bottom-anchored position keep that page visible and legible around
   the dialog, instead of the usual full dimming a plain info popup uses. */
dialog.tourdlg::backdrop{background:rgba(15,30,15,.1)}
dialog.tourdlg{margin-top:auto;margin-bottom:max(16px,env(safe-area-inset-bottom))}
/* Wider than the shared dialog default (420px) — scoped to just this one dialog so
   every form/edit dialog in the app keeps its narrower, tighter width. Help/tour
   content is prose, not form fields, and benefits from the extra breathing room,
   especially now that a topic can be broken into several sections (see .help-section
   below) instead of one dense paragraph. */
#helpDlg{max-width:480px}
.help-section+.help-section{margin-top:14px}

/* ---- Interactive click-through page walkthroughs (PAGE_WALKTHROUGHS) ---- */
/* pointer-events:none on the full-viewport wrapper is load-bearing — without it this
   div silently swallows every real click across the whole screen, since a transparent
   fixed inset:0 element still captures pointer events by default. .walk-callout opts
   back into auto so Skip/Next stay clickable; the spotlight itself is decorative only. */
.walk-overlay{position:fixed;inset:0;z-index:210;pointer-events:none}
.walk-spotlight{position:absolute;border-radius:10px;box-shadow:0 0 0 9999px rgba(15,30,15,.55);transition:top .2s,left .2s,width .2s,height .2s;pointer-events:none}
.walk-callout{position:absolute;background:var(--card);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);padding:14px 16px;width:270px;max-width:calc(100vw - 28px);transition:top .2s,left .2s;pointer-events:auto}
.walk-progress{font-size:10.5px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--brand-text);margin-bottom:6px}
.walk-text{font-size:13.5px;line-height:1.5;color:var(--ink);margin-bottom:12px}
.walk-actions{display:flex;gap:8px;justify-content:flex-end}

/* ---- Segmented control (e.g. Flat rate / Hourly / Quantity) ---- */
.segctrl{display:flex;background:var(--cream);border:1px solid var(--line);border-radius:10px;padding:3px;gap:2px}
.segctrl button{flex:1;border:0;background:none;padding:9px 6px;border-radius:8px;font-family:inherit;font-weight:600;font-size:11.5px;color:var(--muted);cursor:pointer;text-transform:uppercase;letter-spacing:.3px;transition:background .1s,color .1s}
.segctrl button[aria-selected="true"]{background:var(--card);color:var(--ink);box-shadow:var(--shadow-sm)}

/* ---- Collapsible card sections (native <details>/<summary>) ---- */
.section{background:var(--card);border:1px solid var(--line);border-radius:var(--radius-card);margin-bottom:12px;overflow:hidden}
.section>summary{list-style:none;cursor:pointer;padding:14px 16px;display:flex;align-items:center;justify-content:space-between;gap:8px;font-weight:600;font-size:13.5px}
.section>summary::-webkit-details-marker{display:none}
.section>summary .chev{transition:transform .15s;color:var(--muted);flex:none;display:flex}
.section>summary .chev svg{width:16px;height:16px}
.section[open]>summary .chev{transform:rotate(180deg)}
.section>summary:hover{background:var(--cream)}
.section-body{padding:0 16px 16px}
.section-body-top{border-top:1px solid var(--line);padding-top:14px}

/* ---- Floating action button ---- */
.fab{position:fixed;right:20px;bottom:calc(88px + env(safe-area-inset-bottom));width:56px;height:56px;border-radius:50%;background:var(--brand);color:#fff;border:0;box-shadow:var(--shadow-md);display:grid;place-items:center;cursor:pointer;z-index:75;transition:transform .1s,filter .1s}
.fab:active{transform:scale(.94);filter:brightness(.95)}
.fab svg{width:24px;height:24px}
@media (min-width:900px){.fab{bottom:calc(20px + env(safe-area-inset-bottom))}}

/* ---- Update-available banner ---- */
#updateBanner{position:fixed;top:0;left:0;right:0;z-index:190;display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;background:var(--mint-soft);border-bottom:1px solid var(--brand);color:var(--brand-text);font-size:13px;font-weight:600;padding:9px 44px 9px 14px;padding-top:calc(9px + env(safe-area-inset-top));box-shadow:var(--shadow-sm)}
.updateBannerX{position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:0;color:inherit;font-size:20px;line-height:1;cursor:pointer;padding:4px}

/* ---- Catalog picker rows ---- */
.catrow{display:flex;gap:10px;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:16px;padding:12px 13px;margin-bottom:9px}
.catrow .n{font-weight:600;font-size:13.5px}
.catrow .d{font-size:11.5px;color:var(--muted)}
.catrow .qty{width:56px;flex:none}
.catrow .qty input{width:100%;padding:7px 6px;text-align:center}

/* ---- Money section ---- */
.moneytabs,.subtabs{display:flex;gap:6px;overflow-x:auto;margin-bottom:12px;padding-bottom:2px;-webkit-overflow-scrolling:touch}
.moneytabs button,.subtabs button{flex:none;border:1px solid var(--line);background:var(--card);color:var(--brand-text);padding:8px 12px;border-radius:9px;font-family:inherit;font-weight:600;font-size:12px;cursor:pointer;white-space:nowrap}
.moneytabs button[aria-selected="true"],.subtabs button[aria-selected="true"]{background:var(--brand-d);color:#fff;border-color:var(--brand-d)}
.settabs{display:flex;gap:6px;overflow-x:auto;margin-bottom:14px;padding-bottom:2px;-webkit-overflow-scrolling:touch}
.settabs button{flex:none;border:1px solid var(--line);background:var(--card);color:var(--brand-text);padding:8px 12px;border-radius:9px;font-family:inherit;font-weight:600;font-size:12px;cursor:pointer;white-space:nowrap}
.settabs button[aria-selected="true"]{background:var(--brand-d);color:#fff;border-color:var(--brand-d)}
.setsubview{display:none}.setsubview.on{display:block}

.bignum{font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:30px;line-height:1.1;letter-spacing:-.3px}
.bignum small{display:block;font-size:11px;color:var(--muted);font-weight:500;text-transform:uppercase;letter-spacing:.5px;margin-top:2px}
.bignum.pos{color:var(--brand-text)}
.bignum.neg{color:var(--coral)}

.disclaimer{background:var(--yellow-soft);border:1px solid var(--yellow-line);color:var(--yellow-text);font-size:11.5px;padding:10px 12px;border-radius:10px;margin-bottom:12px;line-height:1.4}

.periodbar{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}
.periodbar select{width:auto;flex:none}

.catbar-row{display:flex;justify-content:space-between;align-items:center;font-size:12.5px;padding:6px 0}
.catbar-track{height:6px;background:var(--mint-soft);border-radius:4px;overflow:hidden;margin-top:4px}
.catbar-fill{height:100%;background:var(--brand);border-radius:4px}

.triprow{display:flex;gap:10px;background:var(--card);border:1px solid var(--line);border-radius:16px;padding:13px 14px;margin-bottom:10px;align-items:center;box-shadow:var(--shadow-sm)}
.triprow .ico{width:34px;height:34px;border-radius:9px;background:var(--mint-soft);color:var(--brand-text);display:grid;place-items:center;flex:none}
.triprow .ico svg{width:18px;height:18px}
.triprow .n{font-weight:600;font-size:13.5px}
.triprow .d{font-size:11.5px;color:var(--muted)}
.triprow .p{margin-left:auto;text-align:right;font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:13px}
.triprow .p small{display:block;font-size:10.5px;color:var(--muted);font-weight:500}

.gpspanel{text-align:center;padding:22px 10px}
.gpsdist{font-family:'IBM Plex Mono',monospace;font-weight:700;font-size:40px;color:var(--brand-text)}
.gpsdist small{display:block;font-size:12px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:.6px}
.gpstime{font-family:'IBM Plex Mono',monospace;font-size:14px;color:var(--muted);margin-top:4px}
.pulse{width:9px;height:9px;border-radius:50%;background:var(--coral);display:inline-block;margin-right:6px;animation:pulse 1.4s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}

.calcgrid{display:grid;gap:9px;margin-bottom:10px}

/* ---- Dashboard ---- */
/* Three genuinely different weights, not just three colours on one look — the eye
   should be able to sort these by urgency without reading the text. 'info' is the
   quietest (flat tint, no border/shadow — it recedes), 'warn' is the current
   standard card, 'bad' is the loudest (tinted background + heavier accent). */
.alertcard{display:flex;gap:10px;align-items:center;background:var(--card);border:1px solid var(--line);border-left:3px solid var(--line);border-radius:12px;padding:11px 12px;margin-bottom:8px;cursor:pointer;box-shadow:var(--shadow-sm)}
.alertcard .ico{flex:none;width:28px;height:28px;border-radius:8px;background:var(--line);color:var(--muted);display:grid;place-items:center}
.alertcard .ico svg{width:15px;height:15px}
.alertcard .t{font-weight:600;font-size:13.5px}
.alertcard .d{font-size:11.5px;color:var(--muted)}

.alertcard.info{background:var(--mint-soft);border-color:transparent;border-left:3px solid var(--brand);box-shadow:none}
.alertcard.info .ico{background:var(--card);color:var(--brand-text)}

.alertcard.warn{border-left-color:var(--yellow)}
.alertcard.warn .ico{background:var(--yellow-soft);color:var(--yellow)}

.alertcard.bad{background:var(--coral-soft);border-color:transparent;border-left:4px solid var(--coral)}
.alertcard.bad .ico{background:var(--card);color:var(--coral)}
.alertcard.bad .t{color:var(--coral-text)}

.alertDismiss{flex:none;width:22px;height:22px;border:none;background:transparent;color:var(--muted);font-size:17px;line-height:1;border-radius:6px;cursor:pointer;display:grid;place-items:center;padding:0}
.alertDismiss:hover{background:rgba(0,0,0,.08);color:var(--ink)}

.quickgrid{display:flex;gap:6px;margin-bottom:14px}
.quickgrid button{flex:1;min-width:0;border:1px solid var(--line);background:var(--card);color:var(--brand-text);border-radius:9px;padding:9px 4px;font-family:inherit;font-weight:600;font-size:11px;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:4px}
.quickgrid button svg{width:16px;height:16px}
.quickgrid button:active{background:var(--mint-soft)}

.section-link{display:block;text-align:center;margin:10px 0 4px}

.fin6{grid-template-columns:repeat(3,1fr);gap:6px}

/* Hides the dashboard widgets until JS has applied the saved drag order —
   without this, a page load briefly paints the raw HTML markup order first,
   then visibly jumps into the real order the instant that JS runs. The HTML
   above is already written in the default order so this gap is normally
   sub-frame, but it's still there for anyone who's drag-reordered their
   layout away from the default. */
.dashloading{visibility:hidden}
.widget{margin-bottom:22px}
.widget:last-child{margin-bottom:8px}
.widgethead{display:flex;align-items:center;gap:6px;margin-bottom:8px;cursor:grab;padding:6px 4px;margin:-6px -4px 2px;border-radius:8px;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
.widgethead:active{cursor:grabbing;background:var(--mint-soft)}
.widgethead .eyebrow{margin:0;flex:1}
.draghandle{color:var(--muted);display:flex;padding:5px;border-radius:6px;pointer-events:none}
.widgethead:active .draghandle{color:var(--brand-text)}
.widget.dragging{opacity:.4}

.phothumb{position:relative;width:64px;height:64px;border-radius:8px;overflow:hidden;border:1px solid var(--line);flex:none}
.phothumb img{width:100%;height:100%;object-fit:cover;display:block}
.phothumb .rm{position:absolute;top:2px;right:2px;width:18px;height:18px;font-size:11px;line-height:16px;padding:0}
.phothumb.pdf{display:flex;align-items:center;justify-content:center;background:var(--mint-soft);color:var(--brand-text);font-size:10.5px;font-weight:700;letter-spacing:.4px;text-decoration:none}

/* mini calendar */
.calwrap{background:var(--card);border:1px solid var(--line);border-radius:var(--radius-card);padding:16px;margin-bottom:12px;box-shadow:var(--shadow-sm)}
.calhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
.calhead .m{font-weight:600;font-size:13.5px}
.calhead button{border:0;background:var(--mint-soft);color:var(--brand-text);width:26px;height:26px;border-radius:7px;cursor:pointer;display:grid;place-items:center}
.calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center}
.calgrid .dow{font-size:9.5px;color:var(--muted);font-weight:600;text-transform:uppercase;padding-bottom:4px}
.calgrid .day{position:relative;font-size:12px;padding:10px 0 7px;border-radius:8px;color:var(--ink);cursor:pointer}
.calgrid .day.other{color:var(--muted)}
.calgrid .day.today{background:var(--mint-soft);font-weight:700;color:var(--brand-text)}
.calgrid .day.booked{box-shadow:inset 0 0 0 2px var(--brand)}
/* .dots wraps 1-2 dots per day: brand-green for a job, teal for a quote
   appointment (see .dot.appt) — both can show together on a mixed day. */
.calgrid .day .dots{position:absolute;bottom:3px;left:50%;transform:translateX(-50%);display:flex;gap:3px}
.calgrid .day .dots .dot{position:static;width:6px;height:6px;border-radius:50%;background:var(--brand);box-shadow:0 0 0 1.5px var(--card)}
.calgrid .day .dots .dot.appt{background:var(--teal)}
.calgrid .day .wx{position:absolute;top:-3px;right:-1px;font-size:15px;line-height:1;filter:drop-shadow(0 1px 1px rgba(0,0,0,.15))}
.calgrid .day.rainy{background:var(--coral-soft)}
.calgrid .day.rainy.today{background:var(--coral-soft)}
.calgrid .day.hot{background:var(--heat-bg)}
.calgrid .day.hot.today{background:var(--heat-bg)}
.calgrid .day.hot.rainy{background:linear-gradient(135deg,var(--heat-bg) 50%,var(--coral-soft) 50%)}
.calgrid .day:hover{background:var(--cream)}

/* income/expense chart */
.chartwrap{background:var(--card);border:1px solid var(--line);border-radius:var(--radius-card);padding:18px;margin-bottom:12px;box-shadow:var(--shadow-sm)}
.chartlegend{display:flex;gap:16px;margin-bottom:10px;font-size:11.5px;color:var(--muted)}
.chartlegend span{display:inline-flex;align-items:center;gap:5px}
.chartlegend i{width:9px;height:9px;border-radius:2px;display:inline-block}

/* invoice print sheet */
#invoicePrint,#quotePrint,#accountantPrint,#workOrderPrint,#payrollPrint,#recoveryKeyPrint{display:none}
.inv-doc{font-family:'Poppins',system-ui,sans-serif;color:#20241F;max-width:700px;margin:0 auto}
.inv-band{display:flex;justify-content:space-between;gap:16px;align-items:center;background:var(--brand);color:#fff;border-radius:12px;padding:20px 22px;margin-bottom:22px}
.inv-brand{display:flex;align-items:center;gap:12px}
.inv-brand img{height:44px;border-radius:8px;background:#fff;padding:4px}
.inv-bizname{font-size:15px;font-weight:700}
.inv-bizmeta{font-size:11px;opacity:.9;line-height:1.6;margin-top:2px}
.inv-idbox{text-align:right}
.inv-title{font-size:20px;font-weight:800;letter-spacing:.5px}
.inv-num{font-family:'IBM Plex Mono',monospace;font-size:12.5px;opacity:.9;margin-top:2px}
.inv-dates{font-size:11px;opacity:.9;margin-top:6px;line-height:1.6}
.inv-billto{margin-bottom:18px}
.inv-label{font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);margin-bottom:4px}
.inv-client{font-size:14.5px;font-weight:700}
.inv-meta{font-size:12.5px;color:var(--muted);line-height:1.6}
.inv-table{width:100%;border-collapse:collapse;margin:16px 0;font-size:13px}
.inv-table th{text-align:left;font-size:10.5px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);border-bottom:2px solid var(--ink);padding:8px 4px}
.inv-table td{padding:9px 4px;border-bottom:1px solid var(--line)}
.inv-totals{margin-left:auto;width:260px;font-size:13.5px}
.inv-totals .r{display:flex;justify-content:space-between;padding:4px 0}
.inv-totals .r.grand{background:var(--mint-soft-doc);color:var(--brand-d);border-radius:9px;margin-top:8px;padding:10px 12px;font-weight:700;font-size:16.5px}
.inv-footer{margin-top:28px;padding-top:14px;border-top:1px solid var(--line);font-size:11.5px;color:var(--muted);text-align:center}

/* spelling / grammar check */
.gcheck-panel{display:none;margin-top:6px;font-size:11.5px;line-height:1.5}
.gcheck-panel.on{display:block}
.gcheck-summary{font-weight:700;color:var(--muted);margin-bottom:4px;text-transform:uppercase;letter-spacing:.4px;font-size:10px}
.gcheck-item{padding:4px 0;border-top:1px dashed var(--line)}
.gcheck-item:first-of-type{border-top:0}
.gcheck-bad{color:var(--coral);font-weight:600;text-decoration:underline wavy var(--coral)}
.gcheck-msg{color:var(--muted)}
.gcheck-suggs{display:block;margin-top:3px}
.gcheck-chip{border:1px solid var(--brand);background:var(--card);color:var(--brand-text);border-radius:999px;padding:2px 9px;font-size:10.5px;font-weight:600;cursor:pointer;margin-right:5px;margin-top:2px}
.gcheck-chip:hover{background:var(--brand);color:#fff}

/* lock screen */
#lockScreen{position:fixed;inset:0;z-index:200;display:none;flex-direction:column;align-items:center;justify-content:flex-start;padding:64px 24px;text-align:center;overflow-y:auto;
  background:radial-gradient(circle at 16% 10%, rgba(138,154,133,.18), transparent 42%),
    radial-gradient(circle at 90% 86%, rgba(32,36,31,.07), transparent 46%),
    var(--cream)}
#lockScreen.on{display:flex}
#lockScreen .lockCard{background:var(--card);border:1px solid var(--line);border-radius:22px;box-shadow:var(--shadow-md);padding:34px 28px 26px;width:100%;max-width:340px}
#lockScreen .lockBrand{display:flex;flex-direction:column;align-items:center;margin-bottom:20px}
#lockScreen .lockWord{font-size:27px;font-weight:300;letter-spacing:4px;text-transform:lowercase;color:var(--ink);display:flex;align-items:baseline;gap:6px;line-height:1}
#lockScreen .lockDot{width:12px;height:12px;transform:translateY(-9px);flex:none;object-fit:contain}
#lockScreen .lockTagline{font-size:10px;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted);margin-top:6px}
#lockScreen .mark{width:64px;height:64px;border-radius:16px;margin-bottom:18px}
#lockScreen h2{font-size:16px;margin:0 0 18px;font-weight:600;color:var(--brand-text)}
#lockScreen .lockFooter{margin-top:20px;font-size:12px;color:var(--muted)}
#lockScreen .lockFooter a{color:var(--brand-text);font-weight:600;text-decoration:none}
#lockScreen .lockFooter a:hover{text-decoration:underline}
#lockError{color:var(--coral);font-size:12px;margin-top:10px;min-height:16px}

@media print{
  body *{visibility:hidden}
  #invoicePrint,#invoicePrint *,#quotePrint,#quotePrint *,#accountantPrint,#accountantPrint *,#workOrderPrint,#workOrderPrint *,#payrollPrint,#payrollPrint *,#recoveryKeyPrint,#recoveryKeyPrint *{visibility:visible}
  #invoicePrint,#quotePrint,#accountantPrint,#workOrderPrint,#payrollPrint,#recoveryKeyPrint{display:block;position:absolute;top:0;left:0;width:100%;padding:24px;color:#000;background:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .nav,dialog{display:none!important}
  body.printing-report #v-money,body.printing-report #v-money *{visibility:visible}
  body.printing-report #v-money{position:absolute;top:0;left:0;width:100%;background:#fff;padding:16px}
  body.printing-report .moneytabs,body.printing-report .chartwrap,body.printing-report #repPrint{display:none!important}
}
