/* NovaMil ERP — Nova Nutrition Pvt Ltd. Navy #12305c, Arial, white background. */
:root{--navy:#12305c;--blue:#1b4a8a;--coral:#c0392b;--ink:#1a2332;--muted:#5b6b82;
--line:#d5dde8;--soft:#f4f7fb;--good:#0b7a44;--warn:#b45309;--bad:#b42318;
--gold:#c98a04;--mint:#2e8b6e;
/* ---- design tokens (UI/UX P0, 7 Nov 2026): 8dp spacing, 2 radii + full,
   6 type sizes, 3 shadows. --blue is the SINGLE accent for every interactive
   element (links, focus rings, sort indicators, bar fills, active states);
   --navy is the brand/primary colour. The old second accent colour is gone:
   its two uses (focus outline, tier c2) now use --blue. ---- */
--sp-1:4px;--sp-2:8px;--sp-3:12px;--sp-4:16px;--sp-5:20px;--sp-6:24px;--sp-8:32px;
--r-sm:6px;--r-md:8px;--r-lg:12px;--r-full:999px;
--fs-xs:12px;--fs-sm:13px;--fs-base:15px;--fs-md:17px;--fs-lg:19px;--fs-xl:24px;
--sh-sm:0 1px 3px rgba(18,48,92,.25);--sh-md:0 8px 24px rgba(18,48,92,.14);--sh-lg:0 16px 40px rgba(18,48,92,.22)}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:#fff;color:var(--ink);font-family:Arial,Helvetica,sans-serif;font-size:var(--fs-base);line-height:1.5}
button,input,select,textarea{font:inherit}
input,select,textarea{font-size:16px} /* 16px is functional: stops iOS auto-zoom on focus. Not part of the type scale. */
a{color:var(--blue)}
.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
/* ---- sidebar ---- */
.sidebar{position:fixed;top:0;left:0;bottom:0;width:262px;background:#fff;border-right:1px solid var(--line);z-index:60;display:flex;flex-direction:column;overflow-y:auto}
.sb-logo{padding:var(--sp-4);border-bottom:1px solid var(--line);text-align:center}
.sb-logo .co{font-size:var(--fs-sm);color:var(--muted);margin-top:var(--sp-1)}
.sb-logo .erp{display:inline-block;margin-top:var(--sp-2);background:var(--navy);color:#fff;font-size:var(--fs-xs);font-weight:700;border-radius:var(--r-full);padding:var(--sp-1) var(--sp-3)}
.sb-logo img{width:172px;max-width:100%;height:auto;display:block;margin:0 auto var(--sp-2)}
.sb-nav{padding:var(--sp-2);flex:1}
.sb-group{font-size:var(--fs-xs);font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin:var(--sp-4) var(--sp-2) var(--sp-1)}
.sb-btn{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:transparent;border:0;border-radius:var(--r-md);padding:var(--sp-2) var(--sp-2);cursor:pointer;font-size:var(--fs-base);font-weight:700;color:var(--navy);margin:1px 0;min-height:44px}
.sb-btn:hover{background:var(--soft)}
.sb-btn.active{background:var(--navy);color:#fff}
.sb-foot{border-top:1px solid var(--line);padding:var(--sp-3) var(--sp-4);font-size:var(--fs-xs);color:var(--muted)}
.sb-foot b{color:var(--navy)}
.overlay{display:none}
/* ---- shell ---- */
.shell{margin-left:262px;min-height:100vh;display:flex;flex-direction:column}
.topbar{position:sticky;top:0;z-index:40;background:#fff;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-5);flex-wrap:wrap}
.topbar .ttl{font-size:var(--fs-md);font-weight:700;color:var(--navy)}
@media(max-width:700px){
 .form{grid-template-columns:1fr}
 .grid2{grid-template-columns:1fr}
 .kpis{grid-template-columns:1fr 1fr}
 /* Dashboard (owner 9 Nov 2026): long KPI labels ("Money owed to us
    (Receivables)") wrap to 3 lines in the narrow 2-col cards — a
    slightly tighter label (same 12px token as the desktop label, just
    tighter tracking) keeps the 3-line label compact. (11px hardcoded
    here failed the Part 9 token check — no hardcoded font-size px
    except 14px compact + 16px inputs — so it uses var(--fs-xs).) */
 .kpi .k{font-size:var(--fs-xs);letter-spacing:.02em}
}
.burger{display:block;background:var(--navy);color:#fff;border:0;border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);cursor:pointer;font-weight:700;min-height:44px}
body.side-hidden .sidebar{display:none}
body.side-hidden .shell{margin-left:0}
.searchbox{position:relative;flex:1;min-width:180px;max-width:420px}
.searchbox input{width:100%;border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3)}
.searchres{position:absolute;top:44px;left:0;right:0;background:#fff;border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--sh-md);display:none;z-index:70;max-height:300px;overflow:auto}
.searchres button{display:block;width:100%;text-align:left;background:#fff;border:0;border-bottom:1px solid var(--soft);padding:var(--sp-2) var(--sp-3);cursor:pointer;font-size:var(--fs-base);min-height:44px}
.searchres button:hover{background:var(--soft)}
.iconbtn{position:relative;background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);cursor:pointer;font-size:16px;min-height:44px}
/* v9 (CEO Report #1 item 11): the search icon button is phone-only —
   desktop shows the full search box, so this stays hidden there. */
.search-toggle{display:none}
.iconbtn .n{position:absolute;top:2px;right:2px;background:var(--bad);color:#fff;border-radius:var(--r-full);font-size:var(--fs-xs);font-weight:700;padding:0 var(--sp-1);line-height:1.5}
.pill-top{background:var(--soft);border:1px solid var(--line);color:var(--navy);border-radius:var(--r-full);padding:var(--sp-1) var(--sp-3);font-size:var(--fs-sm);font-weight:700}
.main{padding:var(--sp-5) var(--sp-6) 90px;max-width:1240px;width:100%;margin:0 auto}
/* Wide screens (owner 9 Nov 2026, 23:48 PKT — BDO dashboard: the
   1240px cap + centering left ~273px of empty margin on EACH side at
   ~2048px, which the owner read as over-spacing on the right side of
   the content. The top bar and sidebar are untouched; this only lets
   the content use more of the width on large monitors, so the side
   margins stay reasonable. Breakpoints are set so each wider cap
   still leaves ~50px of margin (sidebar 262px + cap + 2x~50px).
   Below 1920px nothing changes. */
@media(min-width:1920px){.main{max-width:1560px}}
@media(min-width:2000px){.main{max-width:1680px}}
h1{font-size:var(--fs-xl);margin:2px 0 var(--sp-1);color:var(--navy)}
h2{font-size:var(--fs-lg);margin:var(--sp-6) 0 var(--sp-2);color:var(--navy)}
h3{font-size:var(--fs-md);margin:var(--sp-3) 0 var(--sp-2);color:var(--navy)}
.lede{color:var(--muted);margin:0 0 var(--sp-2);max-width:82ch}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));grid-auto-rows:1fr;gap:var(--sp-2);margin:var(--sp-4) 0}
.kpi{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-3) var(--sp-4);background:#fff}
.kpi .k{font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.kpi .v{font-size:var(--fs-lg);font-weight:700;color:var(--navy);margin:3px 0}
.kpi .s{font-size:var(--fs-sm);color:var(--muted)}
.kpi.good .v{color:var(--good)}.kpi.bad .v{color:var(--bad)}
/* ---- UI/UX P2 (8 Nov 2026): leading KPI + drill-down + sparkline ---- */
.kpi.lead{grid-column:span 2}
.kpi.lead .v{font-size:var(--fs-xl)}
.kpi.drill{cursor:pointer}
.kpi.drill:hover{border-color:var(--blue);box-shadow:var(--sh-sm)}
.kpi.drill:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.kpi .spark{display:block;margin-top:var(--sp-2)}
/* Offline banner: a designed state, not a silent failure. Hidden until the
   browser reports offline (or a write is queued while offline). */
.offlinebar{display:none;background:var(--warn);color:#fff;font-weight:700;font-size:var(--fs-sm);padding:var(--sp-2) var(--sp-6);text-align:center}
.offlinebar.show{display:block}
.offlinebar button{background:#fff;color:var(--warn);border:0;border-radius:var(--r-md);padding:var(--sp-1) var(--sp-3);cursor:pointer;font-weight:700;margin-left:var(--sp-2);min-height:32px}
.card{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-4) var(--sp-4);margin:var(--sp-3) 0;background:#fff}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)}
.grid2>*{min-width:0}
.card{min-width:0}
@media(max-width:860px){.grid2{grid-template-columns:1fr}}
.tblwrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:var(--fs-sm);margin:var(--sp-2) 0}
th,td{border:1px solid var(--line);padding:var(--sp-2) var(--sp-2);text-align:left;vertical-align:top}
th{background:var(--soft);color:var(--navy);font-size:var(--fs-sm)}
tr.tot td{font-weight:700;background:var(--soft);color:var(--navy)}
tr.empty td{color:var(--muted);text-align:center;padding:var(--sp-5) var(--sp-2);font-weight:400}
tbody tr:hover{background:var(--soft)}
.tblwrap thead th{position:sticky;top:0;z-index:1}
/* ---- frozen first (identifier) column (UI/UX P0 table upgrade).
   The corner header sticks both ways; the frozen cells keep their own
   background so scrolling rows do not show through, and the row hover
   still colours the frozen cell. ---- */
.tblwrap thead th:first-child{position:sticky;left:0;z-index:3}
.tblwrap td:first-child{position:sticky;left:0;z-index:1;background:#fff}
/* ---- UI/UX P2 (8 Nov 2026): tables become cards under 640px. Each row
   is a card; each cell shows its column label (from data-label, set by
   tbl()) before its value. The frozen first column and sticky header are
   off at this width — they are desktop patterns. ---- */
@media(max-width:640px){
  .tblwrap{overflow-x:visible}
  .tblwrap table{display:block}
  .tblwrap thead{display:none}
  .tblwrap tbody{display:block}
  .tblwrap tr{display:block;border:1px solid var(--line);border-radius:var(--r-md);margin:0 0 var(--sp-2);padding:var(--sp-2) var(--sp-3);background:#fff}
  .tblwrap tr.tot{display:block}
  .tblwrap tr.empty{display:block;text-align:center}
  .tblwrap td{display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--sp-2);border:0;border-bottom:1px solid var(--soft);padding:var(--sp-2) 0;text-align:right}
  .tblwrap td>*{min-width:0}
  .tblwrap td:last-child{border-bottom:0}
  .tblwrap td::before{content:attr(data-label);font-weight:700;color:var(--navy);text-align:left;flex:none}
  .tblwrap td:first-child{position:static;background:transparent}
  .tblwrap td.num{font-variant-numeric:tabular-nums;white-space:nowrap}
  .tblwrap tr.expand td{display:block;border:0}
  .tblwrap tr.expand td::before{content:none}
}
tbody tr:hover td:first-child{background:var(--soft)}
tr.tot td:first-child{background:var(--soft)}
@media(max-width:900px){.act .btn{min-width:44px;min-height:44px}}
.tag{display:inline-block;border-radius:var(--r-full);padding:1px var(--sp-2);font-size:var(--fs-xs);font-weight:700;background:var(--soft);color:var(--navy);white-space:nowrap}
.tag.good{background:#e6f4ec;color:var(--good)}.tag.warn{background:#fdf0dc;color:var(--warn)}
.tag.bad{background:#fdeceb;color:var(--bad)}.tag.blue{background:#e8f0fb;color:var(--blue)}
.btn{background:var(--navy);color:#fff;border:0;border-radius:var(--r-md);padding:var(--sp-2) var(--sp-4);cursor:pointer;font-weight:700;min-height:40px}
.btn.alt{background:#fff;color:var(--navy);border:1px solid var(--navy)}
.btn.green{background:var(--good)}
.btn.red{background:#fff;color:var(--bad);border:1px solid var(--bad)}
.btn.small{padding:var(--sp-1) var(--sp-3);font-size:var(--fs-sm);min-height:34px;color:#fff}
.btn.small.alt{color:var(--navy)}
.btn.small.red{color:var(--bad)}
.btn:disabled{opacity:.45;cursor:not-allowed}
/* v9 (CEO Report #2 item 29): action-cell buttons are small TEXT buttons,
   side by side — not stacked full-width filled buttons. All primary
   actions stay visible (no kebab menu). Text style keeps the cell narrow:
   Payment + History fit the 115px Actions column on one line (Cancel, when
   present, wraps to a second line), so a 3-button row is ~2 lines instead
   of 3 stacked filled rows. The Sales table is exactly at capacity
   (1096px), so the column cannot widen — the buttons are kept minimal. */
.act{display:flex;gap:2px;flex-wrap:wrap}
.act .btn{background:transparent;border:0;box-shadow:none;color:var(--navy);padding:2px;font-size:var(--fs-xs);font-weight:700;text-decoration:underline;min-height:28px;border-radius:var(--r-sm)}
.act .btn.green{color:var(--good)}
.act .btn.red{color:var(--bad)}
.act .btn:hover,.act .btn.green:hover,.act .btn.red:hover,.act .btn.alt:hover{background:var(--soft);text-decoration:none}
@media(max-width:900px){.act .btn{min-height:44px}}
/* Sales Orders row-compact (owner issue, 9 Nov 2026): the Bill cell keeps
   its expand button + SO number on one line (the number never wraps after
   its hyphen); the action buttons flow two per line (narrower buttons, so
   two fit); and the sales table's cell padding is tightened. Together the
   row is set by 2 button lines instead of 3 stacked buttons. Scoped to
   #salesTbl so no other table changes.
   NOTE: the old-data label ("not recorded (old data)") is deliberately
   NOT forced to 2 lines here — forcing it would widen GST/Batch/Margin
   enough that the 15-column table scrolls horizontally and hides the
   Payment button. The label still wraps to 3 lines in the narrow Batch /
   Margin columns, but it no longer sets the row height (the 2-line action
   buttons do), so the rows are shorter anyway. If the owner prefers
   2-line labels, that is a separate call: it needs a wider table. */
.bill-id{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.bill-id .btn{padding:4px 8px}
#salesTbl .act{min-width:100px}
/* v9 (CEO Report #2 item 29): the Sales action buttons are text buttons
   now (see .act .btn) — the 4px 6px filled-button padding would keep them
   too wide to share a line, so this matches the 2px text-button padding. */
#salesTbl .act .btn{padding:2px}
#salesTbl td,#salesTbl th{padding:6px 5px}
label{font-size:var(--fs-sm);font-weight:700;color:var(--navy);display:block;margin:var(--sp-2) 0 3px}
input,select{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-2);background:#fff;width:100%}
.form{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:var(--sp-2);align-items:end}
.note{border:1px solid #eed88f;background:#fffaf0;border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);font-size:var(--fs-sm);margin:var(--sp-2) 0}
.quick{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin:var(--sp-3) 0}
.bar-row{display:grid;grid-template-columns:130px 1fr 108px;gap:var(--sp-2);align-items:center;margin:var(--sp-2) 0;font-size:var(--fs-base)}
.bar-track{background:var(--soft);border-radius:var(--r-full);height:var(--sp-4);overflow:hidden}
.bar-fill{background:var(--blue);height:100%}
.small{font-size:var(--fs-sm);color:var(--muted)}
.toast{position:fixed;bottom:var(--sp-5);left:50%;transform:translateX(-50%);background:var(--navy);color:#fff;padding:var(--sp-3) var(--sp-5);border-radius:var(--r-lg);z-index:99;display:none;max-width:92vw;text-align:center;font-weight:700;box-shadow:var(--sh-md)}
/* ---- toast undo button (UI/UX P0 feedback): a real button inside the toast,
   shown for reversible actions; the toast stays 5s on touch, 4s otherwise. ---- */
.toast .undo-btn{background:transparent;border:1px solid #fff;color:#fff;border-radius:var(--r-md);padding:var(--sp-1) var(--sp-2);font-weight:700;cursor:pointer;margin-left:var(--sp-2);min-height:0}
.flow{display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;margin:var(--sp-2) 0}
.flow span{background:var(--soft);border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);font-size:var(--fs-sm);font-weight:700;color:var(--navy)}
.flow i{font-style:normal;color:var(--muted);font-weight:700}
.chain{list-style:none;margin:0;padding:0}
.chain li{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);margin:var(--sp-2) 0;display:flex;gap:var(--sp-2);align-items:flex-start;flex-wrap:wrap}
.chain li.cur{border-color:var(--navy);background:var(--soft)}
.chain .st{font-weight:700;min-width:170px;color:var(--navy)}
/* ---- price tiers (sample: 4 levels, first product) ---- */
.tier{border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-3) var(--sp-4);background:#fff}
.tier .tn{font-size:var(--fs-xs);font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.tier .tv{font-size:var(--fs-xl);font-weight:700;color:var(--navy);margin:2px 0}
.tier.c1{border-top:4px solid var(--muted)}.tier.c2{border-top:4px solid var(--blue)}.tier.c3{border-top:4px solid var(--gold)}.tier.c4{border-top:4px solid var(--coral)}
footer{border-top:1px solid var(--line);margin-top:var(--sp-8);padding-top:var(--sp-3);color:var(--muted);font-size:var(--fs-sm)}
/* profile menu */
.profilewrap{position:relative}
.profilebtn{display:flex;align-items:center;gap:var(--sp-2);background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-1);cursor:pointer;text-align:left;min-height:44px}
.profilebtn:hover{background:var(--soft)}
.profilemenu{position:absolute;top:52px;right:0;width:250px;background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh-lg);z-index:90;display:none;overflow:hidden}
.profilemenu.open{display:block}
/* Notification bell panel (part 22, 8 Nov 2026): dropdown under the bell. */
.bellpanel{position:absolute;top:52px;right:120px;width:360px;max-width:92vw;background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh-lg);z-index:90;overflow:hidden}
.bellpanel.hidden{display:none}
.bell-head{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--soft)}
.bell-head b{color:var(--navy);font-size:var(--fs-base)}
.bell-item{display:block;width:100%;text-align:left;background:#fff;border:0;border-bottom:1px solid var(--soft);padding:var(--sp-3) var(--sp-4);cursor:pointer;font-size:var(--fs-base);min-height:44px}
.bell-item:hover{background:var(--soft)}
.bell-item.unread{background:#f0f7ff}
.bell-item.unread:hover{background:#e2effc}
.bell-item b{display:block;color:var(--navy);font-size:var(--fs-sm)}
.bell-item span{display:block;color:var(--muted);font-size:var(--fs-sm);margin-top:2px}
.bell-item time{display:block;color:var(--muted);font-size:var(--fs-xs);margin-top:2px}
.bell-empty{padding:var(--sp-4);color:var(--muted);font-size:var(--fs-sm);text-align:center}
.profilemenu .pm-head{padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--soft)}
.profilemenu .pm-head b{color:var(--navy);display:block;font-size:var(--fs-base)}
.profilemenu .pm-head span{font-size:var(--fs-sm);color:var(--muted)}
.profilemenu button{display:block;width:100%;text-align:left;background:#fff;border:0;border-bottom:1px solid var(--soft);padding:var(--sp-3) var(--sp-4);cursor:pointer;font-size:var(--fs-base);min-height:44px}
.profilemenu button:hover{background:var(--soft)}
/* v9 (CEO Report #1 item 11): Alerts + Density in the profile menu are
   phone-only — desktop keeps them in the topbar, so these stay hidden. */
.profilemenu .pm-mobile{display:none}
.avatar{width:36px;height:36px;border-radius:var(--r-full);background:var(--navy);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--fs-base);flex:none}
/* phone bottom app bar */
.appbar{display:none}
@media(max-width:900px){
 .sidebar{display:none;box-shadow:var(--sh-lg)}
 .sidebar.open{display:flex}
 .overlay{display:none;position:fixed;inset:0;background:rgba(10,20,40,.45);z-index:55}
 .overlay.show{display:block}
 .shell{margin-left:0}
 .burger{display:block}
 .main{padding:var(--sp-4) var(--sp-3) 110px}
 .appbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:60;background:#fff;border-top:1px solid var(--line);padding:var(--sp-2) var(--sp-1) calc(var(--sp-2) + env(safe-area-inset-bottom));justify-content:space-around}
 .appbar button{background:none;border:0;color:var(--navy);font-size:var(--fs-xs);font-weight:700;padding:var(--sp-2) var(--sp-2);min-height:44px;min-width:52px;cursor:pointer}
 .appbar button.active{color:var(--blue)}
}
@media print{.sidebar,.topbar,.toast,.overlay,.appbar{display:none}.shell{margin-left:0}}
/* ---- login page ---- */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--soft);padding:var(--sp-5)}
.login-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-8) var(--sp-6);width:100%;max-width:420px;box-shadow:var(--sh-lg)}
.login-card .co{text-align:center;color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-1) 0 var(--sp-5)}
.login-card h1{text-align:center}
.err{background:#fdeceb;color:var(--bad);border:1px solid #f3c1bd;border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);font-size:var(--fs-sm);margin:var(--sp-2) 0;display:none}
.login-logo{width:200px;max-width:80%;height:auto;display:block;margin:0 auto var(--sp-2)}
/* ---- buttons: navy primary, hover darken, shadow, pressed, focus ring ---- */
.btn{box-shadow:var(--sh-sm);transition:background .12s ease,box-shadow .12s ease,transform .05s ease}
.btn:hover{background:#0d2447;box-shadow:var(--sh-md)}
.btn.alt:hover{background:var(--soft);box-shadow:var(--sh-md)}
.btn.green:hover{background:#09683a;box-shadow:var(--sh-md)}
.btn.red:hover{background:#fdeceb;box-shadow:var(--sh-md)}
.btn:active{transform:translateY(1px);box-shadow:none}
.btn:focus-visible,.sb-btn:focus-visible,.profilebtn:focus-visible,.modal-x:focus-visible,.burger:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
input:focus,select:focus,textarea:focus{border-color:var(--blue);outline:2px solid var(--blue);outline-offset:1px}
/* ---- one consistent filter bar for every list page ---- */
.filterbar{display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:end;background:var(--soft);border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);margin:var(--sp-3) 0}
.filterbar .fb-field{display:flex;flex-direction:column;gap:3px;min-width:0}
.filterbar .fb-field label{margin:0;font-size:var(--fs-xs)}
.filterbar .fb-search{flex:1;min-width:200px}
.filterbar .fb-btn{margin-left:auto}
.filterbar .fb-count{align-self:center;font-size:var(--fs-sm);font-weight:700;color:var(--navy);white-space:nowrap}
/* Sales filter actions (v8.5 Item 3, 10 Nov 2026): Clear + Export +
   count wrap as ONE unit so they never split across rows — at 768px
   Clear was orphaned alone on row 2 while Export + count sat on
   row 3. The group is a flex item of the filterbar (it wraps as a
   unit) and a flex row itself; it carries the margin-left:auto that
   used to sit on the first button, so on desktop the actions still
   sit at the right edge. */
.filterbar .fb-actions{display:flex;gap:var(--sp-2);align-items:end;margin-left:auto}
.filterbar .fb-actions .fb-btn{margin-left:0}
/* Rows page-size select (BDO Portfolio, owner 8 Nov 2026): it was only
   ~56px wide and squeezed against the filter bar's right edge. Give it a
   comfortable width so "25" / "All" and the dropdown arrow fit. */
.filterbar #fb-ps{min-width:84px}
@media(max-width:600px){
  .filterbar{flex-direction:column;align-items:stretch}
  .filterbar .fb-btn{margin-left:0}
  .filterbar .fb-btn .btn{width:100%}
  .filterbar .fb-count{align-self:flex-start}
  .filterbar .fb-actions{flex-direction:column;align-items:stretch;margin-left:0}
  .bar-row{grid-template-columns:86px 1fr 82px;gap:var(--sp-2);font-size:var(--fs-sm)}
}
/* ---- modal dialog (Add Product) ---- */
.modal-overlay{position:fixed;inset:0;background:rgba(10,20,40,.5);z-index:80;display:flex;align-items:center;justify-content:center;padding:var(--sp-5)}
.modal-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-5) var(--sp-6);width:100%;max-width:560px;max-height:calc(100vh - 40px);overflow-y:auto;box-shadow:var(--sh-lg)}
.modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-2)}
.modal-head h3{margin:0}
.modal-x{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);width:40px;height:40px;min-height:40px;cursor:pointer;font-size:16px;color:var(--navy);flex:none}
.modal-x:hover{background:var(--soft)}
.modal-foot{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-top:var(--sp-4)}
@media(max-width:600px){
  .modal-overlay{padding:var(--sp-2)}
  .modal-card{max-height:calc(100vh - 20px);padding:var(--sp-4)}
  .modal-foot .btn{flex:1}
}
/* Drawer: record edit with the list still visible behind it. The modal
   stays for short interrupting tasks (payments, cheque clearance).
   UI/UX FIX (9 Nov 2026): this base block MUST sit before the P2
   @media(max-width:640px) block below — the P2 rules turn the drawer into
   a bottom sheet on phones, and any base rule placed AFTER them wins and
   breaks the sheet. DO NOT move this block below the 640px block, and DO
   NOT overwrite this file wholesale from a stale copy — re-applying the
   move is not enough, the source copy must include it. */
.drawer-overlay{position:fixed;inset:0;background:rgba(10,20,40,.5);z-index:80;display:flex;align-items:stretch;justify-content:flex-end}
.drawer-overlay.hidden{display:none}
.drawer-card{background:#fff;border-left:1px solid var(--line);padding:var(--sp-5) var(--sp-6);width:100%;max-width:480px;height:100%;overflow-y:auto;box-shadow:var(--sh-lg);animation:drawerIn .18s ease-out}
@keyframes drawerIn{from{transform:translateX(40px);opacity:.5}to{transform:translateX(0);opacity:1}}
@media(max-width:600px){.drawer-card{max-width:94vw;padding:var(--sp-4)}}

/* ---- UI/UX P2 (8 Nov 2026): modals and drawers become bottom sheets on
   phones (≤640px) — anchored to the bottom, full width, rounded top
   corners, up to 85% of the viewport height. Desktop behaviour unchanged. ---- */
@media(max-width:640px){
  .modal-overlay{align-items:flex-end;padding:0}
  .modal-card{max-width:100%;width:100%;max-height:85vh;border-radius:var(--r-lg) var(--r-lg) 0 0;border-bottom:0;padding:var(--sp-4)}
  .drawer-overlay{align-items:flex-end;justify-content:stretch}
  .drawer-card{max-width:100%;width:100%;height:auto;max-height:85vh;border-left:0;border-top:1px solid var(--line);border-radius:var(--r-lg) var(--r-lg) 0 0;padding:var(--sp-4);animation:sheetUp .18s ease-out}
  @keyframes sheetUp{from{transform:translateY(40px);opacity:.5}to{transform:translateY(0);opacity:1}}
}
/* ---- phones: 44px touch targets ---- */
@media(max-width:900px){
  .btn{min-height:44px;padding:var(--sp-3) var(--sp-5)}
  .btn.small{min-height:44px;padding:var(--sp-2) var(--sp-4)}
  .modal-x{min-height:44px;width:44px;height:44px}
  input,select,textarea{min-height:44px}
}

/* ---- G13 density toggle: compact = more rows on screen, same layout.
   One body class; the toggle lives in the topbar and persists in
   localStorage. Default = comfortable (today's look). ---- */
body.compact{font-size:14px;line-height:1.4}
body.compact .card{padding:var(--sp-3) var(--sp-4);margin-bottom:var(--sp-3)}
body.compact .tbl td,body.compact .tbl th{padding:var(--sp-2) var(--sp-2)}
body.compact .btn{min-height:36px;padding:var(--sp-2) var(--sp-3)}
body.compact .sb-btn{min-height:36px;padding:var(--sp-2) var(--sp-2)}
body.compact .topbar{padding:var(--sp-2) var(--sp-4)}
body.compact .kpi{padding:var(--sp-2) var(--sp-3)}
body.compact .form{gap:var(--sp-2)}
body.compact h1{font-size:var(--fs-lg)}
body.compact .main{padding:var(--sp-4) var(--sp-5)}

/* ---- v10 button audit (10 Nov 2026): the 44px touch floor holds on
   phones even where a desktop rule makes a button shorter. The bulk bar
   (.bulkbar .btn, 36px) and the offline bar (.offlinebar button, 32px)
   beat the ≤900px .btn rule on specificity, and compact density
   (body.compact .btn, 36px) is a user-chosen density, not a licence to
   shrink touch targets on a phone. These three overrides sit AFTER the
   rules they beat, so they win on mobile only. ---- */
@media(max-width:900px){
  .bulkbar .btn{min-height:44px}
  .offlinebar button{min-height:44px}
  body.compact .btn{min-height:44px}
}

/* ---- named utility classes (G3: no inline style="..." in the markup) ----
   Static layout styles live here. The only inline styles left are the two
   computed ones (a bar width % and a legend swatch colour), which cannot be
   classes because their values are computed per row. */
.mt10{margin-top:var(--sp-2)}.mt14{margin-top:var(--sp-4)}.my10{margin:var(--sp-2) 0}
.w100{width:100%}.hidden{display:none}.nowrap{white-space:nowrap}
.ml-auto{margin-left:auto}.center{text-align:center}
.sb-name{font-size:var(--fs-sm);font-weight:700;color:var(--navy);display:block;line-height:1.2}
.sb-role{font-size:var(--fs-xs);color:var(--muted);display:block;line-height:1.2}
.flexrow{display:flex;gap:var(--sp-4);flex-wrap:wrap;align-items:center}
.form-wide{grid-template-columns:repeat(auto-fit,minmax(215px,1fr))}
.maxh270{max-height:270px}.hint{margin:var(--sp-1) 0;font-size:var(--fs-sm)}
.w150{width:150px}.checkbox-inline{width:auto;height:auto;min-height:0}
/* ---- sortable table headers (G1) ---- */
th[onclick]{cursor:pointer;user-select:none}
th[onclick]:hover{background:#e8f0fb}
th.sorted{background:#e8f0fb}
.sort-ind{font-size:var(--fs-xs);color:var(--blue)}
.fb-noresults{color:var(--muted);font-size:var(--fs-sm)}
/* ---- skeleton screens (UI/UX P0 feedback): content-heavy lazy sections
   show a skeleton block while loading instead of bare "Loading…" text.
   No shimmer animation — the layout preview is the point, not motion. ---- */
.skel{background:var(--soft);border-radius:var(--r-md);min-height:16px;margin:var(--sp-2) 0}
.skel.tall{min-height:96px}
/* ---- inline field errors (UI/UX P0 feedback): the error sits below its
   field and names the fix; the field itself gets a red border. ---- */
.field-err{color:var(--bad);font-size:var(--fs-sm);font-weight:700;margin-top:2px;display:none}
input.invalid,select.invalid,textarea.invalid{border-color:var(--bad)}
/* ---- expandable detail rows (UI/UX P0 table upgrade): the detail row
   under a sales-list row shows the payment history inline. ---- */
.tblwrap tr.expand td{background:var(--soft)}
.bulkbar{position:fixed;bottom:var(--sp-5);left:50%;transform:translateX(-50%);background:var(--navy);color:#fff;border-radius:var(--r-lg);padding:var(--sp-2) var(--sp-3);z-index:95;display:none;box-shadow:var(--sh-lg);align-items:center;gap:var(--sp-2);font-weight:700;max-width:94vw;flex-wrap:wrap}
.bulkbar .btn{background:#fff;color:var(--navy);box-shadow:none;min-height:36px}
.bulkbar .btn.red{background:var(--bad);color:#fff}
/* ---- UI/UX P1 (8 Nov 2026): collapsible sidebar groups, breadcrumbs,
   sub-navigation tabs, command palette, drawer. All values are P0 tokens. ---- */
/* Sidebar groups collapse: the header is a button; the group's routes hide. */
.sb-group-btn{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:transparent;border:0;border-radius:var(--r-md);padding:var(--sp-2);cursor:pointer;font-size:var(--fs-xs);font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;margin:var(--sp-4) 0 var(--sp-1);min-height:44px}
.sb-group-btn:hover{background:var(--soft)}
.sb-group-btn .chev{font-size:var(--fs-sm);color:var(--muted);width:16px;flex:none;text-align:center}
.sb-group-btn .cnt{margin-left:auto;font-weight:700;color:var(--muted)}
.sb-group-items.hidden{display:none}
/* Breadcrumbs: one thin strip under the topbar, on every page. */
.crumbs{padding:var(--sp-2) var(--sp-6) 0;font-size:var(--fs-sm);color:var(--muted);display:flex;align-items:center;gap:var(--sp-1);flex-wrap:wrap}
.crumbs a{color:var(--blue);text-decoration:none;font-weight:700;display:inline-flex;align-items:center;min-height:24px}
.crumbs a:hover{text-decoration:underline}
.crumbs .sep{color:var(--muted)}
.crumbs .cur{color:var(--navy);font-weight:700}
/* Sub-navigation tabs: the current group's routes, as top tabs. */
.subnav{display:flex;gap:var(--sp-1);padding:var(--sp-2) var(--sp-6) 0;flex-wrap:wrap}
.subnav:empty{display:none}
.subnav button{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-2) var(--sp-3);cursor:pointer;font-size:var(--fs-sm);font-weight:700;color:var(--navy);min-height:44px}
.subnav button:hover{background:var(--soft)}
.subnav button.active{background:var(--navy);color:#fff;border-color:var(--navy)}
/* v9 (CEO Report #1 item 12): on phones the sub-nav tabs are ONE
   horizontally scrollable strip — they never wrap to 2–3 rows. Tabs keep
   their width (no shrinking); swipe/scroll to reach the rest. */
@media(max-width:640px){
  .subnav{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .subnav::-webkit-scrollbar{display:none}
  .subnav button{flex-shrink:0}
}
/* v9 (CEO Report #2 item 32): on phones the My Day KPI cards are a
   2-column compact grid — small padding, and the value + label share one
   line (value first, label next to it), subtext below. The 3 cards take
   ~2 rows instead of 3 full-width rows, so the route list (the real work)
   shows sooner. Dashboard KPIs are untouched. The label + subtext
   truncate (one line each) — a manager viewing a BDO's day gets a long
   "[Name]'s achievement" label, and the BDO's name is already in the
   "View a BDO's day" selector above the cards.
   min-width:0 on the card AND on the subtext is REQUIRED: without it the
   grid item / flex item cannot shrink below its content width, the
   columns blow out past the viewport (audit caught 479px at 360px), and
   the ellipsis never engages. */
@media(max-width:640px){
  .myday-kpis{grid-template-columns:1fr 1fr}
  .myday-kpis .kpi{padding:var(--sp-2);display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--sp-1);min-width:0}
  .myday-kpis .kpi .v{order:1;font-size:var(--fs-md);margin:0;flex:none}
  .myday-kpis .kpi .k{order:2;min-width:0;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .myday-kpis .kpi .s{order:3;flex-basis:100%;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
}
/* v9 (CEO Report #1 item 11): on phones the topbar collapses to ONE row —
   Menu + title + avatar only. The sync pill, the search box and the
   Alerts/Density buttons leave the topbar (Alerts + Density live in the
   profile menu; search becomes an icon that expands a full-width row).
   The title truncates instead of pushing the avatar off. The bell panel
   aligns to the right edge (its desktop right:120px would push it off
   a 360px screen). */
@media(max-width:640px){
  .topbar{flex-wrap:nowrap}
  .topbar .ttl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .topbar .pill-top{display:none}
  .topbar .searchbox{display:none}
  .topbar #bellBtn{display:none}
  .topbar #densityBtn{display:none}
  .topbar .search-toggle{display:inline-flex}
  .topbar .profilebtn > span:not(.avatar){display:none}
  .topbar .profilebtn{padding:var(--sp-1)}
  .topbar.search-open{flex-wrap:wrap}
  .topbar.search-open .searchbox{display:block;flex-basis:100%;order:10;max-width:none}
  .profilemenu .pm-mobile{display:block}
  .bellpanel{right:var(--sp-2)}
}
/* Command palette: Ctrl+K. Fuzzy pages + actions + records. */
.palette-overlay{position:fixed;inset:0;background:rgba(10,20,40,.5);z-index:90;display:flex;align-items:flex-start;justify-content:center;padding:12vh var(--sp-4) var(--sp-4)}
.palette-overlay.hidden{display:none}
.palette-card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--sh-lg);width:100%;max-width:560px;overflow:hidden}
.palette-input{width:100%;border:0;border-bottom:1px solid var(--line);padding:var(--sp-4) var(--sp-5);font-size:var(--fs-md);outline:none}
.palette-hint{padding:var(--sp-2) var(--sp-5);font-size:var(--fs-sm);color:var(--muted);border-bottom:1px solid var(--soft)}
.palette-results{max-height:320px;overflow-y:auto}
.palette-group{padding:var(--sp-2) var(--sp-5) var(--sp-1);font-size:var(--fs-xs);font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em}
.palette-item{display:flex;align-items:center;gap:var(--sp-2);width:100%;text-align:left;background:#fff;border:0;border-bottom:1px solid var(--soft);padding:var(--sp-2) var(--sp-5);cursor:pointer;font-size:var(--fs-base);color:var(--navy);min-height:44px}
.palette-item:hover{background:var(--soft)}
.palette-item.sel{background:var(--soft);box-shadow:inset 3px 0 0 var(--blue)}
.palette-item .sub{margin-left:auto;font-size:var(--fs-sm);color:var(--muted)}
.palette-empty{padding:var(--sp-4) var(--sp-5);color:var(--muted);font-size:var(--fs-sm)}
/* ---- Print (part 24, section D — CEO Gap 3 / Accountant P0-3) ----
   window.print() is wired to the Results page, the payroll section and
   (via the browser) any page. A printout is a REPORT, not the app: the
   sidebar, topbar, appbar, buttons, filter bars, quick-action rows and
   modals/drawers are hidden, the content spans the full width, and tables
   keep their rows intact (no row split across pages). */
@media print{
  .sidebar,.topbar,.appbar,.burger,.btn,.filterbar,.quick,.palette-overlay,.drawer-overlay,.modal,.toast,.note,.profile-menu{display:none !important}
  body{margin:0;background:#fff;color:#000}
  #page,.main,.content{margin:0 !important;padding:0 !important;width:100% !important;max-width:none !important}
  .card{border:1px solid #999;box-shadow:none;break-inside:avoid}
  table{border-collapse:collapse;width:100%}
  th,td{border:1px solid #999;padding:4px 6px;font-size:var(--fs-xs)}
  tr{break-inside:avoid}
  h1,h2,h3{color:#000}
  a{color:#000;text-decoration:none}
}
