/* Inter - the closest match to the Tellus Power logo lettering. Self-hosted. */
@font-face{font-family:"Inter";font-style:normal;font-weight:100 900;font-display:swap;src:url("/webfonts/inter-latin-wght-normal.woff2") format("woff2")}
:root{ /* default skin: Charcoal */
  --bg:#1c1e21; --panel:#26292d; --panel2:#212327; --line:#383c42;
  --text:#eceef1; --muted:#a3a9b3; --accent:#2fb4cc; --accent2:#56c6da;
  --good:#34d399; --warn:#fbbf24; --bad:#f87171; --gate:#a78bfa;
  --radius:12px; font-size:15px;
}
body.theme-tpgwhite{
  --bg:#f7f8f9; --panel:#ffffff; --panel2:#eff1f3; --line:#dde1e5;
  --text:#17191c; --muted:#5f6670;
  --accent:#0b7c93; --accent2:#0a667960;
  --good:#0a6b3d; --warn:#b45309; --bad:#dc2626; --gate:#7c3aed;
}
/* Bold reads green — the request was literally "if something is bolded its bolded in green".
   Scoped to content areas: a bold LABEL inside a control is furniture, not emphasis. */
body.theme-tpgwhite main b, body.theme-tpgwhite main strong,
body.theme-tpgwhite .modal b, body.theme-tpgwhite .modal strong{color:var(--accent)}
/* …except where bold already means something else, or sits on a coloured chip. */
body.theme-tpgwhite .pill b, body.theme-tpgwhite .badge b, body.theme-tpgwhite th b,
body.theme-tpgwhite .chatmeta b, body.theme-tpgwhite .tixrow b{color:inherit}

body.light{ /* Daylight */
  --bg:#f3f6fb; --panel:#ffffff; --panel2:#eef2f8; --line:#d8e0ec;
  --text:#16202e; --muted:#5b6b82; --accent:#0891b2; --accent2:#0284c7;
  --good:#059669; --warn:#b45309; --bad:#dc2626; --gate:#7c3aed;
}
body.theme-midnight{ /* Midnight (original dark) */
  --bg:#0b0f17; --panel:#121826; --panel2:#0e1422; --line:#1f2a3d;
  --text:#e6edf7; --muted:#8b9bb4; --accent:#22d3ee; --accent2:#38bdf8;
  --good:#34d399; --warn:#fbbf24; --bad:#f87171; --gate:#a78bfa;
}
body.theme-graphite{
  --bg:#1c1e21; --panel:#26292d; --panel2:#212327; --line:#383c42;
  --text:#eceef1; --muted:#a3a9b3; --accent:#2fb4cc; --accent2:#56c6da;
  --good:#34d399; --warn:#fbbf24; --bad:#f87171; --gate:#a78bfa;
}
body.theme-paper{ /* Paper (warm light) */
  --bg:#f5f1e8; --panel:#fffdf7; --panel2:#efe9db; --line:#ddd3bf;
  --text:#2b2620; --muted:#6b6353; --accent:#b45309; --accent2:#d97706;
  --good:#059669; --warn:#b45309; --bad:#dc2626; --gate:#7c3aed;
}
body.theme-tellusblue{
  --bg:#0a1a1e; --panel:#102529; --panel2:#0c1f23; --line:#1c3a40;
  --text:#e4f1f3; --muted:#8aa9ae; --accent:#22b8cf; --accent2:#0b7c93;
  --good:#34d399; --warn:#fbbf24; --bad:#f87171; --gate:#a78bfa;
}
/* theme picker dropdown */
.theme-menu{position:fixed;z-index:200;background:var(--panel);border:1px solid var(--line);
  border-radius:10px;padding:6px;box-shadow:0 10px 30px rgba(0,0,0,.35);display:flex;flex-direction:column;gap:2px;min-width:178px}
.theme-menu button{display:flex;align-items:center;width:100%;text-align:left;background:none;border:1px solid transparent;
  color:var(--text);border-radius:7px;padding:7px 10px;font-size:13px;cursor:pointer}
.theme-menu button:hover{background:var(--panel2);border-color:var(--line)}
.theme-menu button.cur{border-color:var(--accent);color:var(--accent);font-weight:700}
.theme-menu .ico{margin-right:7px;font-size:14px;width:18px;text-align:center;flex-shrink:0}
.theme-menu .sw{display:inline-block;width:14px;height:14px;border-radius:4px;margin-right:9px;flex-shrink:0;border:1px solid rgba(255,255,255,.18)}
*{box-sizing:border-box;margin:0;padding:0}
html{height:100%;overflow:hidden}
/* The emoji families are load-bearing, not decoration: EVERY nav icon, every button glyph and the
   whole toolbar are emoji characters, not an icon font or SVGs. A stack that ends at `sans-serif`
   leaves them to whatever the text font happens to cover, and a font with no emoji coverage draws
   them as blank boxes — the entire sidebar loses its icons (Chris, 2026-08-16). Emoji families go
   AFTER the text families: they only supply emoji glyphs, so they never capture ordinary text. */
/* `dvh` everywhere `vh` used to be. iOS Safari counts its collapsing toolbars as part of `100vh`,
   so a `100vh` app is TALLER than the visible viewport and its bottom row sits permanently under
   the browser chrome, unreachable — the page can't scroll to it because the shell is
   `overflow:hidden` by design. `dvh` tracks the viewport as the toolbars move. The `vh` line stays
   first as the fallback for anything that doesn't know `dvh`. */
body{background:var(--bg);color:var(--text);font-family:"Segoe UI",system-ui,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji","Android Emoji",sans-serif;height:100vh;height:100dvh;overflow:hidden}
a{color:var(--accent)}
button{font-family:inherit}

/* ---------- shell ---------- */
header{display:flex;align-items:center;gap:14px;padding:10px 18px;border-bottom:1px solid var(--line);
  background:var(--panel);position:sticky;top:0;z-index:50}
.brand{display:flex;align-items:center;gap:10px;font-weight:700;white-space:nowrap}
.brand .dot{width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,var(--accent),#6366f1);
  display:grid;place-items:center;color:#04141a;font-weight:900}
.search{flex:1;max-width:640px;display:flex;align-items:center;gap:8px;background:var(--panel2);
  border:1px solid var(--line);border-radius:10px;padding:8px 14px;color:var(--muted)}
.search input{flex:1;background:none;border:none;outline:none;color:var(--text);font-size:14px}
.search .shist{background:none;border:0;color:var(--muted);cursor:pointer;font-size:12px;padding:0 2px;line-height:1;border-radius:4px}
.search .shist:hover:not(:disabled){color:var(--text);background:var(--line)}
.search .shist:disabled{opacity:.3;cursor:default}
.kbd{border:1px solid var(--line);border-radius:5px;padding:1px 6px;font-size:11px;color:var(--muted)}
.hbtn{background:var(--panel2);border:1px solid var(--line);color:var(--text);border-radius:9px;
  padding:7px 12px;cursor:pointer;font-size:14px}
.hbtn:hover{border-color:var(--accent)}
/* Both exist only below 900px: the sidebar is permanent above it, and the search field fits inline. */
#navToggle{display:none;font-size:17px;line-height:1;padding:7px 10px}
#searchToggle{display:none;font-size:15px;line-height:1}
.layout{display:flex;height:calc(100vh - 53px);height:calc(100dvh - 53px);min-height:0}
nav{width:196px;border-right:1px solid var(--line);background:var(--panel);padding:12px 8px;flex-shrink:0;overflow-y:auto}
nav a{display:flex;gap:9px;align-items:center;color:var(--muted);text-decoration:none;padding:9px 12px;
  border-radius:9px;font-size:14px;margin-bottom:2px;border-left:3px solid transparent}
nav a.active{background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 16%,transparent),transparent);color:var(--text);
  border-left-color:var(--accent)}
nav a:hover{color:var(--text)}
main{flex:1;padding:20px 24px;overflow-x:hidden;overflow-y:auto;min-width:0}
h1{font-size:19px;margin-bottom:2px}
.sub{color:var(--muted);font-size:13px;margin-bottom:18px}
.section-title{font-size:12px;letter-spacing:1.6px;color:var(--muted);text-transform:uppercase;margin:22px 0 10px}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}

/* ---------- widgets ---------- */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:12px}
.kpi{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px;cursor:pointer}
.kpi:hover{border-color:var(--accent)}
.kpi .v{font-size:22px;font-weight:700;margin-top:2px}
.kpi .l{font-size:12px;color:var(--muted)}
.kpi .d{font-size:11px;margin-top:2px;color:var(--muted)}
.up{color:var(--good)} .down{color:var(--bad)}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:16px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:980px){.grid2{grid-template-columns:1fr}}
/* report grid: give the wide yield table more room than the compact NCR chart */
.rgrid{display:grid;grid-template-columns:1.7fr 1fr;gap:20px;align-items:start;margin-bottom:6px}
@media(max-width:1100px){.rgrid{grid-template-columns:1fr}}

/* ---- Process Flow (horizontal lifecycle pipe — reference map) ---- */
.flowchip{font-size:12px;border:1px solid var(--line);border-radius:8px;padding:5px 9px;color:var(--muted);white-space:nowrap}
.flowchip.gate{border-color:var(--warn);color:var(--warn)}
.flowchip.gate::before{content:'◆ '}
.flowchip.accent{border-color:var(--accent);color:var(--accent)}
/* The five stations measure ~914px. Anything narrower than that — an iPad either way up, a small
   laptop, a desktop with the window half-screened — was CLIPPING stations 4 and 5, because `main`
   is `overflow-x:hidden`. Its own scroller keeps every station reachable; on a wide screen the
   content fits and no scrollbar appears. */
