/* dashboard-components.css
   Filter bars, multi-select dropdowns, KPI cards, comparison summaries,
   pivot tables, pager, search boxes, and responsive breakpoints.
*/

  .msWrap{position:relative;}
  .msBtn{font-family:var(--font);font-size:12.5px;padding:7px 12px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-2);color:var(--text);cursor:pointer;min-width:118px;text-align:left;}
  .msBtn:after{content:'▾';float:right;color:var(--text-mute);margin-left:8px;}
  .msPanel{display:none;position:absolute;top:calc(100% + 6px);left:0;background:var(--surface);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:8px;min-width:210px;max-height:300px;overflow-y:auto;z-index:30;max-width:calc(100vw - 24px);}
  .msSearch{position:sticky;top:-8px;z-index:1;}
  #msGrowthProduct .msPanel, #msProduct .msPanel{min-width:280px;max-height:340px;}
  .msPanel.open{display:block;}
  .msAll,.msItem{display:flex;align-items:center;gap:8px;padding:6px 7px;font-size:12.5px;border-radius:6px;cursor:pointer;color:var(--text-soft);}
  .msSearch{width:100%;box-sizing:border-box;font-family:var(--font);font-size:12px;padding:6px 8px;border:1px solid var(--border);border-radius:6px;margin-bottom:6px;background:var(--surface-2);}
  /* Bulk actions for searchable lists: acts on whatever the keyword matched, so a
     few thousand products can be selected in one click instead of one at a time. */
  .msBulk{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:0 2px 6px;min-height:0;}
  .msBulk:empty{display:none;}
  .msCount{font-size:11px;color:var(--text-mute);font-weight:600;}
  .msLink{appearance:none;background:none;border:0;padding:2px 6px;border-radius:5px;cursor:pointer;
    font-family:var(--font);font-size:11.5px;font-weight:700;color:var(--primary);background:var(--primary-soft);}
  .msLink:hover{background:#e3e5fc;}
  .msAll{border-bottom:1px solid var(--border);margin-bottom:4px;padding-bottom:8px;font-weight:700;color:var(--text);}
  .msItem:hover,.msAll:hover{background:var(--surface-2);}

  .kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:22px;}
  .kpi{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--primary-2);border-radius:var(--radius);box-shadow:var(--shadow);padding:15px 17px;}
  .kpi:nth-child(2){border-left-color:var(--accent);}
  .kpi:nth-child(3){border-left-color:var(--amber);}
  .kpi:nth-child(4){border-left-color:var(--pink);}
  .kpi:nth-child(5){border-left-color:var(--cyan);}
  .kpi:nth-child(6){border-left-color:var(--primary);}
  /* PO report has 6 KPI cards — fit them on one row on desktop, wrap smartly below. */
  #scpoKpis{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));}
  #scpoKpis .kpi .value{font-size:16.5px;}
  .kpi .label{font-size:10px;text-transform:uppercase;letter-spacing:.7px;color:var(--text-mute);margin-bottom:7px;font-weight:700;}
  .kpi .value{font-size:19px;font-weight:800;letter-spacing:-.3px;}
  .delta{font-size:10.5px;font-weight:700;margin-top:5px;padding:2px 8px;border-radius:20px;display:inline-block;}
  .delta.up{background:var(--success-soft);color:var(--success);}
  .delta.down{background:var(--danger-soft);color:var(--danger);}
  .kpi .prior{font-size:10.5px;color:var(--text-mute);margin-top:4px;}

  .row{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;align-items:stretch;}
  .row .card{margin-bottom:0;}
  .legend2{display:flex;gap:16px;font-size:11.5px;color:var(--text-soft);margin-bottom:10px;}
  .legend2 span{display:inline-flex;align-items:center;gap:5px;}
  .swatch{width:14px;height:3px;display:inline-block;border-radius:2px;}

  .panel{display:none;}
  .panel.active{display:block;}
  #dashEmpty, #resultsBody{display:none;}
  #dashEmpty{font-size:13px;color:var(--text-mute);padding:60px 0;text-align:center;}

  table.pivot{width:100%;border-collapse:collapse;font-size:12px;}
  table.pivot th{background:var(--primary);color:#fff;font-weight:700;font-size:10px;text-transform:uppercase;letter-spacing:.4px;padding:10px 11px;text-align:right;position:sticky;top:0;z-index:3;}
  table.pivot th:first-child{text-align:left;border-radius:8px 0 0 0;}
  table.pivot th:last-child{border-radius:0 8px 0 0;}
  table.pivot td{padding:8px 11px;text-align:right;border-bottom:1px solid #f0f1f6;color:var(--text-soft);white-space:nowrap;font-variant-numeric:tabular-nums;}
  table.pivot td:first-child{text-align:left;color:var(--text);}
  tr.brandRow{background:var(--primary-soft);cursor:pointer;}
  tr.brandRow:hover{background:#e3e5fc;}
  tr.brandRow td{font-weight:700;}
  tr.productRow td{color:var(--text-soft);}
  tr.productRow td:first-child{padding-left:30px;font-weight:400;}
  tr.grandRow{background:var(--text);}
  tr.grandRow td{color:#fff;font-weight:700;border-bottom:none;}
  table.rep tr.grandRow td.num{color:#fff;}
  td.total{font-weight:700;color:var(--text);}
  .pivotWrap{overflow:auto;max-height:65vh;border:1px solid var(--border);border-radius:10px;}
  .expandIcon{display:inline-block;width:16px;height:16px;line-height:16px;text-align:center;border-radius:4px;background:var(--primary);color:#fff;font-weight:700;font-size:11px;margin-right:6px;}

  /* In-page sub-tabs — one panel per item inside a single nav tab (used by
     Settings ▸ Email Notifications: a tab per notification + Recipient Groups).
     Scrolls horizontally rather than wrapping, so a long list stays one row on mobile. */
  .subTabs{display:flex;gap:4px;border-bottom:1px solid var(--border);margin-bottom:16px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
  .subTabs::-webkit-scrollbar{display:none;}
  .subTab{appearance:none;background:none;border:none;border-bottom:2.5px solid transparent;margin-bottom:-1px;
    padding:9px 14px;font-family:var(--font);font-size:13px;font-weight:600;color:var(--text-mute);
    cursor:pointer;white-space:nowrap;display:flex;align-items:center;gap:7px;border-radius:8px 8px 0 0;}
  .subTab:hover{color:var(--text);background:var(--surface-2);}
  .subTab.active{color:var(--primary);border-bottom-color:var(--primary);background:none;}
  .subTab .dot{width:7px;height:7px;border-radius:50%;background:var(--border);flex-shrink:0;}
  .subTab .dot.on{background:var(--success);}
  .subTab .dot.off{background:#cbd0e0;}
  .subPanel{display:none;}
  .subPanel.active{display:block;}
  @media(max-width:880px){ .subTab{padding:9px 11px;font-size:12.5px;} }

  .mobileMenuBtn{display:none;align-items:center;justify-content:center;width:36px;height:36px;border-radius:9px;background:var(--surface);border:1px solid var(--border);color:var(--text);font-size:16px;cursor:pointer;margin-right:10px;flex-shrink:0;}
  /* Mobile-only "Filters" toggle (hidden on desktop; shown via media query). */
  .filterToggle{display:none;align-items:center;gap:6px;height:34px;padding:0 12px;border-radius:8px;background:var(--surface);border:1px solid var(--border);color:var(--text);font-size:12.5px;font-weight:600;cursor:pointer;flex-shrink:0;}
  .filterToggle .chev{transition:transform .15s;}
  body.filtersHidden .filterToggle .chev{transform:rotate(-90deg);}

  /* ---- Roles & Access (Users tab) ---- */
  .roleRow{display:flex;justify-content:space-between;align-items:center;gap:10px;border:1px solid var(--border);border-radius:10px;padding:10px 12px;background:var(--surface-2);}
  .roleRowName{font-weight:700;}
  .roleRowMeta{font-weight:500;color:var(--text-mute);font-size:12px;}
  .roleRowDesc{color:var(--text-mute);font-size:12.5px;margin-top:2px;}
  .roleRowActions{display:flex;gap:6px;flex-shrink:0;}
  .roleBadge{display:inline-block;padding:2px 9px;border-radius:20px;color:#fff;font-size:11px;font-weight:700;letter-spacing:.02em;}
  .roleModalBk{position:fixed;inset:0;background:rgba(20,25,50,.5);display:flex;align-items:center;justify-content:center;z-index:130;padding:16px;}
  .roleModal{background:var(--surface);border-radius:14px;box-shadow:var(--shadow);width:100%;max-width:660px;max-height:88vh;display:flex;flex-direction:column;overflow:hidden;}
  .roleModalHead{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--border);font-size:15px;}
  .roleModalBody{padding:14px 16px;overflow:auto;}
  .roleModalFoot{display:flex;gap:8px;align-items:center;justify-content:flex-end;padding:12px 16px;border-top:1px solid var(--border);}
  .rolePermGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px 18px;}
  @media(max-width:880px){
    .roleRow{flex-direction:column;align-items:stretch;}
    .roleRowActions{justify-content:flex-start;}
    .rolePermGrid{grid-template-columns:1fr;}
    .roleModal{max-height:92vh;}
  }

  /* Purchase Order INT — compact line-item table (tighter cells + narrower inputs). */
  .poLines th, .poLines td{padding:5px 8px;}
  .poLines input.poLineIn{width:64px;max-width:74px;box-sizing:border-box;padding:4px 6px;text-align:right;font-size:12.5px;}
  .poLines td:nth-child(2){min-width:150px;}   /* Product column keeps room */
  /* ---- Stock Position: frozen header + frozen SKU column on the wide tables ----
     The generic `.erpTbl th:nth-child(1)` rule outranks `.erpTbl thead th` on
     specificity, so the header cells need an explicit higher z-index here or they
     slide under the body cells while scrolling. */
  #spTbl thead th, #spAlertTbl thead th, #spGroupTbl thead th{position:sticky;top:0;z-index:6;}
  /* Freeze the first TWO columns (SKU + Product) so rows stay identifiable while
     scrolling sideways. Fixed widths are required so column 2 knows its offset. */
  #spTbl th:nth-child(1), #spTbl td:nth-child(1),
  #spAlertTbl th:nth-child(1), #spAlertTbl td:nth-child(1){
    position:sticky;left:0;z-index:4;width:110px;min-width:110px;max-width:110px;background:var(--surface);}
  #spTbl th:nth-child(2), #spTbl td:nth-child(2),
  #spAlertTbl th:nth-child(2), #spAlertTbl td:nth-child(2){
    position:sticky;left:110px;z-index:4;width:230px;min-width:230px;max-width:230px;background:var(--surface);
    box-shadow:2px 0 5px -3px rgba(20,25,50,.25);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  #spTbl thead th:nth-child(1), #spTbl thead th:nth-child(2),
  #spAlertTbl thead th:nth-child(1), #spAlertTbl thead th:nth-child(2){z-index:7;background:var(--primary);}
  #spTbl tbody tr:nth-child(even) td:nth-child(1), #spTbl tbody tr:nth-child(even) td:nth-child(2),
  #spAlertTbl tbody tr:nth-child(even) td:nth-child(1), #spAlertTbl tbody tr:nth-child(even) td:nth-child(2){background:var(--surface-2);}

  /* Keep the filter card pinned to the top of the viewport while the long report
     page scrolls, so filters are always reachable. */
  #stockPosRoot > .card:first-child,
  #poTrackerRoot > .card:first-child{position:sticky;top:0;z-index:30;}
  @media(max-width:880px){
    #stockPosRoot > .card:first-child,
    #poTrackerRoot > .card:first-child{position:static;}
  }

  /* ---- PO Tracker: cross-filter chips + frozen headers ---- */
  .ptChip{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;
    background:rgba(99,102,241,.12);color:var(--primary);border:1px solid rgba(99,102,241,.3);
    font-size:11.5px;cursor:pointer;transition:background .12s;}
  .ptChip:hover{background:rgba(220,38,38,.13);border-color:rgba(220,38,38,.35);color:var(--danger);}
  #ptTbl thead th, #ptVendorTbl thead th, #ptBrandTbl thead th, #ptProductTbl thead th{position:sticky;top:0;z-index:6;}
  /* ── Sales Growth & Target: stacked metric cells ──────────────────────────
     Each metric is ONE column carrying the current value, the comparison period
     beneath it, and the change alongside — replacing three columns per metric
     (15 across, unusable on a phone). Vertical dividers are essential here: with
     two figures and a badge inside a cell, adjacent columns otherwise run together. */
  #growthLocTable thead th, #growthWeekTable thead th{
    background:var(--primary);color:#fff;font-weight:700;font-size:10px;text-transform:uppercase;
    letter-spacing:.4px;padding:9px 10px;text-align:center;white-space:nowrap;
    border-left:1px solid rgba(255,255,255,.28);border-bottom:none;}
  #growthLocTable thead th:first-child, #growthWeekTable thead th:first-child{text-align:left;border-left:0;}
  #growthLocTable td, #growthWeekTable td{border-left:1px solid var(--border);}
  #growthLocTable td:first-child, #growthWeekTable td:first-child{border-left:0;}
  #growthLocTable td.gCell{padding:0;vertical-align:middle;}
  #growthLocTable td.name{font-weight:700;color:var(--text);white-space:nowrap;}
  /* Sales Rep Insights uses the same stacked cell. Its <td> normally carries
     padding + right alignment, which must give way to the flex layout inside.
     An explicit .gCell class rather than :has() — the pivot must render correctly
     in every browser, not just those with :has() support. */
  /* ── Frozen Brand + Qty In-hand columns ───────────────────────────────────
     Two sticky columns need the FIRST one to have a known width, because the
     second's `left` offset must be an explicit value. Hence the fixed 230px on
     column 1 (with wrapping, so long product names are not truncated) and
     left:230px on the stock column. */
  table.pivot.freezeCol td:first-child, table.pivot.freezeCol th:first-child{
    width:230px;min-width:230px;max-width:230px;white-space:normal;box-shadow:none;}
  table.pivot.freezeCol td.stockCol, table.pivot.freezeCol th.stockCol{
    position:sticky;left:230px;z-index:2;width:104px;min-width:104px;
    text-align:right;font-variant-numeric:tabular-nums;
    box-shadow:2px 0 5px -3px rgba(20,25,50,.25);}
  table.pivot.freezeCol thead th.stockCol{z-index:5;background:var(--primary);}
  /* Sticky cells need their own opaque background per row type, or the columns
     scrolling underneath show through. */
  table.pivot.freezeCol tbody tr.brandRow td.stockCol{background:var(--primary-soft);font-weight:700;}
  table.pivot.freezeCol tbody tr.productRow td.stockCol{background:var(--surface);}
  table.pivot.freezeCol tfoot tr.grandRow td.stockCol{background:var(--text);color:#fff;}
  @media(max-width:880px){
    table.pivot.freezeCol td:first-child, table.pivot.freezeCol th:first-child{
      width:150px;min-width:150px;max-width:150px;}
    table.pivot.freezeCol td.stockCol, table.pivot.freezeCol th.stockCol{
      left:150px;width:80px;min-width:80px;}
  }

  /* Vertical dividers between every rep column. With two figures and a change
     badge stacked inside one cell, adjacent columns merge visually without them —
     same reason the Growth table has them. */
  table.pivot th{border-left:1px solid rgba(255,255,255,.28);}
  table.pivot th:first-child{border-left:0;}
  /* Headers centre both ways. The two-line "Qty In-hand / as on …" cell makes the
     whole row taller, and without this the single-line headers sit on the baseline
     while that one starts at the top. */
  table.pivot thead th, table.pivot thead th:first-child{
    vertical-align:middle;text-align:center;line-height:1.35;}
  table.pivot td{border-left:1px solid var(--border);}
  table.pivot td:first-child{border-left:0;}
  table.pivot tr.grandRow td{border-left-color:#3a4055;}
  /* Stronger horizontal rules: with stacked two-line cells the old hairline made
     rows bleed into one another. Brand rows get a heavier top rule so each brand
     block reads as a group with its products beneath it. */
  table.pivot td{border-bottom:1px solid var(--border);}
  table.pivot tr.brandRow td{border-top:2px solid var(--primary-2);}
  table.pivot tr.productRow td{background:var(--surface);}
  table.pivot tr.productRow:hover td{background:var(--surface-2);}
  table.pivot tr.productRow:hover td.stockCol{background:var(--surface-2);}
  table.pivot tr.productRow td:first-child{padding-left:26px;color:var(--text-soft);}

  table.pivot td.gCell{padding:0;text-align:left;vertical-align:middle;}
  table.pivot td.gCell .gmCur{color:var(--text);}
  table.pivot tr.grandRow td.gCell .gmCur{color:#fff;}
  table.pivot tr.grandRow td.gCell .gmPrv{color:#b8bed0;}
  table.pivot tr.grandRow td.gCell .gmTag{color:#9aa2b8;}
  .gmNone{color:var(--text-mute);display:inline-block;padding:8px 11px;}
  /* The Total row's label centres in its cell — left-aligned it sat oddly against
     the stacked cells beside it, which are visually centred. */
  #growthLocFoot td:first-child{text-align:center;}

  .gm{display:flex;align-items:center;gap:9px;padding:7px 10px;}
  .gmVals{flex:1;min-width:0;}
  .gmLine{display:flex;justify-content:space-between;align-items:baseline;gap:10px;white-space:nowrap;}
  .gmTag{font-size:9px;font-weight:700;letter-spacing:.4px;color:var(--text-mute);text-transform:uppercase;}
  .gmCur{font-size:13px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;}
  .gmPrv{font-size:11.5px;color:var(--text-mute);font-variant-numeric:tabular-nums;}
  .gmFlat{flex:0 0 auto;min-width:62px;text-align:center;font-size:11px;font-weight:700;
    border-radius:999px;padding:3px 7px;background:#f1f3f9;color:var(--text-mute);}
  /* deltaHtml() renders .delta pills; centre them against both figures.
     margin-top:5px is right for a KPI card but pushes the pill off-centre here. */
  .gm .delta{flex:0 0 auto;min-width:62px;text-align:center;margin-top:0;}
  /* Sort arrows must stay visible on the indigo header. */
  #growthLocTable thead th .sortArrow, #growthWeekTable thead th .sortArrow{color:rgba(255,255,255,.75);}
  #growthLocTable thead th .sortArrow.active, #growthWeekTable thead th .sortArrow.active{color:#fff;}
  #growthLocTable thead th.sortable:hover, #growthWeekTable thead th.sortable:hover{background:var(--primary-2);color:#fff;}
  #growthLocTable tr.grandRow .gmCur{color:#fff;}
  #growthLocTable tr.grandRow .gmPrv{color:#b8bed0;}
  #growthLocTable tr.grandRow .gmTag{color:#9aa2b8;}
  #growthLocTable tr.grandRow td{border-left-color:#3a4055;}
  @media(max-width:880px){
    .gm{gap:6px;padding:6px 7px;}
    .gmCur{font-size:12px;} .gmPrv{font-size:10.5px;} .gmTag{font-size:8.5px;}
    .gm .delta, .gmFlat{min-width:52px;font-size:10px;padding:2px 5px;}
  }

  /* ── Sales Rep Grading ────────────────────────────────────────────────────
     Card per rep: rank, name, comparison period, stars, badge; metrics beneath;
     ƒx panel revealed on click. Every parameter above is live, so the whole list
     re-renders as targets, weightings or the coverage floor change. */
  /* Reverted to the app's normal light surfaces (owner's call, 2026-08-08) — this
     report was the only dark screen in the app and the inconsistency cost more than
     the extra punch the stars got. The rg* variables are kept, now pointing at the
     light tokens, so every rule below that references them still resolves. */
  #panel-rep-grading{--rgBg:var(--surface);--rgBg2:var(--surface-2);--rgLine:var(--border);
    --rgTxt:var(--text);--rgSoft:var(--text-soft);--rgMute:var(--text-mute);}

  /* Date field: readable DD-MMM-YY text box + a narrow native picker BESIDE it.
     Deliberately not overlaid — the absolutely-positioned version left both fields
     rendering blank. At 34px the date input shows only its calendar button. */
  .poDateRow{display:flex;gap:5px;align-items:stretch;}
  .poDateRow > input[type=text]{flex:1;min-width:0;}
  .poDateRow > .poPick{flex:0 0 34px;width:34px;padding:0 0 0 4px;overflow:hidden;
    color:transparent;background:var(--surface-2);cursor:pointer;}
  .poPick::-webkit-datetime-edit{display:none;}          /* hide the dd/mm/yyyy text */
  .poPick::-webkit-calendar-picker-indicator{opacity:.6;cursor:pointer;margin:0;}
  .poPick:hover::-webkit-calendar-picker-indicator{opacity:1;}

  /* Duplicate-invoice resolver in the Data Library. */
  #dupCard{border-left:4px solid var(--amber);}
  .dupRow{border:1px solid var(--border);border-radius:10px;padding:11px 13px;margin-bottom:10px;background:var(--surface-2);}
  .dupHead{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:8px;}
  .dupInv{font-size:14px;font-weight:800;color:var(--text);}
  .dupCust{font-size:12.5px;color:var(--text-soft);flex:1;min-width:0;}
  .dupOver{font-size:11.5px;font-weight:700;color:#b45309;background:#fef3c7;border-radius:999px;padding:3px 10px;white-space:nowrap;}
  table.dupTbl{width:100%;border-collapse:collapse;font-size:12.5px;background:var(--surface);border-radius:8px;overflow:hidden;}
  table.dupTbl th{background:var(--primary-soft);color:var(--primary);font-size:10px;text-transform:uppercase;
    letter-spacing:.4px;padding:6px 9px;text-align:left;}
  table.dupTbl td{padding:7px 9px;border-top:1px solid var(--border);}
  table.dupTbl .num{text-align:right;font-variant-numeric:tabular-nums;}
  table.dupTbl th.num{text-align:right;}
  .dupFile{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--text-mute);}
  .dupKeep{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;
    background:var(--success-soft);color:var(--success);border-radius:999px;padding:1px 7px;margin-left:5px;}
  .dupDel{color:var(--danger);border-color:var(--danger-soft);}
  .dupDel:hover{background:var(--danger-soft);}
  @media(max-width:880px){
    .dupFile{display:none;}
    table.dupTbl th:nth-child(2), table.dupTbl td:nth-child(2){display:none;}
  }

  /* Loss-making-line warning on the PO editor — see costSanity() in po-working.js. */
  .poWarn{background:#fff8e8;border:1px solid #f3d3a0;border-radius:10px;padding:10px 14px;
    font-size:12.5px;color:#7a5a12;line-height:1.7;margin-bottom:12px;}
  .poWarn b{color:#6b4a08;}
  .poWarn ul{margin:6px 0 0;padding-left:18px;}
  .poWarn li{margin-bottom:4px;}

  /* "New version available" bar — shown when a release lands while the tab is open.
     Fixed to the bottom so it never covers a toolbar, and dismissible, because it
     must never block work in progress. */
  #appUpdateBar{position:fixed;left:50%;transform:translateX(-50%);bottom:18px;z-index:9999;
    display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:10px;
    background:var(--text,#161a2b);color:#fff;font-size:13px;
    box-shadow:0 6px 24px rgba(0,0,0,.25);max-width:calc(100vw - 24px);}
  #appUpdateBar button{border:0;border-radius:7px;padding:5px 12px;font-size:12.5px;
    font-weight:600;cursor:pointer;background:var(--primary,#4f46e5);color:#fff;}
  #appUpdateBar #appUpdateDismiss{background:transparent;color:#c9cede;padding:5px 6px;font-weight:400;}
  @media (max-width:520px){ #appUpdateBar{bottom:10px;font-size:12px;padding:8px 10px;} }

  /* PO attachments list. The name column takes the slack so long filenames wrap
     rather than pushing the size/date columns off a narrow screen. */
  .poAttTbl{width:100%;}
  .poAttTbl td{padding:7px 10px;vertical-align:middle;border-bottom:1px solid var(--border);}
  .poAttTbl tr:last-child td{border-bottom:0;}
  .poAttTbl td:nth-child(2){width:100%;word-break:break-word;}
  .poAttTbl a{color:var(--primary);text-decoration:none;}
  .poAttTbl a:hover{text-decoration:underline;}
  .poAttTbl .btn.small{padding:3px 9px;font-size:11.5px;text-decoration:none;}
  @media (max-width:640px){
    .poAttTbl td:nth-child(4){display:none;}   /* uploader/date drops on mobile */
    .poAttTbl td{padding:6px 5px;}
  }

  .rgParams{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-top:12px;}
  .rgCol{min-width:0;}
  .rgLbl{font-size:10px;font-weight:800;letter-spacing:.7px;text-transform:uppercase;
    color:var(--rgMute);margin-bottom:8px;}
  .rgRow{display:flex;align-items:center;gap:8px;font-size:12.5px;margin-bottom:6px;color:var(--rgSoft);}
  .rgRow span{flex:1;}
  .rgRow input{width:62px;font-family:var(--font);font-size:12.5px;padding:5px 8px;border-radius:7px;
    border:1px solid var(--rgLine);background:var(--rgBg2);color:var(--rgTxt);text-align:right;}
  .rgOk{color:#5ef08f;font-weight:700;} .rgBad{color:#ff9c9c;font-weight:700;}

  /* Collapsed by default: the method is there when a score is questioned, not
     filling the screen every time the report opens. */
  .rgExplainWrap{margin-top:14px;padding-top:13px;border-top:1px solid var(--rgLine);}
  .rgExToggle{appearance:none;background:none;border:0;padding:0;cursor:pointer;font-family:var(--font);
    font-size:12px;font-weight:800;letter-spacing:.4px;color:var(--rgSoft);display:flex;align-items:center;gap:7px;}
  .rgExToggle:hover{color:var(--rgTxt);}
  .rgExHint{font-weight:400;color:var(--rgMute);font-size:11.5px;letter-spacing:0;}
  .rgExChev{display:inline-block;transition:transform .15s;color:var(--primary-2);}
  .rgExplainWrap.open .rgExChev{transform:rotate(90deg);}
  .rgExplainWrap .rgExplain{display:none;}
  .rgExplainWrap.open .rgExplain{display:block;}
  @media(max-width:880px){ .rgExHint{display:none;} }
  .rgDirty{margin-top:12px;background:rgba(245,158,11,.14);border:1px solid rgba(245,158,11,.4);
    border-radius:8px;padding:8px 12px;font-size:12px;color:#fcd34d;}
  .rgPulse{box-shadow:0 0 0 3px rgba(99,102,241,.35);}
  .rgExplain{padding-top:11px;}
  .rgExRow{display:flex;gap:9px;font-size:12px;line-height:1.7;color:var(--rgSoft);margin-bottom:8px;}
  .rgExRow:last-child{margin-bottom:0;}
  .rgExRow b{color:var(--rgTxt);}
  .rgExN{width:18px;height:18px;border-radius:50%;background:var(--primary-2);color:#fff;font-size:10px;
    font-weight:800;display:flex;align-items:center;justify-content:center;flex:0 0 auto;margin-top:3px;}
  .rgMono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;
    background:#12151f;border-radius:5px;padding:1px 6px;color:#c7cbe8;}

  /* Two columns on desktop, one per row on mobile. Grid fills left-to-right then
     down, so rank order still reads 1-2 across the first row, 3-4 the next.
     align-items:start keeps a card its natural height — without it, expanding one
     card's ƒx panel would stretch its neighbour to match. */
  /* Three across on a desktop, two on a laptop, one on mobile. */
  #rgList{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;align-items:stretch;}
  #rgList .rgCard{margin-bottom:0;}
  /* Three across from 1200px — the card is compact enough now that waiting for a
     1400px screen left most desktops on two. */
  @media(max-width:1200px){ #rgList{grid-template-columns:repeat(2,minmax(0,1fr));} }
  @media(max-width:860px){ #rgList{grid-template-columns:1fr;gap:10px;} }

  /* Location chip beside the rep name — names the territory, or says Multiple. */
  .rgLoc{font-size:10.5px;font-weight:700;letter-spacing:.3px;padding:2px 8px;border-radius:999px;
    background:var(--primary-soft);color:var(--primary);white-space:nowrap;}
  /* "Multiple" carries the full list in its title — cue that it is hoverable, since
     a plain chip gives no hint that there is more behind it. */
  .rgLoc.multi{background:var(--surface-2);color:var(--text-soft);cursor:help;
    border:1px dashed var(--border);}
  .rgLoc.multi:hover{background:var(--primary-soft);color:var(--primary);border-style:solid;}


  /* Targets & Weighting collapses — it is set once and then just occupies screen. */
  #rgParamsBox > summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px;
    font-size:14px;font-weight:700;color:var(--rgTxt);padding:2px 0;}
  #rgParamsBox > summary::-webkit-details-marker{display:none;}
  #rgParamsBox > summary::before{content:'▸';color:var(--text-mute);font-size:12px;transition:transform .15s;}
  #rgParamsBox[open] > summary::before{transform:rotate(90deg);}
  #rgParamsBox > summary .rgSumNote{font-weight:400;font-size:12px;color:var(--rgMute);}

  /* Column layout + stretch so every card in a row is the same height and the
     metric block always starts at the same point down the card. */
  .rgCard{background:var(--rgBg);border:1px solid var(--rgLine);border-radius:12px;
    margin-bottom:10px;overflow:hidden;display:flex;flex-direction:column;}
  .rgCard .rgMetrics{flex:1 1 auto;}
  .rgCard.rgUngraded{border-style:dashed;opacity:.92;}
  /* align-items:flex-start so the rank, badge and chevron sit level with the NAME
     rather than floating to the middle of a now-taller three-line block. */
  .rgHead{display:flex;align-items:flex-start;gap:12px;padding:12px 14px;cursor:pointer;}
  .rgHead:hover{background:var(--rgBg2);}
  /* Was pale indigo on a translucent indigo tint — fine on the old dark card, weak
     on white. Solid soft background, full-strength text. */
  .rgRank{width:26px;height:26px;border-radius:50%;background:var(--primary-soft);color:var(--primary);
    font-size:11.5px;font-weight:800;display:flex;align-items:center;justify-content:center;flex:0 0 auto;}
  .rgWho{flex:1;min-width:0;}
  /* Two fixed lines — name, then location — identical on every card, so the metric
     block always begins at the same height and the rows line up across a row of
     cards. The name truncates rather than wrapping; the full text is in its title. */
  .rgNameRow{display:flex;align-items:center;min-width:0;line-height:1.3;}
  .rgName{font-size:14px;font-weight:800;color:var(--rgTxt);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
  .rgMetaRow{display:flex;align-items:center;gap:8px;min-height:19px;margin-top:3px;}
  /* Period on every card so a shared screenshot says what it compares. */
  .rgPeriod{font-size:10.5px;font-weight:700;background:rgba(99,102,241,.22);color:#a5b4fc;
    border-radius:999px;padding:2px 9px;white-space:nowrap;}
  .rgPeriod i{font-style:normal;opacity:.65;}
  .rgPeriod.warn{background:rgba(245,158,11,.2);color:#fcd34d;}
  .rgStars{margin-top:2px;font-size:13px;letter-spacing:1px;}
  .rgStar{color:#fbbf24;} .rgStarOff{color:#414963;}
  .rgScore{font-size:11px;color:var(--rgMute);font-weight:600;margin-left:7px;letter-spacing:0;}
  .rgNoGrade{font-size:11.5px;color:var(--rgMute);letter-spacing:0;}
  .rgBadge{font-size:10.5px;font-weight:800;border-radius:999px;padding:3px 9px;white-space:nowrap;}
  /* Pale text on a translucent tint was designed for the dark card; on white it left
     "Below target" and "No prior period" barely legible. Solid tints, dark text. */
  .rgBadge.bOn{background:#dcfce7;color:#15803d;}
  .rgBadge.bOff{background:#fee2e2;color:#b91c1c;}
  .rgBadge.bWarn{background:#fef3c7;color:#92400e;}
  .rgChev{color:var(--rgMute);font-size:12px;transition:transform .15s;}
  .rgCard.open .rgChev{transform:rotate(180deg);}

  .rgDisclaim{background:#fffbeb;border-top:1px solid #fde68a;border-bottom:1px solid #fde68a;
    padding:9px 14px;font-size:11.5px;color:#78350f;line-height:1.65;}
  .rgDisclaim b{color:#92400e;}
  .rgMetrics{border-top:1px solid var(--rgLine);}
  /* Metric block: name on its own line, then two rows — month tag left, figure
     centred, growth right. Current sits above prior so the headline reads first and
     the baseline is directly beneath it, in the same column. */
  /* One grid, two rows. The name (col 1) and the growth (col 4) each span both rows
     and centre vertically; the month/figure pairs auto-place into the middle two
     columns. Reads as a single row per metric rather than a stacked block. */
  /* The flexible column is column 1, so ALL the slack falls after the metric name
     and the month / figure / % cluster stays tight against the right edge. With the
     1fr on the value column instead, the figures floated to the middle and left a
     gap before the percentage. */
  .rgM{display:grid;grid-template-columns:1fr auto auto auto;gap:1px 8px;
    padding:9px 14px;align-items:center;border-bottom:1px solid var(--rgLine);}
  .rgM:last-child{border-bottom:0;}
  .rgMName{grid-area:1 / 1 / 3 / 2;display:flex;align-items:center;gap:7px;
    color:var(--rgSoft);font-weight:600;font-size:12px;}
  .rgIcon{width:18px;text-align:center;}
  /* Month, figure and % all right-aligned so the two lines stack cleanly and the
     numbers line up down the card. */
  .rgMTag{text-align:right;font-size:10px;font-weight:700;letter-spacing:.3px;
    color:var(--rgMute);white-space:nowrap;}
  .rgMTag.now{color:var(--primary);}
  .rgMVal{text-align:right;font-weight:800;color:var(--rgTxt);
    font-variant-numeric:tabular-nums;font-size:14px;}
  .rgMVal.prev{font-weight:600;font-size:12px;color:var(--rgSoft);}
  .rgMDelta{grid-area:1 / 4 / 3 / 5;display:flex;align-items:center;justify-content:flex-end;
    margin-left:6px;font-weight:700;font-variant-numeric:tabular-nums;font-size:12.5px;white-space:nowrap;}
  /* Greens/reds picked for a dark card were unreadable once the report went light. */
  .rgUp{color:var(--success);} .rgDown{color:var(--danger);} .rgMute{color:var(--rgMute);}

  .rgFx{display:none;background:var(--surface-2);border-top:1px solid var(--rgLine);padding:12px 14px;}
  .rgCard.open .rgFx{display:block;}
  .fxTitle{font-weight:800;font-size:12px;margin-bottom:9px;color:var(--rgTxt);}
  .fxTag{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;
    background:var(--primary-soft);color:var(--primary);border-radius:999px;padding:2px 7px;margin-left:4px;}
  .fxBlk{border-left:3px solid var(--primary-2);padding:2px 0 2px 10px;margin-bottom:9px;}
  .fxBlk b{color:var(--rgTxt);}
  .fxBlk.b2{border-color:#22d3ee;} .fxBlk.b3{border-color:#fbbf24;}
  .fxLine{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:1.75;color:var(--rgSoft);}
  .fxTot{display:flex;justify-content:space-between;gap:12px;border-top:1px solid var(--rgLine);
    padding-top:8px;margin-top:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
    font-size:11.5px;color:var(--rgSoft);}
  .fxTot b{color:var(--rgTxt);}
  @media(max-width:880px){
    .rgParams{grid-template-columns:1fr;gap:12px;}
    .rgHead{flex-wrap:wrap;gap:8px;}
    .rgBadge{order:3;} .rgChev{order:4;margin-left:auto;}
    .rgMDelta{min-width:64px;}
  }

  /* Phone: the doughnut's legend moves underneath (set in JS), so the card needs
     more vertical room or the ring is squashed to a sliver. */
  @media(max-width:760px){
    #ptChartWrap{height:340px !important;}
    #poTrackerRoot .kpis{grid-template-columns:repeat(auto-fit,minmax(140px,1fr)) !important;gap:8px !important;}
  }
  #ptTbl td:nth-child(3){font-weight:600;}
  #ptVendorTbl tbody tr:hover, #ptBrandTbl tbody tr:hover, #ptProductTbl tbody tr:hover{background:rgba(99,102,241,.07);}
  /* Marks a PO whose value includes non-item costs (freight, duty, etc.). */
  /* user-select:none so copying a PO number out of the table does NOT drag the
     tooltip glyph with it — a pasted "PS-HOL-314-C2 ⓘ" went into the Import
     Tracker's QB PO field and silently broke that PO's join. */
  .ptInfo{cursor:help;color:#d97706;font-weight:700;font-size:12px;user-select:none;-webkit-user-select:none;}

  /* In-table "＋ Add product" row (sleek, full-width, part of the table). */
  .poAddTr td{background:var(--surface-2);border-top:1px solid var(--border);}
  .poAddInline{width:100%;display:flex;align-items:center;gap:7px;padding:9px 12px;background:transparent;border:none;color:var(--primary);font-weight:600;font-size:12.5px;cursor:pointer;transition:background .12s;}
  .poAddInline:hover{background:rgba(99,102,241,.10);}
  .sidebarBackdrop{display:none;position:fixed;inset:0;background:rgba(15,17,35,.45);z-index:45;}
  .sidebarBackdrop.show{display:block;}
  .scrollWrapX{overflow-x:auto;}

  @media(max-width:880px){
    .app{display:block;}
    .sidebar{position:fixed;top:0;left:0;width:240px;height:100vh;transform:translateX(-100%);transition:transform .2s ease;z-index:50;padding:16px 12px;}
    .sidebar.mobileOpen{transform:translateX(0);}
    .navToggle{display:none;}
    /* On mobile the drawer is always "expanded" -- show every label/chevron that
       normally only appears when .sidebar.expanded is toggled on desktop. */
    .sidebar.mobileOpen .sideBrandRow,
    .sidebar.mobileOpen .sideUserBlock,
    .sidebar.mobileOpen .sideCopyright{display:block;}
    .sidebar.mobileOpen .n1-head .lbl,
    .sidebar.mobileOpen .n1-head .chev,
    .sidebar.mobileOpen .n2-head .lbl,
    .sidebar.mobileOpen .n2-head .chev,
    .sidebar.mobileOpen .navitem .lbl{display:inline;}
    .sidebar.mobileOpen .n2-head{padding-left:22px;justify-content:flex-start;}
    .sidebar.mobileOpen .navitem{justify-content:flex-start;}
    /* Let the accordion collapse/expand work the same as desktop -- the toggle JS
       controls each group's inline display; don't force them all open. */
    /* Menu button now lives IN the topbar flow (was position:fixed, which floated
       over the first card on every page). The topbar is sticky so the menu stays
       reachable while scrolling, with a solid page-coloured background. */
    .mobileMenuBtn{display:flex;position:static;box-shadow:none;margin-right:10px;}
    .content{padding:0 14px 50px;}
    .topbar{flex-wrap:wrap;gap:8px 10px;position:sticky;top:0;z-index:41;background:var(--bg);padding:10px 0 8px;margin-bottom:4px;box-shadow:0 8px 8px -8px rgba(15,17,35,.28);}
    .topbar h1{font-size:17px;}
    .filterToggle{display:inline-flex;}
    /* Collapse report filters on demand for more data visibility on small screens. */
    body.filtersHidden .filterbar, body.filtersHidden .pnlFilters{display:none !important;}
    /* Keep PO / form inputs inside their cards (Discount field was overflowing). */
    .poH, .ctSel, .formGrid input, .formGrid select{max-width:100%;box-sizing:border-box;}
    /* Team/user cards: stack the action buttons under the details instead of
       squeezing the name to a one-word-per-line column. */
    .userCard{flex-direction:column;align-items:stretch;}
    .userCard > div:last-child{justify-content:flex-start;}
    .kpis{grid-template-columns:repeat(2,1fr);gap:10px;}
    .kpi{padding:12px 13px;}
    .row{grid-template-columns:1fr;}
    .filterbar{gap:8px;padding:10px 12px;}
    .filterbar select,.filterbar input[type=date]{font-size:12px;padding:6px 8px;}
    .stickyBar{top:0;}
    table.pivot{font-size:11px;}
    table.pivot td,table.pivot th{padding:7px 8px;}
  }
  @media(max-width:480px){
    .kpis{grid-template-columns:1fr 1fr;}
    .kpi .value{font-size:16px;}
    .card{padding:14px 15px;}
  }
  /* ---- Picking Faults Report — mobile fixes ---- */
  @media(max-width:880px){
    /* filter bar: stack controls into a scrollable wrap, don't try to fit in one line */
    .stickyBar{flex-wrap:wrap;gap:8px;}
    .stickyBar .grp{flex-shrink:0;}

    /* two-column chart rows collapse to one column on tablet */
    .row{grid-template-columns:1fr;gap:14px;}

    /* picker performance table: tighten column widths */
    #pickerPerfBody td, #pickerPerfFoot td{font-size:11.5px;padding:7px 8px;white-space:nowrap;}

    /* fault type summary: let it scroll horizontally if needed */
    #faultTypeSummaryBody td, #faultTypeSummaryFoot td{font-size:12px;padding:7px 8px;}

    /* checker ratio: reduce chart height so it doesn't dominate */
    #checkerRatioChart{height:180px !important;}

    /* submission table: tighter cells, allow line-breaks in fault detail */
    #faultsSubmissionsBody td, #faultsSubTabBody td{font-size:11.5px;padding:7px 6px;}
    #faultsSubmissionsBody td:last-child, #faultsSubTabBody td:last-child{white-space:normal;max-width:140px;}

    /* file search boxes: full width so they don't overflow the card */
    .fileSearch{min-width:0;width:100%;max-width:100%;box-sizing:border-box;}

    /* cardHead: stack heading + search vertically on mobile */
    .cardHead{flex-wrap:wrap;gap:10px;}
    .cardHead .fileSearch{width:100%;}

    /* wrapHead tables: reduce min-width per column on mobile */
    table.rep.wrapHead th, table.pivot.wrapHead th{min-width:70px;}

    /* pager: ensure it stays centered and doesn't overflow */
    .pager{flex-wrap:wrap;gap:6px;}
  }
  @media(max-width:480px){
    /* on phone: make KPI row single column so values aren't squashed */
    .kpis{grid-template-columns:1fr;}
    .kpi{padding:11px 13px;}
    .kpi .value{font-size:18px;}

    /* filter bar controls go full width on phone */
    .stickyBar .grp select, .stickyBar .grp input{width:100%;box-sizing:border-box;}
    .stickyBar .grp{width:100%;}
    .msBtn{width:100%;box-sizing:border-box;}

    /* picker performance: hide ratio col on tiny screens to make room */
    #pickerPerfBody tr td:nth-child(3), #pickerPerfFoot td:nth-child(3),
    #pickerPerfBody tr td:nth-child(1), #pickerPerfFoot td:nth-child(1){ display:none; }

    /* submission table columns: hide customer on phone (visible in expanded view) */
    #faultsSubmissionsBody td:nth-child(6), #faultsSubTabBody td:nth-child(6){display:none;}

    /* fault date chart: reduce height so it fits without scrolling */
    #faultsByDateChart{height:200px !important;}

    /* summary strip: stack items vertically */
    .pivotCompareSummary{flex-direction:column;gap:10px;}
    .pcsItem{border-bottom:1px solid var(--border);padding-bottom:8px;}
    .pcsItem:last-child{border-bottom:none;}
  }

  /* ── PO Working (Planning ▸ Procurement) ─────────────────────────────── */
  .poPill{display:inline-block;font-size:11px;font-weight:700;color:#fff;padding:2px 9px;border-radius:999px;letter-spacing:.3px;text-transform:capitalize;vertical-align:middle;}
  .poPrefix{display:flex;align-items:center;padding:0 10px;background:var(--surface-2);border:1px solid var(--border);border-right:0;border-radius:var(--radius-sm) 0 0 var(--radius-sm);font-weight:700;color:var(--text-soft);white-space:nowrap;}
  /* PO Detail: freeze the header row + the first two columns (Batch, PO #) */
  #plTable thead th{position:sticky;top:0;z-index:3;}
  #plTable th:nth-child(1),#plTable td:nth-child(1){position:sticky;left:0;z-index:2;width:96px;min-width:96px;}
  #plTable th:nth-child(2),#plTable td:nth-child(2){position:sticky;left:96px;z-index:2;width:112px;min-width:112px;}
  #plTable tbody td:nth-child(1),#plTable tbody td:nth-child(2){background:var(--surface);}
  #plTable tfoot td:nth-child(1),#plTable tfoot td:nth-child(2){background:var(--surface-2);}
  #plTable thead th:nth-child(1),#plTable thead th:nth-child(2){z-index:4;}
  #plTable tfoot td{position:sticky;bottom:0;background:var(--surface-2);font-weight:700;z-index:1;}
  .poRollup{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:14px;}
  .poRollup .kpi .value.gp{color:var(--success);}
  .poLines input.poLineIn{width:82px;text-align:right;padding:5px 7px;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface);color:var(--text);}
  .poLines input.poLineIn:focus{outline:none;border-color:var(--primary-2);box-shadow:0 0 0 2px var(--primary-soft);}
  .poLines td, .poLines th{white-space:nowrap;}
  .poDel{color:var(--danger);font-weight:700;font-size:11px;}
  .poTypeahead{position:absolute;z-index:40;top:100%;right:0;margin-top:4px;width:420px;max-width:80vw;max-height:340px;overflow:auto;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);}
  .poTypeItem{display:flex;flex-direction:column;gap:2px;padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--border);}
  .poTypeItem:last-child{border-bottom:none;}
  .poTypeItem:hover{background:var(--surface-2);}
  .poTypeMeta{font-size:11px;color:var(--text-mute);}
  .poModalBk{position:fixed;inset:0;background:rgba(20,25,50,.42);display:flex;align-items:center;justify-content:center;z-index:120;padding:20px;}
  .poModal{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 12px 40px rgba(20,25,50,.28);padding:22px;width:420px;max-width:100%;}
  @media(max-width:640px){
    .poTypeahead{width:88vw;}
    .poLines input.poLineIn{width:64px;}
  }

  /* ── SALES ▸ Sales Invoices ─────────────────────────────────────────────── */
  /* Invoice vs Credit Note has to be readable at a glance: a credit note buried in
     a list of invoices is the row people misread. */
  .invPill{font-size:10px;font-weight:800;letter-spacing:.3px;border-radius:999px;
    padding:2px 8px;white-space:nowrap;}
  .invPill.i{background:var(--primary-soft);color:var(--primary);}
  .invPill.c{background:#fee2e2;color:#b91c1c;}
  /* Invoice Date + Transaction No. stay put while the rest scrolls.
     The second column's offset is MEASURED after render (--invCol1) rather than
     hard-coded: a fixed 110px did not match the real date-column width, so the
     scrolling columns slid underneath and overlapped the frozen pair. */
  table.invTbl{border-collapse:separate;border-spacing:0;}
  table.invTbl th, table.invTbl td{border-bottom:1px solid var(--border);}
  table.invTbl th:nth-child(1), table.invTbl td:nth-child(1){
    position:sticky;left:0;z-index:2;background:var(--surface);white-space:nowrap;}
  table.invTbl th:nth-child(2), table.invTbl td:nth-child(2){
    position:sticky;left:var(--invCol1,110px);z-index:2;background:var(--surface);white-space:nowrap;
    box-shadow:2px 0 0 0 var(--border);}   /* marks where the frozen pair ends */
  table.invTbl thead th{position:sticky;top:0;z-index:3;background:var(--primary);color:#fff;}
  table.invTbl thead th:nth-child(1), table.invTbl thead th:nth-child(2){z-index:5;background:var(--primary);}
  table.invTbl tbody tr:hover td:nth-child(1), table.invTbl tbody tr:hover td:nth-child(2){background:var(--surface-2);}
  table.invTbl tfoot td{position:sticky;bottom:0;background:var(--surface-2);z-index:3;}
  table.invTbl tfoot td:nth-child(1), table.invTbl tfoot td:nth-child(2){z-index:5;}
  table.invTbl th[data-inv-sort]{cursor:pointer;user-select:none;}
  /* Below tablet the frozen pair would eat the screen — only the date stays put. */
  @media(max-width:880px){
    table.invTbl th:nth-child(2), table.invTbl td:nth-child(2){position:static;box-shadow:none;}
  }

  /* Placeholder for a SALES tab whose page is not built yet. The nav shows the whole
     module so the shape is agreed up front, but an empty panel would read as broken —
     this says plainly that it is queued, and nothing pretends to hold data. */
  .soon{border:1px dashed var(--border);border-radius:var(--radius);background:var(--surface);
    padding:38px 24px;text-align:center;color:var(--text-mute);}
  .soon h3{margin:0 0 6px;font-size:16px;color:var(--text);}
  .soon p{margin:0;font-size:13px;line-height:1.7;}
  .soon .soonTag{display:inline-block;margin-bottom:12px;padding:3px 11px;border-radius:999px;
    background:var(--primary-soft);color:var(--primary);font-size:11px;font-weight:700;
    text-transform:uppercase;letter-spacing:.5px;}

  /* Customer Log — tag marking a record auto-created from invoice history with only
     a name, so incomplete records are obvious at a glance. */
  .cuTag{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:999px;
    background:var(--primary-soft);color:var(--primary);font-size:9.5px;font-weight:700;
    text-transform:uppercase;letter-spacing:.4px;vertical-align:middle;}
  .cuTag.off{background:#fee2e2;color:#b91c1c;}
  table.cuTbl th[data-cu-sort]{cursor:pointer;user-select:none;}

  /* Customer form sections — QB-style collapsible groups. Only the first is open, so
     40 fields do not present as a wall. */
  .cuSec{border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin-bottom:10px;background:var(--surface-2);}
  .cuSec > summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px;
    font-size:13.5px;font-weight:700;color:var(--text);}
  .cuSec > summary::-webkit-details-marker{display:none;}
  .cuSec > summary::after{content:'▸';margin-left:auto;color:var(--text-mute);transition:transform .15s;}
  .cuSec[open] > summary::after{transform:rotate(90deg);}

  /* ── ACCOUNTING ▸ Chart of Accounts ─────────────────────────────────────── */
  /* Account kind at a glance — the statement a line lands on is the first thing
     anyone checks when a balance looks wrong. */
  .kPill{display:inline-block;padding:2px 8px;border-radius:999px;font-size:10px;
    font-weight:700;text-transform:uppercase;letter-spacing:.4px;}
  .kPill.kAsset{background:#e0f2fe;color:#0369a1;}
  .kPill.kLiab {background:#fee2e2;color:#b91c1c;}
  .kPill.kEq   {background:#ede9fe;color:#6d28d9;}
  .kPill.kInc  {background:#dcfce7;color:#15803d;}
  .kPill.kExp  {background:#fef3c7;color:#92400e;}
  /* Behaviour flags — "no manual" is the one that stops a ledger drifting from its
     sub-ledger, so it reads as a warning rather than a neutral tag. */
  .fPill{display:inline-block;padding:1px 7px;border-radius:999px;font-size:9.5px;
    font-weight:700;background:var(--surface-2);color:var(--text-soft);text-transform:uppercase;letter-spacing:.3px;}
  .fPill.ctrl{background:var(--primary-soft);color:var(--primary);}
  .fPill.no{background:#fef3c7;color:#92400e;}

  /* Voucher entry — the contra line is generated, so it is shown differently from
     the lines the user types. */
  table.vchTbl tfoot .vchContra td{background:var(--primary-soft);}
  table.vchTbl .vNeed{border-color:var(--amber)!important;background:#fffbeb;}
  .vSt{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.4px;
    padding:2px 8px;border-radius:999px;background:var(--surface-2);color:var(--text-soft);}
  .vSt.posted{background:#dcfce7;color:#15803d;}
  .vSt.draft{background:#fef3c7;color:#92400e;}
  .vSt.reversed{background:#fee2e2;color:#b91c1c;}

  /* Financial statements — the integrity check is stated on the page, not implied. */
  .finChk{padding:9px 13px;border-radius:9px;font-size:12.5px;font-weight:700;margin-bottom:6px;}
  .finChk.ok{background:#dcfce7;color:#15803d;}
  .finChk.bad{background:#fee2e2;color:#b91c1c;}
  table.finTbl td{padding:6px 11px;}
  table.finTbl tr.finHead td{background:var(--primary);color:#fff;font-weight:800;font-size:10.5px;
    text-transform:uppercase;letter-spacing:.5px;padding:7px 11px;}
  table.finTbl tr.finGrp td{background:var(--surface-2);font-weight:700;}
  table.finTbl tr.finTot td{border-top:1px solid var(--border);font-weight:800;}
  table.finTbl tr.finNet td{border-top:2px solid var(--text);border-bottom:3px double var(--text);
    font-weight:800;font-size:13.5px;}
  table.finTbl td.num{text-align:right;font-variant-numeric:tabular-nums;}
  @media print{ .filterbar,.sidebar,.topbar{display:none!important;} .card{box-shadow:none;border:0;} }

  /* ── Mobile: shared fixes for every data screen ──────────────────────────
     Found by auditing all 14 data screens: eleven carry NO breakpoint of their
     own, so whatever they get on a phone comes from here. Three problems were
     common to all of them. */
  @media(max-width:880px){

    /* 1. Scroll affordance. Every wide table already sits in .scrollTable with
       overflow:auto, so it DOES scroll — but nothing said so, and a column
       clipped mid-value ("£2,988,127" then a cut edge) reads as a broken report
       rather than a scrollable one. The right-edge shading is painted only while
       there is more to scroll to: it is attached to the scrolling box, so it
       scrolls away as you reach the end. */
    .scrollTable{
      background-image:linear-gradient(to right, rgba(15,23,42,.10), rgba(15,23,42,0) 14px);
      background-position:right center; background-repeat:no-repeat;
      background-size:14px 100%; background-attachment:local;
      -webkit-overflow-scrolling:touch;
    }

    /* 2. Fixed heights. These boxes are 260–620px on a screen about 1100px tall,
       so one table plus one chart filled the viewport and left a dead band below
       — the empty space under Faults by Date Range. Capped against the VIEWPORT
       rather than a new pixel guess, which stays right on any handset. */
    .scrollTable{ max-height:56vh !important; }

    /* 3. Column freezing. A frozen first column costs a fixed slice of a 540px
       screen and leaves the figures nowhere to go. Desktop keeps it; on a phone
       the whole table scrolls as one piece, which is predictable. */
    .erpTbl.freezeCol th:nth-child(1),
    .erpTbl.freezeCol td:nth-child(1){ position:static; box-shadow:none; }
  }
