:root{
  --bg:#f4f6f9; --panel:#ffffff; --panel2:#f7f9fc; --line:#e7eaf0; --line2:#dde2ec;
  --ink:#0f1628; --ink2:#38415a; --mut:#6b7488; --faint:#9aa2b4;
  /* Row banding and row hover. A single grey works in neither theme: over white it must darken and
     over the dark panel it must lighten, and one value does almost nothing in both. */
  --band:rgba(15,22,40,.075); --bandhov:rgba(37,99,235,.13);
  --brand:#0e1626; --brand2:#182338;
  --accent:#2563eb; --accent2:#1d4ed8; --accent-soft:#eaf1ff; --indigo:#4f46e5; --indigo-soft:#eceafe;
  --ok:#16a34a; --ok-bg:#e7f6ec; --soon:#d97706; --soon-bg:#fdf0df; --bad:#dc2626; --bad-bg:#fdeaea;
  --radius:15px; --radius-s:11px;
  --shadow:0 1px 2px rgba(15,22,40,.04),0 4px 16px rgba(15,22,40,.05);
  --shadow-s:0 1px 2px rgba(15,22,40,.05);
  --font:'Inter',system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-head:'Space Grotesk','Poppins',system-ui,-apple-system,"Segoe UI",sans-serif;
  --mono:'JetBrains Mono',ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --side-w:236px;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  --bg:#0b1120; --panel:#141d31; --panel2:#1a2438; --line:#25314a; --line2:#31405f;
  --ink:#eaf0fb; --ink2:#c3ccdd; --mut:#93a0ba; --faint:#6b7794; --brand:#0a1120; --brand2:#141d31;
  --accent:#5b8cff; --accent2:#3b73f0; --accent-soft:#182642; --indigo:#8b83ff; --indigo-soft:#231f45;
  --ok:#3ecb7d; --ok-bg:#123024; --soon:#f0a83a; --soon-bg:#33280f; --bad:#f56b6b; --bad-bg:#331717;
  --shadow:0 1px 2px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.30);
  --band:rgba(255,255,255,.065); --bandhov:rgba(91,140,255,.20);
}}
:root[data-theme="dark"]{
  --bg:#0b1120; --panel:#141d31; --panel2:#1a2438; --line:#25314a; --line2:#31405f;
  --ink:#eaf0fb; --ink2:#c3ccdd; --mut:#93a0ba; --faint:#6b7794; --brand:#0a1120; --brand2:#141d31;
  --accent:#5b8cff; --accent2:#3b73f0; --accent-soft:#182642; --indigo:#8b83ff; --indigo-soft:#231f45;
  --ok:#3ecb7d; --ok-bg:#123024; --soon:#f0a83a; --soon-bg:#33280f; --bad:#f56b6b; --bad-bg:#331717;
  --shadow:0 1px 2px rgba(0,0,0,.35),0 8px 24px rgba(0,0,0,.30);
  --band:rgba(255,255,255,.065); --bandhov:rgba(91,140,255,.20);
}
*{box-sizing:border-box}
html,body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);line-height:1.45;-webkit-text-size-adjust:100%}
a{color:inherit;text-decoration:none}
h1,h2,h3{margin:0;font-family:var(--font-head);letter-spacing:-.02em}
.mono{font-family:var(--mono)}

.app{display:flex;min-height:100vh}

/* ---------- sidebar ---------- */
.sidebar{width:var(--side-w);flex:none;background:var(--panel);border-right:1px solid var(--line);
  display:flex;flex-direction:column;position:sticky;top:0;height:100vh;z-index:40}
.side-brand{display:flex;align-items:center;gap:.6rem;padding:1.1rem 1.1rem .9rem}
.side-brand .mark{width:34px;height:34px;border-radius:9px}
.side-brand-t b{font-family:var(--font-head);font-weight:800;font-size:1.15rem;display:block;letter-spacing:-.02em}
.side-brand-t small{font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;color:var(--faint);font-weight:700}
.side-nav{display:flex;flex-direction:column;gap:.12rem;padding:.4rem .6rem;overflow-y:auto;flex:1}
.side-nav a{display:flex;align-items:center;gap:.7rem;padding:.6rem .7rem;border-radius:10px;
  font-weight:600;font-size:.92rem;color:var(--ink2);position:relative}
.side-nav a i{font-style:normal;font-size:1.15rem;width:1.4rem;text-align:center;line-height:1}
.side-nav a:hover{background:var(--panel2)}
.side-nav a.active{background:var(--accent-soft);color:var(--accent2)}
.side-nav a.active::before{content:"";position:absolute;left:-.6rem;top:.5rem;bottom:.5rem;width:3px;border-radius:3px;background:var(--accent)}
.side-foot{border-top:1px solid var(--line);padding:.8rem;display:flex;flex-direction:column;gap:.5rem}
.tenant-card,.user-mini{display:flex;align-items:center;gap:.6rem}
.tenant-card{background:var(--panel2);border:1px solid var(--line);border-radius:11px;padding:.55rem .65rem}
.tenant-card .tlogo{width:44px;height:44px;flex:none;display:grid;place-items:center;font-size:1.6rem}
.tenant-card .tlogo img{width:100%;height:100%;object-fit:contain}
/* both text lines switched off → the logo fills the card on its own */
.tenant-card.solo{background:none;border-color:transparent;padding:.35rem 0;margin:0 -.8rem}
.tenant-card .tlogo.solo{display:block;width:100%;height:auto;text-align:center;font-size:2.4rem}
.tenant-card .tlogo.solo img{width:100%;height:auto;max-height:120px;object-fit:contain}
.tenant-card b,.user-mini b{font-size:.85rem;display:block;letter-spacing:-.01em}
.tenant-card small,.user-mini small{font-size:.7rem;color:var(--faint)}
.user-mini{padding:.1rem .3rem}
/* display:flex above beats the UA's [hidden] rule, so restate it */
.user-mini[hidden]{display:none}
.user-mini .role.acting{color:var(--soon);font-weight:700}
.ua{width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);color:var(--indigo);
  display:grid;place-items:center;font-weight:800;font-size:.8rem;flex:none}
.role{color:var(--indigo)!important;font-weight:700}

/* ---------- topbar ---------- */
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:.8rem;background:var(--panel);
  border-bottom:1px solid var(--line);padding:.65rem 1.3rem;padding-top:calc(.65rem + env(safe-area-inset-top))}
.hamburger{display:none;background:var(--panel2);border:1px solid var(--line);border-radius:9px;width:36px;height:36px;font-size:1.1rem;cursor:pointer;color:var(--ink)}
.crumb{font-family:var(--font-head);font-weight:700;font-size:1rem;color:var(--ink)}
.tenant{margin:0 auto;display:flex;align-items:center;gap:.5rem;min-width:0;font-weight:800;font-family:var(--font-head);letter-spacing:-.01em;color:var(--ink);font-size:1.02rem}
.tenant .tlogo-top{height:28px;max-width:150px;object-fit:contain;flex:none}
.tenant .tname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.topbar-right{display:flex;align-items:center;gap:.7rem}
.icon-btn{background:var(--panel2);border:1px solid var(--line);border-radius:9px;width:36px;height:36px;font-size:1rem;cursor:pointer;color:var(--ink)}
.rolesel{background:var(--panel2);border:1px solid var(--line);border-radius:9px;color:var(--ink);font-size:.78rem;font-weight:700;padding:.35rem .5rem;cursor:pointer}
.sync{display:inline-flex;align-items:center;gap:.4rem;font-size:.73rem;font-weight:700;background:var(--panel2);
  border:1px solid var(--line);padding:.32rem .6rem;border-radius:999px;color:var(--mut)}
.sync .d{width:8px;height:8px;border-radius:50%;background:var(--ok)}
.sync.off .d{background:var(--soon)} .sync.pending .d{background:#f5b301}
.user-chip{display:flex;align-items:center;gap:.5rem}
.user-chip .uc-t b{font-size:.82rem;display:block;line-height:1.1}
.user-chip .uc-t small{font-size:.68rem;color:var(--faint)}

.scrim{display:none;position:fixed;inset:0;background:rgba(8,12,22,.45);z-index:35}

/* ---------- page ---------- */
/* full width by default — most of Rivet is registers and tables. :root.narrow puts the old
   readable-column cap back for anyone who prefers it (My settings → Page width). */
main{padding:1.5rem 1.8rem 3rem;max-width:none;width:100%;margin:0 auto}
:root.narrow main{max-width:1240px}
.loading{color:var(--faint);padding:2rem 0}
.page-head{display:flex;align-items:flex-start;gap:.8rem;margin:.2rem 0 1.3rem;flex-wrap:wrap}
.page-head .ph-ic{font-size:1.7rem;line-height:1}
.page-head h1{font-size:1.75rem;font-weight:800}
.page-head .sub{color:var(--mut);font-size:.9rem;margin-top:.15rem}
.page-head .spacer{margin-left:auto}
.count-chip{font-size:.75rem;font-weight:700;color:var(--mut);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:999px}

/* toolbar / filter card */
.toolbar{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);
  padding:.8rem;display:flex;gap:.7rem;flex-wrap:wrap;align-items:center;margin-bottom:1.3rem}
.search{flex:1;min-width:200px;position:relative}
.search input{width:100%;padding:.65rem .8rem .65rem 2.2rem}
.search::before{content:"🔍";position:absolute;left:.7rem;top:50%;transform:translateY(-50%);font-size:.85rem;opacity:.6}
input,select,textarea{background:var(--panel);border:1px solid var(--line2);border-radius:10px;color:var(--ink);
  padding:.65rem .75rem;font-size:.92rem;font-family:inherit}
input:focus,select:focus,textarea:focus{outline:2px solid var(--accent);outline-offset:0;border-color:var(--accent)}
select{cursor:pointer}

/* an eye on a password field — a password typed one-handed into a phone in a cab gets mistyped,
   and you cannot correct what you cannot see. Sits inside the field so it works in a flex row
   alongside the 🎲 generate button. */
.pwwrap{position:relative;display:block;flex:1;min-width:0}
.pwwrap>input{width:100%;padding-right:2.6rem}
.pw-eye{position:absolute;right:.3rem;top:.3rem;bottom:.3rem;width:2rem;display:grid;place-items:center;
  background:none;border:none;border-radius:8px;cursor:pointer;font-size:1rem;line-height:1;padding:0;color:var(--mut)}
.pw-eye:hover{color:var(--ink);background:var(--soft,rgba(120,120,140,.12))}
.pw-eye:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* stat cards */
.statgrid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin-bottom:1.6rem}
.stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:1.05rem 1.15rem;position:relative}
.stat .k{font-size:.68rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--mut)}
.stat .v{font-family:var(--font-head);font-size:2rem;font-weight:800;letter-spacing:-.03em;margin-top:.2rem;line-height:1}
.stat .c{font-size:.78rem;color:var(--faint);margin-top:.35rem}
.stat .ic{position:absolute;top:1rem;right:1.1rem;font-size:1.05rem;opacity:.85}
.stat.bad .v{color:var(--bad)} .stat.warn .v{color:var(--soon)} .stat.good .v{color:var(--ok)}
.stat.dark{background:var(--brand);border-color:var(--brand2);color:#eaf0fb}
.stat.dark .k{color:#9fb0d0} .stat.dark .c{color:#8494b4}
.stat.clickable{cursor:pointer;transition:transform .08s ease,box-shadow .14s ease,border-color .14s ease}
.stat.clickable:hover{transform:translateY(-1px);box-shadow:0 5px 20px rgba(15,22,40,.09);border-color:var(--line2)}
.stat.clickable:active{transform:translateY(0)}

/* card grid + entity cards */
.cardgrid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}
.ecard{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);
  padding:1rem 1.1rem;display:flex;flex-direction:column;gap:.55rem}
.ecard-top{display:flex;align-items:center;gap:.6rem}
.idchip{font-family:var(--mono);font-weight:700;font-size:.86rem;background:var(--brand);color:#eaf0fb;
  padding:.28rem .55rem;border-radius:8px;letter-spacing:.02em;white-space:nowrap}
.ecard-sub{font-family:var(--mono);font-size:.8rem;color:var(--mut)}
/* "NEW" marker on freshly-added forms */
.formtile{position:relative;overflow:hidden}
.formtile.isnew{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent),var(--shadow)}
.newflag{position:absolute;top:0;right:0;background:var(--accent);color:#fff;font-family:var(--font-head);
  font-weight:800;font-size:.66rem;letter-spacing:.04em;padding:.2rem .55rem;border-bottom-left-radius:9px;
  box-shadow:0 2px 6px rgba(0,0,0,.18);z-index:1}
.tabbar.sub{margin:.1rem 0 .7rem}
.tabbar.sub button{font-size:.82rem;padding:.32rem .7rem}
.ecard-fields{display:grid;grid-template-columns:1fr 1fr;gap:.5rem .8rem;margin-top:.1rem}
.ecard-fields .fl{font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
.ecard-fields .fv{font-size:.9rem;font-weight:600;color:var(--ink)}
.compliance{display:flex;flex-direction:column;gap:.3rem;border-top:1px dashed var(--line2);padding-top:.55rem}
.crow{display:flex;align-items:center;gap:.5rem;font-size:.82rem}
.crow .cl{color:var(--mut);min-width:74px}
.crow .cv{margin-left:auto;font-weight:600;font-variant-numeric:tabular-nums}
.dot{width:9px;height:9px;border-radius:50%;background:var(--faint);flex:none}
.dot.ok{background:var(--ok)} .dot.soon{background:var(--soon)} .dot.overdue{background:var(--bad)}
.ecard-foot{display:flex;align-items:center;gap:.5rem;border-top:1px solid var(--line);padding-top:.6rem;margin-top:.1rem}
.ecard-foot .fnote{font-size:.7rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--faint)}
.ecard-foot .acts{margin-left:auto;display:flex;gap:.35rem}
.iconbtn{width:30px;height:30px;border-radius:8px;border:1px solid var(--line);background:var(--panel);color:var(--mut);cursor:pointer;font-size:.8rem}
.iconbtn:hover{background:var(--panel2);color:var(--ink)}
.avatar-tile{width:40px;height:40px;border-radius:10px;background:var(--panel2);border:1px solid var(--line);display:grid;place-items:center;font-size:1.1rem;flex:none}

/* status pills */
.status{font-size:.68rem;font-weight:800;letter-spacing:.03em;padding:.22rem .6rem;border-radius:999px;white-space:nowrap;text-transform:uppercase}
.status.active,.status.available,.status.ok,.status.current,.status.passed{background:var(--ok-bg);color:var(--ok)}
.status.soon,.status.attention,.status.medium,.status.maintenance{background:var(--soon-bg);color:var(--soon)}
.status.overdue,.status.failed,.status.open,.status.high,.status.oos{background:var(--bad-bg);color:var(--bad)}
.status.none,.status.neutral{background:var(--panel2);color:var(--mut);border:1px solid var(--line)}
.status.role{background:var(--indigo-soft);color:var(--indigo)}
.status.done{background:var(--indigo-soft);color:var(--indigo)}
.ml-auto{margin-left:auto}

/* profile cards */
.profile{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:1rem 1.1rem}
.profile-top{display:flex;align-items:center;gap:.7rem}
.profile-top .ua{width:40px;height:40px;font-size:.9rem;border-radius:10px}
.profile-top b{font-size:1rem;display:block}
.profile-top small{color:var(--mut);font-size:.8rem}
.profile-rows{margin-top:.7rem;display:flex;flex-direction:column;gap:.25rem;border-top:1px solid var(--line);padding-top:.6rem}
.profile-rows .r{display:flex;font-size:.82rem}.profile-rows .r .l{color:var(--faint)}.profile-rows .r .v{margin-left:auto;font-weight:600;font-family:var(--mono);font-size:.78rem}

/* tabs (segmented) */
.tabbar{display:inline-flex;gap:.2rem;background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:.25rem;box-shadow:var(--shadow-s);margin-bottom:1.2rem;flex-wrap:wrap}
.tabbar button{border:none;background:transparent;color:var(--mut);font-weight:700;font-size:.85rem;padding:.45rem .85rem;border-radius:8px;cursor:pointer;font-family:inherit}
.tabbar button.on{background:var(--panel2);color:var(--ink);box-shadow:var(--shadow-s)}
/* toggles alongside the tabs rather than being one of them, so it stacks with the tab that is on */
/* form builder + live preview, side by side.
   Below 1100px there is not room for two columns of form, so the preview is hidden and the toggle
   in the header brings it up in place of the editor rather than squeezed beside it. */
.fb-split{display:block}
.fb-split .fb-prev{display:none}
/* Two columns of form need the room even for someone who has chosen the narrower page width, so
   this overrides it while a builder with its preview open is on screen — and stops overriding by
   itself the moment you navigate away, with no class to set on the way in and forget on the way
   out. It sits after :root.narrow deliberately: equal specificity, so the later rule wins. */
main:has(.fb-split.on){max-width:none}
/* Same reasoning for the planner: the narrow page width exists so long text is readable, and a grid of
   days is not long text. Capping it at 1240px means the columns fight for room that the window has and
   the page is refusing to give them. It stops applying by itself when you navigate away, with no class
   to set on the way in and forget on the way out. */
/* Both selectors on purpose. :has() carries the specificity of its argument, so main:has(.plan-wrap)
   is only (0,1,1) — which loses to `:root.narrow main{max-width:1240px}` above at (0,2,1). The cap
   therefore kept winning for anyone with the full-width preference off, which is the default, and
   the planner sized itself to 1240px however wide the screen was: the JS measures wrap.clientWidth,
   so capping the wrapper caps the grid. The second selector matches that specificity and, coming
   later, wins. */
main:has(.plan-wrap),:root.narrow main:has(.plan-wrap){max-width:none}
/* The planner report is the same case, for the same reason: a row per person across a period is a
   grid, not prose, and a card capped at 52rem turned it into a ribbon down the left of a monitor with
   the rest of the screen empty. Both the page cap and the card's own cap have to go — lifting one and
   not the other leaves it exactly as narrow. */
main:has(.rp-page),:root.narrow main:has(.rp-page){max-width:none}
.formcard.rp-page{max-width:none}
@media (min-width: 1100px){
  .fb-split.on{display:grid;grid-template-columns:minmax(0,1fr) minmax(400px,38%);gap:1.1rem;align-items:start}
  .fb-split.on .fb-edit{min-width:0}
  .fb-split.on .fb-edit .formcard{max-width:none}
  /* The pane holds still and scrolls ITSELF to whatever is being edited on the left. Letting it
     flow with the page meant hunting for the question you had just changed, because the editor
     spends far more height per question than the form does and the two columns drift apart. You
     scroll the editor; the preview keeps up on its own. */
  .fb-split.on .fb-prev{display:block;position:sticky;top:.8rem;height:calc(100vh - 1.6rem);
    overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;
    background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow-s)}
}
.fb-prev-head{display:flex;gap:.6rem;align-items:center;justify-content:space-between;padding:.7rem .8rem;
  border-bottom:1px solid var(--line);background:var(--panel);border-radius:14px 14px 0 0;position:sticky;top:0;z-index:2}