#scbanner{overflow-x:auto;overscroll-behavior-x:contain}
.pflow{display:flex;align-items:stretch;min-width:min-content;padding:4px 2px 10px}
.pnode{position:relative;flex:1 0 160px;min-width:160px;max-width:230px;background:var(--panel2);border:1px solid var(--line);border-radius:14px;padding:14px 13px;margin-right:26px;display:flex;flex-direction:column;gap:9px}
.pnode:last-child{margin-right:0}
.pnode::after{content:"▸";position:absolute;right:-19px;top:50%;transform:translateY(-50%);color:var(--accent2);font-size:22px;opacity:.85}
.pnode:last-child::after{content:""}
.pnode.start{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.pnode.active{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.pnode.pdone{border-color:var(--good)}
.pnode.pdim{opacity:.42}
.pcount{display:inline-block;min-width:18px;text-align:center;background:var(--accent);color:#04141a;border-radius:9px;font-size:11px;font-weight:800;padding:1px 6px;vertical-align:middle}
.pcount.done{background:var(--good)}
.pnum{width:28px;height:28px;border-radius:50%;background:var(--accent);color:#04141a;font-weight:800;font-size:14px;display:grid;place-items:center}
.pnode.start .ptitle::after{content:" · START";color:var(--accent);font-weight:800;font-size:11px}
.ptitle{font-weight:700;font-size:14px;line-height:1.25}
.pchips{display:flex;flex-wrap:wrap;gap:5px}
.proutes{display:flex;flex-direction:column;gap:9px}
.proute{display:flex;flex-wrap:wrap;align-items:center;gap:5px}
.rlabel{font-size:10px;font-weight:800;padding:3px 7px;border-radius:6px;text-transform:uppercase;letter-spacing:.5px}
.rlabel.warn{background:var(--warn);color:#04141a}
.rlabel.good{background:var(--good);color:#04141a}

/* ---- step rows (planning wizard) ---- */
.prow{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:11px 0;border-top:1px solid var(--line)}
.prow .picn{font-size:15px;font-weight:800;flex-shrink:0;width:20px;text-align:center}
.prow .ptl{font-weight:600;font-size:14px}
@media(max-width:640px){.prow{flex-direction:column;align-items:stretch;gap:8px}.prow .btn{width:100%;text-align:center}}

/* ---------- kanban (forms) ---------- */
.kanban{display:flex;gap:14px;overflow-x:auto;padding-bottom:10px;align-items:flex-start}
.kcol{flex:0 0 270px;background:var(--panel2);border:1px solid var(--line);border-radius:var(--radius);
  display:flex;flex-direction:column;max-height:calc(100vh - 230px);max-height:calc(100dvh - 230px)}
.kcol-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 12px;
  border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--panel2);
  border-radius:var(--radius) var(--radius) 0 0;z-index:1}
.kcol-title{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kcount{flex-shrink:0;font-size:11px;color:var(--muted);background:var(--panel);border:1px solid var(--line);
  border-radius:10px;padding:1px 8px}
.kcol-body{padding:9px;overflow-y:auto;display:flex;flex-direction:column;gap:9px}
.kcard{background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:9px 10px;
  cursor:pointer;transition:border-color .15s,transform .15s}
.kcard:hover{border-color:var(--accent);transform:translateY(-1px)}
.kserials{font-size:13px;font-weight:600;line-height:1.3}
.kserials a{text-decoration:none}
.kmuted{color:var(--muted);font-weight:400}
.kemail{font-size:12px;color:var(--accent2);margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kdate{font-size:11px;color:var(--muted);margin-top:3px}
.kprev{font-size:11px;color:var(--muted);margin-top:5px;line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

/* ---------- production schedules ---------- */
.schedtbl{border-collapse:collapse;width:100%;font-size:12.5px}
.schedtbl{table-layout:auto}
.schedtbl th,.schedtbl td{border:1px solid var(--line);padding:5px 8px;text-align:left;
  min-width:150px;max-width:340px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.schedtbl thead th{position:sticky;background:var(--panel);font-weight:600;cursor:pointer}
.schedtbl thead tr:first-child th{top:0;z-index:2}
.schedtbl thead tr.filterrow th{top:30px;z-index:1;padding:2px 4px;cursor:default;font-weight:400}
.colfbtn{width:100%;background:var(--panel2);border:1px solid var(--line);color:var(--muted);border-radius:5px;
  cursor:pointer;font-size:11px;padding:1px 4px;font-family:inherit}
.colfbtn:hover{border-color:var(--accent);color:var(--text)}
.colfbtn.active{background:var(--accent);color:#04141a;border-color:var(--accent);font-weight:700}
.colfpop{position:fixed;z-index:1000;width:340px;max-width:94vw;max-height:calc(100vh - 20px);max-height:calc(100dvh - 20px);background:var(--panel);border:1px solid var(--line);
  border-radius:10px;box-shadow:0 12px 38px rgba(0,0,0,.5);padding:12px;display:flex;flex-direction:column;gap:9px}
.colfpop .cfsearch{width:100%;min-width:0;font-size:13px;padding:6px 8px}
.colfpop .cfall{font-size:13px;font-weight:600;display:flex;gap:8px;align-items:center;border-bottom:1px solid var(--line);padding-bottom:7px}
.colfpop .cflist{max-height:62vh;overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding:2px 0}
.colfpop .cflist label{font-size:13.5px;display:flex;gap:10px;align-items:center;padding:9px 10px;border-radius:6px;
  cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:22px}
.colfpop .cflist label:hover{background:var(--panel2)}
.colfpop .cflist input[type=checkbox]{width:16px;height:16px;flex:0 0 auto;margin:0;cursor:pointer}
.colfpop .cfbtns{display:flex;gap:6px;border-top:1px solid var(--line);padding-top:6px}
.colfpop .cfbtns .btn{flex:1;padding:5px}
.schedtbl tbody td[contenteditable]{cursor:text}
.schedtbl tbody td[contenteditable]:focus{outline:2px solid var(--accent);outline-offset:-2px;background:var(--panel);white-space:normal}
/* admin Users table: fit all columns (incl. the action buttons) into the pane — no horizontal scroll. */
#admBody .schedtbl{table-layout:fixed}
#admBody .schedtbl input,#admBody .schedtbl select{min-width:0;width:100%}
#admBody .schedtbl th,#admBody .schedtbl td{overflow:hidden}
/* Chris, 2026-08-28: "user screen is too bunched" - this table gained an 8th column (Status)
   at some point and these widths were never updated for it. table-layout:fixed with percentages
   that already summed to 100% across 7 columns left the real last column (Actions - Save, Overrides,
   invite, password, delete: five buttons) with nothing, so it was squeezed to whatever table-layout
   could scrape together rather than the 21%-and-nowrap treatment the rule below was written for -
   that treatment was landing on Status instead, one column early. Re-split across all 8. */
#admBody .schedtbl th:nth-child(1),#admBody .schedtbl td:nth-child(1){width:6%}
#admBody .schedtbl th:nth-child(2),#admBody .schedtbl td:nth-child(2){width:13%}
#admBody .schedtbl th:nth-child(3),#admBody .schedtbl td:nth-child(3){width:19%}
#admBody .schedtbl th:nth-child(4),#admBody .schedtbl td:nth-child(4){width:12%}
#admBody .schedtbl th:nth-child(5),#admBody .schedtbl td:nth-child(5){width:11%}
#admBody .schedtbl th:nth-child(6),#admBody .schedtbl td:nth-child(6){width:9%}
#admBody .schedtbl th:nth-child(7),#admBody .schedtbl td:nth-child(7){width:9%}
#admBody .schedtbl th:nth-child(8),#admBody .schedtbl td:nth-child(8){width:21%;white-space:nowrap}
#admBody .schedtbl td:nth-child(8) .btn{padding:6px 8px;margin:0 2px 0 0}
.schedtbl tbody tr:nth-child(even){background:var(--panel2)}
.schedtbl tbody tr:hover{background:rgba(34,211,238,.08)}
.sheettab{background:var(--panel2);border:1px solid var(--line);color:var(--text);border-radius:8px;
  padding:6px 12px;cursor:pointer;font-size:13px;font-family:inherit;white-space:nowrap}
.sheettab:hover{border-color:var(--accent)}
.sheettab.active{background:var(--accent);color:#04141a;border-color:var(--accent);font-weight:600}

/* ---------- customizable sidebar ---------- */
.nav-sec{font-size:11px;letter-spacing:1.4px;text-transform:uppercase;color:var(--muted);
  padding:12px 12px 4px;display:flex;align-items:center;gap:6px}
.nav-sec-label{flex:1}
.nav-sec-label[contenteditable]{outline:1px dashed var(--line);border-radius:4px;padding:1px 5px;cursor:text}
.nav-grip{cursor:grab;color:var(--muted);font-size:13px}
.nav-secdel{cursor:pointer;color:var(--bad);font-size:12px}
.nav-editcaret{cursor:pointer;color:var(--muted);font-size:10px;width:12px;display:inline-block;text-align:center}
.nav-editcaret:hover{color:var(--accent)}
.nav-seccount{font-size:10px;color:var(--muted);border:1px solid var(--line);border-radius:20px;
  padding:0 5px;margin-left:auto;font-variant-numeric:tabular-nums}
.nav-sec[draggable="true"]{cursor:grab}
.nav-drop{outline:2px dashed var(--accent);outline-offset:-2px;border-radius:8px}
.nav-tools{display:flex;flex-direction:column;gap:5px;padding:14px 6px 4px;margin-top:10px;border-top:1px solid var(--line)}
.navbtn{background:var(--panel2);border:1px solid var(--line);color:var(--text);border-radius:8px;
  padding:6px 10px;cursor:pointer;font-size:12px;font-family:inherit;text-align:left}
.navbtn:hover{border-color:var(--accent)}
.navbtn.primary{background:var(--accent);color:#04141a;border-color:var(--accent);font-weight:600}
nav a[draggable="true"]{cursor:grab}
/* ---------- accordion sidebar (2026-08-06) ----------
   38 always-visible rows collapse to 17 group rows; one group open at a time. Solo groups are a
   single destination and render as a plain link, so nothing opens to reveal one child. */
.nav-grp{margin-bottom:2px}
.nav-ghead{display:flex;align-items:center;gap:9px;width:100%;font-family:inherit;font-size:14px;
  padding:9px 12px;background:none;border:0;border-left:3px solid transparent;border-radius:9px;
  color:var(--muted);cursor:pointer;text-align:left;transition:color .16s ease,background .16s ease}
.nav-ghead:hover{color:var(--text);background:var(--panel2)}
.nav-ghead:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.nav-gic{flex:0 0 auto;width:18px;text-align:center}
/* WRAPS, and does not ellipsize. At 196px the sidebar cannot show a two-word section name on
   one line — "Technical Resources" came out as "Technical Resour…", which is worse than a
   second line: a truncated name is unreadable, a wrapped one costs 18px. min-width:0 stays,
   or the flex item refuses to shrink below its longest word and overflows the rail instead. */
.nav-gnm{flex:1;min-width:0;white-space:normal;overflow-wrap:break-word;line-height:1.25}
/* DRAWN, NOT TYPED. This was a 10px "▸" glyph - small, and it renders slightly differently in
   every font the themes fall back to. Two borders on an empty box give a chevron with real stroke
   weight that stays crisp at any size and turns on a proper curve rather than a linear flip. The
   glyph in the markup is hidden with font-size:0 rather than removed, so nothing in the JS changes. */
.nav-gchev{flex:0 0 auto;font-size:0;width:7px;height:7px;margin-right:3px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;border-radius:0 0 2px 0;
  color:var(--muted);opacity:.5;transform:rotate(-45deg);transform-origin:65% 65%;
  transition:transform .24s cubic-bezier(.34,1.3,.5,1),opacity .18s ease}
.nav-ghead:hover .nav-gchev{opacity:.85}
.nav-grp.open>.nav-ghead{color:var(--text)}
.nav-grp.open>.nav-ghead .nav-gchev{transform:rotate(45deg);opacity:.7}
/* the group holding the current page keeps the accent marker even while you look at another group */
.nav-grp.holds>.nav-ghead{border-left-color:var(--accent)}
/* grid-template-rows 0fr->1fr animates open without needing a measured pixel height */
.nav-kids{display:grid;grid-template-rows:0fr;transition:grid-template-rows .22s cubic-bezier(.22,1,.36,1)}
.nav-grp.open .nav-kids{grid-template-rows:1fr}
.nav-kids>div{overflow:hidden}
nav a.nav-kid{padding:7px 12px 7px 39px;font-size:13.5px;margin-bottom:1px;border-left:3px solid transparent}
.nav-kic{display:none}   /* child icons show only in the mobile drawer — see the max-width:900px block */
nav a.nav-kid:hover{background:var(--panel2)}
nav a.nav-solo{font-size:14px}
@media (prefers-reduced-motion:reduce){
  .nav-kids{transition:none}.nav-gchev{transition:none}}
/* ---------- subtabs + collapsible sections (2026-08-06) ---------- */
.subtabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin:14px 0 0;flex-wrap:wrap}
.subtab{font-family:inherit;font-size:14px;padding:8px 14px;background:none;border:0;color:var(--muted);
  cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color .16s ease,border-color .16s ease}
.subtab:hover{color:var(--text)}
.subtab.on{color:var(--accent);border-bottom-color:var(--accent);font-weight:600}
.subtab:focus-visible{outline:2px solid var(--accent);outline-offset:-3px;border-radius:6px 6px 0 0}
.subtab .n{font-size:11.5px;color:var(--muted);margin-left:6px;font-variant-numeric:tabular-nums}
.subtab.on .n{color:var(--accent)}
.sect{border:1px solid var(--line);border-radius:12px;background:var(--panel);margin-bottom:10px;overflow:hidden}
.sect-head{display:flex;align-items:center;gap:10px;width:100%;font-family:inherit;font-size:14px;
  padding:11px 14px;background:var(--panel);border:0;color:var(--text);cursor:pointer;text-align:left;
  transition:background .16s ease}
.sect-head:hover{background:var(--panel2)}
.sect-head:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.sect-chev{color:var(--muted);font-size:11px;flex:0 0 auto;transition:transform .2s ease}
.sect.open .sect-chev{transform:rotate(90deg)}
.sect-nm{font-weight:600}
.sect-cnt{font-size:11.5px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);
  border-radius:20px;padding:0 8px;font-variant-numeric:tabular-nums}
.sect-right{margin-left:auto;display:flex;align-items:center;gap:8px}
.sect-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .24s cubic-bezier(.22,1,.36,1)}
.sect.open .sect-body{grid-template-rows:1fr}
.sect-body>div{overflow:hidden;min-height:0}
.sect table{width:100%;border-collapse:collapse}
.sect thead th{background:var(--panel2);border-top:1px solid var(--line)}
/* toolbar shared by the list pages */
.ptools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:14px 0 12px}
/* one width for every box on the page, so their left AND right edges line up */
/* Was max-width:1120px — a hard cap that clipped the PO table on every screen, because the row
   carries up to six action buttons in a nowrap cell. Readable line length still matters, so it
   is a generous cap rather than none, and the table below scrolls if it needs to. */
.po-wrap{max-width:min(1600px,100%)}
.po-wrap .sect{margin-bottom:8px}
/* overflow:hidden on .sect protects the rounded corners; it also swallowed everything past the
   right edge. Scroll the CONTENT instead, so nothing is ever unreachable. */
.po-wrap .sect>.sect-body,.po-wrap .sect>table{overflow-x:auto;max-width:100%}
.po-wrap .sect table{min-width:0}
.po-detail{overflow-x:auto}
/* uniform header: same height whether it is a status group or the new-PO panel */
.po-wrap .sect-head{padding:10px 14px;min-height:41px}
.po-wrap .sect-nm{font-size:13.5px}
/* one padding token for every panel body */
.sect-pad{padding:14px}
.sect-body>.sect-pad{padding-top:0;padding-bottom:0;transition:padding .24s cubic-bezier(.22,1,.36,1)}
.sect.open .sect-body>.sect-pad{padding-top:14px;padding-bottom:14px}
.po-detail{background:var(--panel2);border-top:1px solid var(--line)}
/* tables inside a box sit flush to its edges and share one cell rhythm */
.po-wrap .sect table{font-size:13px}
.po-wrap .sect thead th{padding:7px 14px;font-size:10.5px}
.po-wrap .sect tbody td{padding:9px 14px;vertical-align:middle}
/* the detail form: compact, and every control the same height as every other */
.po-detail .section-title{margin:14px 0 6px;font-size:11px;letter-spacing:1.2px}
.po-detail label,.sect-pad label{display:block;font-size:12px;color:var(--muted)}
.po-detail label>input,.po-detail label>select,.po-detail label>textarea,
.sect-pad label>input,.sect-pad label>select,.sect-pad label>textarea{margin-top:4px;color:var(--text)}
.po-wrap input,.po-wrap select,.po-wrap textarea{border-radius:8px;padding:7px 10px;font-size:13.5px}
.po-wrap select{min-width:0}
.po-wrap .btn,.po-wrap .btn.ghost{padding:7px 13px;font-size:13px;border-radius:8px}
/* ---------- CRM: clickable KPI tiles ---------- */
.crmkpi{cursor:pointer;transition:border-color .16s ease,background .16s ease}
.crmkpi:hover{border-color:var(--accent)}
.crmkpi.on{border-color:var(--accent);background:var(--panel2)}
.kpi .ksub{font-size:11px;color:var(--muted);margin-top:2px}
/* ---------- training file library ---------- */
.trext{display:inline-block;min-width:34px;text-align:center;font-size:10px;font-weight:700;
  letter-spacing:.4px;color:var(--muted);background:var(--panel2);border:1px solid var(--line);
  border-radius:5px;padding:2px 4px}
.pagewrap .sect tbody a{color:var(--accent);text-decoration:none}
.pagewrap .sect tbody a:hover{text-decoration:underline}
/* ---------- training area cards ---------- */
.trgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:8px}
.trcard{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line);
  border-radius:10px;background:var(--panel);color:var(--text);text-decoration:none;
  transition:border-color .16s ease,background .16s ease}
.trcard:hover{border-color:var(--accent);background:var(--panel2)}
.trcard-n{flex:0 0 auto;min-width:34px;text-align:center;font-size:15px;font-weight:700;
  color:var(--accent);font-variant-numeric:tabular-nums}
.trcard-t{flex:1;min-width:0;font-size:13px;line-height:1.3}
.chip-note{display:inline-block;margin-top:4px;font-size:11px;color:var(--muted);
  background:var(--panel2);border:1px solid var(--line);border-radius:20px;padding:1px 8px;cursor:help}
/* expanded PO row */
tr.po-on > td{background:var(--panel2)}
tr.porow:hover > td{background:var(--panel2)}
@media (prefers-reduced-motion:reduce){.sect-body{transition:none}.sect-chev{transition:none}}

/* ---------- login ---------- */
#appLogin{position:fixed;inset:0;z-index:5000;background:var(--bg);display:flex;align-items:center;justify-content:center;padding:16px}
#appLogin .loginbox{width:330px;max-width:92vw;background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:26px 22px;box-shadow:0 24px 70px rgba(0,0,0,.55)}
#whoami{font-size:13px}

/* ---------- pipeline ---------- */
.pipewrap{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:22px 18px 14px}
.pipe{display:flex;flex-wrap:wrap;row-gap:34px}
.node{position:relative;display:flex;flex-direction:column;align-items:center;width:7.65%;min-width:74px;cursor:pointer}
.node .shape{width:46px;height:46px;display:grid;place-items:center;font-weight:800;font-size:11.5px;
  background:var(--panel2);border:2px solid var(--line);border-radius:50%;transition:.15s;position:relative;z-index:2}
.node.gate .shape{border-radius:10px;transform:rotate(45deg);border-color:var(--gate)}
.node.gate .shape span{transform:rotate(-45deg)}
/* sortable tables — header cells are clickable (the JS skips editable tables) */
thead th{cursor:pointer;user-select:none}
.node .nm{font-size:10.5px;color:var(--muted);margin-top:9px;text-align:center;line-height:1.25;max-width:86px}
.node .nm.shp-link{color:var(--accent);font-weight:700;text-decoration:none;cursor:pointer}
.node .nm.shp-link:hover{text-decoration:underline}
.node .cnt{position:absolute;top:-9px;right:calc(50% - 32px);background:var(--accent);color:#04141a;font-weight:800;
  font-size:11px;border-radius:999px;padding:1px 7px;z-index:3}
.node.empty .cnt{background:var(--line);color:var(--muted)}
/* Green count (.cnt, accent) = units IN PRODUCTION (NCR-held units excluded).
   Open NCRs get their own separate red TRIANGLE badge (.ncr) on the opposite corner. */
.node .ncr{position:absolute;top:-13px;left:calc(50% - 37px);background:var(--bad);color:#fff;font-weight:800;
  font-size:10px;line-height:1;z-index:3;width:26px;height:23px;display:grid;place-items:center;
  padding-top:7px;box-sizing:border-box;
  /* heavily rounded-corner triangle */
  clip-path:path('M10.5 6.3 Q13 2 15.5 6.3 L21.5 16.7 Q24 21 19 21 L7 21 Q2 21 4.5 16.7 Z')}
.node.alert .shape{border-color:var(--bad);box-shadow:0 0 10px rgba(248,113,113,.3);animation:pulse 1.6s infinite}
@keyframes pulse{50%{box-shadow:0 0 20px rgba(248,113,113,.6)}}
.node:not(:last-child)::after{content:"";position:absolute;top:22px;left:calc(50% + 26px);
  width:calc(100% - 52px);height:2px;background:linear-gradient(90deg,var(--line),var(--accent2));opacity:.55;z-index:1}
.node:not(:last-child)::before{content:"▸";position:absolute;top:13px;right:-7px;color:var(--accent2);opacity:.7;z-index:1}
.node.selected .shape{border-color:var(--accent);box-shadow:0 0 14px rgba(34,211,238,.45)}
.legend{display:flex;gap:18px;margin-top:18px;font-size:11.5px;color:var(--muted);flex-wrap:wrap}
.legend i{display:inline-block;width:11px;height:11px;border-radius:50%;margin-right:5px;vertical-align:-1px}
.legend .gi{border-radius:3px;transform:rotate(45deg)}

/* ---------- board ---------- */
.board{display:flex;gap:12px;overflow-x:auto;padding-bottom:10px}
.col{min-width:212px;flex:0 0 212px;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius)}
.col.hl{border-color:var(--accent)}
.col h3{font-size:12px;padding:10px 12px;border-bottom:1px solid var(--line);color:var(--muted);display:flex;justify-content:space-between}
.col h3 b{color:var(--text)}
.cards{padding:9px;display:flex;flex-direction:column;gap:8px;min-height:60px}
.card{background:var(--panel2);border:1px solid var(--line);border-left:3px solid var(--accent);border-radius:9px;
  padding:9px 11px;cursor:pointer;transition:.12s;position:relative}
.card:hover{transform:translateY(-2px);border-color:var(--accent)}
.card.fail{border-left-color:var(--bad)}
.card.prog{border-left-color:var(--warn)}
/* waiting on a 2nd sign-off: a small corner mark on the traveler card so it reads at a glance
   across the board, without pushing the serial or meta lines around */
.card.await{border-left-color:#f472b6}
.card.qcf{border-left-color:#38bdf8}
.card.await .sn,.card.qcf .sn,.card.ncro .sn{padding-right:74px}
/* three marks at once need more room than the two the 74px was sized for */
.card:has(.sig i+i+i) .sn{padding-right:104px}
/* corner marks — both can be up at once, so they sit side by side */
.card .sig{position:absolute;top:6px;right:7px;display:flex;gap:4px;line-height:1}
.card .sig i{font-style:normal;font-size:10px;font-weight:800;padding:2px 5px;border-radius:5px}
.card .sig .m-sig{background:rgba(244,114,182,.18);color:#f472b6;border:1px solid rgba(244,114,182,.55);font-size:12px;padding:1px 5px}
.card .sig .m-qc{background:rgba(56,189,248,.18);color:#38bdf8;border:1px solid rgba(56,189,248,.55)}
.card .sig .m-ncr{background:rgba(248,113,113,.18);color:var(--bad);border:1px solid rgba(248,113,113,.55)}
.card .sn{font-weight:700;font-size:13.5px}
.card .meta{font-size:11.5px;color:var(--muted);margin-top:3px;line-height:1.45}
.tag{display:inline-block;font-size:10px;font-weight:700;border-radius:5px;padding:1px 6px;margin:5px 4px 0 0}
.tag.fail{background:rgba(248,113,113,.16);color:var(--bad)}
.tag.att{background:rgba(251,191,36,.14);color:var(--warn)}
.tag.ok{background:rgba(52,211,153,.13);color:var(--good)}
.tag.prog{background:rgba(251,191,36,.14);color:var(--warn)}

/* ---------- tables & forms ---------- */
table{width:100%;border-collapse:collapse;font-size:13.5px}
th{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:var(--muted);text-align:left;
  padding:8px 10px;border-bottom:1px solid var(--line)}
td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
tr:hover td{background:rgba(34,211,238,.04)}
/* Christian, 2026-09-18: "the click has to be obvious light highlight the row" - Open Orders
 * rows jump to Planning on click; the generic tr:hover above is a 4% tint, too subtle to read as
 * clickable. This wins on specificity (class beats element) without needing !important. */
.dorow:hover td{background:var(--panel2)}
.badge{display:inline-block;font-size:11px;font-weight:700;border-radius:6px;padding:2px 8px}
/* STATUS BADGES OWN THEIR HUES instead of borrowing theme accents. "in progress" took --warn and
   "released" took --accent2, and on Graphite those two tokens are BOTH #fbbf24 - so the badges were
   pixel-identical. On Paper they were two browns, and on Tellus White --accent2 is #15603060, an
   8-digit hex whose 0x60 alpha left "released" barely legible. Hue is the thing that separates a
   status at a glance, so it is pinned here; only the text tone is re-tuned for the light skins
   below. The inset ring adds a second, non-colour cue for anyone who reads these by shape. */
.b-open,.b-in_progress{background:rgba(251,191,36,.16);color:#fbbf24;box-shadow:inset 0 0 0 1px rgba(251,191,36,.45)}
.b-planned,.b-queued,.b-pending{background:rgba(139,155,180,.15);color:var(--muted);box-shadow:inset 0 0 0 1px rgba(139,155,180,.32)}
.b-released{background:rgba(56,189,248,.16);color:#38bdf8;box-shadow:inset 0 0 0 1px rgba(56,189,248,.5)}
.b-complete,.b-passed,.b-closed,.b-shipped,.b-field{background:rgba(52,211,153,.15);color:var(--good);box-shadow:inset 0 0 0 1px rgba(52,211,153,.38)}
/* Light skins: the same hues, darkened enough to hold contrast on a pale background. */
body.theme-tpgwhite .b-open,body.theme-tpgwhite .b-in_progress,
body.light .b-open,body.light .b-in_progress,
body.theme-paper .b-open,body.theme-paper .b-in_progress{color:#b45309}
body.theme-tpgwhite .b-released,body.light .b-released,body.theme-paper .b-released{color:#0369a1}
.b-failed,.b-scrapped,.b-critical,.b-major{background:rgba(248,113,113,.16);color:var(--bad)}
.b-minor{background:rgba(251,191,36,.15);color:var(--warn)}
input,select,textarea{background:var(--panel2);border:1px solid var(--line);color:var(--text);
  border-radius:8px;padding:8px 11px;font-size:14px;font-family:inherit;outline:none}
input:focus,select:focus,textarea:focus{border-color:var(--accent)}
/* disabled = visibly disabled */
/* "same as billing" already set ship.disabled, but a disabled field looked identical to an
 * editable one, so the box read as broken rather than locked. Theme-safe: derives from the
 * same vars, so it holds in all three palettes. */
input:disabled,select:disabled,textarea:disabled{background:var(--bg);color:var(--muted);
  cursor:not-allowed;opacity:.7}
select{min-width:130px;cursor:pointer}
.btn{background:var(--accent);color:#04141a;border:none;border-radius:8px;padding:8px 16px;font-weight:700;
  cursor:pointer;font-size:13.5px}
.btn:hover{filter:brightness(1.12)}
.btn.ghost{background:var(--panel2);color:var(--text);border:1px solid var(--line)}
.btn.danger{background:var(--bad);color:#fff}
.btn.good{background:var(--good);color:#06281c}
.btn:disabled{opacity:.45;cursor:not-allowed}
.formrow{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin-bottom:14px}
.formrow label{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:var(--muted)}

/* ---------- bars ---------- */
.bars .bar{display:flex;align-items:center;gap:10px;margin-bottom:7px;font-size:12.5px}
.bars .bar .lbl{width:46px;color:var(--muted);font-weight:700}
.bars .bar .trk{flex:1;background:var(--panel2);border-radius:6px;height:16px;overflow:hidden}
.bars .bar .fil{height:100%;background:linear-gradient(90deg,var(--accent2),var(--accent));border-radius:6px}
.bars .bar.bad .fil{background:linear-gradient(90deg,#ef4444,var(--bad))}
.bars .bar .num{width:26px;text-align:right;font-weight:700}
/* Christian, 2026-09-15: "sold by part number.. make it a bar graph.. looks nicer" - part
 * numbers run far longer than a 3-letter stage code, so this variant widens the label column
 * (with ellipsis for the rare one that still doesn't fit) and gives the count a little more room
 * for 3-digit quantities. Same bar/track/fill visual language as WIP/Failures by stage. */
.bars.parts .bar .lbl{width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11.5px}
.bars.parts .bar .num{width:34px}

/* ---------- modal ---------- */
.overlay{position:fixed;inset:0;background:rgba(4,8,15,.72);display:none;place-items:center;z-index:100}
.overlay.open{display:grid}
/* tickets open near-full-screen for comfortable thread reading */
.modal.ticketfull{width:min(1400px,96vw)!important;max-width:96vw!important;height:93vh;max-height:93vh;display:flex;flex-direction:column;padding:0;overflow:hidden;position:relative}
/* The sales-order editor carries a serial + telemetry per line and does not fit the 760px default.
 * Widened again 2026-08-12 ("cant fit anything") — near-full-viewport, with tighter side padding so
 * the extra width goes to the table rather than the margins. The line table gets its own horizontal
 * scroll so a long SIM or Nayax id can never force the whole dialog sideways. */
.modal.sowide{width:min(1680px,98vw);padding:20px 18px}
/* The plan editor sits between the two: wider than a plain dialog so the model dropdown reads in
 * full, narrower than the sales-order table which genuinely needs the whole screen. */
.modal.planwide{width:min(1180px,96vw)}
.modal.sowide .panel{overflow-x:auto}
/* A notch larger than the 13px the other document editors use — this table is read across a wide
 * screen (serial, SIM, modem) rather than scanned in a narrow column, and 13px was hard going. */
.modal.sowide table{width:100%;font-size:15.5px}
.modal.sowide table input,.modal.sowide table select{font-size:15px}
.modal.sowide table th{font-size:13px;letter-spacing:.02em}
.modal.sowide .section-title{font-size:15px}
.modal.ticketfull>.close{position:absolute;top:14px;right:20px;float:none;z-index:4}
.modal.ticketfull .tkbody{flex:1 1 auto;min-height:0;overflow:auto;padding:22px 24px 14px}
.modal.ticketfull .tkfoot{flex:0 0 auto;background:var(--panel);border-top:2px solid var(--line);box-shadow:0 -12px 22px -8px rgba(0,0,0,.30);padding:12px 24px;border-radius:0 0 16px 16px;position:relative;z-index:2}
.modal.ticketfull #convList .ai-msg{max-width:100%}
/* A LINGERING .ticketfull IS WHY MODALS OPENED CUT OFF AND OFF-CENTRE. openTicket() adds the class to
 * the shared #modalBody and nothing removed it, so the next dialog (sales order, unit traveler, quote)
 * inherited height:93vh + padding:0 + overflow:hidden. Full-screen styling now applies only while the
 * ticket's own .tkbody is actually on the page; anything else falls back to a normal centred dialog. */
.modal.ticketfull:not(:has(.tkbody)){
  width:min(760px,94vw)!important;max-width:94vw!important;height:auto!important;max-height:88vh!important;
  display:block!important;padding:22px 24px!important;overflow:auto!important;border-radius:16px}
.modal.ticketfull:not(:has(.tkbody))>.close{position:static;float:right;background:none;border:0;width:auto;height:auto}
/* Standard packs: seven editable columns and an add-row, which the 760px default cropped - Chris
 * could not see the full screen. Same treatment as .sowide: near-full width, tighter side padding so
 * the gain goes to the table, and the table scrolls inside itself rather than pushing the dialog. */
.modal.packwide{width:min(1400px,96vw)!important;max-width:96vw!important;max-height:90vh;overflow:auto;padding:20px 18px}
.modal.packwide .panel{overflow-x:auto}
.modal.packwide table{width:100%;font-size:14.5px}
.modal.packwide table th{font-size:13px;letter-spacing:.02em}
/* ---------- ticket window polish (2026-07-25) ----------
 * Slim, low-contrast scrollbars everywhere: the app stacks scroll regions (page, modal body, tables)
 * and default chrome bars made every one of them shout. */
*{scrollbar-width:thin;scrollbar-color:rgba(140,150,165,.30) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(140,150,165,.28);border-radius:8px;border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:rgba(140,150,165,.55);background-clip:content-box}
::-webkit-scrollbar-corner{background:transparent}
/* no stepper arrows — they're the ugliest part of a default bar and nobody clicks them */
::-webkit-scrollbar-button{display:none;width:0;height:0}
/* Quiet until you're actually in the region. Every scroll area in the app is listed here: sidebar,
 * page body, modals, ticket body, assistant log, kanban columns, panels and dropdown lists. */
nav,main,.modal,.tkbody,.ai-log,.kcol-body,.panel,.colfpop .cflist,.serchips,.board,.kanban{
  scrollbar-color:rgba(140,150,165,.15) transparent}
nav:hover,main:hover,.modal:hover,.tkbody:hover,.ai-log:hover,.kcol-body:hover,.panel:hover,
.colfpop .cflist:hover,.serchips:hover,.board:hover,.kanban:hover{scrollbar-color:rgba(140,150,165,.45) transparent}
nav::-webkit-scrollbar-thumb,main::-webkit-scrollbar-thumb,.modal::-webkit-scrollbar-thumb,
.tkbody::-webkit-scrollbar-thumb,.ai-log::-webkit-scrollbar-thumb,.kcol-body::-webkit-scrollbar-thumb,
.panel::-webkit-scrollbar-thumb,.colfpop .cflist::-webkit-scrollbar-thumb,.serchips::-webkit-scrollbar-thumb,
.board::-webkit-scrollbar-thumb,.kanban::-webkit-scrollbar-thumb{
  background:rgba(140,150,165,.14);background-clip:content-box}
nav:hover::-webkit-scrollbar-thumb,main:hover::-webkit-scrollbar-thumb,.modal:hover::-webkit-scrollbar-thumb,
.tkbody:hover::-webkit-scrollbar-thumb,.ai-log:hover::-webkit-scrollbar-thumb,.kcol-body:hover::-webkit-scrollbar-thumb,
.panel:hover::-webkit-scrollbar-thumb,.colfpop .cflist:hover::-webkit-scrollbar-thumb,
.serchips:hover::-webkit-scrollbar-thumb,.board:hover::-webkit-scrollbar-thumb,.kanban:hover::-webkit-scrollbar-thumb{
  background:rgba(140,150,165,.45);background-clip:content-box}
/* the sidebar gets the slimmest bar of all — it is 196px wide, a fat bar eats the labels */
nav::-webkit-scrollbar{width:8px}
nav::-webkit-scrollbar-thumb{border-width:2.5px}
/* reserve the gutter so the page doesn't jump sideways when the bar fades in */
main{scrollbar-gutter:stable}
/* bottoming out an inner list shouldn't start scrolling the page behind it */
/* `.panel` REMOVED from this list. overscroll-behavior:contain stops a scroll container handing the
   wheel back to the page when it has nothing left to scroll - correct for a ticket body, the
   assistant log, a kanban column or a filter popover, all of which are deliberate inner scrollers.
   `.panel` is the generic container used on every screen in the ERP, and a great many are written
   <div class="panel" style="padding:0;overflow:auto">, which makes them scroll containers too. With
   `contain` on them the wheel died over any panel and the page stood still - you had to reach for
   the scrollbar to move down. The rest of the list keeps the behaviour it was written for. */
.tkbody,.ai-log,.kcol-body,.colfpop .cflist,.serchips{overscroll-behavior:contain}
/* The title scrolls WITH the thread. It was sticky, which meant it sat on top of the From/To lines
 * and the first messages — a permanently visible subject is not worth covering the content. */
.modal.ticketfull .tkbody>h2{position:static;display:flex;margin:0 0 12px;padding:0 52px 12px 0;
  border-bottom:1px solid var(--line);font-size:16.5px;line-height:1.4}
.modal.ticketfull{border-radius:18px;box-shadow:0 32px 80px -20px rgba(0,0,0,.65)}
.modal.ticketfull>.close{width:30px;height:30px;display:grid;place-items:center;border-radius:50%;
  background:var(--panel2);border:1px solid var(--line);font-size:16px;line-height:1;transition:all .15s}
.modal.ticketfull>.close:hover{color:var(--bad);border-color:var(--bad)}
/* From / To / Cc read as plain email headers — no chips, no borders — and sit clear of the sticky
 * title, which was crowding them. */
.modal.ticketfull .tkaddr{margin:12px 0 2px;font-size:12.5px;color:var(--muted);line-height:1.75}
.modal.ticketfull .tkaddr>div{overflow-wrap:anywhere}
.modal.ticketfull .tkaddr span{display:inline-block;min-width:38px;color:var(--muted);opacity:.65;
  font-size:11px;text-transform:uppercase;letter-spacing:.7px}
.modal.ticketfull .tkaddr b{color:var(--text);font-weight:600}
/* the controls that remain chips get soft square corners, not lozenges */
.modal.ticketfull .tkbody>.row .pill{font-size:11.5px;padding:4px 10px;border-radius:7px}
/* the ✕ sits in the padding reserved by the h2 above, so it never lands on the subject */
.modal.ticketfull>.close{top:16px;right:18px}
/* conversation: roomier bubbles, clearer inbound/outbound */
.modal.ticketfull #convList .ai-msg{border-radius:13px;padding:11px 14px}
.modal.ticketfull #convList .ai-msg.bot{background:var(--panel2);border-color:var(--line)}
.modal.ticketfull #convList .ai-msg.me{background:rgba(34,211,238,.09)}
/* loading / error state — used to render with no padding against the modal edge, which read as cut off */
.modal.ticketfull .tkloading{display:flex;align-items:center;justify-content:center;gap:11px;
  min-height:220px;color:var(--muted);font-size:14px}
.tkspin{width:17px;height:17px;border:2px solid var(--line);border-top-color:var(--accent);
  border-radius:50%;animation:tkspin .8s linear infinite;flex:0 0 auto}
@keyframes tkspin{to{transform:rotate(360deg)}}
/* NOTHING in a ticket scrolls sideways — it wraps. Long recipient lists (#7147 has a dozen), quoted
 * reply chains, raw URLs and log lines were pushing the body wider than the modal, which is what put a
 * horizontal bar there in the first place. Wrap everywhere, then the bar isn't needed at all. */
.modal.ticketfull .tkbody{overflow-x:hidden}
.modal.ticketfull .tkbody,.modal.ticketfull .tkaddr,.modal.ticketfull .tkbody>h2,
.modal.ticketfull #convList .ai-msg,.modal.ticketfull #convList .ai-msg>div,
.modal.ticketfull .tkbody td,.modal.ticketfull .tkbody th{overflow-wrap:anywhere;word-break:break-word;min-width:0}
.modal.ticketfull #convList .ai-msg>div{white-space:pre-wrap}
/* tables inside a ticket size to the modal instead of dictating its width */
.modal.ticketfull .tkbody table{table-layout:fixed;width:100%}
.modal.ticketfull .tkbody .panel{overflow-x:hidden}
/* To / Cc labels in the composer — mail-client plain, not chips */
.modal.ticketfull .tklbl{font-size:11px;text-transform:uppercase;letter-spacing:.7px;color:var(--muted);
  opacity:.7;min-width:22px}
/* composer sits flush and calm at the bottom */
.modal.ticketfull .tkfoot textarea{border-radius:10px}
.modal.ticketfull .tkfoot .btn{border-radius:9px}
/* two-scan gate-start verification dialog (sits above the traveler modal) */
.sv-ov{position:fixed;inset:0;background:rgba(0,0,0,.62);display:flex;align-items:center;justify-content:center;z-index:1000;padding:16px}
.sv-card{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:22px 24px;width:min(560px,96vw);box-shadow:0 24px 60px -12px rgba(0,0,0,.6)}
.sv-card h2{font-size:18px;margin:0 0 4px}
.sv-step{margin-top:14px;padding:12px 14px;border:1px solid var(--line);border-radius:12px;background:var(--panel2)}
.sv-step.sv-locked{opacity:.5}
.sv-h{font-size:14px;margin-bottom:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.sv-n{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;background:var(--accent,#2b7);color:#fff;font-size:13px;font-weight:800}
.sv-stat{color:var(--muted);font-size:12.5px;font-weight:600}
/* charger auto-detect list on a ticket — one dense row per charger. A site-wide fault names every
   station on the site, so cards took over the ticket; rows keep serial + warranty scannable. */
.chgrid,.chlist{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:9px;overflow:hidden;margin:2px 0 4px;max-width:720px}
.chlist.clipped .chrow:nth-child(n+7){display:none}
.chrow{display:flex;align-items:center;gap:10px;padding:6px 10px;font-size:12.5px;min-width:0}
.chrow+.chrow{border-top:1px solid var(--line)}
.chrow:nth-child(even){background:rgba(127,127,127,.045)}
.chrow .chsn{font-size:13px;color:var(--accent);cursor:pointer;letter-spacing:.4px;white-space:nowrap}
.chrow .chsn:hover{text-decoration:underline}
.chrow .pill{white-space:nowrap;font-size:11px;padding:1px 7px}
.chrow .chmeta{flex:1;min-width:0;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chrow .chwhy{opacity:.75;font-style:italic}
.chrow .chlinks{display:flex;gap:10px;white-space:nowrap;opacity:.8;transition:.12s}
.chrow:hover .chlinks{opacity:1}
.chrow .chlinks a{font-size:11.5px;color:var(--accent);cursor:pointer;text-decoration:none;font-weight:600}
.chrow .chlinks a:hover{text-decoration:underline}
.chrow .chlinks a i{font-style:normal;margin-right:3px;font-size:11px;opacity:.85}
.chmore{display:inline-block;margin:6px 0 2px;font-size:12px;color:var(--accent);cursor:pointer;font-weight:600}
.chmore:hover{text-decoration:underline}
/* 760px was too tight for the form-heavy dialogs: the Edit-plan header is a 2-up grid and its
 * model <select> truncates the option text (part no + kW + connectors + variant) well before the
 * end. 94vw still caps it, so nothing changes on a phone or iPad. */
.modal{width:min(980px,94vw);max-height:88vh;overflow:auto;background:var(--panel);border:1px solid var(--line);
  border-radius:16px;padding:22px 24px}
.modal h2{font-size:17px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.modal .close{float:right;cursor:pointer;color:var(--muted);font-size:20px}
.tl{margin-top:14px;border-left:2px solid var(--line);padding-left:18px}
.tl .step{position:relative;padding:7px 0 7px 6px;font-size:13.5px;display:flex;gap:10px;flex-wrap:wrap}
.tl .step::before{content:"";position:absolute;left:-25px;top:13px;width:11px;height:11px;border-radius:50%;background:var(--line)}
.tl .step.done::before{background:var(--good)}
.tl .step.fail::before{background:var(--bad);box-shadow:0 0 8px rgba(248,113,113,.6)}
.tl .step.cur::before{background:var(--warn)}
.tl .who{color:var(--muted);font-size:12px}
.pill{font-size:11px;border:1px solid var(--line);border-radius:999px;padding:2px 9px;color:var(--muted)}
.pill.bad{border-color:var(--bad);color:var(--bad)}
.pill.warn{border-color:var(--warn);color:var(--warn)}
.pill.good{border-color:var(--good);color:var(--good)}
.actions{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap;align-items:center}

/* ---------- assistant ---------- */
#aiPanel{position:fixed;top:53px;right:-360px;width:350px;height:calc(100vh - 53px);height:calc(100dvh - 53px);background:var(--panel);
  border-left:1px solid var(--line);z-index:60;display:flex;flex-direction:column;transition:right .2s}
#aiPanel.open{right:0}
.ai-head{padding:12px 14px;border-bottom:1px solid var(--line);font-weight:700;font-size:14px;display:flex;justify-content:space-between}
.ai-head .x{cursor:pointer;color:var(--muted)}
.ai-log{flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:9px}
.ai-msg{border-radius:10px;padding:9px 12px;font-size:13px;line-height:1.5;max-width:92%}
.ai-msg.bot{background:var(--panel2);border:1px solid var(--line)}
.ai-msg.me{background:rgba(34,211,238,.12);border:1px solid var(--accent);align-self:flex-end}
.ai-msg a{display:inline-block;margin-top:5px;font-size:12px}
.ai-input{display:flex;gap:8px;padding:10px;border-top:1px solid var(--line)}
.ai-input input{flex:1}
.ai-input button{background:var(--accent);border:none;border-radius:8px;width:38px;font-size:16px;cursor:pointer;color:#04141a;font-weight:900}

/* ---------- search results ---------- */
.sr-group{margin-bottom:18px}
/* results flow into as many columns as the window allows — one full-width row per hit wasted the
   screen and pushed later record types below the fold */
.sr-list{display:grid;gap:7px;grid-template-columns:repeat(auto-fill,minmax(310px,1fr))}
.sr-item{display:block;background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:9px 12px;text-decoration:none;color:var(--text);min-width:0}
.sr-item:hover{border-color:var(--accent)}
.sr-item .t{font-weight:700;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sr-item .s{font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* Deep-linked table row (work order / purchase order from search). */
tr.logi-flash > td{background:color-mix(in srgb, var(--accent) 18%, transparent)}
@media (prefers-reduced-motion:reduce){ tr.logi-flash > td{background:transparent;outline:2px solid var(--accent)} }

/* Deep-linked PO card: a brief ring so you can see which order the link landed on. */
.logiorder.logi-flash{outline:2px solid var(--accent);outline-offset:2px;
  animation:logiFlash 2.2s ease-out}
@keyframes logiFlash{0%{outline-color:var(--accent)}100%{outline-color:transparent}}
@media (prefers-reduced-motion:reduce){ .logiorder.logi-flash{animation:none} }

/* ---------- fold a PO / work-order group in the Shipped + Delivered tables ---------- */
.fgrp-toggle{cursor:pointer;font-size:11.5px;color:var(--muted);border:1px solid var(--line);
  border-radius:999px;padding:1px 9px;white-space:nowrap;user-select:none;
  transition:border-color .15s ease-out,color .15s ease-out}
.fgrp-toggle:hover{border-color:var(--accent);color:var(--accent)}
.fgrp-toggle.folded{color:var(--accent);border-color:var(--accent)}
@media (prefers-reduced-motion:reduce){ .fgrp-toggle{transition:none} }

/* ---------- collapsible PO card (Logistics → Shipping) ----------
   Click the PO header to fold the order away; the header keeps the shipped/ready counts visible so
   a collapsed card still tells you where the order stands. */
.logiorder-head{cursor:pointer;user-select:none}
.logi-caret{display:inline-block;width:12px;margin-right:4px;color:var(--muted);
  transition:transform .15s ease-out}
.logi-caret::before{content:"\25BE"}
.logiorder.collapsed .logi-caret{transform:rotate(-90deg)}
.logiorder.collapsed .logiorder-body{display:none}
.logiorder-head:hover .logi-caret{color:var(--accent)}
@media (prefers-reduced-motion:reduce){ .logi-caret{transition:none} }

/* ---------- collapsed serial list (shipments) ----------
   A shipment carries 20+ serials; inline they drowned the row. Collapsed by default: the count is
   the affordance, the serials are one click away, the row's actions never move. */
.serlist{margin-top:6px}
.serlist>summary{display:inline-flex;align-items:center;gap:5px;cursor:pointer;list-style:none;
  font-size:11.5px;color:var(--muted);padding:2px 10px;border:1px solid var(--line);
  border-radius:999px;transition:border-color .15s ease-out,color .15s ease-out}
.serlist>summary::-webkit-details-marker{display:none}
.serlist>summary::before{content:"\25B8";font-size:10px;transition:transform .15s ease-out}
.serlist[open]>summary::before{transform:rotate(90deg)}
.serlist>summary:hover,.serlist>summary:focus-visible{border-color:var(--accent);color:var(--accent)}
.serlist-body{margin-top:7px;padding:9px;background:var(--panel2);border:1px solid var(--line);
  border-radius:8px}
.serchips{display:flex;flex-wrap:wrap;gap:5px;max-height:134px;overflow:auto}
.serchip{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;
  padding:2px 7px;border:1px solid var(--line);border-radius:6px;background:var(--panel);
  color:var(--text);text-decoration:none;white-space:nowrap;cursor:pointer;
  transition:border-color .15s ease-out,color .15s ease-out}
.serchip:hover{border-color:var(--accent);color:var(--accent)}
.serlist-copy{margin-top:8px;font-size:11.5px;color:var(--muted);background:none;
  border:1px solid var(--line);border-radius:6px;padding:3px 9px;cursor:pointer;
  transition:border-color .15s ease-out,color .15s ease-out}
.serlist-copy:hover{border-color:var(--accent);color:var(--accent)}
@media (prefers-reduced-motion:reduce){
  .serlist>summary,.serlist>summary::before,.serchip,.serlist-copy{transition:none}
}

/* ---------- Product Information: live search dropdown ---------- */
.pi-results{position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:60;
  max-height:min(60vh,400px);overflow-y:auto;background:var(--panel);border:1px solid var(--line);
  border-radius:10px;box-shadow:0 12px 32px rgba(0,0,0,.35);padding:5px}
.pi-optgrp{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);padding:9px 10px 3px}
.pi-opt{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:8px 10px;border-radius:7px;font-size:13.5px;cursor:pointer;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pi-opt.active{background:var(--accent);color:#fff}
.pi-opt .oc{font-size:11.5px;color:var(--muted);flex-shrink:0}
.pi-opt.active .oc{color:rgba(255,255,255,.85)}
.pi-empty{padding:14px 12px;font-size:13px;color:var(--muted);text-align:center}

/* ---------- toast ---------- */
#toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(--panel);
  border:1px solid var(--accent);border-radius:10px;padding:11px 20px;font-size:13.5px;display:none;z-index:200;
  max-width:80vw;box-shadow:0 8px 30px rgba(0,0,0,.4)}
#toast.err{border-color:var(--bad)}
#toast.show{display:block}

iframe.embed{width:100%;height:calc(100vh - 200px);height:calc(100dvh - 200px);border:1px solid var(--line);border-radius:var(--radius);background:#fff}
.note{background:rgba(34,211,238,.07);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:10px;padding:12px 16px;font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:14px}
/* ---------- service map ----------
   Map beside a fixed-width site list. The 380px column is a hard number on purpose: the list holds
   an address and a ticket count and stops being readable much below it. Below 900px the two stack
   instead, because a 380px column on a 390px screen leaves the map nothing. */
.svcgrid{display:grid;grid-template-columns:1fr 380px;gap:12px;margin-top:12px;align-items:start}
#svcMap{height:620px;border:1px solid var(--line);border-radius:8px;z-index:0}
#svcSide{max-height:620px;overflow:auto}

/* ---------- support analytics: weekday rollup ---------- */
.sawd-row{display:flex;align-items:center;gap:10px;margin-bottom:8px}
.sawd-row.weekend{opacity:.62}
.sawd-l{width:84px;flex:none;font-size:12.5px}
.sawd-trk{flex:1;min-width:0;height:16px;background:var(--panel2);border-radius:4px;overflow:hidden}
.sawd-fil{height:100%;border-radius:4px}
.sawd-v{width:132px;flex:none;text-align:right;font-size:12.5px}
.sawd-v .sub{margin:0;font-weight:400}

/* ================= MOBILE LAYER — iPhone first, iPad portrait inherits =====================
   Breakpoint 900px covers iPhone (390-430) and iPad portrait (768-834); iPad landscape (1024)
   keeps the desktop layout. Three rules govern everything below:
     1. Content moves or scrolls, it never disappears. Clipping is the bug we are fixing.
     2. The identifier (serial, WO, ticket id) survives truncation and horizontal scroll.
     3. Fixes live in the shared pattern — there are 131 tables, they improve together or not at all.
   ========================================================================================= */
@media(max-width:900px){

  /* ---- header: one row, never overflowing --------------------------------------------------
     brand + search + 4 controls sat on a non-wrapping flex row. Under ~500px the search box shoved
     the bell, assistant and theme buttons off the right edge, where `body{overflow:hidden}` clipped
     them permanently. Vertical padding stays 10px so the header height stays exactly 53px — the
     drawer and the assistant panel are both positioned against that number. */
  header{gap:6px;padding:10px max(8px,env(safe-area-inset-right)) 10px max(8px,env(safe-area-inset-left))}
  #brandName{display:none}
  #brandLogo{height:26px}
  .brand{margin-right:auto}       /* the only flexible thing left, so the controls sit hard right */
  header{flex-wrap:nowrap}
  header .hbtn{padding:9px 10px;min-width:0;flex:none}
  #navToggle,#searchToggle{display:grid;place-items:center}
  #userSel{max-width:88px;min-width:0;padding:9px 6px;font-size:12.5px}
  /* Two controls are injected here at sign-in — a #whoami pill carrying the user's name and a
     "⎋ Logout" button. Both hold TEXT, and together they were wide enough to push Logout past the
     right edge where overflow:hidden ate it (Chris, 2026-08-16). The glyph alone is enough on a
     phone; the name is in Admin and on the profile, so it is not lost, just not in the top bar. */
  #whoami{display:none}
  #logoutBtn .hlbl{display:none}
  #logoutBtn{font-size:16px;line-height:1}

  /* Search leaves the header row entirely. As a flex:1 child it was taking every spare pixel and
     then refusing to shrink below its content, which is what pushed the right-hand buttons off the
     edge. Fixed to just under the 53px header, so opening it costs no header height. */
  .search{position:fixed;top:53px;left:0;right:0;z-index:80;display:none;max-width:none;flex:none;
    border-radius:0;border-left:0;border-right:0;background:var(--panel);
    padding:8px max(10px,env(safe-area-inset-right)) 8px max(10px,env(safe-area-inset-left));
    box-shadow:0 12px 24px -12px rgba(0,0,0,.5)}
  body.search-open .search{display:flex}
  .search .kbd{display:none}
  .search input{font-size:16px;min-width:0}   /* under 16px iOS Safari zooms the page on focus */

  /* ---- sidebar: off-canvas drawer ----------------------------------------------------------
     The 56px icon rail was the wrong answer for a phone: 17 unlabelled glyphs, and the accordion's
     children are text with no icon at all. A drawer shows the real labels at full size and costs
     one tap. `main` gets the whole width back. */
  nav{position:fixed;top:53px;left:0;bottom:0;z-index:90;width:min(288px,84vw);
    padding:10px 8px max(10px,env(safe-area-inset-bottom));
    transform:translateX(-100%);transition:transform .24s cubic-bezier(.22,1,.36,1);
    box-shadow:0 0 44px rgba(0,0,0,.5);overscroll-behavior:contain;will-change:transform}
  body.nav-open nav{transform:none}
  #navScrim{position:fixed;inset:53px 0 0;z-index:89;background:rgba(4,8,15,.5);
    opacity:0;pointer-events:none;transition:opacity .24s ease;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}
  body.nav-open #navScrim{opacity:1;pointer-events:auto}
  .nav-kic{display:inline-block;width:18px;text-align:center}  /* room here for a child icon too */
  nav a.nav-kid{padding-left:20px;gap:9px;display:flex;align-items:center}

  /* ---- main ------------------------------------------------------------------------------- */
  main{padding:14px max(12px,env(safe-area-inset-right)) max(18px,env(safe-area-inset-bottom)) max(12px,env(safe-area-inset-left))}
  h1{font-size:18px}
  .sub{margin-bottom:12px}
  .section-title{margin:16px 0 8px}

  /* ---- tables: scroll, never clip ----------------------------------------------------------
     Tables ship inside `<div class="panel" style="padding:0;overflow:hidden">` — an INLINE style, so
     only `!important` reaches it. With `main{overflow-x:hidden}` above it, every column past the
     viewport was simply unreachable on a phone: no scrollbar, no gesture, gone. `:has(>table)` hits
     the table's own wrapper whatever it is — 61 are `.panel`, the rest are bare divs with their own
     inline overflow — and nothing else, so panels holding popovers are untouched. Matching the
     wrapper rather than `.panel` is what makes this one rule cover all 131 tables.
     The layered gradients are the standard local/scroll background trick: the shadow
     appears at an edge ONLY while there is more table in that direction, so "swipe sideways" is
     discoverable rather than something you have to already know. */
  main:has(>table),main :has(>table),.modal :has(>table){overflow-x:auto!important;-webkit-overflow-scrolling:touch;
    background-image:
      linear-gradient(to right,var(--panel),rgba(0,0,0,0)),
      linear-gradient(to left,var(--panel),rgba(0,0,0,0)),
      linear-gradient(to right,rgba(0,0,0,.28),rgba(0,0,0,0)),
      linear-gradient(to left,rgba(0,0,0,.28),rgba(0,0,0,0));
    background-position:left center,right center,left center,right center;
    background-repeat:no-repeat;
    background-size:22px 100%,22px 100%,13px 100%,13px 100%;
    background-attachment:local,local,scroll,scroll}
  /* `width:auto`, NOT the inherited `width:100%`. Inside a horizontally scrolling container,
     `width:100%` means "fit the container", so the browser compresses every column to its MINIMUM
     width — seven crushed fields instead of a table you scroll. `width:auto` lets each column take
     its natural width and the wrapper scroll, which is the whole point of making it scrollable.
     `min-width:100%` keeps a narrow table filling the panel rather than shrinking to its content. */
  main:has(>table)>table,main :has(>table)>table,.modal :has(>table)>table{width:auto;min-width:100%}
  /* Its own cells must not re-wrap into a tall thin column either. */
  main :has(>table)>table:not(.stack) td,main :has(>table)>table:not(.stack) th{white-space:nowrap}
  main :has(>table)>table:not(.stack) td input,main :has(>table)>table:not(.stack) td select{width:auto}
  th{padding:7px 8px;font-size:10.5px;letter-spacing:.6px}
  td{padding:8px}
  table{font-size:13px}

  /* ---- modals: full-height sheets ----------------------------------------------------------
     A centred 88vh dialog on a phone wastes the edges and, worse, measures against iOS's inflated
     `vh` — so the footer holding Save/Close sat under the browser toolbar. Full-bleed sheet, real
     `dvh`, and the safe-area inset so the last button clears the home indicator. */
  .overlay{place-items:stretch}
  .modal,.modal.sowide,.modal.ticketfull{width:100%!important;max-width:100%!important;
    height:100dvh!important;max-height:100dvh!important;border-radius:0;border-left:0;border-right:0;
    padding-bottom:max(14px,env(safe-area-inset-bottom))}
  .modal.sowide{padding-left:12px;padding-right:12px}
  .modal.ticketfull .tkbody{padding:16px 14px 12px}

  /* ---- ticket reply composer -------------------------------------------------------------
     On a phone this was eating roughly half the screen before you had typed anything: four mode
     buttons wrapping onto two rows, then To and Cc each forced onto their own row by min-width
     220px/200px, then a 3-row textarea, attach row and action row. The thread you are replying TO
     had almost nothing left. Everything below shrinks the chrome, not the typing area, and the
     whole footer is capped so a long Cc list can never push Send off-screen. */
  .modal.ticketfull .tkfoot{border-radius:0;padding:9px 12px max(10px,env(safe-area-inset-bottom));
    max-height:47dvh;overflow-y:auto;overscroll-behavior:contain}
  .modal.ticketfull .tkfoot .row{margin-bottom:5px!important;gap:5px!important}
  .modal.ticketfull .tkfoot .row .btn.ghost{padding:5px 8px!important;font-size:11.5px;min-height:0}
  /* To and Cc share one row instead of claiming one each. */
  .modal.ticketfull .tkfoot #rTo,.modal.ticketfull .tkfoot #rCc{min-width:0!important;flex:1 1 108px!important}
  .modal.ticketfull .tkfoot input{min-height:38px;padding:7px 10px}
  .modal.ticketfull .tkfoot textarea{height:66px;min-height:0;padding:8px 10px}
  .modal.ticketfull .tkfoot textarea:focus{height:132px}   /* grows only while you are actually typing */
  .modal.ticketfull .tkfoot #rAttWrap,.modal.ticketfull .tkfoot .actions{margin-top:5px!important}
  .modal.ticketfull .tkfoot .actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
  .modal.ticketfull .tkfoot #rHint{font-size:11px}
  .modal.sowide .panel{overflow-x:auto}

  /* ---- boards on a tablet: one column per screen with the next peeking, still a board -------- */
  .kanban,.board{gap:10px;scroll-snap-type:x proximity}
  .kcol{flex:0 0 62%;scroll-snap-align:start}
  .board>*{scroll-snap-align:start}

  /* ---- stat tiles, forms, assistant -------------------------------------------------------- */
  .kpis{grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:9px}
  .kpi{padding:10px 12px}
  .kpi .v{font-size:20px}
  .formrow{gap:9px}
  .formrow label{flex:1 1 100%}     /* one field per line — half-width fields are unusable at 390px */
  .formrow label input,.formrow label select,.formrow label textarea{width:100%}
  #aiPanel{width:100%;right:-100%;height:calc(100dvh - 53px)}
  #aiPanel.open{right:0}

  /* ---- service map ------------------------------------------------------------------------
     Stack, and give the map a share of the viewport rather than a fixed 620px that a phone in
     portrait cannot show in one piece. The site list drops its own scroller and runs on down the
     page — nested scroll areas on touch are a trap: you swipe expecting the page and move the list. */
  .svcgrid{grid-template-columns:1fr;gap:10px}
  #svcMap{height:56dvh;min-height:260px}
  #svcSide{max-height:none;overflow:visible}
  #svcBar{gap:6px}
  #svcQ{min-width:0!important;flex:1 1 100%}
  #svcBar .btn,#svcBar .hbtn{flex:0 1 auto}

  /* ---- support analytics: the weekday rollup ----------------------------------------------
     84 + 132px of fixed columns leaves ~100px of bar at 390px. Shrink the label to its 3-letter
     form and drop the "/day · n total" tail to a second line so the bar keeps real width. */
  .sawd-l{width:38px;overflow:hidden;white-space:nowrap}
  .sawd-v{width:auto;min-width:52px;font-size:12px}
  .sawd-v .sub{display:block;font-size:10.5px}
}

/* Mon…Sun is too wide for a phone label column; the codebase has no i18n layer, so trimming to
   three characters in CSS is safe here and keeps one source of truth for the day names in JS. */
@media(max-width:520px){
  .sawd-l{font-size:0}
  .sawd-l::before{content:attr(data-short);font-size:12.5px}
}

/* ================= PHONE: the two patterns that break most pages ==========================
   Found by auditing app.js rather than by waiting for each page to be reported (Chris: "there are
   still a lot of pages not correct", 2026-08-16). Two inline habits account for the bulk of it, and
   because they are INLINE styles only `!important` can reach them.

   Cut at 700px, not 900px: an iPad in portrait is 768px and genuinely has room for two columns.
   These rules are for phones, where it does not.
   ========================================================================================= */
@media(max-width:700px){

  /* 1. FIXED COLUMN COUNTS. 15 inline grids hardcode `1fr 1fr`, `1fr 1fr 1fr`, `340px 1fr` or
        `2fr 1fr`. At 390px that is three ~110px columns of unreadable stubs. Collapse to one.
        Grids already written with auto-fit/auto-fill are excluded — they respond correctly on
        their own, and flattening them would waste space they are using well. */
  main [style*="grid-template-columns"]:not([style*="auto-fit"]):not([style*="auto-fill"]),
  .modal [style*="grid-template-columns"]:not([style*="auto-fit"]):not([style*="auto-fill"]){
    grid-template-columns:1fr!important}

  /* 2. LARGE INLINE MIN-WIDTHS. Hundreds of controls carry `min-width:200px`-and-up, sized for a
        desktop row. As flex children on a 390px screen they refuse to shrink and shove their
        neighbours off the edge. Releasing the floor lets them wrap; any explicit `width` still
        applies, so nothing collapses to nothing.
        Scoped to the flex containers (.row / .formrow) rather than "everything in main" ON PURPOSE:
        the same inline min-widths appear on inputs INSIDE table cells, where they are what keeps a
        column readable while the table scrolls sideways. Flattening those would trade a scrollable
        table for a squashed one. */
  main .row>[style*="min-width"],.modal .row>[style*="min-width"],
  main .formrow [style*="min-width"],.modal .formrow [style*="min-width"]{min-width:0!important}
  /* A search/filter box gets its own line rather than sharing one with the buttons beside it —
     at 390px "Search subject, contact, serial…" was rendering as "Search subject, cor". */
  main .row>input,.modal .row>input{flex:1 1 100%}
  main .row>select,.modal .row>select{flex:1 1 150px}

  /* 3. LAST RESORT. If something still overflows, it must be reachable — a clipped page hides data
        with no way to get at it. Scrolling sideways is ugly; losing a column is worse. */
  main{overflow-x:auto}

  /* 4. BOARDS STACK. Field Reports is a kanban: a horizontal scroller of 270px columns, each with
        its OWN vertical scroller capped at `100dvh - 230px`. On a 390px screen that is two nested
        scroll areas inside the page scroll — you swipe expecting the page and move a column
        instead, and each column shows about two thirds of a card. Stacked, every column is a
        full-width section flowing down the page: one scroll direction, nothing clipped.
        Phone only — an iPad in portrait has room for a real board and keeps one above. */
  .kanban,.board{display:block;overflow-x:visible}
  .kcol,.board>*{width:100%;flex:none;max-height:none;margin:0 0 12px}
  .kcol-body{max-height:none;overflow:visible}
  .kcol-head{position:static}      /* sticky is meaningless once the column is not its own scroller */
  /* .kcard is a wrapping flex row, so a SHORT title left room for the email beside it while a long
     one pushed it down — the same card laid out two different ways depending on its text. */
  .kcard .kprev{flex:1 1 100%}

  /* 5. THE PROCESS-FLOW STRIP RUNS DOWN, NOT ACROSS. Supply Chain draws its five stations as a
        `.pflow` row of `.pnode` cards joined by ▸ arrows. Each node is `flex:1 0 160px` with a 26px
        gap, and `.pflow` carries `min-width:min-content` — so the strip measured 914px inside a
        390px viewport and dragged the whole page sideways. Stacked, the arrows turn to point down
        and the flow reads top-to-bottom, which is how a five-step process reads on a phone anyway. */
  .pflow{flex-direction:column;min-width:0}
  .pnode{flex:0 0 auto;min-width:0;max-width:none;margin:0 0 26px}
  .pnode:last-child{margin-bottom:0}
  .pnode::after{content:"▾";right:auto;left:50%;top:auto;bottom:-24px;transform:translateX(-50%);font-size:20px}

  /* 6. WIDE TABLES BECOME CARDS. Sideways-scrolling a 7-column table of form fields is miserable:
        every column crushes to its minimum, headers truncate to "NA…" / "RO…", and a <select>
        renders as a bare chevron. One row becomes one card, each cell a labelled line, with the
        label lifted from the matching <th> by stackTablesForPhone() in app.js.
        Only tables with 4+ columns stack — a 2 or 3 column table already reads fine as a table. */
  /* !important throughout, and not for convenience: several screens pin their own column widths
     with ID-level specificity that a class selector cannot outrank — Admin sets
     `#admBody .schedtbl td:nth-child(n){width:12%…}` with `table-layout:fixed` and
     `overflow:hidden`, which is what kept every stacked cell at a fixed fraction of the row and
     clipped the field inside it. A card has one column, so every one of those has to go. */
  table.stack,table.stack tbody,table.stack tr,table.stack td{display:block!important;width:100%!important;
    max-width:100%!important;overflow:visible!important}
  table.stack thead{display:none!important}
  table.stack{min-width:0!important;table-layout:auto!important;border-collapse:separate!important}
  table.stack tr{background:var(--panel2);border:1px solid var(--line);border-radius:10px;
    margin:0 0 10px;overflow:hidden}
  table.stack tr:last-child{margin-bottom:0}
  table.stack td{display:flex!important;align-items:center;gap:10px;justify-content:space-between;
    white-space:normal!important;border:0;border-bottom:1px solid var(--line);padding:7px 11px;min-height:0}
  /* 44px is a TOUCH TARGET rule, so it applies to cells you actually touch. Applying it to every
     cell turned a 9-column ticket into a 400px-tall card of mostly whitespace. */
  table.stack td:has(input,select,textarea,button,a){min-height:44px;padding-top:5px;padding-bottom:5px}
  table.stack tbody tr:nth-child(even){background:var(--panel2)}   /* zebra is meaningless on cards */
  table.stack tr>td:last-child{border-bottom:0}
  /* The label comes from the header row, so a new table is handled without touching its markup. */
  table.stack td::before{content:attr(data-l);flex:0 0 34%;min-width:0;color:var(--muted);
    font-size:10.5px;text-transform:uppercase;letter-spacing:.7px;line-height:1.3}
  table.stack td[data-l=""]::before,table.stack td:not([data-l])::before{display:none}
  /* Action cells (the empty-header column) get the full width for their buttons. */
  table.stack td[data-l=""],table.stack td:not([data-l]){justify-content:flex-start;flex-wrap:wrap;gap:7px;text-align:left!important}
  /* A row-select checkbox is not worth a 44px band of its own at the top of every card — the box
     itself is the target and it sits beside the id instead. */
  table.stack td[data-l=""]:has(>input[type=checkbox]:only-child){min-height:0!important;padding:5px 11px}
  table.stack td>input,table.stack td>select,table.stack td>textarea{flex:1 1 auto;width:100%;min-width:0!important}
  table.stack tr:hover td{background:transparent}
  /* A stacked table is not a scroller: drop the edge shadows and the horizontal overflow. */
  main :has(>table.stack){overflow-x:visible!important;background-image:none!important}

  /* ---- Demand Orders: 14 columns is too many to stack flat ---------------------------------
     Stacked as-is this is a ~900px card per line that opens with "PO ENTERED" — you scroll a
     screen and a half before learning WHICH order you are looking at. A row becomes a flex-wrap
     container so `order` can lead with identity, the Qty/Del/Rem triplet can share one line, and
     the bookkeeping fields can sink to the bottom. Nothing is hidden; it is re-ranked. */
  table.dotbl.stack tr{display:flex!important;flex-wrap:wrap;align-items:stretch}
  table.dotbl.stack td{flex:1 1 100%}
  /* identity first: which PO, whose, and what */
  table.dotbl.stack td:nth-child(3){order:-3;font-weight:700}
  table.dotbl.stack td:nth-child(4){order:-2}
  table.dotbl.stack td:nth-child(7){order:-1}
  /* Ship Date / Ship Status / Status keep document order at 0 */
  /* the quantity triplet reads across, label above figure — three numbers compared, not three rows */
  table.dotbl.stack td:nth-child(10),table.dotbl.stack td:nth-child(11),table.dotbl.stack td:nth-child(12){
    order:1;flex:1 1 0;flex-direction:column;align-items:center;justify-content:center;gap:1px;
    text-align:center;border-right:1px solid var(--line)}
  table.dotbl.stack td:nth-child(12){border-right:0}
  table.dotbl.stack td:nth-child(10)::before,table.dotbl.stack td:nth-child(11)::before,
  table.dotbl.stack td:nth-child(12)::before{flex:0 0 auto}
  /* bookkeeping: entered date, internal PO, BABA, cable — real data, just not the headline */
  table.dotbl.stack td:nth-child(1),table.dotbl.stack td:nth-child(2),
  table.dotbl.stack td:nth-child(8),table.dotbl.stack td:nth-child(9){
    order:2;font-size:12px;min-height:0;padding-top:5px;padding-bottom:5px;opacity:.82}
  /* actions last. Its header is a "→ Planning" LINK, not a column name, so it makes no label. */
  table.dotbl.stack td:nth-child(14){order:5;justify-content:flex-start}
  table.dotbl.stack td:nth-child(14)::before{display:none}
  /* ---- collapsed detail, every wide table ---------------------------------------------------
     Applies to any stacked table matched by a rule in PHONE_CARD_RULES, not just Demand Orders:
     the fields that identify and rank a row stay, the rest sit behind the card's own toggle.
     `order:6` only bites on .dotbl, whose <tr> is a flex container; elsewhere the injected cell
     is simply the last child and lands at the bottom anyway. */
  table.stack td.dosec{display:none!important}
  table.stack tr.doexp td.dosec{display:flex!important}
  table.stack td.domore{order:6;flex:1 1 100%;justify-content:flex-start;border-bottom:0;padding:5px 11px;min-height:0!important}
  table.stack td.domore::before{display:none}
  .domore-btn{font-size:11.5px;padding:5px 10px;min-height:0!important}
}

/* ---- touch input, any screen size (iPad landscape is 1024px and still a touch device) --------
   Sized by input method rather than viewport: a 35px-tall control is a miss-tap on glass whether
   the glass is 390px or 1024px wide. `pointer:coarse` is the honest test. */
@media(pointer:coarse){
  .btn,.hbtn,.navbtn,.subtab{min-height:44px}
  .btn{padding:11px 18px}
  .hbtn{padding:10px 12px}
  input,select,textarea{font-size:16px;min-height:44px;padding:10px 12px}
  input[type=checkbox],input[type=radio]{min-height:0;width:20px;height:20px}
  nav a,.nav-ghead{min-height:44px}
  /* Station Kiosk / Checklists: rows sat 9px apart, which is a miss-tap with gloves on. The form is
     built from generic .row/.btn/input, so this data attribute is the only handle on its items. */
  [data-kfitem]{margin:14px 0}
  [data-kfitem] .row{gap:10px}
  th{cursor:default}                     /* sortable headers have no hover to hint with here */
  tr:hover td{background:transparent}    /* hover sticks after a tap on touch — kill it */
  .kpi:hover{border-color:var(--line)}
}

@media (prefers-reduced-motion:reduce){
  nav{transition:none}
  #navScrim{transition:none}
}

/* the hold badge sits BESIDE the status pill — a hold is a separate fact from the production state */
.doheld{display:inline-block;font-size:10px;font-weight:800;line-height:1;padding:3px 7px;border-radius:999px;
  white-space:nowrap;vertical-align:middle;background:rgba(251,191,36,.16);color:var(--warn);border:1px solid rgba(251,191,36,.55)}

/* The Projects task window carries a lot now — edit fields, the description and the whole comment
 * thread — so it gets more room than the standard 760px modal. Scoped with :has() so no other modal
 * is affected and nothing has to add/remove a class as modals are reused. */
.modal:has(.pjwide){width:min(1100px,95vw);max-height:92vh}
.pjwide .pjcols{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start}
@media (max-width:820px){.pjwide .pjcols{grid-template-columns:1fr}}
.pjchip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--accent);color:var(--accent);
  border-radius:999px;padding:2px 8px;font-size:12px;cursor:pointer}
.pjchip .x{opacity:.7;font-weight:700}
.pjchip:hover .x{opacity:1;color:var(--bad)}

/* The deleted-tickets bin is a table, not a paragraph — it needs table width. Self-scoping: the
   rule only matches while #delBody is the dialog being shown, so no other modal is affected. */
.modal:has(#delBody){width:min(1200px,95vw);max-height:92vh}
.modal:has(#delBody) table{width:100%}
.modal:has(#delBody) td,.modal:has(#delBody) th{white-space:normal}

/* KB article editor — a writing surface, not a markup box. Sized and coloured like the reader so
   what you edit is what the article will look like. */
.kbtools{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:6px 0}
.kbtools .btn{padding:3px 9px;font-size:12.5px;line-height:1.4}
.kbedit{width:100%;min-height:320px;max-height:60vh;overflow:auto;padding:14px 16px;
  background:var(--panel2);border:1px solid var(--line);border-radius:8px;
  font-size:14px;line-height:1.6;outline:none}
.kbedit:focus{border-color:var(--accent)}
.kbedit h1,.kbedit h2,.kbedit h3{margin:.7em 0 .35em;line-height:1.25}
.kbedit p{margin:0 0 .7em}
.kbedit ul,.kbedit ol{margin:0 0 .7em 1.3em}
.kbedit img{max-width:100%;height:auto}
.kbedit table{border-collapse:collapse}
.kbedit table td,.kbedit table th{border:1px solid var(--line);padding:5px 8px}

/* Quoted email history — a divider you can open, the way a mail client does it. Muted and inset so
   the new reply above it stays the thing your eye lands on. */
.qdiv{display:flex;align-items:center;gap:8px;margin:10px 0 0}
.qdiv::after{content:"";flex:1;height:1px;background:var(--line)}
.qtoggle{flex:0 0 auto;font-size:11.5px;line-height:1;padding:3px 9px;cursor:pointer;
  color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:20px}
.qtoggle:hover{color:var(--text);border-color:var(--muted)}
.qbody{margin:8px 0 0;padding:8px 0 0 12px;border-left:2px solid var(--line);
  color:var(--muted);font-size:12.5px;white-space:pre-wrap}

/* Unread, the way a mail client does it: bold, and nothing else. No dot — bold is already the
   signal, and two signals for one state is noise. Cleared the moment the row is opened. */
/* Unread: heavy and at full text colour. Read: normal weight, muted. The distance between the two
   is what makes it legible down a long column — 600 against 400 is almost nothing on a dark UI. */
.tixrow.tixowed td{font-weight:800;color:var(--text)}
.tixrow.tixowed td .pill{font-weight:800}
.tixrow:not(.tixowed) td{font-weight:400;color:var(--muted)}
.tixrow:not(.tixowed) td b{font-weight:600}

/* ---------- TICKET THREAD ----------
   One block per message, stacked in a single column. NOT staggered: a support reply is long, and a
   bubble alternating sides makes the eye travel while throwing away a fifth of the width. Direction
   is carried by the bar on the left edge instead, which costs no space and survives fast scanning. */
#convList{gap:10px}
.chatrow{display:block;width:100%;max-width:100%}
.chatmeta{font-size:11.5px;color:var(--muted);margin:0 0 4px 2px}
.chatbub{position:relative;border-radius:8px;padding:12px 15px 12px 18px;font-size:13.5px;
  line-height:1.55;border:1px solid var(--line);background:var(--panel2);
  border-left:5px solid var(--line)}
/* Customer: neutral and recessed — the thing being answered. */
.chatrow.in .chatbub{border-left-color:#6b7f8c;background:var(--panel2)}
.chatrow.in.team .chatbub{border-left-color:var(--good);background:rgba(34,197,94,.10)}
.chatrow.in.team .chatmeta b{color:var(--good)}
/* Us: clearly ours. Tinted enough to separate at a glance from the message above it. */
.chatrow.out .chatbub{border-left-color:var(--accent);background:rgba(34,211,238,.13)}
/* Internal: never left the building, and should look like it. */
.chatrow.note .chatbub{border-left-color:var(--warn);background:rgba(240,190,40,.11);border-style:dashed;border-left-style:solid}
/* The author line carries the same colour, so who is speaking reads from the heading alone. */
.chatrow.out .chatmeta b{color:var(--accent)}
.chatrow.note .chatmeta b{color:var(--warn)}
.chatmeta b{font-size:12.5px}
.chattext,.chatbub>div{white-space:pre-wrap;overflow-wrap:anywhere}
.chatmore{display:inline-block;margin-top:6px;font-size:12px;cursor:pointer;color:var(--accent)}
/* Quoted history belongs to its message, so it stays inside the block rather than as a rule across
   the page competing with the blocks for attention. */
.chatbub .qtoggle{display:inline-block;margin:7px 0 0;padding:1px 9px;font-size:12px;line-height:1.5;
  letter-spacing:1px;cursor:pointer;color:var(--muted);background:transparent;
  border:1px solid var(--line);border-radius:20px}
.chatbub .qtoggle:hover{color:var(--text);border-color:var(--muted);letter-spacing:0}
/* Outlook writes <hr tabindex="-1" style="display:inline-block; width:98%"> above the quoted
   From:/Sent:/To: block of every reply. Inside a narrow bubble that inline-block collapses to a
   stub, which is the stray "___" that appears next to a signature image and looks like corruption.
   Forced back to a real full-width divider, and toned down so it separates without shouting. */
/* Outlook puts an <hr> above the From:/Sent:/To: block of EVERY reply, so a thread that has been
   answered five times arrives with five rules stacked down it. The "On <date>, X wrote:" line
   already says where each quote starts, so the rules are just noise and they go. */
.chatbub hr{display:none!important}
/* A pasted signature image should not dominate the message it was signed with. */
.chatbub img{max-width:100%;height:auto}
.chatbub .qbody{margin:8px 0 0;padding:8px 0 0 11px;border-left:2px solid var(--line);
  color:var(--muted);font-size:12.5px}

/* The @-tag popup: highlight the row the keyboard is on, so Enter is predictable. */
#mentionPop button.cur{background:var(--panel2);outline:1px solid var(--accent)}

/* No drag grips on text boxes. The default two-way resize handle put a grip on every textarea in
   the ERP, and two side by side fall out of alignment the moment one is dragged. Auto-growth (see
   autoGrow in app.js) does what the dragging was for: fit the content, then scroll. */
textarea{resize:none;overflow-y:auto;min-height:2.6em;line-height:1.5}
/* Labels in a grid row stretch so their fields share a baseline and a height. */
label{align-self:stretch}
/* Text fields fill their label. NOT checkboxes or radios — making those display:block put the
   control on its own line and pushed "same as billing" away from the box it belongs to. */
label > textarea, label > select,
label > input:not([type=checkbox]):not([type=radio]){display:block;width:100%}
/* The option row is inline and tight under its field. */
.addrgrid .opt{display:flex;align-items:center;gap:6px;margin:6px 0 0;font-size:12px;color:var(--muted);cursor:pointer}
.addrgrid .opt input{width:auto;margin:0;flex:0 0 auto}
.addrgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px;align-items:start}
.addrgrid > div{display:flex;flex-direction:column;min-width:0}
.addrgrid .hd{display:flex;justify-content:space-between;align-items:center;gap:8px;
  margin:0 0 4px;font-size:13px}
.addrgrid .hd .opt{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--muted);cursor:pointer}
/* min-height, not height: an inline height set by auto-grow must be able to win, or the box
   snaps between the two values as you type. */
.addrgrid textarea{min-height:5.2em}
/* Sits tight under the box it controls — 6px, not a grid row away. */

/* align-items:start on .addrgrid means both boxes begin on the same line and share a height,
   so anything placed after a textarea extends only ITS column and moves neither box. That is
   why the control can sit directly under the ship-to field without breaking the alignment. */

/* The shipments ledger is wider than a narrow window and scrolls sideways inside its panel, which
   carried the row actions - the Undo among them - off the right edge where they could not be found.
   Pin that column so it stays put while the rest of the row scrolls under it. Same sticky idiom as
   .schedtbl thead th. var(--panel) is defined in every palette, so this holds in all themes. */
td.logiact,th.logiact{position:sticky;right:0;background:var(--panel);
  box-shadow:-9px 0 9px -9px rgba(0,0,0,.30)}


/* ---- a mouse means desktop sizing, whatever the screen can do -------------------------------
   The touch block above keys off `pointer:coarse`, which asks "can this device be touched". On a
   touchscreen LAPTOP the answer is yes even when the machine is being driven with a mouse, so every
   control took the 44px tap target and the 16px font: buttons, filter dropdowns, nav items and
   checkboxes all rendered a size and a half too big, and the ticket table - sized in real pixels -
   pushed its last column off the right of the screen.
   `any-pointer:fine` asks the question that actually matters: is a mouse or trackpad present. When
   one is, the base sizes are restored. A phone or tablet with no pointing device never matches this,
   so genuine touch use keeps its larger targets. */
@media(any-pointer:fine){
  .btn,.hbtn,.navbtn,.subtab{min-height:0}
  .btn{padding:8px 16px;font-size:13.5px}
  .hbtn{padding:7px 12px;font-size:14px}
  input,select,textarea{font-size:14px;min-height:0;padding:8px 11px}
  input[type=checkbox],input[type=radio]{width:16px;height:16px;min-height:0}
  nav a,.nav-ghead{min-height:0}
  nav a{padding:9px 12px;font-size:14px}
}


/* ---- checkboxes & radios ----------------------------------------------------------------------
   Left to the OS these paint as white Windows boxes, which is the one bright rectangle on an
   otherwise dark row. Rebuilt to sit in the theme: the panel colour with a border, and a plain white
   tick when checked - no accent fill, because the fill was louder than the thing it was marking. */
input[type=checkbox],input[type=radio]{
  -webkit-appearance:none;appearance:none;flex:0 0 auto;
  width:16px;height:16px;min-height:0;margin:0;padding:0;
  border:1.5px solid var(--line);border-radius:4px;background:var(--panel2);
  display:inline-grid;place-content:center;vertical-align:middle;cursor:pointer;
  transition:border-color .12s ease;
}
input[type=radio]{border-radius:50%}
input[type=checkbox]:hover,input[type=radio]:hover{border-color:var(--accent)}
input[type=checkbox]:checked,input[type=radio]:checked{background:var(--panel2);border-color:var(--accent)}
/* The tick is drawn rather than typed: a font glyph sits differently in every theme's font stack. */
input[type=checkbox]:checked::after{
  content:'';width:10px;height:10px;background:#fff;
  clip-path:polygon(14% 44%,0 60%,39% 100%,100% 18%,84% 2%,39% 68%);
}
input[type=radio]:checked::after{content:'';width:6px;height:6px;border-radius:50%;background:#fff}
input[type=checkbox]:disabled,input[type=radio]:disabled{opacity:.45;cursor:not-allowed}
input[type=checkbox]:focus-visible,input[type=radio]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The light skins need a dark tick - white on near-white is invisible. */
body.theme-tpgwhite input[type=checkbox]:checked::after,
body.light input[type=checkbox]:checked::after,
body.theme-paper input[type=checkbox]:checked::after{background:var(--text)}
body.theme-tpgwhite input[type=radio]:checked::after,
body.light input[type=radio]:checked::after,
body.theme-paper input[type=radio]:checked::after{background:var(--text)}


/* ---- conversation laid out like a messages app ------------------------------------------------
   The three kinds were already coloured differently but every one of them ran the full width, so a
   thread read as one undifferentiated column and you had to check the name on each bubble to know
   who was talking. Position carries that now: THEIRS LEFT, OURS RIGHT, an internal note centred so
   it is obviously something the customer never saw. Held to 78% so the side a bubble sits on stays
   readable at a glance, and the accent rule moves to the right edge on our own messages because a
   left rule on a right-aligned bubble points at nothing. */
.chatrow{display:flex;flex-direction:column;width:100%;max-width:100%}
.chatrow.in{align-items:flex-start}
.chatrow.out{align-items:flex-end}
/* A CONVERSATION ROW IS NOT AN INFO PANEL. The row is written class="chatrow note", and `.note` is
   a general-purpose callout used all over the ERP - green tint, full border, thick accent rule down
   the left. Every internal note was therefore being drawn inside that panel. That is the giant box,
   and no amount of styling .chatbub could ever have removed it, because the box was never .chatbub.
   Neutralised in this one context; the note's own small amber bubble carries the meaning. */
.chatrow.note{align-items:center;background:none;border:0;border-left:0;border-radius:0;padding:0;margin:0}
.chatrow.in .chatbub,.chatrow.out .chatbub{max-width:78%;min-width:180px}
.chatrow.note{align-items:center}
/* An internal note is an aside, not a message. It was taking the same width and weight as a real
   customer email, so a one-line "@Dario called him" carried the same visual authority as the outage
   it was about. Italic, a size down, and only as wide as its own text - it stops competing. */
/* The note keeps the amber box that marks it internal - it just stops being a full-width band. The
   row centres it and the box shrinks to its own words, so a one-line aside looks like one. */
.chatrow.note .chatbub{max-width:70%;width:-moz-fit-content;width:fit-content;min-width:0;
  padding:7px 13px;border-radius:9px;border-left-width:1px;
  font-style:italic;font-size:12.5px;line-height:1.5}
.chatrow.note .chatmeta{font-size:10.5px;font-style:italic}
.chatrow.out .chatmeta{text-align:right;margin-right:2px;margin-left:0}
/* Ours: rule on the right, and the one square corner sits on the side it was sent from. */
.chatrow.out .chatbub{border-left:0;border-right:4px solid var(--accent);
  padding:12px 18px 12px 15px;border-radius:12px 12px 4px 12px}
.chatrow.in .chatbub{border-radius:12px 12px 12px 4px}

/* A narrow window cannot afford 22% of dead margin - let them fill the width and lean on colour. */
@media(max-width:900px){
  .chatrow.in .chatbub,.chatrow.out .chatbub{max-width:94%}
}


/* ---- an email brings its own layout ------------------------------------------------------------
   The ERP styles tables for its own data grids - full width, collapsed borders, 9px cell padding and
   a rule under every row. An Outlook signature IS a table, so it inherited all of that: stretched
   across the bubble, cells pushed apart, and a horizontal line drawn under each row of the sender's
   contact details. Nothing about it resembled the signature they actually sent.
   Inside a message body the ERP's grid styling is stood down and the mail's own markup governs -
   its widths, its padding, its alignment. This is scoped to .chatbub only; every real table in the
   ERP is untouched. */
.chatbub table{width:auto;max-width:100%;border-collapse:collapse;font-size:inherit;color:inherit}
.chatbub td,.chatbub th{padding:0;border:0;background:none;
  font-size:inherit;font-weight:inherit;text-transform:none;letter-spacing:normal;
  color:inherit;text-align:inherit;vertical-align:inherit;white-space:normal}
.chatbub tr{background:none}
/* Sized by the sender. An explicit width/height in the mail is respected; only something genuinely
   wider than the bubble is reined in, and then in proportion. */
.chatbub img{max-width:100%}
.chatbub p{margin:0 0 8px}
.chatbub ul,.chatbub ol{margin:6px 0;padding-left:22px}


/* ---- quoted replies get ONE rule, not one per level -------------------------------------------
   Every mail client wraps the message it is answering in a <blockquote> carrying its own
   border-left:1px #ccc - so a thread that has been replied to five times arrives with five vertical
   lines stacked down the left, each one indenting the text further until the words run out of room.
   The outermost quote keeps a single rule, which is what marks the boundary between "now" and
   "earlier"; every level inside it is flattened. Marked important because the border and margin come
   from the sender's own inline style and would otherwise win. */
.chatbub blockquote{border-left:2px solid var(--line)!important;border-right:0!important;
  border-top:0!important;border-bottom:0!important;
  margin:8px 0 8px 0!important;padding:0 0 0 12px!important}
.chatbub blockquote blockquote{border-left:0!important;margin:0!important;padding:0!important}


/* ---- a sideways scroller must not swallow the page's wheel -------------------------------------
   CSS will not let the two axes disagree: set `overflow-x:auto` and the browser computes
   `overflow-y` as auto as well. Every wide-table wrapper and card rail therefore became a VERTICAL
   scroll container too, so a wheel over one of them moved nothing and the page sat still - you had
   to reach for the scrollbar. (I did exactly this to the ticket table earlier and it is worth not
   repeating.) Pinning overflow-y to hidden keeps the sideways scroll and hands vertical movement
   back to the page. Nothing is clipped: each of these sizes to its own content, and the containers
   that are MEANT to scroll vertically - the modal shell, the ticket body, a kanban column - are
   deliberately not in this list. */
.po-detail,
.po-wrap .sect > .sect-body,
.po-wrap .sect > table,
.modal.sowide .panel,
.modal.packwide .panel,
.kanban,
.board,
#scbanner{overflow-y:hidden}


/* ---- the ticket list on a phone ---------------------------------------------------------------
   stackTablesForPhone() turns any 4+ column table into cards, which saves the ticket list from
   being nine columns crushed into 390px. But it treats every column alike, so a ticket became nine
   identical "Label ......... value" lines and the SUBJECT - the only part anyone scans for - was
   the third line down, in the same weight as its department.
   Given a card is one column, the shape can follow what the row is for: the number and subject read
   as a heading, and the fields that answer "does this need me" (status, waiting, last message) stay
   as labelled lines beneath. Nothing is hidden - department, assignee and type are still there. */
@media(max-width:900px){
  table.tixtbl.stack tr{padding:2px 0}
  /* number: small, accent, sits directly above the subject like a card eyebrow */
  table.tixtbl.stack td[data-l="#"]{display:block!important;border-bottom:0;
    padding:9px 12px 0;font-size:11.5px;font-weight:700;letter-spacing:.04em;color:var(--accent)}
  table.tixtbl.stack td[data-l="#"]::before{display:none!important}
  /* subject: the heading, wrapping onto as many lines as it needs */
  table.tixtbl.stack td[data-l="Subject"]{display:block!important;border-bottom:1px solid var(--line);
    padding:1px 12px 9px;font-size:15px;font-weight:600;line-height:1.35;color:var(--text)}
  table.tixtbl.stack td[data-l="Subject"]::before{display:none!important}
  /* everything else keeps its label, quieter than the heading */
  table.tixtbl.stack td{font-size:12.5px;padding:6px 12px}
  table.tixtbl.stack td::before{color:var(--muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.05em}
  /* the select-all checkbox has no business being a row of its own on a phone */
  table.tixtbl.stack td[data-l=""]:has(> input[type=checkbox]:only-child){display:none!important}
  /* a whole card is the tap target, so the row keeps its own spacing rather than 44px per line */
  table.tixtbl.stack td:last-child{border-bottom:0}
  /* LABEL LEFT, VALUE RIGHT - "ASSIGNEE ......... Christian". The label anchors the left edge so the
     fields read as a list, and every value lands on the same right edge so they can be compared
     down the card without the eye hunting. The only cell that broke the pattern was Type: it carries
     an inline text-align:center, correct for a narrow column on the desktop table, which left the
     AC/DC marker floating inboard. Overridden here so it lands with the rest. The number and subject
     are the card's heading rather than fields, so they keep the full width and stay left. */
  table.tixtbl.stack td{justify-content:space-between!important;text-align:right!important;gap:12px}
  table.tixtbl.stack td::before{flex:0 0 auto;text-align:left}
  table.tixtbl.stack td[data-l="#"],
  table.tixtbl.stack td[data-l="Subject"]{text-align:left!important}
}

/* CRM Tasks: line the editor row up.

   The columns were sized by each control's own intrinsic minimum, because
   .schedtbl is table-layout:auto and the one rule that normalises controls
   inside such a table - min-width:0;width:100% - is scoped to #admBody, which
   this table is not in. So the two <select>s sat at the global
   select{min-width:130px}, the text inputs claimed a browser default of about
   twenty characters, and the Owner box - an <input list=...> - took its own
   width again and lined up with neither. Fixed layout with explicit widths is
   the same fix #admBody already uses, so both tables now behave alike. */
.tasktbl{table-layout:fixed}
.tasktbl th,.tasktbl td{overflow:hidden}
.tasktbl input,.tasktbl select{min-width:0;width:100%}
.tasktbl th:nth-child(1),.tasktbl td:nth-child(1){width:20%}
.tasktbl th:nth-child(2),.tasktbl td:nth-child(2){width:18%}
.tasktbl th:nth-child(3),.tasktbl td:nth-child(3){width:11%}
.tasktbl th:nth-child(4),.tasktbl td:nth-child(4){width:12%}
.tasktbl th:nth-child(5),.tasktbl td:nth-child(5){width:12%}
.tasktbl th:nth-child(6),.tasktbl td:nth-child(6){width:15%}
.tasktbl th:nth-child(7),.tasktbl td:nth-child(7){width:12%;white-space:nowrap}


/* ---- TASK COMMENTS ---------------------------------------------------------------------------
   Newest first, with the composer at the top where the new comment will land. The thread used to be
   a flat run of hairlines: every entry the same weight, the author line and the text in the same
   block, nothing to show where one comment ended and the next began. Each entry is now a row - an
   initials badge, then who and when, then the text indented under them - so a long thread can be
   skimmed by author instead of read straight through.
   Colours are theme variables only; this app ships four themes and a hard-coded grey breaks three. */
.pjmeta{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:0 0 10px}
.pjcmt-hd{display:flex;align-items:center;gap:8px;margin:14px 0 8px;padding-top:12px;border-top:1px solid var(--line)}
.pjcmt-n{font-size:12px;font-weight:700;color:var(--muted);background:var(--panel2);border:1px solid var(--line);border-radius:999px;padding:1px 8px}
.pjcmt-new{display:flex;gap:6px;align-items:center;margin:0}
.pjcmt-new input{flex:1;min-width:150px}
.pjcmt-att:not(:empty){margin:6px 0 0}
.pjcmt-list{margin-top:12px}
.pjcmt{display:flex;gap:10px;padding:10px 8px;border-radius:8px}
.pjcmt + .pjcmt{border-top:1px solid var(--line)}
.pjcmt:hover{background:var(--panel2)}
.pjcmt-avatar{flex:0 0 36px;width:36px;height:36px;border-radius:50%;background:var(--panel2);
  border:1px solid var(--line);color:var(--muted);font-size:11px;font-weight:700;
  display:flex;align-items:center;justify-content:center;margin-top:1px;letter-spacing:.5px}
.pjcmt-mine .pjcmt-avatar{border-color:var(--accent);color:var(--accent)}
.pjcmt-main{flex:1;min-width:0}
.pjcmt-head{display:flex;align-items:baseline;gap:8px;margin:0 0 3px}
.pjcmt-who{font-size:13px}
.pjcmt-when{font-size:11.5px;color:var(--muted);white-space:nowrap}
.pjcmt-edited{font-style:italic}
/* The edit link is an action on hover rather than a permanent piece of furniture - it appeared on
   every row before, which is most of what made the thread look busy. */
.pjcmt-act{margin-left:auto;font-size:11.5px;color:var(--muted);cursor:pointer;text-decoration:none;
  opacity:0;transition:opacity .12s}
.pjcmt:hover .pjcmt-act,.pjcmt-act:focus{opacity:1}
.pjcmt-act:hover{color:var(--accent);text-decoration:underline}
.pjcmt-body{white-space:pre-wrap;line-height:1.5;overflow-wrap:anywhere}
.pjcmt-pics{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.pjcmt-pics img{max-width:160px;max-height:120px;border-radius:6px;border:1px solid var(--line);display:block}
.pjcmt-empty{color:var(--muted);padding:8px 2px}
/* The ERP is worked from an iPhone and an iPad, and there is no hover there - the edit link has to
   be visible or it may as well not exist. The badge goes, to give the text the full width. */
@media (max-width:900px){
  .pjcmt-avatar{display:none}
  .pjcmt{padding:9px 2px;gap:0}
  .pjcmt-act{opacity:1}
  .pjcmt-new{flex-wrap:wrap}
}

/* ---- TASK BRIEF ------------------------------------------------------------------------------
   Read-only until an admin presses Edit. The brief is what the team planned from, so it reads as
   settled text rather than as an input box waiting to be typed in. */
.pjdesc-hd{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 5px}
.pjdesc-hd .btn{margin-left:auto;padding:3px 10px;font-size:12px;white-space:nowrap}
/* NOT A BOX (Chris, 2026-09-01: "same color as the form.. not dark.. unless i hit edit").
   A panel2 well with a border reads as a field you can type in - which is the exact impression this
   change exists to remove, and it made the brief look like the darkest thing in a light form. Read
   mode is now plain prose sitting in the form, flush with the labels above it, on the form's own
   background. Pressing Edit is what summons a real input, and the box appearing IS the signal that
   the mode changed. */
.pjdesc-view{white-space:pre-wrap;line-height:1.5;background:transparent;border:0;
  padding:2px 0 6px;min-height:22px;overflow-wrap:anywhere;color:var(--text)}
#pjDesc{width:100%;min-height:120px}
@media (max-width:900px){ .pjdesc-hd .btn{margin-left:0} }

/* ---- TASK IDENTITY (project / name / brief) --------------------------------------------------
   Settled text on the form's own background until Edit is pressed. No wells, no borders, nothing
   that reads as "type here" - the controls appearing IS the signal that the mode changed. */
.pjidentwrap{position:relative;margin-top:10px;padding-top:2px}
.pjedit{position:absolute;top:0;right:0;padding:3px 10px;font-size:12px;white-space:nowrap}
.pjident{display:flex;flex-direction:column;gap:10px}
.pjfield{display:flex;flex-direction:column;gap:2px}
.pjlabel{font-size:12px;color:var(--muted)}
.pjro{background:transparent;border:0;padding:1px 0;overflow-wrap:anywhere;color:var(--text)}
.pjro-title{font-size:16px;font-weight:700;line-height:1.3}
.pjident select,.pjident input,.pjident textarea{width:100%}
.pjident textarea{min-height:120px}
/* Room for the Edit button so a long task name does not run under it. */
.pjident .pjfield:first-child .pjlabel{padding-right:76px}

/* ---- PROFILE PICTURES ------------------------------------------------------------------------
   One shape at three sizes. A user without a picture keeps initials, and so does every customer on
   a ticket - they are not users and never will have one - so both have to sit on the same footprint
   or the left edge of a thread ripples. */
.av{border-radius:50%;flex:0 0 auto;object-fit:cover;display:inline-block;vertical-align:middle;
  background:var(--panel2);border:1px solid var(--line)}
.av-i{color:var(--muted);font-weight:700;text-align:center;letter-spacing:.5px;
  display:inline-flex;align-items:center;justify-content:center}
/* Sizes bumped on request. The stored file is a 160px square, so every one of these is still being
   downscaled even on a 3x screen - no re-upload was needed and nothing turns soft. */
.av-sm{width:26px;height:26px;font-size:11px}
.av-md{width:40px;height:40px;font-size:14px}
.pjcmt-avatar{width:36px;height:36px;font-size:13px;margin-top:1px}
.pjcmt-mine img.pjcmt-avatar,.pjcmt-mine .av-i.pjcmt-avatar{border-color:var(--accent)}
.pjcmt-mine .av-i.pjcmt-avatar{color:var(--accent)}
/* The bubble meta line becomes a row so the face sits on the text baseline block rather than
   dropping the line height. */
.chatmeta{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
/* The Users table cell: the button is only a hit area, the face is the control. */
.u-avbtn{background:none;border:0;padding:0;cursor:pointer;line-height:0;border-radius:50%}
.u-avbtn:hover .av{border-color:var(--accent)}
.u-avbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- YOUR PROFILE ----------------------------------------------------------------------------- */
.av-xl{width:112px;height:112px;font-size:34px}
#whoami{display:inline-flex;align-items:center;gap:7px;cursor:pointer}
#whoami:hover .av{border-color:var(--accent)}
.whoname{line-height:1}
.myprof{display:flex;gap:22px;flex-wrap:wrap;align-items:flex-start;margin-top:6px}
.myprof-face{display:flex;flex-direction:column;align-items:center;flex:0 0 auto}
.myprof-facts{flex:1;min-width:220px;display:flex;flex-direction:column;gap:10px}
@media (max-width:900px){
  .myprof{gap:16px}
  .myprof-face{width:100%}
  /* The header is exact below 900px - the name goes, the face stays as the way in. */
  #whoami .whoname{display:none}
}

/* ---- MENTIONS ---------------------------------------------------------------------------------
   The name, underlined in the accent colour - not "@name". The @ is a storage detail: the server
   matches on it to decide who to notify, so it stays in the text and simply is not drawn. */
a.mention{color:var(--accent);text-decoration:underline;text-underline-offset:2px;
  cursor:pointer;font-weight:600}
a.mention:hover{text-decoration-thickness:2px}
a.mention:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}

/* Tellus Power Group: form controls follow the page font; header shows the logo with "ERP" underneath, like the login screen. */
input,select,textarea,button{font-family:inherit}
.brand{flex-direction:column;align-items:center;gap:3px}
#brandName{display:none}
#brandSub{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);font-weight:600;line-height:1;margin-left:.2em}

/* Purchasing list: a wider PO # column so the 'for ...' reference line has room. */
.po-wrap .sect table th:first-child,.po-wrap .sect table td:first-child{min-width:280px}

/* Purchasing: let the table use a wide screen (it was capped at 1600px). */
.po-wrap{max-width:min(2200px,100%)}

/* Purchasing: when the window is narrower than the table (browser zoom, small screen) the table scrolls sideways instead of cutting the row buttons off. */
.po-wrap .sect{overflow-x:auto}

/* ...and the wrapper directly around the table was clipping it (overflow-x:hidden), so no scrollbar ever appeared. */
.po-wrap .sect-pad{overflow-x:auto}

/* The real clipper: the plain div inside .sect-body (used for the collapse animation) hid anything wider than the window. Scroll it sideways instead. */
.po-wrap .sect-body>div{overflow-x:auto !important;overflow-y:hidden;scrollbar-width:none}
.po-wrap .sect-body>div::-webkit-scrollbar{display:none}
/* Pinned sideways scrollbar: sits at the bottom of the window while a Purchasing table is wider than the screen. */
.po-hbar{position:fixed;bottom:0;height:16px;overflow-x:scroll;overflow-y:hidden;z-index:60;background:var(--panel2);border-top:1px solid var(--line)}
.po-hbar>div{height:1px}
.po-wrap{padding-bottom:22px}

/* Purchasing: the open PO editor stays pinned to the visible width while the list columns scroll sideways, so its fields (price, terms) are never cut off. */
.po-wrap .po-detail{position:sticky;left:0;box-sizing:border-box;width:calc(100vw - 290px)}
@media (max-width:900px){.po-wrap .po-detail{width:calc(100vw - 60px)}}

/* PO form: every field in the top grid is the same height and starts on the same line (the address box used to be tall,
 * the Reference box dropped down because its label wrapped, and the rest were short). */
.po-fgrid{align-items:start}
.po-fgrid>label{display:block;white-space:nowrap}
.po-fgrid input,.po-fgrid select,.po-fgrid textarea{display:block;white-space:normal;height:56px;box-sizing:border-box;margin:4px 0 0}
.po-fgrid textarea{resize:none}

/* Open PO editor: everything (top fields, line rows, notes, terms, buttons) is the same length - 960px - so no box is
 * shorter or longer than the rest. */
.po-wrap .po-detail>*{max-width:960px}

/* po-tall-rows: line-item boxes are the same height (56px) as the top fields of the open PO editor. */
.po-wrap .po-detail table tbody input,.po-wrap .po-detail table tbody select{height:56px;box-sizing:border-box}

/* po-fullwidth: the open PO editor spans the full width of the PO row above it (the 960px cap made it look shortened). */
.po-wrap .po-detail>*{max-width:none !important}
.po-wrap .po-detail table{width:100% !important;max-width:none !important}
.po-wrap .po-detail .po-fgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:0 14px;width:100%}
.po-wrap .po-detail .po-fgrid input,.po-wrap .po-detail .po-fgrid select,.po-wrap .po-detail .po-fgrid textarea,.po-wrap .po-detail textarea{width:100% !important}
.po-wrap .po-detail table tbody td:nth-child(2) input{width:100% !important}

/* po-fullwidth2: reach the right edge of the panel (was ~50px short). */
.po-wrap .po-detail{width:calc(100vw - 262px) !important}

/* po-descw: line-item columns share the row proportionally so the Description box is not stretched across the screen. */
.po-wrap .po-detail table{table-layout:fixed}
.po-wrap .po-detail table :is(th,td):nth-child(1){width:22%}
.po-wrap .po-detail table :is(th,td):nth-child(2){width:40%}
.po-wrap .po-detail table :is(th,td):nth-child(3){width:11%}
.po-wrap .po-detail table :is(th,td):nth-child(4){width:21%}
.po-wrap .po-detail table :is(th,td):nth-child(5){width:6%}
.po-wrap .po-detail table tbody td input{width:100% !important}

/* po-descw2: Description box about 28% of the row (~440px on a wide screen); the other columns share the rest evenly. */
.po-wrap .po-detail table :is(th,td):nth-child(1){width:25%}
.po-wrap .po-detail table :is(th,td):nth-child(2){width:28%}
.po-wrap .po-detail table :is(th,td):nth-child(3){width:14%}
.po-wrap .po-detail table :is(th,td):nth-child(4){width:25%}
.po-wrap .po-detail table :is(th,td):nth-child(5){width:8%}

/* po-pricectr: Unit price text and heading centred in their column (was hard right). */
.po-wrap .po-detail table tbody td:nth-child(4) input{text-align:center !important}
.po-wrap .po-detail table thead th:nth-child(4){text-align:center !important}

/* Purchasing lists (Christian, 2026-10-12: "scroll is out of screen ... it only happens when I click on a purchase order"). Each status section is a
 * wide table in its own scroll box, and opening a PO makes that box much taller, so its HORIZONTAL scrollbar - at the bottom - ended up far below the
 * screen. The box is flipped upside down and its table flipped back, which puts the scrollbar at the TOP of the box, just under the section heading,
 * where it stays in view however tall the list or the open order gets. */
.poscroll{overflow:auto;transform:rotateX(180deg)}
.poscroll > table{transform:rotateX(180deg)}

/* purch-fit (Christian, 2026-10-12: "it's not using the entire screen and you have to use horizontal scroll"). The PO list's columns added up to ~1,417px
 * (PO # with its long "for ..." reference, a 340px Item minimum, and a nowrap row of buttons ~350px) whatever the screen, and the page was capped at
 * 1,600px. The list now shares whatever width there is, the buttons wrap, the cap is gone, and an opened order fills the list's full width. Only below
 * ~1,000px of room does it scroll sideways (scrollbar at the top of the box). */
.po-wrap{max-width:none}
.po-wrap .poscroll > table{table-layout:fixed;width:100%;min-width:1000px}
.po-wrap .poscroll > table > thead > tr > th:nth-child(1){width:13% !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(2){width:auto !important;min-width:0 !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(3){width:14% !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(4){width:10% !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(5){width:11% !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(6){width:12% !important}
.po-wrap .poscroll > table > thead > tr > th:nth-child(7){width:15% !important}
.po-wrap .poscroll > table > tbody > tr.porow > td{overflow-wrap:anywhere}
.po-wrap .poscroll > table > tbody > tr.porow > td:first-child .sub{max-width:100% !important}
.po-wrap .poscroll > table > tbody > tr.porow > td:last-child:not(:first-child){white-space:normal !important}
.po-wrap .poscroll > table > tbody > tr.porow > td:last-child .btn{margin:0 4px 4px 0}
.po-wrap .po-detail{width:auto !important}

/* HEADER HEIGHT IS NOT A CONSTANT (Christian, 2026-10-13, screenshot of Material Requirement Planning on Green: "the mrp txt is getting
 * cut off by the main search"). .layout was sized as 100dvh - 53px, which is only right when the header is exactly 53px. On a real screen the
 * header is often a few px taller (emoji font metrics, browser zoom, the Admin select), so the shell came out taller than the window; the
 * page is overflow:hidden but can still be scrolled by script (focus / scrollIntoView on the page's search box), and when it was the whole
 * page - sidebar and the page title with it - slid up under the sticky header. The body area now simply takes whatever height is left
 * under the header, so the two can never disagree. */
body{display:flex;flex-direction:column}
header{flex:0 0 auto}
.layout{flex:1 1 auto;height:auto;min-height:0}

/* ===== COMMAND-BAR LAYOUT: the menu across the top, squared corners. Desktop only (the phone drawer is unchanged). ===== */
@media (min-width:901px){
  body.layout-bar{--radius:3px}
  body.layout-bar .btn, body.layout-bar .hbtn, body.layout-bar input, body.layout-bar select, body.layout-bar textarea,
  body.layout-bar .panel, body.layout-bar .card, body.layout-bar .modal, body.layout-bar .loginbox, body.layout-bar .theme-menu{border-radius:3px}
  body.layout-bar th{text-transform:uppercase;letter-spacing:.05em;font-size:12px}
  body.layout-bar:not(.nav-editing) .layout{flex-direction:column}
  body.layout-bar:not(.nav-editing) nav{width:auto;flex-shrink:0;display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:0;padding:0 10px;overflow:visible;border-right:0;border-bottom:2px solid var(--accent);position:relative;z-index:60}
  body.layout-bar:not(.nav-editing) nav > a, body.layout-bar:not(.nav-editing) .nav-ghead{width:auto;display:flex;align-items:center;gap:7px;margin:0;padding:12px 10px;border-radius:0;border-left:0;border-bottom:3px solid transparent;white-space:nowrap;font-size:12px;text-transform:uppercase;letter-spacing:.05em;background:none}
  body.layout-bar:not(.nav-editing) nav a.active, body.layout-bar:not(.nav-editing) .nav-grp.holds .nav-ghead{border-bottom-color:var(--accent);color:var(--text);background:none}
  body.layout-bar:not(.nav-editing) .nav-grp{position:relative}
  body.layout-bar:not(.nav-editing) .nav-kids{display:none;position:absolute;top:100%;left:0;min-width:240px;background:var(--panel);border:1px solid var(--line);border-top:2px solid var(--accent);box-shadow:0 14px 32px rgba(0,0,0,.4);z-index:120;transition:none}
  body.layout-bar:not(.nav-editing) .nav-grp.open .nav-kids{display:block}
  body.layout-bar:not(.nav-editing) .nav-kids > div{overflow:visible}
  body.layout-bar:not(.nav-editing) nav a.nav-kid{padding:9px 16px;border-radius:0;border-left:3px solid transparent;margin:0;text-transform:none;letter-spacing:0;font-size:13.5px;white-space:nowrap}
  body.layout-bar:not(.nav-editing) .nav-gchev{display:none}
  body.layout-bar:not(.nav-editing) .nav-gnm, body.layout-bar:not(.nav-editing) nav > a span{white-space:nowrap}
  body.layout-bar:not(.nav-editing) .nav-ghead{height:auto}
}