/* the question you are editing, called out so the eye lands on it without hunting */
.fb-pq{border-radius:10px;transition:background .18s}
.fb-pq.here{background:var(--accent-soft);box-shadow:0 0 0 2px var(--accent,#2563eb) inset}
.fb-prev-head .s{font-size:.74rem;color:var(--faint)}
.fb-prev-body{padding:.8rem}
.fb-prev-body .formsec{margin-bottom:1rem}
/* a gated section still shows, dimmed, so you can see it exists and what opens it */
.fb-prev-body .fb-prev-off{opacity:.4}
.fb-prev-gate{font-size:.66rem;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--accent,#2563eb)}
/* timesheet filters — staff, job and a period, above the grid */
.tsfilt{display:flex;flex-wrap:wrap;gap:.45rem;align-items:center;background:var(--panel);border:1px solid var(--line);
  border-radius:11px;padding:.6rem .7rem;margin-bottom:.7rem}
.tsfilt .fl{margin:0;font-size:.68rem}
.tsfilt select,.tsfilt input[type=date]{min-width:0;width:auto;padding:.34rem .5rem;font-size:.84rem}
.tsfilt select{max-width:210px}
.tsquick{display:flex;gap:.3rem;flex-wrap:wrap;margin-left:auto}
/* the answer to "how many hours on this job", stated rather than left to be added up */
.tstotal{display:flex;flex-wrap:wrap;align-items:baseline;gap:.5rem;background:var(--panel2);border:1px solid var(--line);
  border-left:3px solid var(--accent,#2563eb);border-radius:10px;padding:.55rem .8rem;margin-bottom:.8rem}
.tstotal b{font-size:1.05rem}
.tstotal span{color:var(--mut);font-size:.82rem}
.tstable .tsjob{display:block;font-size:.63rem;font-weight:700;color:var(--accent,#2563eb);line-height:1.25;
  margin-top:1px;overflow:hidden;text-overflow:ellipsis}
/* a camera on every question — small enough to ignore on the 62 questions that will never need it */
.qphoto-add{display:flex;align-items:flex-start;gap:.5rem;margin-top:.4rem;flex-wrap:wrap}
.qcam{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--line);
  border-radius:8px;background:var(--panel2);cursor:pointer;font-size:.95rem;flex:none;opacity:.75}
.qcam:hover{opacity:1;border-color:var(--accent,#2563eb)}
.qcam-thumb{position:relative;display:inline-flex;flex-direction:column;gap:.15rem}
.qcam-thumb img{max-height:64px;border-radius:6px;border:1px solid var(--line);cursor:pointer;display:block}
.qcam-x{position:absolute;top:-7px;right:-7px;width:20px;height:20px;border-radius:50%;border:1px solid var(--line);
  background:var(--panel);color:var(--mut);font-size:.7rem;line-height:1;cursor:pointer;padding:0}
.qcam-cap{font-size:.7rem;font-weight:700;color:var(--faint);max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tabbar button.mine-chip{margin-left:.35rem;border-left:1px solid var(--line);border-radius:8px;padding-left:.8rem}
.tabbar button.mine-chip.on{background:var(--accent,#2563eb);color:#fff;box-shadow:var(--shadow-s)}
/* somebody else's actions: the chip carries their name, so a filter is never on invisibly */
.tabbar button.mine-chip.other{background:var(--accent-soft);color:var(--accent2,#1d4ed8);font-weight:700}
.who-split{display:inline-flex;align-items:stretch}
.tabbar .who-split button.mine-chip{margin-right:0;border-top-right-radius:0;border-bottom-right-radius:0}
.tabbar button.who-arrow{padding:.45rem .5rem;border-radius:0 8px 8px 0;color:var(--mut);font-size:.7rem;
  border-left:1px solid var(--line)}
.tabbar button.who-arrow.on{background:var(--accent-soft);color:var(--accent2,#1d4ed8)}
.tabbar button.who-arrow:hover{background:var(--panel2);color:var(--ink)}
.who-menu{position:absolute;z-index:30;margin-top:-.9rem;background:var(--panel);border:1px solid var(--line);
  border-radius:11px;box-shadow:var(--shadow);padding:.35rem;min-width:260px;max-width:340px;
  max-height:min(420px,60vh);overflow:auto}
.who-head{font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--faint);padding:.4rem .6rem .3rem}
.who-menu button{display:flex;width:100%;align-items:baseline;gap:.5rem;justify-content:space-between;
  background:none;border:none;text-align:left;padding:.45rem .6rem;border-radius:8px;cursor:pointer;
  font:inherit;font-size:.88rem;font-weight:600;color:var(--ink)}
.who-menu button:hover{background:var(--panel2)}
.who-menu button span{font-size:.74rem;font-weight:600;color:var(--faint);white-space:nowrap}
.who-empty{padding:.5rem .6rem;font-size:.82rem;color:var(--mut)}
/* who is carrying it — on an open action the person, on a completed one who signed it off */
.awho{display:flex;flex-wrap:wrap;gap:.5rem;align-items:baseline;margin-top:.35rem;font-size:.78rem}
.awho-on{font-weight:700;color:var(--accent2,#1d4ed8)}
.awho-off{color:var(--faint);font-style:italic}
.awho-done{font-weight:700;color:var(--ok,#15803d)}
.awho-was{color:var(--faint)}
/* actions grouped by person */
.agroup{border:1px solid var(--line);border-radius:12px;margin-bottom:.7rem;overflow:hidden;background:var(--panel)}
.agroup-head{display:flex;align-items:center;gap:.55rem;padding:.6rem .8rem;cursor:pointer;background:var(--panel2);
  border-bottom:1px solid var(--line)}
.agroup-head:hover{background:var(--panel)}
.agroup-head b{font-family:var(--font-head);font-size:.98rem}
.agroup-n{color:var(--mut);font-size:.8rem}
.agroup-caret{color:var(--faint);font-size:.7rem;transition:transform .15s}
.agroup.shut .agroup-caret{transform:rotate(-90deg)}
.agroup.shut .agroup-body{display:none}
.agroup.shut .agroup-head{border-bottom:none}
.agroup-body{padding:.7rem .8rem;display:grid;gap:.6rem}
.agroup-body .acard{margin:0}
.agroup-empty{color:var(--faint);font-size:.84rem;font-style:italic}
.agroup.isnone .agroup-head{background:var(--accent-soft)}
.agroup.isidle{opacity:.62}
.agroup-idle{display:flex;align-items:center;gap:.45rem;margin:-.4rem 0 .9rem;font-size:.84rem;color:var(--mut)}

/* issue / action cards */
.acard{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:1rem 1.15rem;margin-bottom:.8rem}
.acard .row1{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.4rem}
.acard h3{font-size:1rem;font-family:var(--font);font-weight:700;letter-spacing:0}
.acard .desc{color:var(--mut);font-size:.88rem;margin-top:.15rem}
.acard .meta{color:var(--faint);font-family:var(--mono);font-size:.74rem;margin-top:.5rem}
.acard .cta{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.7rem}

/* buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;background:var(--accent);color:#fff;
  border:none;border-radius:10px;padding:.62rem 1rem;font-weight:700;font-size:.9rem;cursor:pointer;font-family:inherit}
.btn:hover{background:var(--accent2)} .btn:active{transform:translateY(1px)}
.btn.block{width:100%} .btn.sm{padding:.42rem .7rem;font-size:.82rem;border-radius:9px}
.btn.ghost{background:var(--panel);color:var(--ink);border:1px solid var(--line2)}
.btn.ghost:hover{background:var(--panel2)}

/* inspections / form fill */
/* One width for every card, rather than a number chosen per page. These were 620, 680, 720, 760, 820,
   840, 860, 900 and 920 pixels, all inline, all picked by hand at different times — so a page of cards
   was a page of slightly different widths and the ragged right edge read as carelessness. 52rem sits
   where most of them already were. A card that genuinely needs more room says so with .formcard-wide
   rather than by inventing another number. */
.formcard{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:1.2rem 1.3rem;max-width:52rem}
.formcard-wide{max-width:72rem}
label.fl{display:block;font-size:.72rem;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--mut);margin:.9rem 0 .35rem}
.formsec{margin-top:1.1rem}.formsec h3{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:var(--accent);font-family:var(--font);font-weight:800}
.q{padding:.8rem 0;border-bottom:1px solid var(--line)}
.q .qt{font-weight:600;font-size:.94rem}
.q .crit{color:var(--bad);font-size:.64rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;margin-left:.4rem}
.seg{display:grid;grid-template-columns:1fr 1fr 1fr;gap:.4rem;margin-top:.5rem}
.seg button{padding:.55rem;border-radius:9px;border:1px solid var(--line2);background:var(--panel);font-weight:700;color:var(--mut);cursor:pointer;font-size:.88rem;font-family:inherit;-webkit-tap-highlight-color:transparent}
.seg button:focus{outline:none}
.seg button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.seg button.sel[data-v="Pass"],.seg button.sel[data-v="Yes"]{background:var(--ok-bg);border-color:var(--ok);color:var(--ok)}
.seg button.sel[data-v="Fail"],.seg button.sel[data-v="No"]{background:var(--bad-bg);border-color:var(--bad);color:var(--bad)}
.seg button.sel[data-v="N/A"]{background:var(--soon-bg);border-color:var(--soon);color:var(--soon)}
/* Measurement questions: the comparator is a row of words, not Pass/Fail, so the rules above never
   matched and a tapped button gave no feedback at all. Wraps to as many columns as fit. */
.mseg{grid-template-columns:repeat(auto-fit,minmax(104px,1fr))}
.mseg button{padding:.5rem .35rem;font-size:.82rem}
.mseg button.sel{background:var(--accent);border-color:var(--accent);color:#fff}
.qmeasure .munit{color:var(--mut)}

.empty{color:var(--faint);text-align:center;padding:2.6rem 1rem;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);font-style:italic}
.eyebrow{font-size:.7rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);margin:1.6rem 0 .7rem}
.list-plain{display:flex;flex-direction:column;gap:.55rem}
.lrow{display:flex;align-items:center;gap:.8rem;background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:.75rem .95rem;box-shadow:var(--shadow-s)}
.lrow .main{min-width:0;flex:1}
.lrow .t{font-weight:700;font-size:.92rem;display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}
.lrow .s{color:var(--mut);font-size:.8rem;margin-top:.12rem}
.lrow .rt{margin-left:auto;text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}

/* combobox */
.combo{position:relative}
.combo-list{position:absolute;left:0;right:0;top:calc(100% + 4px);background:var(--panel);border:1px solid var(--line2);
  border-radius:10px;max-height:264px;overflow:auto;z-index:25;box-shadow:var(--shadow)}
.combo-list>div{padding:.55rem .7rem;cursor:pointer;font-size:.9rem;border-bottom:1px solid var(--line)}
.combo-list>div:last-child{border-bottom:none}
.combo-list>div:hover{background:var(--accent-soft)}
.combo-list .none{color:var(--faint);cursor:default}
/* clear-selection ✕ inside a combo field (big enough to hit with a thumb) */
.combo.has-clear > input{padding-right:2.4rem}
.combo-clear{position:absolute;top:0;right:0;height:100%;width:2.3rem;display:flex;align-items:center;justify-content:center;
  background:none;border:none;padding:0;margin:0;cursor:pointer;color:var(--mut);font-size:.95rem;line-height:1;border-radius:0 10px 10px 0}
.combo-clear:hover{color:var(--bad);background:var(--panel2)}
.combo-clear[hidden]{display:none}

/* edit pages */
.detail-head{background:var(--brand);color:#eaf0fb;border-radius:var(--radius);padding:1.1rem 1.25rem;
  display:flex;align-items:center;gap:.9rem;flex-wrap:wrap;margin-bottom:1.2rem;box-shadow:var(--shadow)}
.editgrid{display:grid;grid-template-columns:1fr 1fr;gap:.35rem 1.1rem}
.editgrid input,.editgrid select{width:100%}
@media(max-width:640px){.editgrid{grid-template-columns:1fr}}

/* photos */
.photo-edit{display:flex;align-items:center;gap:1rem;margin-bottom:.7rem}
.photo-frame{width:96px;height:72px;border-radius:10px;border:1px solid var(--line2);background:var(--panel2);display:grid;place-items:center;overflow:hidden;flex:none}
.photo-frame img{width:100%;height:100%;object-fit:cover}
.photo-frame .ph{font-size:1.5rem;opacity:.5}
.thumb{width:36px;height:27px;border-radius:6px;overflow:hidden;flex:none;border:1px solid var(--line2)}
.thumb img{width:100%;height:100%;object-fit:cover}

/* status picker (colour-coded) */
.statuspick{position:relative}
.statuspick-btn{display:flex;align-items:center;gap:.4rem;width:100%;justify-content:flex-start;background:var(--panel);border:1px solid var(--line2);border-radius:10px;padding:.5rem .6rem;cursor:pointer;font-family:inherit}
.statuspick-btn .caret{margin-left:auto;color:var(--faint)}
.statuspick-btn:disabled{opacity:.55;cursor:default}
.statuspick-list{position:absolute;left:0;right:0;top:calc(100% + 4px);background:var(--panel);border:1px solid var(--line2);border-radius:10px;box-shadow:var(--shadow);z-index:26;padding:.3rem}
.statuspick-opt{padding:.4rem .5rem;border-radius:8px;cursor:pointer}
.statuspick-opt:hover{background:var(--panel2)}

/* inspection defect capture */
.faildetail{margin-top:.55rem;padding:.65rem;border:1px solid color-mix(in srgb,var(--bad) 32%,transparent);background:var(--bad-bg);border-radius:10px}
.faildetail textarea{width:100%}
.fdrow{display:flex;align-items:center;gap:.6rem;margin-top:.5rem}
.fdimg{width:58px;height:44px;object-fit:cover;border-radius:7px;border:1px solid var(--line2)}
.notify-block{margin-top:1rem;padding-top:.85rem;border-top:1px solid var(--line)}
.chk{display:inline-flex;align-items:center;gap:.4rem;margin-right:1.1rem;font-weight:600;font-size:.92rem;cursor:pointer}
.chk input{width:auto}

/* QR overlay */
.qr-overlay{position:fixed;inset:0;background:rgba(8,12,22,.55);z-index:70;display:grid;place-items:center;padding:1rem}
.qr-card{background:var(--panel);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);padding:1.3rem;text-align:center;max-width:340px;width:100%}
.qr-card .qr-label{font-family:var(--mono);font-weight:700;margin-bottom:.6rem}
.qr-img{width:250px;height:250px;max-width:100%;background:#fff;border-radius:10px;padding:8px;border:1px solid var(--line)}
.qr-url{font-size:.7rem;color:var(--faint);word-break:break-all;margin-top:.5rem}
.qr-actions{display:flex;gap:.5rem;justify-content:center;margin-top:.9rem}

/* jobs */
.jchips{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin:.2rem 0 .3rem}
.jchip{display:inline-flex;align-items:center;gap:.35rem;background:var(--panel2);border:1px solid var(--line2);border-radius:999px;padding:.3rem .6rem;font-size:.82rem;font-weight:600}
.jchip small{color:var(--faint);font-weight:500}
.jchip .jx{border:none;background:none;color:var(--bad);cursor:pointer;font-size:.85rem;line-height:1;padding:0}
.muted{color:var(--faint);font-size:.85rem}
/* mobile-connectivity block on an asset (revealed by the tick box) */
.mobilebox{margin-top:.5rem;border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:10px;padding:.65rem .8rem;background:var(--panel2)}
.mobilebox[hidden]{display:none}
.mobile-toggle{align-items:center;gap:.4rem}
.mobilebox.locked{border-left-color:var(--faint);color:var(--mut);font-size:.85rem}
/* ---- switches instead of tick boxes -------------------------------------------------------------
   Red for off, green for on, as asked. Driven by a class on <body> so it is one preference rather
   than a change to every form in the app, and no markup moves.
   Deliberately NOT applied to multi-select answers on a form or to row-selection boxes in a list:
   those are "pick several of these", where a tick is the right idea and a row of red switches reads
   as a row of faults. */
/* A heading inside a card, for grouping settings. The field-label style is small grey uppercase,
   which is right for "Position" but reads as a caption when it is actually a section title. */
.subhead{font-family:var(--font-head);font-size:1rem;font-weight:800;color:var(--ink);
  letter-spacing:-.01em;margin:1.1rem 0 .5rem}
.subhead:first-of-type{margin-top:.2rem}
/* ---- control styles -------------------------------------------------------------------------
   One body class picks the look. Worth having more than a switch: a site phone is used in gloves,
   in the rain, at arm's length, and a 20px tick box is not a serious target. `chunky` and `tick`
   exist for that; `ios` for anyone who finds red/green shouty. */

/* TICK — a proper box with a tick, sized to be hit with a glove */
body.ctrl-tick input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input){
  appearance:none;-webkit-appearance:none;flex:none;margin:0;cursor:pointer;position:relative;
  width:1.5rem;height:1.5rem;border-radius:6px;border:2px solid var(--line2);background:var(--panel);
  transition:background .12s ease,border-color .12s ease;vertical-align:middle}
body.ctrl-tick input[type="checkbox"]:checked:not(.qmulti input){background:var(--ok);border-color:var(--ok)}
body.ctrl-tick input[type="checkbox"]:checked:not(.qmulti input)::after{
  content:'';position:absolute;left:.42rem;top:.16rem;width:.32rem;height:.68rem;
  border:solid #fff;border-width:0 3px 3px 0;transform:rotate(45deg)}

/* CHUNKY — big square, for gloves and bright sun */
body.ctrl-chunky input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input){
  appearance:none;-webkit-appearance:none;flex:none;margin:0;cursor:pointer;position:relative;
  width:2.4rem;height:2.4rem;border-radius:10px;border:2px solid var(--line2);background:var(--panel);
  transition:background .12s ease,border-color .12s ease;vertical-align:middle}
body.ctrl-chunky input[type="checkbox"]:checked:not(.qmulti input){background:var(--ok);border-color:var(--ok)}
body.ctrl-chunky input[type="checkbox"]:checked:not(.qmulti input)::after{
  content:'';position:absolute;left:.78rem;top:.34rem;width:.5rem;height:1.05rem;
  border:solid #fff;border-width:0 4px 4px 0;transform:rotate(45deg)}
body.ctrl-chunky label.chk,body.ctrl-chunky label.fb-chk{gap:.7rem;font-size:1rem}

body.switches input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input){
  appearance:none;-webkit-appearance:none;flex:none;margin:0;position:relative;cursor:pointer;
  width:2.5rem;height:1.4rem;border-radius:999px;border:1px solid rgba(0,0,0,.12);
  background:var(--sw-off);transition:background .18s ease;vertical-align:middle}
body.switches input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input)::after{
  content:'';position:absolute;top:50%;left:3px;transform:translateY(-50%);
  width:1.05rem;height:1.05rem;border-radius:50%;background:#fff;
  box-shadow:0 1px 3px rgba(0,0,0,.35);transition:left .18s ease}
body.switches input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input):checked{background:var(--sw-on)}
body.switches input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input):checked::after{left:calc(100% - 1.05rem - 3px)}
/* ON / OFF written on the switch, so there is no reading of colour required — which also covers
   anyone who cannot tell the red from the green. The knob is ::after, so the word is ::before, and
   it sits on whichever side the knob is not. */
body.switches.sw-labels input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input){width:3.5rem}
body.switches.sw-labels input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input)::before{
  content:'OFF';position:absolute;top:50%;right:.4rem;transform:translateY(-50%);
  font-size:.56rem;font-weight:800;letter-spacing:.06em;color:#fff;line-height:1;
  font-family:var(--font);text-shadow:0 1px 1px rgba(0,0,0,.25)}
body.switches.sw-labels input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input):checked::before{
  content:'ON';right:auto;left:.52rem}
body.switches input[type="checkbox"]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
body.switches input[type="checkbox"]:disabled{opacity:.5;cursor:default}
body.switches label.chk,body.switches label.fb-chk{gap:.6rem;align-items:center}
/* switch colours are a preference — red/green reads as fault/ok on plant, but a wall of red
   switches for ordinary options bothers some people, so grey-off is offered too */
:root{--sw-off:#d95757;--sw-on:#21a557}
:root[data-theme="dark"]{--sw-off:#b34141;--sw-on:#1d8f4c}
body.sw-greygreen{--sw-off:#b9c0cd;--sw-on:#21a557}
body.sw-greyblue{--sw-off:#b9c0cd;--sw-on:#2563eb}
body.sw-redblue{--sw-off:#d95757;--sw-on:#2563eb}
body.sw-amber{--sw-off:#b9c0cd;--sw-on:#d97706}
/* a quieter switch: no colour shouting, just on or off */
body.sw-ios{--sw-off:#d5d9e0;--sw-on:#34c759}
body.sw-ios input[type="checkbox"]:not(.qmulti input){border-color:transparent}
/* a bigger switch, same idea, sized for a gloved thumb */
body.switches.sw-big input[type="checkbox"]:not(.qmulti input):not(.alert-cb):not(.tsel-all input){width:4rem;height:2rem}
body.switches.sw-big input[type="checkbox"]:not(.qmulti input)::after{width:1.55rem;height:1.55rem}
body.switches.sw-big.sw-labels input[type="checkbox"]:not(.qmulti input)::before{font-size:.68rem}
:root[data-theme="dark"] body.sw-greygreen,:root[data-theme="dark"] body.sw-greyblue,:root[data-theme="dark"] body.sw-amber{--sw-off:#4a5568}
.swatch{display:inline-block;width:1.5rem;height:.85rem;border-radius:999px;vertical-align:middle;margin-right:.35rem}
/* printable QR label sheet */
.labelsheet{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:.5rem}
.label{display:flex;align-items:center;gap:.55rem;border:1px dashed var(--line2);border-radius:8px;padding:.5rem;background:#fff}
.label .lqr{width:66px;height:66px;flex:none;display:block}
.label .ltext{min-width:0;color:#0f1628}
.label .lref{font-weight:800;font-size:.86rem;line-height:1.15;word-break:break-word}
.label .lname{font-size:.72rem;color:#6b7488;line-height:1.2;margin-top:.1rem}
.label .lcode{font-size:.68rem;color:#6b7488;letter-spacing:.06em;margin-top:.2rem}
@media print{
  body.label-print>*:not(.qr-overlay){display:none !important}
  body.label-print .qr-overlay{position:static;background:none;padding:0;display:block}
  body.label-print .qr-card{max-width:none;max-height:none;overflow:visible;box-shadow:none;border:none;padding:0}
  body.label-print .noprint{display:none !important}
  body.label-print .labelsheet{grid-template-columns:repeat(3,1fr);gap:3mm}
  body.label-print .label{break-inside:avoid;border:1px dashed #bbb}
}
.archbanner{display:flex;align-items:flex-start;gap:.7rem;max-width:840px;margin:0 0 1rem;padding:.75rem .9rem;
  border:1px solid var(--line);border-left:3px solid var(--mut);border-radius:var(--radius-s);background:var(--panel2)}
.archbanner b{font-family:var(--font-head);font-size:.95rem}
.archbanner .s{font-size:.8rem;color:var(--mut)}
.keepbox{border:1px solid var(--line);border-left:3px solid var(--ok);border-radius:10px;padding:.6rem .75rem;background:var(--panel2)}
.showarch{display:inline-flex;margin:.2rem 0 .8rem;font-size:.85rem}
.meter-row{display:flex;align-items:center;gap:.5rem;margin-top:.4rem}
.meter-row input{flex:1;min-width:0}
.meter-unit{font-weight:800;color:var(--mut);font-size:.85rem;flex:none}
.fb-qunit{max-width:110px}
/* Plain bar across the foot of the screen, in the same place and shape as the offline banner, so
   it reads as part of the app rather than a notification bubble. It stays until it is acted on. */
#updBanner{position:fixed;left:0;right:0;bottom:0;z-index:400;display:flex;align-items:center;gap:.7rem;
  background:var(--brand);color:#fff;padding:.55rem .9rem;font-size:.84rem;font-weight:600;
  box-shadow:0 -4px 14px -6px rgba(0,0,0,.35);padding-bottom:calc(.55rem + env(safe-area-inset-bottom))}
#updBanner .ub-t{flex:1;min-width:0}
#updBanner .ub-go{flex:none;border:none;border-radius:7px;background:#fff;color:var(--brand);
  font:inherit;font-weight:800;padding:.35rem .9rem;cursor:pointer}
#updBanner .ub-x{flex:none;border:none;background:transparent;color:rgba(255,255,255,.65);
  font:inherit;font-size:1rem;line-height:1;padding:.25rem .35rem;cursor:pointer}
#updBanner .ub-x:hover{color:#fff}
#updBanner.working{background:var(--accent)}
.ub-spin{display:inline-block;width:.8rem;height:.8rem;margin-right:.4rem;vertical-align:-1px;
  border:2px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:ubspin .7s linear infinite}
@keyframes ubspin{to{transform:rotate(360deg)}}
body.sitemode-on #updBanner{bottom:38px}
body.has-updbanner #offlineBanner{bottom:44px}
@media print{ #updBanner{display:none} }
/* base-map toggle, sitting on the map rather than beside it */
.mapbase{position:absolute;right:.5rem;top:.5rem;z-index:500;display:flex;gap:2px;
  background:var(--panel);border:1px solid var(--line2);border-radius:9px;padding:2px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
.mapbase button{border:none;background:transparent;font:inherit;font-size:.74rem;font-weight:700;color:var(--ink2);
  padding:.32rem .5rem;border-radius:7px;cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent}
.mapbase button:hover{background:var(--panel2)}
.mapbase button.on{background:var(--accent);color:#fff}
.leaflet-canvas{position:relative}
@media(max-width:640px){ .mapbase button{font-size:.7rem;padding:.3rem .4rem} }
/* our own date picker — the native one behaves differently on every device and traps people in the
   month/year spinner on a phone */
.dp-input{cursor:pointer}
.dp-input::-webkit-calendar-picker-indicator{display:none;-webkit-appearance:none}
.dp-overlay{z-index:1400}
.dp-card{background:var(--panel);border:1px solid var(--line2);border-radius:var(--radius);box-shadow:var(--shadow);
  padding:.8rem;width:320px;max-width:94vw}
.dp-head{display:flex;align-items:center;gap:.35rem;margin-bottom:.6rem}
.dp-head select{flex:1;min-width:0;padding:.4rem .3rem;font-size:.9rem;font-weight:700}
.dp-nav{width:2.1rem;height:2.1rem;flex:none;border:1px solid var(--line2);background:var(--panel2);border-radius:9px;
  font-size:1.1rem;line-height:1;cursor:pointer;color:var(--ink);-webkit-tap-highlight-color:transparent}
.dp-nav:hover{border-color:var(--accent);color:var(--accent)}
.dp-dow{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;margin-bottom:.2rem}
.dp-dow span{text-align:center;font-size:.68rem;font-weight:800;color:var(--mut);text-transform:uppercase}
.dp-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.dp-pad{display:block}
.dp-day{aspect-ratio:1;border:none;background:transparent;border-radius:9px;font:inherit;font-size:.9rem;font-weight:600;
  color:var(--ink);cursor:pointer;-webkit-tap-highlight-color:transparent}
.dp-day:hover:not(:disabled){background:var(--accent-soft);color:var(--accent)}
.dp-day.istoday{box-shadow:inset 0 0 0 1.5px var(--accent);color:var(--accent);font-weight:800}
.dp-day.sel{background:var(--accent);color:#fff;font-weight:800}
.dp-day.off,.dp-day:disabled{color:var(--faint);cursor:default;opacity:.45}
.dp-quick{display:grid;grid-template-columns:repeat(4,1fr);gap:.3rem;margin-top:.7rem}
.dp-quick button{padding:.4rem .2rem;border:1px solid var(--line2);background:var(--panel2);border-radius:8px;
  font:inherit;font-size:.74rem;font-weight:700;color:var(--ink2);cursor:pointer;-webkit-tap-highlight-color:transparent}
.dp-quick button:hover{border-color:var(--accent);color:var(--accent)}
.dp-foot{display:flex;justify-content:space-between;gap:.5rem;margin-top:.7rem}
@media(max-width:640px){ .dp-card{width:100%} .dp-day{font-size:1rem} }
/* while placing the pin, the cursor says so */
.leaflet-canvas.pinning{cursor:crosshair}
.leaflet-canvas.pinning .leaflet-container{cursor:crosshair}
.seg.toast-seg{display:grid;grid-template-columns:repeat(auto-fit,minmax(78px,1fr));gap:.4rem}
.seg.toast-seg button{padding:.45rem .3rem;line-height:1.25}
.seg.toast-seg button small{font-weight:600;opacity:.7;font-size:.68rem}
.seg.toast-seg button.on{background:var(--accent);border-color:var(--accent);color:#fff}
.seg.jsa-seg{display:grid;gap:.4rem}
.seg.jsa-seg button{padding:.5rem .3rem;line-height:1.25}
.seg.jsa-seg button small{font-weight:600;opacity:.7;font-size:.7rem}
.seg.jsa-seg button.on{background:var(--accent);border-color:var(--accent);color:#fff}
.seg.risk-seg{display:grid;gap:.4rem}
.seg.risk-seg button.on{background:var(--accent);border-color:var(--accent);color:#fff}
.lrow.off{opacity:.6}
.seg.colour-seg{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.4rem}
.seg.colour-seg button{text-align:left;padding:.5rem .6rem;font-size:.82rem}
.seg.colour-seg button.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent);color:var(--accent)}
.seg.theme-seg,.seg.ctrl-seg{display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:.4rem}
.seg.theme-seg button.on,.seg.ctrl-seg button.on{background:var(--accent);border-color:var(--accent);color:#fff}
.seg.usage-seg{display:grid;gap:.4rem}
.seg.usage-seg button.on{background:var(--accent);border-color:var(--accent);color:#fff}
@media(max-width:640px){ .seg.usage-seg{grid-template-columns:1fr !important} }
/* the "how close to due" bar on register cards */
.ubar{height:6px;border-radius:99px;background:var(--line);overflow:hidden;margin:.55rem 0 .25rem}
.ubar span{display:block;height:100%;border-radius:99px;background:var(--ok);transition:width .2s ease}
.ubar.soon span{background:var(--soon)}
.ubar.overdue span{background:var(--bad)}
.ubar.overdue{background:var(--bad-bg)}
.ubar.none span{background:var(--faint)}
.ulab{display:flex;justify-content:space-between;gap:.5rem;font-size:.74rem;color:var(--mut);font-weight:600}
.ulab .bad{color:var(--bad);font-weight:800}
.ulab .warn{color:var(--soon);font-weight:800}
/* form-email preview — the real HTML, in an iframe, so what you see is what sends */
.empreview{border:1px solid var(--line);border-radius:var(--radius-s);overflow:hidden;background:#f4f6f9;height:520px}
.empreview iframe{width:100%;height:100%;border:none;display:block;background:#f4f6f9}
@media(max-width:640px){ .empreview{height:380px} }
/* timesheets — a week per screen, staff down the side, days across */
.tsbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin:0 0 .8rem}
.tsweek{padding:0 .3rem}
.tsweek b{font-family:var(--font-head);font-size:.98rem}
.tsweek .s{display:block;font-size:.75rem;color:var(--mut)}
.tswrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--radius-s);background:var(--panel)}
.tstable{border-collapse:collapse;width:100%;min-width:640px;font-size:.9rem}
.tstable th,.tstable td{padding:.5rem .55rem;text-align:right;border-bottom:1px solid var(--line);white-space:nowrap}
.tstable thead th{font-size:.72rem;letter-spacing:.04em;text-transform:uppercase;color:var(--mut);font-weight:800;background:var(--panel2)}
.tstable thead th span{display:block;font-size:.68rem;font-weight:600;letter-spacing:0;text-transform:none;color:var(--faint)}
.tstable .tsname{text-align:left;position:sticky;left:0;background:var(--panel);min-width:180px}
.tstable thead .tsname{background:var(--panel2)}
.tstable .tsname .s{font-size:.74rem;color:var(--mut);font-weight:400;white-space:normal}
.tstable tbody tr{cursor:pointer}
.tstable tbody tr:hover td{background:var(--panel2)}
.tstable tbody tr:hover .tsname{background:var(--panel2)}
.tstable tr.islocked .tsname{color:var(--mut)}
.tstable .istoday{background:var(--accent-soft)}
.tstable .tstot{font-variant-numeric:tabular-nums}
.tstime{display:block;font-size:.66rem;font-weight:600;color:var(--mut);letter-spacing:-.02em;margin-top:.1rem}
.tstable tfoot th{background:var(--panel2);font-size:.85rem;color:var(--ink);border-bottom:none}
.ts-day{padding:.3rem 0}
.ts-dayhead{font-size:.76rem;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--mut);margin:.5rem 0 .1rem}
@media print{ .tsbar,.side,.topbar,#offlineBanner,#siteBanner{display:none !important} .tswrap{border:none} .tstable{font-size:9pt} }
/* budgeted vs actual hours on a job — managers only */
.hoursbar{height:9px;border-radius:99px;background:var(--line);overflow:hidden;margin:.4rem 0 .25rem}
.hoursbar span{display:block;height:100%;background:var(--ok);border-radius:99px}
.hoursbar.warn span{background:var(--soon)} .hoursbar.over span{background:var(--bad)}
/* release history in the changelog modal */
.cl-rel{padding:.7rem 0;border-bottom:1px solid var(--line)}
.cl-rel:last-child{border-bottom:none}
.cl-relhead{display:flex;align-items:center;gap:.5rem}
.cl-relhead b{font-family:var(--font-head);font-size:1.05rem}
.cl-reldate{margin-left:auto;font-size:.78rem;color:var(--mut)}
.cl-reltitle{font-size:.88rem;color:var(--ink2);margin:.1rem 0 .3rem}
.cl-rel.latest .cl-relhead b{color:var(--accent)}
.cl-tag{font-size:.62rem;font-weight:800;letter-spacing:.06em;padding:.12rem .38rem;border-radius:5px;margin-right:.4rem;
  text-transform:uppercase;background:var(--panel2);color:var(--mut);border:1px solid var(--line);white-space:nowrap}
.cl-tag.new{background:var(--accent-soft);color:var(--accent);border-color:transparent}
.cl-tag.fix{background:var(--ok-bg);color:var(--ok);border-color:transparent}
.cl-tag.security{background:var(--bad-bg);color:var(--bad);border-color:transparent}
/* offline-pack status on the Inspections page — "is this phone ready for a job with no signal?" */
.packbar{display:flex;align-items:center;gap:.7rem;padding:.65rem .85rem;margin:0 0 .9rem;
  border:1px solid var(--line);border-left:3px solid var(--ok);border-radius:var(--radius-s);background:var(--panel2)}
.packbar.isoff{border-left-color:var(--soon)}
.packbar .pb-ico{font-size:1.2rem;flex:none}
.packbar b{font-family:var(--font-head);font-size:.94rem}
.packbar .s{font-size:.8rem;color:var(--mut)}
/* form tiles — an icon or picture per form, plus a colour band by type, so the right one is
   findable at a glance instead of by reading eighteen identical cards */
.ft-ico{width:52px;height:52px;border-radius:13px;display:grid;place-items:center;font-size:1.65rem;flex:none;
  background:var(--tone-bg,var(--panel2));border:1px solid var(--tone-line,var(--line));line-height:1}
.formtile{border-top:3px solid var(--tone,var(--line2))}
.formtile.tone-sun{--tone:#d97706;--tone-bg:#fdf0df;--tone-line:#f2d3a4}
.formtile.tone-sky{--tone:#2563eb;--tone-bg:#eaf1ff;--tone-line:#c5d8ff}
.formtile.tone-red{--tone:#dc2626;--tone-bg:#fdeaea;--tone-line:#f6c9c9}
.formtile.tone-amber{--tone:#b45309;--tone-bg:#fdf3e3;--tone-line:#f0d6ab}
.formtile.tone-indigo{--tone:#4f46e5;--tone-bg:#eceafe;--tone-line:#d3cdfa}
.formtile.tone-green{--tone:#16a34a;--tone-bg:#e7f6ec;--tone-line:#bfe6cc}
.formtile.tone-grey{--tone:#6b7488;--tone-bg:var(--panel2);--tone-line:var(--line)}
:root[data-theme="dark"] .formtile.tone-sun{--tone-bg:#33240f;--tone-line:#5c421c}
:root[data-theme="dark"] .formtile.tone-sky{--tone-bg:#152744;--tone-line:#26436f}
:root[data-theme="dark"] .formtile.tone-red{--tone-bg:#3a1a1a;--tone-line:#5f2a2a}
:root[data-theme="dark"] .formtile.tone-amber{--tone-bg:#352614;--tone-line:#5c4423}
:root[data-theme="dark"] .formtile.tone-indigo{--tone-bg:#231f45;--tone-line:#3a3370}
:root[data-theme="dark"] .formtile.tone-green{--tone-bg:#14301f;--tone-line:#245538}
/* a form with its own picture wears it as a banner, with the icon riding on the corner */
.formtile.haspic{padding-top:0}
.ft-pic{position:relative;margin:0 -1.1rem;height:118px;overflow:hidden;background:var(--panel2)}   /* bleeds to the .ecard padding */
.ft-pic img{width:100%;height:100%;object-fit:cover;display:block}
.ft-pic-ico{position:absolute;left:.7rem;bottom:.55rem;width:40px;height:40px;border-radius:11px;display:grid;place-items:center;
  font-size:1.25rem;background:var(--panel);border:1px solid var(--line);box-shadow:0 2px 8px rgba(0,0,0,.25)}
@media(max-width:640px){ .ft-ico{width:46px;height:46px;font-size:1.45rem} .ft-pic{height:96px} }
/* the icon picker in the form builder */
.fb-icon{display:flex;gap:.8rem;align-items:flex-start;margin:.2rem 0 .1rem}
.fb-icon-prev{width:74px;height:74px;border-radius:14px;border:1px solid var(--line);background:var(--panel2);
  display:grid;place-items:center;font-size:2.1rem;flex:none;overflow:hidden}
.fb-icon-prev img{width:100%;height:100%;object-fit:cover}
.fb-icon-grid{display:flex;flex-wrap:wrap;gap:.3rem;margin-bottom:.5rem}
.fb-ic{width:36px;height:36px;border-radius:9px;border:1px solid var(--line);background:var(--panel);cursor:pointer;
  font-size:1.15rem;line-height:1;padding:0;-webkit-tap-highlight-color:transparent}
.fb-ic:hover{border-color:var(--accent)}
.fb-ic.on{background:var(--accent-soft);border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.fb-icon-row{display:flex;gap:.45rem;align-items:center;flex-wrap:wrap}
@media(max-width:640px){ .fb-icon{flex-direction:column} .fb-icon-prev{width:62px;height:62px;font-size:1.8rem} }
/* collapsible sections on the job page — bigger, bolder headings that fold away */
details.jsec{border:1px solid var(--line);border-radius:var(--radius-s);background:var(--panel2);margin:.75rem 0;overflow:hidden}
details.jsec>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.6rem;padding:.72rem .85rem;
  font-family:var(--font-head);font-size:1.04rem;font-weight:800;letter-spacing:-.01em;color:var(--ink);-webkit-tap-highlight-color:transparent}
details.jsec>summary::-webkit-details-marker{display:none}
details.jsec>summary::marker{content:''}
details.jsec>summary:hover{background:var(--panel)}
details.jsec>summary:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
details.jsec[open]>summary{border-bottom:1px solid var(--line);background:var(--panel)}
.jsec-t{flex:none}
.jsec-badge{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap;font-family:var(--font);font-weight:600}
.jsec-warn{color:var(--bad);font-weight:800;font-size:.78rem;white-space:nowrap}
.jsec-chev{margin-left:auto;color:var(--mut);font-size:.85rem;transition:transform .16s ease;flex:none}
details.jsec[open]>summary .jsec-chev{transform:rotate(180deg)}
.jsec-body{padding:.7rem .85rem .95rem}
.jsec-body>.eyebrow:first-child{margin-top:.2rem}
.jsec-body>p:first-child{margin-top:0}
/* a nested section (the signed-off list inside the register) sits flush and quieter */
.jsec-body details.jsec{background:var(--panel);margin:.6rem 0 0}
.jsec-body details.jsec>summary{font-size:.92rem;padding:.55rem .7rem}
@media(max-width:640px){ details.jsec>summary{font-size:.98rem;padding:.65rem .7rem} .jsec-body{padding:.6rem .7rem .8rem} }
/* site register — sign-on buttons, muster list, induction box */
.regbar{display:flex;gap:.5rem;flex-wrap:wrap;margin:.1rem 0 .6rem}
.eyebrow.sub{opacity:.75;margin-top:.9rem}
.lrow.reg-row.off{opacity:.72}
.indbox{margin-top:.6rem;border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:10px;padding:.6rem .75rem;background:var(--panel2);max-height:150px;overflow:auto}
.seg.ind-dir{display:grid;grid-template-columns:1fr 1fr;gap:.4rem}
.seg.ind-dir button.on{background:var(--accent);border-color:var(--accent);color:#fff}
.musterlist{margin:.2rem 0 0;padding-left:1.4rem}
.musterlist li{padding:.4rem 0;border-bottom:1px solid var(--line);font-size:1.02rem}
.musterlist li:last-child{border-bottom:none}
.musterlist .s{font-size:.8rem;color:var(--mut);font-weight:400}
/* printing the muster prints the muster, not the app behind it */
@media print{
  body.muster-print>*:not(.qr-overlay){display:none !important}
  body.muster-print .qr-overlay{position:static;background:none;padding:0;display:block}
  body.muster-print .qr-card.muster{max-width:none;max-height:none;overflow:visible;box-shadow:none;border:none;background:#fff;color:#000}
  body.muster-print .qr-card.muster .btn{display:none}
  body.muster-print .musterlist li{break-inside:avoid}
}
/* asset that mirrors someone's company phone — say where it's really edited */
.assignedbox{margin-top:.9rem;border:1px solid var(--line);border-left:3px solid var(--soon);border-radius:10px;
  padding:.6rem .8rem;background:var(--panel2);font-size:.85rem;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
/* signature pad */
.sigwrap canvas{width:100%;height:150px;border:1px dashed var(--line2);border-radius:10px;background:#fff;touch-action:none;display:block}
.sigwrap .btn{margin-top:.4rem}

.toast{position:fixed;left:50%;transform:translateX(-50%);z-index:600;background:var(--brand);color:#fff;
  border-radius:12px;font-weight:600;box-shadow:0 6px 24px rgba(0,0,0,.35);max-width:92vw;
  display:flex;align-items:center;gap:.6rem;padding:.7rem .6rem .7rem 1.1rem;font-size:.9rem}
.toast[hidden]{display:none}
.toast .toast-msg{flex:1;min-width:0}
.toast .toast-x{flex:none;border:none;background:transparent;color:rgba(255,255,255,.6);font:inherit;
  font-size:.95rem;line-height:1;padding:.25rem .4rem;cursor:pointer;border-radius:6px}
.toast .toast-x:hover{color:#fff;background:rgba(255,255,255,.12)}
/* Full-width bar, the same shape as the offline banner, for anyone who wants a message they cannot
   miss rather than a pill floating in the corner. */
.toast.style-bar{left:0;right:0;transform:none;max-width:none;border-radius:0;
  padding:.7rem 1rem .7rem 1.2rem;box-shadow:0 -4px 14px -6px rgba(0,0,0,.35)}
.toast.style-bar.pos-top{top:0;bottom:auto;box-shadow:0 4px 14px -6px rgba(0,0,0,.35);
  padding-top:calc(.7rem + env(safe-area-inset-top))}
.toast.style-bar.pos-bottom{bottom:0;top:auto;padding-bottom:calc(.7rem + env(safe-area-inset-bottom))}
.toast.style-bar.pos-middle{top:50%;transform:translateY(-50%)}
/* a message that is waiting to be dismissed says so */
.toast.sticky{border-left:4px solid var(--soon)}
.toast.sticky .toast-x{color:#fff;background:rgba(255,255,255,.16)}
/* where it sits — clear of the offline and update bars at the foot */
.toast.pos-bottom{bottom:1.5rem;top:auto}
body.sitemode-on .toast.pos-bottom,body.has-updbanner .toast.pos-bottom{bottom:4rem}
.toast.pos-top{top:calc(1rem + env(safe-area-inset-top));bottom:auto}
.toast.pos-middle{top:50%;bottom:auto;transform:translate(-50%,-50%)}
/* how big */
.toast.size-sm{font-size:.8rem;padding:.5rem .5rem .5rem .85rem}
.toast.size-lg{font-size:1.05rem;padding:.95rem .8rem .95rem 1.3rem;font-weight:700}
@media(max-width:640px){ .toast{max-width:94vw} .toast.size-lg{font-size:1rem} }

/* ---- the side menu, put away ------------------------------------------------------------------
   Below 900px the sidebar is already off-canvas and the hamburger pops it out over the page. This
   is the same behaviour made available on a wide screen, because a planner grid or a long-section
   drawing wants the whole window and the menu is not being read while you look at one.

   Deliberately the same mechanism rather than a second one: body.nav-open already slides it in,
   already dims behind it, already closes on a nav click and on the scrim. All this adds is the
   state that says the menu is not pinned, so those rules apply above 900px too. */
@media(min-width:901px){
  body.nav-hidden .sidebar{
    position:fixed;left:0;top:0;transform:translateX(-100%);
    transition:transform .22s ease;box-shadow:0 10px 40px rgba(0,0,0,.25);
  }
  body.nav-hidden.nav-open .sidebar{transform:none}
  body.nav-hidden.nav-open .scrim{display:block}
  body.nav-hidden .hamburger{display:block}
  /* the same control both ways: put away while pinned, pin again while popped out */
  body.nav-hidden .side-collapse{color:var(--accent2)}
}
.side-collapse{
  display:none;background:none;border:0;color:var(--faint);cursor:pointer;
  font-size:1rem;line-height:1;padding:.3rem .35rem;border-radius:7px;margin-left:auto;
}
.side-collapse:hover{background:var(--panel2);color:var(--ink)}
@media(min-width:901px){ .side-collapse{display:block} }
/* a pinned sidebar must not animate on load — the page would open with the menu sliding in every
   time, which reads as slowness rather than as polish */
.sidebar{transition:none}

/* responsive */
@media(max-width:900px){
  .sidebar{position:fixed;left:0;top:0;transform:translateX(-100%);transition:transform .22s ease;box-shadow:0 10px 40px rgba(0,0,0,.25)}
  body.nav-open .sidebar{transform:none}
  body.nav-open .scrim{display:block}
  .hamburger{display:block}
  .tenant{display:none}
  main{padding:1.1rem 1rem 4rem}
  .user-chip .uc-t{display:none}
  .ecard-fields{grid-template-columns:1fr 1fr}
}

/* ---- admin "View as" impersonation ---- */
.actas-bar{display:flex;align-items:center;gap:.55rem;flex-wrap:wrap;padding:.5rem .9rem;margin:.6rem 1.1rem 0;
  background:var(--soon-bg);border:1px solid var(--soon);border-radius:10px;font-size:.86rem;color:var(--ink)}
.actas-bar .ai{font-size:1rem}
.actas-bar .ar{color:var(--mut)}
.actas-bar .btn{margin-left:auto}
.viewas{position:relative}
.viewas .va-btn{display:inline-flex;align-items:center;gap:.3rem;background:var(--panel);border:1px solid var(--line);
  border-radius:9px;padding:.34rem .55rem;font:inherit;font-size:.82rem;cursor:pointer;color:var(--ink);white-space:nowrap}
.viewas.acting .va-btn{border-color:var(--soon);background:var(--soon-bg)}
.viewas .va-cur{font-weight:600;max-width:130px;overflow:hidden;text-overflow:ellipsis}
.viewas .va-menu{position:absolute;right:0;top:calc(100% + 6px);min-width:210px;background:var(--panel);
  border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);padding:.4rem;z-index:60}
.viewas .va-h{font-size:.68rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mut);padding:.4rem .5rem .2rem}
.viewas .va-opt{display:block;width:100%;text-align:left;background:none;border:0;border-radius:8px;padding:.45rem .55rem;
  font:inherit;font-size:.86rem;cursor:pointer;color:var(--ink)}
.viewas .va-opt:hover{background:var(--accent-soft)}
.viewas .va-admin{color:var(--accent);font-weight:600}
.viewas .va-sep{height:1px;background:var(--line);margin:.35rem .3rem}

/* ---- import preview table ---- */
.imp-table{border-collapse:collapse;width:100%;font-size:.84rem}
.imp-table th,.imp-table td{text-align:left;padding:.4rem .6rem;border-bottom:1px solid var(--line);white-space:nowrap}
.imp-table th{position:sticky;top:0;background:var(--panel);font-size:.72rem;text-transform:uppercase;letter-spacing:.05em;color:var(--mut);z-index:1}
.imp-table td:nth-child(3),.imp-table td:nth-child(4){white-space:normal}
.imp-insert td:nth-child(2){color:var(--ok);font-weight:600}
.imp-update td:nth-child(2){color:var(--soon);font-weight:600}
.imp-skip{opacity:.7}
.imp-skip td:nth-child(2){color:var(--bad);font-weight:600}
.linkperson{margin-top:.9rem;padding-top:.9rem;border-top:1px solid var(--line)}

/* ---- driver licence photos (landscape card ratio) ---- */
.lic-photos{display:flex;gap:1.1rem;flex-wrap:wrap;margin-top:.4rem}
.lic-photo{flex:1;min-width:210px}
.lic-photo .fl{margin-bottom:.3rem}
.lic-frame{width:100%;max-width:300px;aspect-ratio:1.586/1;display:grid;place-items:center;overflow:hidden}
.lic-frame img{width:100%;height:100%;object-fit:cover}
.lic-frame .ph{font-size:1.8rem;opacity:.5}

/* ---- driver licence classes: at-a-glance chips + editor ---- */
.dlchips{display:inline-flex;flex-wrap:wrap;gap:.25rem;align-items:center;vertical-align:middle}
.dlchip{display:inline-grid;place-items:center;min-width:1.5rem;height:1.4rem;padding:0 .35rem;border-radius:6px;
  font:600 .74rem/1 var(--mono);border:1px solid var(--line);background:var(--panel);color:var(--ink)}
.dlchip.ok{border-color:var(--ok);color:var(--ok)}
.dlchip.soon{border-color:var(--soon);background:var(--soon-bg);color:var(--soon)}
.dlchip.overdue{border-color:var(--bad);background:var(--bad-bg);color:var(--bad)}
.dlchip.none{opacity:.85}
.dlsep{color:var(--mut);font-weight:700;padding:0 .1rem}
.dl-current{margin-left:.5rem}
.lc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:.4rem .9rem;margin:.3rem 0 .5rem}
.lc-row{display:flex;flex-direction:column;gap:.3rem;padding:.35rem 0;border-bottom:1px solid var(--line)}
.lc-tick{display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.9rem}
.lc-tick input{width:auto}
.lc-tick b{font-family:var(--mono);min-width:1.1rem}
.lc-lbl{color:var(--mut);font-size:.84rem}
.lc-detail{display:flex;gap:.4rem;flex-wrap:wrap;padding-left:1.6rem}
.lc-detail select,.lc-detail input{padding:.3rem .4rem;font-size:.8rem}
.lc-detail input[type=date]{max-width:150px}

/* ---- training multi-select ---- */
.train-tools{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;padding:.4rem 0 .6rem;border-bottom:1px solid var(--line);margin-bottom:.4rem}
.train-tools .tsel-all{display:flex;align-items:center;gap:.4rem;font-size:.86rem;color:var(--mut);cursor:pointer}
.train-tools input{width:auto}
.tsel{width:auto;margin-right:.2rem;flex:none}

/* ---- per-course training config table ---- */
.cc-table{border-collapse:collapse;width:100%;min-width:640px;font-size:.84rem}
.cc-table th,.cc-table td{padding:.35rem .5rem;border-bottom:1px solid var(--line);text-align:left;vertical-align:middle}
.cc-table th{font-size:.7rem;text-transform:uppercase;letter-spacing:.04em;color:var(--mut);position:sticky;top:0;background:var(--panel)}
.cc-table input[type=number]{width:64px;padding:.25rem .35rem;font-size:.8rem}
.cc-table input[type=checkbox]{width:auto}
.cc-table td.s{color:var(--mut)}

/* ---- people categories ---- */
.cat-block{margin-bottom:1.3rem}
.cat-head{display:flex;align-items:center;gap:.6rem;margin:.2rem 0 .6rem;padding-bottom:.35rem;border-bottom:2px solid var(--line)}
.cat-name{font-family:var(--font-head);font-weight:800;font-size:1.02rem}
.cat-count{background:var(--accent-soft);color:var(--accent);border-radius:20px;padding:.05rem .5rem;font-size:.75rem;font-weight:700}
.cat-tools{margin-left:auto;display:flex;gap:.15rem}
.cat-tools .iconbtn:disabled{opacity:.3;cursor:default}

/* ---- job location map ---- */
.mapblock{margin:.3rem 0 .8rem}
.mapembed{margin-top:.5rem}
.mapembed iframe{width:100%;height:260px;border:1px solid var(--line);border-radius:10px}

/* ---- interactive job map (Leaflet) ---- */
.maprow{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin-bottom:.4rem}
.maphint{color:var(--mut);font-size:.8rem}
.leaflet-canvas{height:320px;border:1px solid var(--line);border-radius:12px;overflow:hidden;z-index:1}
.leaflet-canvas .leaflet-tile{filter:none}
/* geofence editor under the job map */
.fencebox{margin-top:.55rem;border:1px solid var(--line);border-left:3px solid #2563eb;border-radius:10px;padding:.65rem .8rem;background:var(--panel2)}
.fencebox .fb-head{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;font-size:.92rem}
.fencebox .fb-read{color:var(--mut);font-size:.8rem;font-family:var(--mono)}
.fencebox .fb-row{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;margin-top:.3rem}
.fencebox .fb-row .fl{margin:0;white-space:nowrap}
#j_fence_btn.on{background:var(--accent);color:#fff;border-color:transparent}
/* the drag-to-resize handle sitting on the circle's edge */
.fence-handle span{display:block;width:14px;height:14px;border-radius:50%;background:#fff;border:3px solid #2563eb;box-shadow:0 1px 4px rgba(0,0,0,.4);cursor:ew-resize}
/* the combo you're typing in must sit above later combos so its dropdown isn't covered */
.combo{position:relative}
.combo:focus-within{z-index:1000}
.combo-list{z-index:1200;max-height:280px;overflow-y:auto}

/* ---- form builder ---- */
.fb-sec{border:1px solid var(--line);border-radius:12px;padding:.7rem .8rem;margin-top:.7rem;background:var(--panel2)}
.fb-sechead{display:flex;align-items:center;gap:.35rem;margin-bottom:.5rem}
.fb-sectitle{flex:1;font-weight:700;font-family:var(--font-head)}
.fb-qs{display:flex;flex-direction:column;gap:.5rem}
.fb-q{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:.5rem .6rem}
.fb-qrow{display:flex;gap:.35rem;align-items:center;flex-wrap:wrap}
.fb-qrow .fb-qtext{flex:1;min-width:160px}
.fb-qrow .fb-qtype{width:130px;flex:none}
.fb-qopts{display:flex;gap:1rem;align-items:center;flex-wrap:wrap;margin-top:.4rem;font-size:.82rem;color:var(--mut)}
.fb-chk{display:flex;align-items:center;gap:.35rem;cursor:pointer}
.fb-chk input{width:auto}
.fb-inline{display:flex;align-items:center}
.fb-inline input{padding:.3rem .4rem;font-size:.82rem}

/* ---- multi-select question ---- */
.qmulti{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}
.mchk{display:flex;align-items:center;gap:.6rem;cursor:pointer;font-size:.92rem}
.mchk input{width:auto;flex:none}
.mchk.na-opt{padding-bottom:.55rem;border-bottom:1px dashed var(--line);margin-bottom:.15rem}

/* ---- form fill: use the screen better + stay tidy on mobile ---- */
.formfill{max-width:860px;width:100%}
.formfill .seg{grid-template-columns:repeat(3,minmax(0,1fr))}
.formfill .q{max-width:100%}
@media (max-width:520px){
  .seg{gap:.3rem}
  .seg button{padding:.5rem .3rem;font-size:.82rem}
}

/* ---- account menu (folds theme / view-as / logout) ---- */
.acct{position:relative}
.user-chip{background:none;border:none;cursor:pointer;padding:0;font:inherit;color:inherit}
.acct-menu{position:absolute;right:0;top:calc(100% + 8px);min-width:210px;max-width:calc(100vw - 1rem);background:var(--panel);border:1px solid var(--line);
  border-radius:12px;box-shadow:var(--shadow);padding:.4rem;z-index:1100}
.acct-menu .am-user{display:flex;align-items:center;gap:.55rem;padding:.4rem .5rem}
.acct-menu .am-user b{font-size:.86rem;display:block;line-height:1.15}
.acct-menu .am-user small{font-size:.7rem;color:var(--faint)}
.acct-menu .am-h{font-size:.66rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mut);padding:.35rem .5rem .15rem}
.acct-menu .am-item{display:block;width:100%;text-align:left;background:none;border:0;border-radius:8px;padding:.5rem .55rem;
  font:inherit;font-size:.88rem;color:var(--ink);cursor:pointer}
.acct-menu .am-item:hover{background:var(--accent-soft)}
.acct-menu .am-admin{color:var(--accent);font-weight:600}
.acct-menu .am-logout{color:var(--bad);font-weight:600}
.acct-menu .am-sep{height:1px;background:var(--line);margin:.35rem .3rem}

/* mobile: collapse right-side controls into the account menu so the page can't scroll sideways */
@media(max-width:900px){
  html,body{overflow-x:hidden}
  #themeBtn,#logoutBtn,#viewAs{display:none}
  #syncText{display:none}
  .sync{padding:.3rem;gap:0}
  .topbar{gap:.5rem;padding-left:1rem;padding-right:1rem}
  .crumb{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

  /* The account menu hangs off its own button — position:absolute with right:0, so its 210px extends
     to the LEFT. On a narrow screen that runs past the edge, and html/body carry overflow-x:hidden
     just above to stop the page scrolling sideways, so the overhang is not scrolled to, it is cut
     off. Half a menu, with no way to reach the rest of it.

     Anchored to the viewport instead of to the button on mobile: a sheet under the top bar, inset
     from both edges, so it cannot overhang whichever side the button happens to sit on. Being fixed
     also takes it out of the overflow rule's reach entirely. */
  .acct-menu{position:fixed;left:.5rem;right:.5rem;min-width:0;
    top:calc(env(safe-area-inset-top) + 3.4rem);
    max-height:min(70vh,32rem);overflow-y:auto;-webkit-overflow-scrolling:touch}
  /* A finger needs more room than a mouse, and this is now the only way to reach view-as, the theme
     and logging out on a phone — the top bar's own buttons are hidden five lines above. */
  .acct-menu .am-item{padding:.7rem .6rem;font-size:.95rem}
}

/* ---- permission groups editor ---- */
.capgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:.4rem .9rem;margin:.3rem 0 .4rem}
.cap{padding:.3rem 0}
.grouptile .avatar-tile{font-size:1.2rem}

/* ---- egress IP box ---- */
.ipbox{background:var(--panel2);border:1px solid var(--line);border-radius:9px;padding:.5rem .7rem;font-size:.86rem;margin:.3rem 0 .5rem;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.ipbox b{font-family:var(--mono)}

/* ---- dashboard reorderable blocks ---- */
.dashblock{margin-bottom:1.4rem}
.db-head{display:flex;align-items:center;gap:.5rem;margin:.2rem 0 .5rem}
.db-title{font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:var(--accent);font-weight:800;font-family:var(--font)}
.db-move{margin-left:auto;display:flex;gap:.15rem;opacity:.5}
.dashblock:hover .db-move{opacity:1}
.db-move .iconbtn:disabled{opacity:.3;cursor:default}

/* ---- dashboard collapse ---- */
.signon-row.off{opacity:.6}
/* signed on from outside the job's geofence — flagged so it reads at a glance */
.lrow.offsite{background:rgba(209,22,22,.07);border-left:3px solid #d11616;padding-left:.5rem}
.signon-row .acts{display:flex;align-items:center;gap:.3rem;margin-left:auto}
/* declaration box (above the signature) */
.declaration { background: rgba(37, 194, 110, .08); border: 1px solid var(--line); border-left: 3px solid var(--ok, #25c26e); border-radius: 8px; padding: .6rem .8rem; font-size: .86rem; font-style: italic; margin: .3rem 0; }
.pdf-view .declaration { background: #f5faf6; color: #222; }
/* offline / syncing banner */
#offlineBanner { position: fixed; left: 0; right: 0; bottom: 0; z-index: 300; padding: 9px 14px; text-align: center; font-size: .82rem; font-weight: 600; box-shadow: 0 -4px 14px -6px rgba(0, 0, 0, .3); }
/* site mode bar — sits under the offline banner so both can show at once */
#siteBanner { position: fixed; left: 0; right: 0; bottom: 0; z-index: 299; display: flex; gap: .7rem; align-items: center; justify-content: center; flex-wrap: wrap;
  padding: 8px 14px; font-size: .82rem; font-weight: 600; background: #1f2937; color: #fff; box-shadow: 0 -4px 14px -6px rgba(0, 0, 0, .35); }
#siteBanner.on { background: #15803d; }
#siteBanner.warn { background: #b45309; }
#siteBanner .sb-acts { display: flex; gap: .4rem; }
#siteBanner .sb-btn { background: rgba(255, 255, 255, .18); color: #fff; border: 1px solid rgba(255, 255, 255, .35); border-radius: 6px; padding: 3px 10px; font: inherit; font-size: .78rem; cursor: pointer; }
#siteBanner .sb-btn:hover { background: rgba(255, 255, 255, .3); }
#siteBanner .sb-btn.ghost { background: transparent; }
body.sitemode-on #offlineBanner { bottom: 38px; }
#offlineBanner.offline { background: #b45309; color: #fff; }
#offlineBanner.pending { background: #2563eb; color: #fff; }
/* ---- print / Save-as-PDF (completed forms) ---- */
.print-head { display: none; }
@media print { #offlineBanner, #siteBanner { display: none !important; } .declaration { border: 1px solid #ccc; border-left: 3px solid #333; background: #f7f7f7 !important; font-size: .78rem; } }
.pdf-preview { background: #fff; color: #111; border: 1px solid var(--line); border-radius: 8px; padding: 16px 18px; margin-top: .3rem; box-shadow: 0 8px 22px -12px rgba(0, 0, 0, .35); max-width: 660px; overflow: hidden; }
.pdf-preview b { color: #000; }
/* completed-form view rendered like the PDF (white "paper", on screen, both themes) */
/* The card is paper, so everything on it must be light — re-assert the light tokens here or the
   rows inside keep painting themselves --panel (dark navy) under the near-black answer text.
   Keep in step with the light :root block at the top and with the @media print copy below. */
.formcard.pdf-view {
  --bg:#f4f6f9; --panel:#ffffff; --panel2:#f7f9fc; --line:#e7eaf0; --line2:#dde2ec;
  --ink:#0f1628; --ink2:#38415a; --mut:#6b7488; --faint:#9aa2b4;
  --brand:#0e1626; --brand2:#182338;
  --accent:#2563eb; --accent2:#1d4ed8; --accent-soft:#eaf1ff; --indigo:#4f46e5; --indigo-soft:#eceafe;
  --ok:#16a34a; --ok-bg:#e7f6ec; --soon:#d97706; --soon-bg:#fdf0df; --bad:#dc2626; --bad-bg:#fdeaea;
  --shadow:0 1px 2px rgba(15,22,40,.04),0 4px 16px rgba(15,22,40,.05);
  --shadow-s:0 1px 2px rgba(15,22,40,.05);
  background: #fff; color: #111; font-family: var(--pdf-font, 'Inter', system-ui, sans-serif);
}
.pdf-view .ecard-fields { display: none; }
.pdf-view .print-head { display: flex; align-items: center; gap: 16px; border-bottom: 2.5px solid #222; padding-bottom: 10px; margin-bottom: 12px; }
.pdf-view .print-head.pos-center { flex-direction: column; align-items: center; text-align: center; }
.pdf-view .print-head.pos-center .print-meta { margin-left: 0; text-align: center; }
.pdf-view .print-head.pos-right .print-logo { order: 3; margin-left: 12px; }
.pdf-view .print-logo { max-height: var(--pdf-logo, 64px); max-width: 220px; }
.pdf-view .print-org-name { font-family: var(--font-head); font-weight: 800; font-size: calc(1.4rem * var(--pdf-org-scale, 1)); display: var(--pdf-org-display, block); }
.pdf-view .print-title { font-size: calc(.95rem * var(--pdf-title-scale, 1)); color: #444; }
/* signatures sized from one setting: register thumbs, the signer's and the approver's */
.pdf-view #soList .thumb { width: var(--pdf-sig-w, 91px) !important; height: var(--pdf-sig-h, 38px) !important; }
.pdf-view .thumb img { object-fit: contain; background: #fff; }
.pdf-view .sig-img { max-height: calc(var(--pdf-sig-h, 38px) * 1.9); max-width: 300px; }
.pdf-view .print-meta { margin-left: auto; text-align: right; font-size: .84rem; color: #333; line-height: 1.5; }
.pdf-view .print-meta b { color: #000; }
.pdf-view small, .pdf-view .muted { color: #555 !important; }
.pdf-view .eyebrow { color: var(--pdf-sec-color, #b45309) !important; font-weight: var(--pdf-sec-weight, 800); font-size: calc(.74rem * var(--pdf-sec-scale, 1)); margin-top: 1rem; }
.pdf-view .list-plain { display: grid; grid-template-columns: repeat(var(--pdf-cols, 2), 1fr); gap: 2px 20px; align-items: start; }
.pdf-view #soList { grid-template-columns: 1fr; }
.pdf-view .lrow { border: none; }
.pdf-view .lrow .t { color: var(--pdf-q-color, #111); font-size: calc(.92rem * var(--pdf-q-scale, 1)); }
.pdf-view .lrow .s { color: var(--pdf-a-color, #111); }
.pdf-view .lrow .s b { font-weight: var(--pdf-a-weight, 800); color: var(--pdf-a-color, #111); background: var(--pdf-a-bg, transparent); padding: var(--pdf-a-pad, 0); border-radius: 3px; }
.pdf-view .lrow.fail .t, .pdf-view .lrow.fail .s b { color: var(--pdf-fail-color, #d11616); }
.pdf-view .lrow.fail { background: var(--pdf-fail-bg, transparent); border-radius: 4px; padding: 2px 6px; }
.pdf-view.rs-table .lrow { border-bottom: 1px solid #ddd; padding: 3px 4px; }
.pdf-view.rs-table .lrow .main { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: baseline; }
.pdf-view.rs-table .lrow .s { text-align: right; }
.pdf-view.rs-boxed .lrow { border: 1px solid #ddd; border-radius: 6px; padding: 4px 8px; }
@media (max-width: 640px) {
  .pdf-view .list-plain { grid-template-columns: 1fr; }
  /* header stacks on mobile: logo + org on top, details below in a readable 2-col grid */
  .pdf-view .print-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .pdf-view .print-head.pos-center { align-items: center; }
  .pdf-view .print-head .print-meta { margin-left: 0; text-align: left; width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 3px 14px; font-size: .82rem; }
  .pdf-view .print-head.pos-center .print-meta { text-align: left; }
  .pdf-view .print-org-name { font-size: 1.25rem; }
  /* sign-on register → each person a tile with a big, readable signature */
  .signon-row { flex-direction: column; align-items: stretch; border: 1px solid var(--line); border-radius: 8px; padding: 8px !important; margin-bottom: 8px; }
  .signon-row .thumb { width: 100% !important; height: 74px !important; }
  .signon-row .thumb img { width: 100%; height: 100%; object-fit: contain; }
  .signon-row .acts { margin-left: 0; margin-top: 8px; justify-content: flex-start; }
}
@media print {
  @page { size: A4; margin: 8mm; }
  /* Paper is white whatever the screen theme was. Without this the dark tokens survive into the
     print job and anything not explicitly recoloured below (org name, headings, the declaration,
     the signer's name) prints near-white on white. Mirrors .formcard.pdf-view above.
     The two theme selectors are repeated because @media adds no specificity — a bare :root here
     would lose to :root[data-theme="dark"] and the print job would stay dark. */
  :root, :root[data-theme="dark"], :root:not([data-theme="light"]) {
    --bg:#f4f6f9; --panel:#ffffff; --panel2:#f7f9fc; --line:#e7eaf0; --line2:#dde2ec;
    --ink:#0f1628; --ink2:#38415a; --mut:#6b7488; --faint:#9aa2b4;
    --brand:#0e1626; --brand2:#182338;
    --accent:#2563eb; --accent2:#1d4ed8; --accent-soft:#eaf1ff; --indigo:#4f46e5; --indigo-soft:#eceafe;
    --ok:#16a34a; --ok-bg:#e7f6ec; --soon:#d97706; --soon-bg:#fdf0df; --bad:#dc2626; --bad-bg:#fdeaea;
  }
  html, body { color: var(--ink) !important; }
  .sidebar, .topbar, .scrim, .toast, #menuBtn, .btn, .iconbtn, .review-row, .acts, .db-caret, .db-move, .maprow, .sync, .ver-badge, .detail-head { display: none !important; }
  html, body { background: #fff !important; font-size: var(--pdf-fs, 9px) !important; }
  .main { margin: 0 !important; padding: 0 !important; }
  #app { max-width: 100% !important; }
  .formcard, .formfill { box-shadow: none !important; border: none !important; max-width: 100% !important; margin: 0 !important; padding: 0 !important; }
  /* branded header (screen-hidden, print-only) */
  .formfill, .print-head, .print-head *, .list-plain, .lrow, .qt { font-family: var(--pdf-font, 'Inter', system-ui, sans-serif) !important; }
  .print-head { display: flex !important; align-items: center; gap: 16px; border-bottom: 2.5px solid #222; padding-bottom: 9px; margin-bottom: 11px; }
  .print-head.pos-center { flex-direction: column !important; align-items: center !important; text-align: center; }
  .print-head.pos-center .print-meta { margin-left: 0 !important; text-align: center; }
  .print-head.pos-right .print-logo { order: 3; margin-left: 12px; }
  .print-logo { max-height: var(--pdf-logo, 64px); max-width: 220px; }
  .print-org-name { font-family: var(--font-head); font-weight: 800; font-size: calc(1.5rem * var(--pdf-org-scale, 1)); line-height: 1.12; display: var(--pdf-org-display, block); }
  .print-title { font-size: calc(1.05rem * var(--pdf-title-scale, 1)); color: #333; margin-top: 1px; }
  .print-meta { margin-left: auto; text-align: right; font-size: .82rem; color: #222; line-height: 1.5; }
  .print-meta b { color: #000; }
  h2, h3 { font-size: .9rem !important; margin: .12rem 0 !important; }
  .eyebrow { margin: .3rem 0 .1rem !important; font-size: calc(.64rem * var(--pdf-sec-scale, 1)) !important; break-after: avoid; color: var(--pdf-sec-color, #b45309) !important; font-weight: var(--pdf-sec-weight, 800) !important; }
  .ecard-fields { display: none !important; }
  /* ANSWERS AS TILES — 2-column grid so a typical form fits one page (big crew lists overflow) */
  .list-plain { display: grid !important; grid-template-columns: repeat(var(--pdf-cols, 2), 1fr); gap: 1px 16px; align-items: start; margin: 0 !important; }
  .lrow, .signon-row { break-inside: avoid; margin: 0 !important; padding: .1rem 0 !important; border: none !important; display: flex !important; }
  .lrow .main { min-width: 0; }
  .qt, .lrow .t { font-size: calc(.78rem * var(--pdf-q-scale, 1)) !important; line-height: 1.22 !important; margin: 0 !important; color: var(--pdf-q-color, #111) !important; }
  .lrow .s { font-size: .74rem !important; line-height: 1.22 !important; color: var(--pdf-a-color, #111) !important; }
  .lrow .s b { font-weight: var(--pdf-a-weight, 800) !important; color: var(--pdf-a-color, #111) !important; background: var(--pdf-a-bg, transparent) !important; padding: var(--pdf-a-pad, 0) !important; border-radius: 3px; }
  .lrow.fail .s b { color: var(--pdf-fail-color, #d11616) !important; }
  .lrow.fail .t { color: var(--pdf-fail-color, #d11616) !important; }
  .lrow.fail { background: var(--pdf-fail-bg, transparent) !important; border-radius: 3px; padding-left: 4px !important; padding-right: 4px !important; }
  /* row layouts: table = Q left / A right with a rule; boxed = each Q/A in a border */
  .rs-table .lrow { border-bottom: 1px solid #bbb; padding: .18rem 0 !important; }
  .rs-table .lrow .main { display: grid !important; grid-template-columns: 1fr auto; gap: 3px 10px; align-items: baseline; }
  .rs-table .lrow .s { text-align: right; white-space: nowrap; }
  .rs-boxed .lrow { border: 1px solid #ccc; border-radius: 4px; padding: .2rem .4rem !important; }
  .dot { width: 5px !important; height: 5px !important; margin-top: 3px; }
  /* keep the sign-on register full-width (single column) so signatures line up */
  #soList { display: block !important; }
  #soList .signon-row { margin: .12rem 0 !important; }
  /* signatures are the point of a signed form — size them from the PDF setting, never squash them */
  .thumb { width: var(--pdf-sig-w, 91px) !important; height: var(--pdf-sig-h, 38px) !important; border: 1px solid #ddd !important; }
  .thumb img, .signon-row img { max-height: var(--pdf-sig-h, 38px) !important; width: 100% !important; height: 100% !important; object-fit: contain !important; background: #fff !important; }
  #soList .signon-row { align-items: center; gap: 8px; }
  .sig-img { max-height: calc(var(--pdf-sig-h, 38px) * 1.9) !important; max-width: 300px !important; }
  .formfill > img { max-height: calc(var(--pdf-sig-h, 38px) * 1.9) !important; max-width: 220px !important; }
  a[href]::after { content: '' !important; }
}
.db-caret{background:none;border:none;cursor:pointer;color:var(--mut);font-size:1.35rem;line-height:1;width:40px;height:40px;min-width:40px;display:inline-flex;align-items:center;justify-content:center;border-radius:9px;padding:0;transition:background .12s,color .12s}
.db-caret:hover{color:var(--ink);background:var(--soft,rgba(120,120,140,.12))}
.dashblock.collapsed{margin-bottom:.5rem}

/* ---- version badge + changelog ---- */
.side-brand{position:relative}
.ver-badge{margin-left:auto;background:var(--accent-soft);color:var(--accent2);border:none;border-radius:20px;
  padding:.15rem .5rem;font-size:.66rem;font-weight:800;cursor:pointer;font-family:var(--mono);letter-spacing:.02em}
.ver-badge:hover{background:var(--accent);color:#fff}
.changelog .cl-list{margin:.2rem 0 .2rem 1.1rem;padding:0;font-size:.86rem;color:var(--ink2);line-height:1.5}
.changelog .cl-list li{margin:.1rem 0}

/* ---- compliance alert actions ---- */
.alert-row .alert-acts{display:flex;gap:.1rem;margin-left:.4rem;opacity:.55}
.alert-row:hover .alert-acts{opacity:1}
.alert-row.is-muted{opacity:.6}
.ack-toggle{padding:.5rem 0 .2rem;text-align:center}
.resp-row:hover{background:var(--panel2)}

/* ---- compliance alert word-buttons + bulk ---- */
.alert-row .alert-cb{width:auto;flex:none;margin-right:.1rem}
.alert-acts{display:flex;gap:.3rem;margin-left:.5rem;flex-wrap:wrap}
.ab{border:1px solid var(--line2);background:var(--panel);border-radius:7px;padding:.28rem .55rem;font-size:.76rem;font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap}
.ab:disabled{opacity:.4;cursor:default}
.ab-ack{color:var(--ok);border-color:var(--ok)} .ab-ack:hover:not(:disabled){background:var(--ok-bg)}
.ab-snooze{color:var(--soon);border-color:var(--soon)} .ab-snooze:hover:not(:disabled){background:var(--soon-bg)}
.ab-book{color:var(--accent);border-color:var(--accent)} .ab-book:hover:not(:disabled){background:var(--accent-soft)}
.ab-undo{color:var(--mut)}
.alert-bulk{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;padding:.5rem .1rem;border-bottom:1px solid var(--line);margin-bottom:.3rem}
.alert-bulk .tsel-all{display:flex;align-items:center;gap:.4rem;font-size:.85rem;color:var(--mut);cursor:pointer}
.alert-bulk input{width:auto}

/* ---- inspection review row ---- */
.review-row{margin-top:1rem;padding-top:.8rem;border-top:1px solid var(--line);display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}

/* ---- date/time quick-fill row ---- */
.dt-row{display:flex;gap:.5rem;align-items:center;margin-top:.4rem}
.dt-row input{flex:0 0 auto}
.dt-now{white-space:nowrap}

/* ---- multi-person chips ---- */
.qpeople .pchips{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.4rem}
.pchip{display:inline-flex;align-items:center;gap:.35rem;background:var(--accent-soft);color:var(--accent2);
  border-radius:20px;padding:.25rem .3rem .25rem .6rem;font-size:.84rem;font-weight:600}
.pchip-x{background:none;border:none;color:var(--accent2);cursor:pointer;font-size:.8rem;padding:0 .2rem;line-height:1}
.pchip-x:hover{color:var(--bad)}

/* ---- photo upload question + response photos ---- */
.qphoto .ph-thumbs{display:flex;flex-wrap:wrap;gap:.5rem}
.ph-thumb{position:relative;width:88px;height:88px;border-radius:8px;overflow:hidden;border:1px solid var(--line)}
.ph-thumb img{width:100%;height:100%;object-fit:cover}
.ph-del{position:absolute;top:2px;right:2px;background:rgba(0,0,0,.55);color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;font-size:.7rem;line-height:1}
/* photo labels — a shot of a manhole is worth far more to the office marked MH-1 */
.ph-thumb{height:auto;padding-bottom:0}
.ph-thumb img{height:88px}
.ph-cap{position:absolute;left:0;right:0;bottom:0;border:none;background:rgba(15,22,40,.82);color:#fff;
  font:inherit;font-size:.66rem;font-weight:700;padding:.2rem .25rem;cursor:pointer;text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-tap-highlight-color:transparent}
.ph-thumb.labelled .ph-cap{background:var(--accent);color:#fff}
.cap-preview{border:1px solid var(--line);border-radius:10px;overflow:hidden;max-height:180px;display:grid;place-items:center;background:var(--panel2)}
.cap-preview img{max-width:100%;max-height:180px;object-fit:contain;display:block}
.resp-photos{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.4rem}
.resp-photos img{width:120px;height:120px;object-fit:cover;border-radius:8px;border:1px solid var(--line);display:block}
.resp-photos .rp{position:relative;display:inline-block;text-decoration:none}
.rp-cap{position:absolute;left:0;right:0;bottom:0;background:rgba(15,22,40,.82);color:#fff;font-size:.68rem;
  font-weight:700;padding:.18rem .3rem;text-align:center;border-radius:0 0 8px 8px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media print{ .rp-cap{background:none;color:#000;position:static;padding:.1rem 0;font-size:8px} .resp-photos img{width:90px;height:90px} }

/* ---- mobile: stop compliance/expiring rows squishing ---- */
@media(max-width:560px){
  .lrow{flex-wrap:wrap}
  .lrow .rt{width:100%;text-align:left;margin-top:.2rem;padding-left:1.4rem}
  .alert-row .alert-acts{margin-left:1.4rem;margin-top:.3rem}
  .statgrid{grid-template-columns:1fr 1fr;gap:.6rem}
  .stat{padding:.8rem .9rem}
  .stat .v{font-size:1.6rem}
}

/* ---- collapsible settings cards ---- */
.formcard.collapsed > :not(h3){display:none !important}
.formcard > h3{display:flex;align-items:center;gap:.55rem}
.formcard > h3 .card-caret{display:inline-grid;place-items:center;width:26px;height:26px;flex:none;
  background:var(--accent);color:#fff;border-radius:8px;font-size:.95rem;font-weight:900;line-height:1}
.formcard > h3:hover .card-caret{background:var(--accent2)}
.formcard.collapsed{padding-bottom:1rem;cursor:pointer}
.formcard.collapsed:hover{border-color:var(--accent)}

/* ---- form builder: notify recipient toggles ---- */
.fb-notify{display:flex;gap:1rem;flex-wrap:wrap;margin:.2rem 0 .3rem}

/* ---- upload progress bar ---- */
.upl-progress{margin-top:.7rem;max-width:420px}
.upl-bar{height:8px;background:var(--panel2);border:1px solid var(--line);border-radius:999px;overflow:hidden}
.upl-bar span{display:block;height:100%;width:0;background:var(--accent);transition:width .25s ease}
.upl-text{font-size:.8rem;color:var(--mut);margin-top:.3rem}

/* ---------- filter panel (Inspections → History & search) ----------
   The quick tabs answer "what needs attention"; the dropdowns answer "find me that one form". Two
   different jobs, so the button sits beside the tab group rather than in it — same row, its own
   control. The panel below is hidden until asked for, but forced open whenever a filter is set: a
   list quietly showing a third of its rows with no visible reason is how people conclude records
   have gone missing. */
.filtline{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin:0 0 .9rem}
/* the tab group carries its own bottom margin; inside this row that would sit the button
   half a line high, so the row owns the spacing instead */
.filtline .tabbar{margin-bottom:0}
/* matched to the tab pills beside it — .btn.sm is a couple of pixels shorter on its own */
.filtline .btn.sm{padding:.5rem .8rem}
.filtline .s{color:var(--mut);font-size:.8rem}
.btn.fx-on{border-color:var(--accent);color:var(--accent)}
.fx-n{display:inline-block;min-width:1.35em;padding:0 .3em;border-radius:999px;
  background:var(--accent);color:#fff;font-size:.72rem;font-weight:800;text-align:center}
.filtbox{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.5rem .8rem;align-items:end;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-s);
  padding:.75rem .85rem .9rem;margin:0 0 1rem;box-shadow:var(--shadow-s)}
.filtbox[hidden]{display:none}
.filtbox .fl{margin:0 0 .3rem}
.filtbox select{width:100%}

/* ---- job conversation ----
   Right for yours, left for everybody else's, which is the shape every crew already reads without
   being taught it. Deliberately not a chat SDK: SendBird and CometChat are hosted services, so the
   conversation would live on somebody else's platform — which is the WhatsApp problem this feature
   exists to solve, bought rather than inherited. The look is the part worth copying and it is CSS. */
.chat-thread{display:flex;flex-direction:column;gap:.45rem;padding:.2rem 0}
.chat-row{display:flex;gap:.5rem;align-items:flex-end;max-width:100%}
.chat-row.mine{flex-direction:row-reverse}
/* Initials rather than a photo: most staff records have no picture, and a broken avatar on every
   line is worse than none. */
.chat-av{width:28px;height:28px;border-radius:50%;flex:none;display:grid;place-items:center;
  font-size:.68rem;font-weight:800;background:var(--panel2);color:var(--mut);border:1px solid var(--line)}
.chat-row.mine .chat-av{display:none}
.chat-b{max-width:min(78%,34rem);border-radius:14px;padding:.45rem .65rem;background:var(--panel);
  border:1px solid var(--line);box-shadow:var(--shadow-s);min-width:0}
.chat-row.mine .chat-b{background:var(--chat-mine,#2f6fed);border-color:var(--chat-mine,#2f6fed);color:#fff}
/* the tail, which is what makes it read as speech rather than as a list of cards */
.chat-b{border-bottom-left-radius:4px}
.chat-row.mine .chat-b{border-bottom-left-radius:14px;border-bottom-right-radius:4px}
.chat-who{font-size:.7rem;font-weight:800;color:var(--accent);margin-bottom:.1rem}
.chat-row.mine .chat-who{display:none}
.chat-t{white-space:pre-wrap;overflow-wrap:anywhere;font-size:.92rem;line-height:1.35}
.chat-when{font-size:.64rem;color:var(--faint);margin-top:.15rem;text-align:right}
.chat-row.mine .chat-when{color:rgba(255,255,255,.75)}
.chat-b img{max-width:100%;border-radius:9px;display:block;margin-top:.3rem;cursor:zoom-in}
.chat-filed{font-size:.64rem;color:var(--faint);margin-top:.1rem}
.chat-row.mine .chat-filed{color:rgba(255,255,255,.72)}
/* a run of messages from the same person reads as one turn, so only the first carries the name */
.chat-row.cont .chat-av{visibility:hidden}
.chat-row.cont .chat-who{display:none}

/* The conversation scrolls inside itself, and the box you type in stays put.
   Left to grow, the list pushes the composer further down the page with every message — so the more
   there is to reply to, the further you scroll to reply. Capped and scrolled to the newest, which is
   the end anybody wants to be at. */
.chat-list{max-height:var(--chat-h,48vh);overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;padding-right:.25rem}

/* A standing warning about the box itself, not about a record — used where Rivet has found something
   it cannot fix on its own and needs a person at a terminal. */
.warnbox{border:1px solid var(--soon,#d08700);background:var(--soon-bg,rgba(208,135,0,.09));
  border-radius:10px;padding:.6rem .75rem;color:var(--ink)}
.warnbox code{user-select:all}

/* ---- maintenance: cards that use the screen they are on ----
   Every card here was capped at 760px, so on a desk monitor the page was a narrow ribbon down the
   left with half the width empty, and on a phone it was five long cards to scroll past to reach the
   backups. Laid out as columns that fit themselves to the space: two or three across on a monitor,
   one on a phone, without a breakpoint per size. 30rem is the width below which these cards stop
   being readable — the fields inside are label-over-value pairs, not prose. */

/* Only where somebody asked for it. Off, a page is the single column it has always been; on, runs of
   cards become columns fitted to the space. It is a preference rather than a default because a wide
   layout is genuinely better on a desk monitor and genuinely worse on a laptop held at arm's length
   in a ute, and this app is used in both. */
/* Columns of widgets, packed by height.

   Three attempts got here. Natural heights in a grid left a hole beside every short card. Stretching
   made every card as tall as the tallest in its row, so a two-field password card became a mostly
   empty rectangle. Multi-column packed properly but flowed down each column, so opening a section
   reordered the page under the cursor.

   What is wanted is what a dashboard does: each card keeps the height its contents need, and the next
   one goes wherever there is room. CSS cannot do that on its own — masonry is still a proposal — so
   the columns are real elements and applyWideLayout puts each card in whichever is shortest so far.
   Order is preserved well enough to scan, no card is stretched, and nothing moves when one expands
   except the column it is in. */
body.wide .widgetcols{display:flex;gap:1.2rem;align-items:flex-start;max-width:100rem}
body.wide .widgetcols>.cardcol{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:1.2rem}
body.wide .widgetcols>.cardcol>.formcard{max-width:none;margin:0;width:100%}

/* ---- allocation planner ----
   Rows are people, columns are days, and every colour on it comes from the work_type table rather than
   from here — the trades are the customer's and so are the colours they read them by. This file only
   decides the shape of the grid. */
.plan-wrap{overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--panel);
  -webkit-overflow-scrolling:touch;max-height:calc(100vh - 13rem)}
/* The columns share whatever width there is. --plan-col is worked out from the space available and
   the number of days on screen, so a week fills a laptop and four weeks on the same laptop scroll
   rather than being squeezed to something nobody can read. table-layout:fixed keeps every column the
   same width regardless of what is written in it — without it, one long address stretches its day and
   the grid stops lining up with its own header. */
.plan{border-collapse:separate;border-spacing:0;table-layout:fixed;width:100%;font-size:.82rem}
.plan th,.plan td{border-right:1px solid var(--line);border-bottom:1px solid var(--line);vertical-align:top}
/* The width has to be on the HEADER cells. table-layout:fixed takes every column's width from the
   first row and ignores what the body cells ask for — so setting it on td.day did nothing at all, the
   browser divided the space equally, and the grid looked like it was refusing to scale. */
.plan thead th{position:sticky;top:0;z-index:3;background:var(--panel2);padding:.4rem .5rem .45rem;
  width:var(--plan-col,11rem);
  font-size:.68rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--mut);white-space:nowrap;text-align:left;
  border-bottom:2px solid var(--line2)}
.plan thead th.who{width:var(--plan-name,11rem)}
/* the name column stays put while the month scrolls past it — a row of blocks with no name is useless */
.plan .who{position:sticky;left:0;z-index:2;background:var(--panel);width:var(--plan-name,11rem);
  min-width:var(--plan-name,11rem);padding:.5rem .6rem}
.plan thead .who{z-index:4;background:var(--panel2)}
.plan .who b{display:block;font-size:.86rem;line-height:1.2}
.plan .who .cnt{font-size:.7rem;color:var(--faint);font-weight:700}
.plan .who .role{font-size:.7rem;color:var(--mut)}
.plan td.day{width:var(--plan-col,11rem);min-width:var(--plan-col,11rem);padding:.3rem;background:var(--panel)}
.plan td.day.wknd{background:var(--panel2)}
.plan td.day.today{box-shadow:inset 0 0 0 2px var(--accent)}
.plan tr.unalloc td,.plan tr.unalloc .who{background:var(--accent-soft)}
/* Hovering a row lights the whole row, not just the day under the pointer. Following one person
   across a month is the thing this grid is for, and a background-color here was being overridden by
   the weekend and closure shading — so it is a layer, like the banding, and composes with them. */
.plan tbody tr:hover td,.plan tbody tr.alt:hover td{background-image:linear-gradient(var(--bandhov),var(--bandhov))}
.plan tbody tr:hover td.who{box-shadow:inset 3px 0 0 var(--accent)}
/* a block: coloured head, coloured body, both from the work type */
.pblock{border-radius:8px;overflow:hidden;margin-bottom:.3rem;cursor:pointer;border:1px solid rgba(0,0,0,.1);
  box-shadow:0 1px 2px rgba(15,22,40,.08)}
.pblock:last-child{margin-bottom:0}
.pblock .ph{padding:.22rem .4rem;font-weight:800;font-size:.72rem;line-height:1.25;display:flex;gap:.25rem;align-items:baseline}
.pblock .pb{padding:.28rem .4rem;color:#1f2430;line-height:1.3;overflow-wrap:anywhere}
.pblock .pt{font-weight:700}
.pblock .pl{opacity:.85}
.pblock.tight .pb{padding:.15rem .35rem;font-size:.74rem}
.pblock.roomy .pb{padding:.45rem .55rem}
.plan .addhere{width:100%;border:1px dashed var(--line2);background:none;color:var(--faint);border-radius:8px;
  padding:.2rem;cursor:pointer;font:inherit;font-size:.75rem;opacity:0;transition:opacity .12s}
.plan td.day:hover .addhere{opacity:1}

/* dragging on the planner */
.pblock{position:relative;touch-action:none;user-select:none}
.pblock.dragging{opacity:.35}
.pgrip{position:absolute;top:0;right:0;bottom:0;width:10px;cursor:ew-resize;
  background:linear-gradient(90deg,transparent,rgba(0,0,0,.14))}
.pgrip:hover{background:linear-gradient(90deg,transparent,rgba(0,0,0,.28))}
.plan td.day.dropok{box-shadow:inset 0 0 0 2px var(--accent);background:var(--accent-soft)}
.plan td.day.inrun{background:var(--accent-soft)}
/* what follows the finger — deliberately not the real block, so the grid does not reflow mid-drag */
.pghost{position:fixed;z-index:200;pointer-events:none;opacity:.9;transform:translate(-50%,-50%);
  box-shadow:0 8px 24px rgba(15,22,40,.28);max-width:14rem}

/* You are on site: the one thing worth putting above everything else on a job card, because signing
   off is what the job's hours are made of and a button three sections down gets forgotten. */
.onsitebar{display:flex;align-items:center;gap:.7rem;margin-bottom:.9rem;padding:.7rem .9rem;
  border:1px solid var(--line);background:var(--panel);border-radius:12px}
/* green when you are on, plain when you are not — the colour is the state, so it reads before the words do */
.onsitebar.on{border-color:var(--ok);background:var(--ok-bg)}
.onsitebar .main{flex:1;min-width:0}
.onsitebar .s{color:var(--mut);font-size:.8rem}

/* When the columns get narrow the blocks shed detail rather than the grid getting unreadable.
   A fortnight on a laptop is worth having if each block says the type and the time; it is worth
   nothing if it says all five things in a column three words wide. */
.plan-wrap.cramped .pblock .pl{display:none}
.plan-wrap.cramped .pblock .ph{font-size:.66rem}
.plan-wrap.cramped .pblock .pb{padding:.2rem .35rem;font-size:.76rem}
.plan-wrap.tiny .pblock .pb>div:not(.pt){display:none}
.plan-wrap.tiny .pblock .ph span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* the handle for moving a person up or down — on the name, not the row, because the row is full of
   blocks that already drag somewhere else */
.rowgrip{display:inline-block;cursor:grab;color:var(--faint);margin-right:.35rem;
  font-size:.9rem;line-height:1;touch-action:none;user-select:none}
.rowgrip:hover{color:var(--ink)}
.plan tr.rowdrag{opacity:.55}
.plan tr.rowdrag .who{box-shadow:inset 0 0 0 2px var(--accent)}

/* a caret on a combo, so a field you can type into still looks like something you can pick from */
.combo{position:relative}
.combo-caret{position:absolute;right:.35rem;top:50%;transform:translateY(-50%);border:none;background:none;
  color:var(--mut);cursor:pointer;font-size:.8rem;line-height:1;padding:.35rem}
.combo-caret:hover{color:var(--ink)}
/* only where there is actually a caret to make room for — the app has five other combos and none of
   them should grow a gap on the right for a button they do not have */
.combo:has(.combo-caret)>input{padding-right:1.9rem}

/* Ticking a crew rather than picking one name. Scrolls at a height that still shows enough of the
   list to feel like a list — a box four names tall reads as a dropdown somebody has to hunt in. */
.crewpick{max-height:11rem;overflow:auto;border:1px solid var(--line);border-radius:10px;
  padding:.4rem .55rem;background:var(--panel);display:grid;gap:.15rem}
.crewpick .chk{margin:0;font-size:.86rem}

/* The leader's star sits in the block's own header, so it carries whatever colour the work type was
   given rather than needing a colour of its own that might vanish against it. */
.pblock .ph .lead{margin-left:auto;font-size:.9em;line-height:1;text-shadow:0 0 2px rgba(0,0,0,.35)}
.pblock.leader{box-shadow:inset 0 0 0 2px var(--accent)}

/* Crew by day, on the job card. One row per day, newest first: the eye goes down the dates and the
   names sit as chips rather than a table, because the question is "who", not "who and what and when"
   — the detail is on the chip's title for anyone who wants it. */
.crewday{border-top:1px solid var(--line);padding:.5rem 0}
.crewday:first-child{border-top:0}
.cdhead{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;margin-bottom:.3rem}
.cdhead b{font-size:.86rem}
.cdn{font-size:.72rem;color:var(--mut)}
.cdlead{font-size:.72rem;color:var(--accent);font-weight:700}
.cdppl{display:flex;flex-wrap:wrap;gap:.3rem}
.cdp{font-size:.78rem;background:var(--panel2);border:1px solid var(--line);border-radius:999px;
  padding:.12rem .5rem;white-space:nowrap}
.cdp.lead{border-color:var(--accent);font-weight:700}
.cdp .cdt{color:var(--mut);font-size:.72rem}

/* Stretching a block across the week used to be a tinted cell and a hope. The ghost shows the shape
   of what will exist when the finger lifts; the pill says it in words, because on a fortnight the
   run leaves the screen and on a phone the finger covers the cell being read. */
.pblock.prun-preview{opacity:.42;pointer-events:none;border:1px dashed rgba(0,0,0,.45)}
.plan td.day.inrun{background:var(--accent-soft);box-shadow:inset 0 0 0 1px var(--accent)}
.runpill{position:fixed;z-index:9999;pointer-events:none;transform:translate(14px,-140%);
  background:var(--accent);color:#fff;border-radius:999px;padding:.28rem .7rem;
  font-size:.78rem;font-weight:700;box-shadow:0 4px 14px rgba(0,0,0,.3);white-space:nowrap;
  display:flex;gap:.45rem;align-items:baseline}
.runpill span{font-weight:400;opacity:.9;font-size:.74rem}
/* The grip is what somebody has to find before any of this happens, so it says so rather than
   waiting to be discovered: visible on the block, not only on hover. */
.pgrip{background:linear-gradient(90deg,transparent,rgba(0,0,0,.14))}
.pgrip:hover{background:linear-gradient(90deg,transparent,rgba(0,0,0,.4))}
.pgrip::after{content:'⋮';position:absolute;right:1px;top:50%;transform:translateY(-50%);
  font-size:.7rem;color:rgba(0,0,0,.5);line-height:1}

/* Search above the crew list. Seventy names is a scroll, so the list gets taller too — a box that
   shows four names at a time is a dropdown wearing a list's clothes. */
.crewfind{width:100%;margin-bottom:.35rem}
.crewpick{max-height:15rem}
.crewnone{padding:.3rem .1rem}

/* A night shift's +1. Small and attached to the time rather than a separate line, because the block
   is already short of room and "22:00–06:00" without it reads as a shift running backwards. */
.pblock .pt .nx{font-size:.7em;vertical-align:super;opacity:.75;margin-left:.1em;font-weight:700}

/* Unsaved-changes bar. Fixed to the bottom because that is where a thumb is and where the eye lands
   after typing, and above the safe-area inset so a phone's home bar does not sit on the button. */
.dirtybar{position:fixed;left:50%;transform:translateX(-50%);z-index:900;
  bottom:calc(env(safe-area-inset-bottom,0px) + 1rem);
  display:flex;align-items:center;gap:.8rem;white-space:nowrap;
  background:var(--accent);color:#fff;border-radius:999px;padding:.5rem .6rem .5rem 1.1rem;
  box-shadow:0 6px 22px rgba(0,0,0,.35);font-size:.85rem;font-weight:600;max-width:94vw}
.dirtybar .btn{background:#fff;color:var(--accent);border-color:#fff;font-weight:700}
@media (max-width:520px){ .dirtybar{font-size:.8rem;gap:.5rem;padding-left:.9rem} }

/* Colour swatches that say what they colour. The label was only in a title attribute, so on a laptop
   it appeared after a pause and on a phone never at all. */
.swat{display:flex;flex-direction:column;gap:.15rem}
.swat>span:first-child{font-size:.66rem;text-transform:uppercase;letter-spacing:.04em;color:var(--mut);font-weight:700}
.swat input[type=color]{width:3rem;height:2rem;padding:0;border-radius:6px}
.icongrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(2.4rem,1fr));gap:.3rem;margin:.2rem 0 .7rem}
.iconopt{font-size:1.25rem;line-height:1;padding:.35rem;border:1px solid var(--line);border-radius:8px;
  background:var(--panel);cursor:pointer}
.iconopt:hover{border-color:var(--accent);background:var(--accent-soft)}

/* Work types: two lines per type. One line meant a preview, a name, an icon, three colour swatches
   and two buttons all fighting for the same 400px, which is what "smooshed" looks like. */
/* A heavier rule between types than between ordinary list rows: each block here is a group of six
   controls, and a hairline let them read as one long list rather than as one type per band. */
.wtrow{border-top:3px solid var(--line2);padding:.85rem .1rem}
.wtrow:first-child{border-top:0}
.wttop{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
.wtbot{display:flex;gap:1.1rem;align-items:flex-end;flex-wrap:wrap;margin:.5rem 0 0 .2rem}
.wtrow .pblock .ph{display:block;padding:.25rem .5rem;font-size:.78rem;border-radius:6px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:560px){
  .wttop .pblock{width:100%!important}
  .wtbot{gap:.8rem}
}

/* A work type's icon as a picture. Sized to the emoji it replaces so a block laid out for one does
   not jump when the other is used, and contained rather than stretched — a logo squashed to a square
   is worse than a small logo. */
.wticon{width:1.15em;height:1.15em;object-fit:contain;vertical-align:-.15em;display:inline-block}
.wticon.lg{width:2rem;height:2rem;border:1px solid var(--line);border-radius:6px;background:var(--panel);padding:1px}
.pblock .ph .wticon{width:1.1em;height:1.1em}

/* Leave and non-working days. Both say "not available", so they read as the same kind of thing —
   hatched rather than coloured, because a colour would compete with the work types, which are the
   thing the planner is actually for. */
.leaveblk{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
  color:var(--mut);border:1px dashed var(--line2);border-radius:6px;padding:.2rem .35rem;
  margin-bottom:.25rem;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plan td.day.onleave{background:repeating-linear-gradient(135deg,transparent,transparent 5px,var(--panel2) 5px,var(--panel2) 10px)}
.plan td.day.shut{background:repeating-linear-gradient(135deg,transparent,transparent 4px,var(--line) 4px,var(--line) 8px);opacity:.75}
.plan th.dayhd{cursor:pointer}
.plan th.dayhd:hover{background:var(--accent-soft)}
.plan th.dayhd.shut{background:var(--line)}
.shutlbl{font-size:.62rem;font-weight:700;color:var(--bad);text-transform:uppercase;letter-spacing:.03em;
  margin-top:.1rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Calculators. The answers are the point, so they get the weight: a column of labels against big
   right-aligned figures, which is how anybody reading a quantity expects to read one. */
.calc-card{display:flex;align-items:center;gap:.8rem;cursor:pointer}
.calc-ic{font-size:1.7rem;line-height:1;flex:none}
.presetrow{display:flex;gap:.3rem;flex-wrap:wrap;margin-top:.2rem}
.calcout{border:1px solid var(--line);border-radius:12px;overflow:hidden}
.corow{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  padding:.5rem .8rem;border-top:1px solid var(--line);background:var(--panel)}
.corow:first-child{border-top:0}
.corow:nth-child(even){background:var(--panel2)}
.corow.bad{background:var(--accent-soft)}
.corow .col{font-size:.85rem;color:var(--mut)}
.corow .cov{font-size:1.05rem;font-weight:700;font-variant-numeric:tabular-nums;text-align:right}
.corow .cov small{font-weight:400;font-size:.72rem;color:var(--mut)}

/* Results laid out the way the long-section on the plan lays them out: the quantity down the left,
   an end of the pipe in each column, and the figures that belong to the whole run spanning both.
   Numbers are tabular so the two ends line up digit for digit — which is the whole point of reading
   them side by side. */
.lsec-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--line);border-radius:12px}
.lsec{border-collapse:collapse;width:100%;min-width:22rem}
.lsec thead th{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;color:var(--mut);
  font-weight:700;text-align:right;padding:.45rem .7rem;background:var(--panel2);
  border-bottom:1px solid var(--line)}
.lsec thead th:first-child{width:40%;text-align:left}
.lsec tbody th{font-size:.85rem;color:var(--mut);font-weight:400;text-align:left;
  padding:.5rem .7rem;border-top:1px solid var(--line)}
.lsec tbody td{font-size:1.05rem;font-weight:700;font-variant-numeric:tabular-nums;
  text-align:right;padding:.5rem .7rem;border-top:1px solid var(--line);white-space:nowrap}
.lsec tbody td.mid{text-align:center;color:var(--accent)}
.lsec tbody tr:first-child th,.lsec tbody tr:first-child td{border-top:0}
.lsec tbody tr:nth-child(even) th,.lsec tbody tr:nth-child(even) td{background:var(--panel2)}
.lsec small{font-weight:400;font-size:.72rem;color:var(--mut)}

/* The planner report. Numbers people repeat out loud go in cards; everything else is a table, because
   a report is read down a column and compared, not admired. */
.rp-period{display:flex;gap:.4rem;flex-wrap:wrap;align-items:center}
.rp-period .rpsp.on{background:var(--accent);color:var(--on-accent,#fff);border-color:var(--accent)}
.rp-dates{display:inline-flex;gap:.3rem;align-items:center;font-size:.8rem;color:var(--mut)}
.rp-dates input{padding:.25rem .4rem;font-size:.8rem}
.rp-cards{display:grid;gap:.6rem;margin:.9rem 0 .2rem;
  grid-template-columns:repeat(auto-fit,minmax(10.5rem,1fr))}
.rp-card{border:1px solid var(--line);border-radius:12px;padding:.6rem .7rem;background:var(--panel2)}
.rp-card b{display:block;font-size:1.35rem;font-variant-numeric:tabular-nums;line-height:1.15}
.rp-card span{display:block;font-size:.78rem;color:var(--mut)}
.rp-card i{display:block;font-style:normal;font-size:.72rem;color:var(--faint);margin-top:.2rem}
.rp-card.warn{border-color:var(--soon);box-shadow:inset 3px 0 0 var(--soon)}
.rp-bar{height:6px;border-radius:4px;background:var(--line2);overflow:hidden;margin-bottom:.2rem}
.rp-bar i{display:block;height:100%;background:var(--accent)}
.rp-tab td{vertical-align:top}
.rp-nil td{color:var(--mut)}
.rp-flag{margin:.8rem 0 .2rem;padding:.55rem .7rem;border-radius:10px;
  border:1px solid var(--soon);background:var(--accent-soft);font-size:.84rem}
.cal-add{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;margin:.6rem 0 .2rem}
.cal-add select{max-width:22rem}
.cal-prev{margin-top:.6rem;border:1px solid var(--line);border-radius:12px;padding:.5rem .6rem;background:var(--panel2)}
.cal-prev table{width:100%}
.cal-yr{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--mut);
  font-weight:700;margin:.7rem 0 .2rem}
.cal-yr:first-child{margin-top:.2rem}
.cal-row{display:grid;grid-template-columns:9.4rem minmax(0,1fr) 8.4rem auto;gap:.4rem;
  align-items:center;padding:.25rem 0;border-top:1px solid var(--line);max-width:100%}
.cal-row:first-of-type{border-top:0}
.cal-row input,.cal-row select{padding:.28rem .4rem;font-size:.82rem;
  width:100%;min-width:0;box-sizing:border-box}
.cal-row .btn{white-space:nowrap}
.cal-past{margin-top:.7rem;padding-top:.6rem;border-top:1px solid var(--line);
  display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}
.cal-prev input.cal-pd{padding:.2rem .3rem;font-size:.78rem;width:8.6rem}
.cal-prev input.cal-pd:disabled{opacity:.5}
.cal-ul{margin:.25rem 0 0 1rem;padding:0;font-size:.82rem}
.cal-ul li{margin:.1rem 0}
.fb-roles{display:flex;flex-wrap:wrap;gap:.3rem .9rem;margin-top:.25rem}
.vismx th.vg{text-align:center;white-space:nowrap}
.vismx td.k{white-space:normal;min-width:14rem}
.vismx input[type=checkbox]{width:1.05rem;height:1.05rem}
.vismx td,.vismx th{padding:.3rem .5rem}
.featrow{display:flex;gap:.6rem;align-items:flex-start;padding:.5rem 0;border-top:1px solid var(--line)}
.featrow:first-child{border-top:0}
.featrow input{margin-top:.15rem}
.boxlevels{border:1px solid var(--line);border-radius:10px;overflow:hidden;margin:.2rem 0 .1rem}
.boxlevels>div{display:grid;grid-template-columns:9.5rem 1fr;gap:.6rem;padding:.5rem .7rem;
  border-top:1px solid var(--line);font-size:.8rem}
.boxlevels>div:first-child{border-top:0}
.boxlevels b{color:var(--ink)}
.boxlevels span{color:var(--mut)}
.boxnow{margin-top:.45rem;padding:.45rem .6rem;border-radius:8px;background:var(--panel2);
  border-left:3px solid var(--accent);color:var(--ink2);font-size:.8rem}
.lockbox{display:flex;gap:.8rem;align-items:flex-start;justify-content:space-between;
  flex-wrap:wrap;padding:.6rem .75rem;border-radius:10px;border:1px solid var(--line);
  background:var(--panel2);font-size:.84rem}
.lockbox.on{border-color:var(--soon);background:var(--soon-bg)}
.lockbox>div{flex:1;min-width:16rem}
.reccodes{display:grid;grid-template-columns:repeat(2,1fr);gap:.3rem;
  font-family:var(--font-mono,monospace);font-size:.95rem;letter-spacing:.04em}
.reccodes span{background:var(--panel2);border:1px solid var(--line);
  border-radius:6px;padding:.3rem .45rem;text-align:center;user-select:all}
@media (max-width:640px){ .boxlevels>div{grid-template-columns:1fr;gap:.15rem} }
@media (max-width:640px){ .cal-row{grid-template-columns:1fr 1fr;gap:.3rem}
  .cal-row .cal-rl{grid-column:1/-1} }
@media print{
  .side-nav,.rp-period,.topbar{display:none!important}
  .jsec-body{display:block!important}
  .rp-cards{grid-template-columns:repeat(4,1fr)}
}
/* Editor rows: one line each, wrapping rather than shrinking, because a formula box 40px wide is
   the same mistake the work types dialog made. */
.cerow{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap;padding:.35rem 0;border-top:1px solid var(--line)}
.cerow:first-child{border-top:0}
.cerow input{min-width:0}
.cerow .f-key{max-width:11rem;font-family:ui-monospace,monospace;font-size:.82rem}
.cerow .f-label{flex:1;min-width:8rem}
.cerow .ce-msg{flex-basis:100%;font-size:.74rem;padding-left:.2rem}

/* Figures & rates: a form, not a config file. Label on the left with the name the formulas use
   underneath it, the value on the right where a number belongs. */
.ratelist{border:1px solid var(--line);border-radius:12px;overflow:hidden}
.raterow{display:flex;justify-content:space-between;align-items:center;gap:1rem;
  padding:.55rem .8rem;border-top:1px solid var(--line);background:var(--panel);flex-wrap:wrap}
.raterow:first-child{border-top:0}
.raterow:nth-child(even){background:var(--panel2)}
.raterow .rl{min-width:0;flex:1}
.raterow .rl b{font-size:.88rem}
.raterow .rv{display:flex;align-items:center;gap:.4rem}
.raterow .rv.wrap{flex-wrap:wrap;gap:.6rem}
.raterow .rate-val{max-width:8rem;text-align:right;font-variant-numeric:tabular-nums;font-weight:700}
.raterow .ru{font-size:.76rem;color:var(--mut);min-width:2.5rem}
.cerow.preset{align-items:flex-end}

/* Job programmes above the crew. A band rather than more rows in the same table: the question "what
   is on this fortnight" is a different one from "where is Nick on Tuesday", and mixing them makes a
   grid where neither is easy to read. Column widths come from the same variables, so the two tables
   line up under each other. */
/* Job rows live in the planner's own table now, not a band above it. As a separate table they did
   not inherit the column widths — those are set on .plan-wrap and the band sat outside it — so the
   bars never lined up with the days and never scrolled with them either. Same table, same columns,
   one scroll. Half height, because a job is context for the rows underneath rather than a lane of
   its own. */
.plan tr.jobrow td{padding:.15rem .2rem;vertical-align:middle;background:var(--panel2)}
.plan tr.jobrow td.who{padding:.2rem .6rem}
.plan tr.jobrow td.who b{display:block;font-size:.8rem;line-height:1.15;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plan tr.jobrow td.who .role{font-size:.66rem}
.plan tr.jobrow:last-of-type td{border-bottom:3px solid var(--line2)}
.jobbar{background:var(--accent);color:#fff;border-radius:999px;padding:.22rem .7rem;
  font-size:.78rem;font-weight:700;cursor:pointer;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;box-shadow:0 1px 3px rgba(0,0,0,.25)}
.jobbar:hover{filter:brightness(1.08)}
.jobbar.done{background:var(--mut)}
.jobbar.cancelled{background:var(--bad);text-decoration:line-through}

/* A reference table under a calculator. The matching row is lifted rather than the others dimmed —
   the neighbours are how somebody checks they are reading the right line. */
.reftab-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.reftable{border-collapse:collapse;font-size:.78rem;min-width:100%;white-space:nowrap}
.reftable th{text-align:left;padding:.35rem .55rem;background:var(--panel2);
  border-bottom:2px solid var(--line2);font-size:.7rem;text-transform:uppercase;letter-spacing:.03em;color:var(--mut)}
.reftable td{padding:.3rem .55rem}
.reftable td.k{font-weight:700;white-space:nowrap}
.reftable td.nil{color:var(--faint)}
/* Banded by group, not by row — see the comment where the rows are built. The line above each new
   group does the separating, so the shading can stay light enough to read numbers through. */
.reftable tr.band td{background:var(--panel2)}
.reftable tr.gtop td{border-top:1px solid var(--line2)}
.reftable tr:first-child td{border-top:0}
/* The match has to win over the banding, so it is stated after it and carries its own rule above
   and below — a tinted row in a table that is already tinted needs an edge to be findable. */
.reftable tr.hit td{background:var(--accent-soft)}
.reftable tr.hit td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.reftable tr.hit td.k{color:var(--accent)}

/* Clocking on from the dashboard: a job to pick and a time to correct, on one line where there is
   room and stacked where there is not. The time sits next to the button because it is part of the
   same decision, not a setting somewhere else. */
.onsitebar.qc{flex-wrap:wrap}
.onsitebar .qc-job{max-width:15rem;min-width:9rem}
.onsitebar .qc-t{display:flex;align-items:center;gap:.35rem;font-size:.72rem;color:var(--mut);
  text-transform:uppercase;letter-spacing:.03em;font-weight:700}
.onsitebar .qc-t input{width:6.2rem}
@media (max-width:560px){
  .onsitebar.qc .main{flex-basis:100%}
  .onsitebar .qc-job{max-width:none;flex:1}
}

/* A run that carries on past the right-hand edge. The arrow sits on the block's own right side so it
   reads as "continues", not as a separate thing to click. */
.pblock.runson{position:relative}
.pblock.runson::after{content:'▸';position:absolute;right:-2px;top:50%;transform:translateY(-50%);
  font-size:1.1rem;line-height:1;color:var(--accent);text-shadow:0 0 3px var(--panel),0 0 3px var(--panel)}

/* Week columns, for spans past a month. Two lines in the heading so the range is readable, and the
   blocks say "6 days" rather than stacking six identical ones nobody can fit. */
/* A week heading is a way in, so it looks like one. */
.plan th.dayhd.wk{cursor:pointer;line-height:1.15}
.plan th.dayhd.wk:hover{background:var(--accent-soft);color:var(--accent)}
.plan th.dayhd.wk:hover .wkto{opacity:1}
.wkto{font-size:.62rem;font-weight:400;opacity:.7}
.pblock.wk{margin-bottom:.25rem}
.pblock.wk .pb{padding:.15rem .35rem}
.pblock.wk .pt{font-size:.72rem;font-weight:700}
/* The job bar carries its dates. A blue bar with nothing written on it says a job is on without
   saying which, or when. */
.jobbar{display:flex;align-items:baseline;gap:.5rem}
.jobbar b{overflow:hidden;text-overflow:ellipsis}
.jbdate{font-weight:400;font-size:.72rem;opacity:.85;white-space:nowrap}

/* Column headings that can be read at a glance. They were one line of .68rem muted text, which on a
   month view is a grey smear you have to lean into. The date carries the weight because the date is
   what is being looked for; the weekday sits above it small, and the month prints only where it
   changes, since AUG repeated down thirty columns is noise pretending to be information. */
.plan thead th.dayhd{line-height:1.1}
.dh-dow{display:block;font-size:.6rem;font-weight:700;color:var(--faint);letter-spacing:.06em}
.dh-d{display:inline-block;font-size:1.05rem;font-weight:800;letter-spacing:0;color:var(--txt);
  font-variant-numeric:tabular-nums}
.dh-d.wk{font-size:.82rem}
.dh-m{display:inline-block;margin-left:.25rem;font-size:.68rem;font-weight:800;color:var(--accent);letter-spacing:.04em}
/* Weekends recede rather than disappear — dimming the whole cell to 60% took the date with it. */
.plan thead th.dayhd.wknd{background:var(--panel)}
.plan thead th.dayhd.wknd .dh-d{color:var(--mut);font-weight:700}
/* Today, and the first column of a new month, are the two things worth finding without reading. */
.plan thead th.dayhd.istoday{background:var(--accent-soft)}
.plan thead th.dayhd.istoday .dh-d,.plan thead th.dayhd.istoday .dh-dow{color:var(--accent)}
.plan thead th.dayhd.mstart{border-left:2px solid var(--line2)}
.plan td.day.mstart{border-left:2px solid var(--line2)}

/* Alternate people shaded. A translucent layer rather than a colour, so the weekend, closure and
   leave shading underneath still reads through it instead of being replaced. */
.plan tbody tr.alt td,.plan tbody tr.alt td.who{background-image:linear-gradient(var(--band),var(--band))}
/* A firmer line under each person than between the days, so the eye tracks along a row not down it */
.plan tbody tr[data-row] td{border-bottom:1px solid var(--line2)}

/* Leave is editable from where it shows, so it looks like it is. */
.leaveblk{cursor:pointer}
.leaveblk:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}

/* ---- run bars -------------------------------------------------------------------------------
   One bar per run, drawn over the day cells rather than instead of them. The cells still carry
   data-cell, so dragging and dropping still knows which day the pointer is on — a colspan bar would
   have taken that with it. The layer sits in the row's first day cell and is positioned in column
   widths, which is why --plan-col has to be a real pixel value (sizeCols sets it). */
.plan td.day.runhost{position:relative;overflow:visible}
.runlayer{position:absolute;inset:0;pointer-events:none;z-index:2}
/* touch-action and user-select are what make this draggable at all. Without them a press-and-drag
   starts a text selection or a scroll and the browser stops sending pointer events partway through,
   so the bar appears to be stuck — which is exactly how it behaved. The old blocks carried these and
   the bars that replaced them did not. */
.runbar{position:absolute;height:30px;pointer-events:auto;cursor:grab;
  touch-action:none;user-select:none;-webkit-user-select:none;
  display:flex;align-items:center;gap:.4rem;padding:0 .5rem 0 0;
  border:1px solid var(--line2);border-left-width:0;border-radius:8px;
  background:var(--panel);box-shadow:0 1px 2px rgba(0,0,0,.18);overflow:hidden;
  font-size:.78rem;color:var(--ink)}
.runbar:hover{box-shadow:0 2px 8px rgba(0,0,0,.28);z-index:3}
.runbar.dragging{opacity:.5}
.rb-ic{flex:none;align-self:stretch;display:flex;align-items:center;justify-content:center;
  width:1.7rem;font-size:.9rem;border-radius:7px 0 0 7px}
.rb-t{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto}
.rb-s{color:var(--mut);font-size:.72rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}
.rb-d{margin-left:auto;flex:none;font-size:.68rem;font-weight:800;color:var(--mut);
  background:var(--panel2);border-radius:999px;padding:.05rem .4rem}
.rb-star{color:var(--accent);margin-right:.15rem}
.runbar.leader{border-color:var(--accent)}
.runbar.runson{border-right:0;border-radius:8px 0 0 8px}
.runbar.runson::after{content:'▸';position:absolute;right:.15rem;top:50%;transform:translateY(-50%);
  color:var(--accent);font-size:1rem;line-height:1}
.runbar .pgrip{position:absolute;right:0;top:0;bottom:0;width:12px;cursor:ew-resize;
  background:linear-gradient(90deg,transparent,rgba(0,0,0,.14))}
.runbar .pgrip:hover{background:linear-gradient(90deg,transparent,rgba(0,0,0,.4))}
/* the add button must not sit under a bar it cannot be seen through */
.plan td.day .addhere{position:relative;z-index:1}
@media (max-width:640px){
  .runbar .rb-s{display:none}
  .runbar{height:26px;font-size:.72rem}
}

/* The dragged copy follows the pointer, so it must not keep the absolute placement its original had
   inside the run layer. */
.runbar.pghost{position:fixed!important;z-index:9999;pointer-events:none;opacity:.9;
  transform:translate(-14px,-50%);box-shadow:0 8px 24px rgba(0,0,0,.4)}
.runbar.prun-preview{position:static!important;opacity:.45;pointer-events:none;
  border-style:dashed;width:auto!important;margin-bottom:.2rem}

/* The section switches. Buttons rather than tickboxes: they are a view being chosen, pressed often,
   and a row of four ticks reads as settings rather than as a control. */
.secsw{display:inline-flex;gap:.25rem;margin-left:.8rem;flex-wrap:wrap;vertical-align:middle}
.secbtn{font-size:.72rem;padding:.2rem .5rem;opacity:.55}
.secbtn.on{opacity:1;background:var(--accent-soft);color:var(--accent);border-color:var(--accent)}
/* A heading only when more than one kind is on screen — with people alone it would be a label on a
   list of one thing. */
.plan tr.sechead td{position:sticky;left:0;background:var(--panel2);
  font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--mut);
  padding:.35rem .6rem;border-top:3px solid var(--line2)}
.plan tr.sechead .sn{color:var(--faint);font-weight:700}

/* Forecast work reads as provisional: outlined rather than filled, so a quarter with pipeline in it
   cannot be mistaken at a glance for a quarter that is sold. */
.jobbar.forecast{background:transparent!important;color:var(--accent);
  border:2px dashed var(--accent);box-shadow:none;font-weight:700}
.jobbar.forecast .jbdate{opacity:.75}

/* Choosing what is being booked. Buttons, because it changes which list is underneath — a dropdown
   would hide the fact that plant and people are different pools rather than one filtered list. */
.kindsw{display:flex;gap:.25rem;flex-wrap:wrap}
.kindbtn{font-size:.74rem;padding:.25rem .55rem;opacity:.6}
.kindbtn.on{opacity:1;background:var(--accent-soft);color:var(--accent);border-color:var(--accent)}
.kindbtn:disabled{opacity:.35;cursor:not-allowed}
.kindbtn.on:disabled{opacity:.8}

/* "Coming up" — jobs that start beyond the right-hand edge. Not bars, because there is nowhere
   truthful to draw them: a job starting in six weeks has no column on a one week view, and clamping
   it to the edge would say it starts on Friday. A line each, with how far off it is. */
.plan tr.jobrow.upcoming td{background:var(--panel)}
.upbar{display:flex;align-items:center;gap:.6rem;padding:.25rem .6rem;border-radius:8px;
  border:1px dashed var(--line2);background:var(--panel2);cursor:pointer;font-size:.8rem}
.upbar:hover{border-color:var(--accent);background:var(--accent-soft)}
.upwhen{font-weight:800;color:var(--accent);white-space:nowrap;font-size:.76rem}
.upname{font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upstat{margin-left:auto;font-size:.7rem;color:var(--mut);text-transform:uppercase;letter-spacing:.04em}

/* Forecast jobs coming due. A line each: how far off, what it is, and the two things to do about it. */
.fcdue .fclist{margin-top:.4rem;display:grid;gap:.25rem}
.fcrow{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;font-size:.84rem}
.fcwhen{font-weight:800;font-size:.74rem;color:var(--mut);min-width:6.5rem;white-space:nowrap}
.fcrow.late .fcwhen{color:var(--bad)}
.fcname{font-weight:700;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fcname:hover{color:var(--accent);text-decoration:underline}
.fcacts{margin-left:auto;display:flex;gap:.35rem}

/* Which units the measurements are typed in. A pair of buttons rather than a dropdown, because it is
   two choices and the one in force should be readable without opening anything. */
.unitsw{display:inline-flex;gap:.25rem}
.unitbtn{font-size:.74rem;padding:.2rem .6rem;opacity:.55}
.unitbtn.on{opacity:1;background:var(--accent-soft);color:var(--accent);border-color:var(--accent)}
.unitbtn:disabled{cursor:not-allowed}
.unitbtn.on:disabled{opacity:.85}
.unitnote{color:var(--mut);margin-top:.5rem}

/* A calculator's diagram. Sized to the panel, and its text scales with the drawing rather than being
   baked at one size — a label that stays 11px on a phone is unreadable at the width it ends up. */
.calcdiag{margin:.2rem 0 .9rem;padding:.4rem;border:1px solid var(--line);border-radius:12px;
  background:var(--panel2);overflow-x:auto}
.calcdiag svg{display:block;width:100%;max-width:680px;height:auto;margin:0 auto;color:var(--ink2)}
.cdiag .cd-s{font-size:11px;fill:var(--mut)}
.cdiag .cd-b{font-size:12px;font-weight:700;fill:var(--ink)}
@media (max-width:520px){ .calcdiag{padding:.2rem} .cdiag .cd-s{font-size:13px} .cdiag .cd-b{font-size:14px} }

/* ---- purchase order lines -----------------------------------------------------------------------
   A row per item: part number, what it is, how many, and the unit — then a full-width line
   underneath for anything the supplier needs told about that item specifically. Stacks on a phone,
   because this gets filled in standing at a machine as often as sitting at a desk. */
.po-lines{display:flex;flex-direction:column;gap:.55rem;margin-top:.4rem}
.po-line{
  display:grid;gap:.35rem;
  grid-template-columns:minmax(0,1fr);
  padding:.6rem;border:1px solid var(--line);border-radius:8px;background:var(--panel2);
}
.po-line input{min-width:0}
.po-line .pl-notes{grid-column:1/-1;font-size:.88rem}
@media (min-width:720px){
  .po-line{grid-template-columns:minmax(0,7rem) minmax(0,1fr) 5.5rem 6rem 2rem;align-items:center}
  .po-line .pl-del{justify-self:end}
}

/* The odometer on a fleet card's header — a fact you read across a list rather than one you go
   looking for, so it sits with the rego rather than in the grid below. Tabular figures so the
   numbers line up down a column of cards instead of jittering. */
.odo-chip{
  font-size:.78rem;font-weight:650;color:var(--mut);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;
  padding:.1rem .4rem;border:1px solid var(--line);border-radius:6px;
  white-space:nowrap;
}

/* ---- Telematics ---- */
/* A sub-item under Vehicles. Indented rather than nested in a collapsible group: there is one of
   them, and a fold-out that hides a single link is a control that exists to be clicked twice. */
.side-nav a.sub{padding-left:1.5rem;font-size:.92rem}
.side-nav a.sub i{font-size:1rem}

.tel2col{display:grid;gap:1rem;grid-template-columns:1fr 1fr;margin-bottom:1rem}
@media(max-width:900px){.tel2col{grid-template-columns:1fr}}
.telchart{display:flex;flex-direction:column;gap:.45rem}
.telbar-row{display:flex;align-items:center;gap:.6rem;font-size:.84rem}
.tb-l{width:6.6rem;flex:none;color:var(--mut)}
.tb-track{flex:1;height:.72rem;background:var(--panel2);border-radius:6px;overflow:hidden}
.tb-fill{display:block;height:100%;border-radius:6px}
.tb-n{width:2.4rem;flex:none;text-align:right;font-variant-numeric:tabular-nums;font-weight:700}
/* One scale for the whole page: green is talking, amber has gone quiet, red is long gone, grey
   never started. The same four colours carry the bars, the pills and the type table, so a colour
   means the same thing wherever it appears. */
.tb-fill.live,.telpill.live{background:var(--ok)}
.tb-fill.quiet,.telpill.quiet{background:var(--soon)}
.tb-fill.silent,.telpill.silent{background:var(--bad)}
.tb-fill.never,.telpill.never{background:var(--faint)}
.telpill{display:inline-block;padding:.08rem .45rem;border-radius:999px;font-size:.73rem;
  font-weight:700;color:#fff;white-space:nowrap;font-variant-numeric:tabular-nums}

.teltable{width:100%;border-collapse:collapse;font-size:.85rem}
.teltable th{text-align:left;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--mut);font-weight:800;padding:.3rem .45rem;border-bottom:1px solid var(--line)}
.teltable td{padding:.4rem .45rem;border-bottom:1px solid var(--line);vertical-align:top}
.teltable tr:last-child td{border-bottom:0}
.teltable .num{text-align:right;font-variant-numeric:tabular-nums}
.teltable th.num{text-align:right}

.tel-row{cursor:default}
.tel-row[data-veh]{cursor:pointer}
.tel-meta{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;margin-top:.2rem;font-size:.8rem}
.tel-loc{font-size:.8rem;color:var(--mut)}
/* A fix older than a month is faded, not hidden. For a trailer that walked off a site two years
   ago it is the only lead there is; dropping it to keep the column tidy throws that away. */
.tel-loc.stale{opacity:.55;font-style:italic}
.teljob{font-size:.78rem;background:var(--accent-soft);color:var(--accent2);
  padding:.08rem .45rem;border-radius:999px;font-weight:700}
.tel-nomatch{color:var(--faint);font-weight:700}
/* A device bolted to a TN360 vehicle that Rivet has never heard of. Not an error — it is usually
   plant Rivet does not carry — but it is why a count here will not match a count on the fleet page,
   and an unexplained mismatch is the kind of thing that gets a working screen distrusted. */
.tel-warn{color:var(--soon);font-weight:700}

/* The printed telematics report. Same approach as the label sheet: hide the app, let the overlay
   become the page. One stylesheet doing both jobs beats a print-only copy that drifts. */
@media print{
  body.tel-print>*:not(.qr-overlay){display:none !important}
  body.tel-print .qr-overlay{position:static;background:none;padding:0;display:block}
  body.tel-print .qr-card{max-width:none;max-height:none;overflow:visible;box-shadow:none;border:none;padding:0}
  body.tel-print .noprint{display:none !important}
  /* A row split across a page break is a row somebody has to read twice to be sure of. */
  body.tel-print .teltable tr{break-inside:avoid}
  body.tel-print .teltable thead{display:table-header-group}
  body.tel-print .teltable{font-size:9pt}
}
.telreport .teltable td{vertical-align:top}

/* ---- collapsible nav sections ---- */
/* The header is styled as a nav row rather than a button so the eye reads one list, not a list with
   two controls in it. It carries no accent bar: it is a lid, not a destination. */
.nav-group{display:flex;flex-direction:column;gap:.12rem}
.nav-ghead{display:flex;align-items:center;gap:.7rem;padding:.6rem .7rem;border-radius:10px;width:100%;
  font-family:inherit;font-weight:600;font-size:.92rem;color:var(--ink2);background:none;border:0;
  cursor:pointer;text-align:left}
.nav-ghead i{font-style:normal;font-size:1.15rem;width:1.4rem;text-align:center;line-height:1}
.nav-ghead span{flex:1;min-width:0}
.nav-ghead:hover{background:var(--panel2)}
.nav-ghead .caret{font-size:.7rem;opacity:.55;transition:transform .15s ease;font-weight:400}
.nav-group.open .nav-ghead .caret{transform:rotate(180deg)}
/* Closed is display:none rather than height:0 — a hidden row that still answers to keyboard focus
   is how somebody tabs into a menu they cannot see. */
.nav-gbody{display:none;flex-direction:column;gap:.12rem}
.nav-group.open .nav-gbody{display:flex}
.nav-gbody a{padding-left:1.5rem;font-size:.9rem}
.nav-gbody a i{font-size:1rem}
/* A section holding the page you are on is marked even when you have shut it, so a collapsed menu
   still says where you are. */
.nav-group:not(.open):has(a.active) .nav-ghead{color:var(--accent2);background:var(--accent-soft)}

/* ---- reporting a problem ---- */
.navbadge{margin-left:auto;background:var(--bad);color:#fff;font-size:.68rem;font-weight:800;
  min-width:1.15rem;height:1.15rem;border-radius:999px;display:inline-flex;align-items:center;
  justify-content:center;padding:0 .3rem;font-style:normal}
/* A count inside a folded section is not a flag, so the heading carries a dot of its own. */
.nav-group.has-flag:not(.open) .nav-ghead::after{content:"";width:.45rem;height:.45rem;border-radius:50%;
  background:var(--bad);margin-left:auto;flex:none}
.nav-group.has-flag:not(.open) .nav-ghead .caret{margin-left:.4rem}

.fbpill{display:inline-block;padding:.05rem .4rem;border-radius:999px;font-size:.68rem;font-weight:800;color:#fff}
.fbpill.bad{background:var(--bad)} .fbpill.warn{background:var(--soon)}
.fbpill.good{background:var(--ok)} .fbpill.mut{background:var(--faint)}
.fb-row{cursor:pointer}
.fb-facts{display:flex;flex-wrap:wrap;gap:.35rem}
.fb-facts span{font-size:.72rem;background:var(--panel2);border:1px solid var(--line);
  border-radius:999px;padding:.1rem .5rem;color:var(--mut)}
/* Monospace and scrollable: it is a log, and wrapping a stack trace to fit makes it unreadable. */
.fb-diag{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.7rem;line-height:1.35;
  background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:.5rem .6rem;
  margin:.4rem 0 0;max-height:240px;overflow:auto;white-space:pre}
.fb-errs{font-size:.76rem;margin-top:.4rem;display:flex;flex-direction:column;gap:.25rem}
.fb-errs>div{background:var(--bad-bg);border-radius:6px;padding:.25rem .45rem}
