/* ======================================================================
   Shared "kv" (table-first) invoice-list style, rolled out one list page
   at a time to replace the prod-grid card layout (see list-cards.css).
   Colors/type/spacing are design tokens read directly off a reference
   app (KAVIS CRMS) via getComputedStyle()/CSSOM, not estimated - kept
   here as ONE shared file so every list page that adopts this style
   stays pixel-identical instead of drifting copy to copy.
   ====================================================================== */
.kv-root{
	--ink:#1C2024; --muted:#5B636A; --surface:#FFFFFF; --sunk:#F4F4F2;
	--line:#D3D6D3; --line-strong:#B9BDBA; --bg:#E9EAE7;
	--ok:#2C7A4B; --ok-bg:#E1F0E6;
	--warn:#8F5B00; --warn-bg:#FAEFD3;
	--bad:#B23628; --bad-bg:#F7E1DD;
	--info:#1E5AA0; --info-bg:#E2EBF6;
	/* System font stack only - no webfont request, so this stylesheet never
	   blocks or swaps fonts; see body.lgx below for why. */
	font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;
	font-variant-numeric:tabular-nums;
}
.kv-root, .kv-root *{ box-sizing:border-box; }
/* The host theme sets font-family directly on bare h1/p/input/button/table tags
   (e.g. style.css's "h1,h2,...{font-family:'Open Sans'}"), and a rule that
   explicitly sets a property on an element always wins over an inherited value,
   regardless of whose selector is less specific. This forces every element under
   .kv-root to explicitly inherit the system font from it instead, so none of the
   host theme's own per-tag font rules can win that tie.
   Excludes icon-font glyphs: Font Awesome's real mechanism is `.fa{font-family:
   FontAwesome}` on the HOST <i> tag (the glyph itself is a `:before{content:...}`
   with no font-family of its own - it inherits from its host), and this theme's
   own icon-* classes work the same way via "fontello". That rule and this one
   are the same specificity (one class + the universal selector contributes
   nothing), so without the :not() exclusions below this file - loading AFTER
   font-awesome.css - would win the tie and silently replace every icon glyph
   with a blank space in the system font (confirmed: WhatsApp Web/Gift-to-RP/
   branch icons in the dropdown menus and badges). */
.kv-root *:not(.fa):not([class*="fa-"]):not([class*="icon-"]){ font-family:inherit; }

.kv-wrap{ background:var(--surface); border:1px solid var(--line); border-radius:8px; overflow:hidden; color:var(--ink); font-size:14px; }
.kv-tablewrap{ overflow-x:auto; }
table.kv-table{ width:100%; border-collapse:collapse; font-size:13.5px; font-family:inherit; }
table.kv-table thead th{ text-align:left; font-size:12.5px; font-weight:600; color:var(--muted); padding:8px 12px; background:var(--sunk); border-bottom:1px solid var(--line); white-space:nowrap; }
table.kv-table thead th.num{ text-align:right; }
table.kv-table tbody td{ padding:8px 12px; border-bottom:1px solid var(--line); color:var(--ink); vertical-align:middle; white-space:nowrap; }
table.kv-table tbody tr:last-child td{ border-bottom:0; }
table.kv-table tbody tr:hover{ background:var(--sunk); }
.kv-invno{ font-weight:700; color:var(--ink); }
/* second identifier under the primary one in the id column - e.g. the customer
   list shows the auto id with the manually entered Cust ID beneath it. Muted and
   normal weight so the id column still reads as one value plus a reference. */
.kv-idsub{ display:block; font-weight:400; font-size:11.5px; color:var(--muted); line-height:1.5; }
.kv-amt{ font-variant-numeric:tabular-nums; text-align:right; font-weight:400; }
.kv-total{ font-weight:600; }
.kv-cust{ font-weight:400; color:var(--ink); }
.kv-subtags{ margin-top:2px; display:flex; flex-wrap:wrap; gap:4px; }
.kv-tag{ display:inline-block; font-size:10.5px; font-weight:500; color:var(--muted); background:var(--sunk); border-radius:4px; padding:1px 6px; white-space:nowrap; }

.kv-pill{ display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:500; padding:2px 9px; border-radius:999px; background:var(--sunk); color:var(--muted); white-space:nowrap; }
.kv-pill:before{ content:""; width:6px; height:6px; border-radius:50%; background:currentColor; flex:0 0 auto; }
.kv-pill.paid{ background:var(--ok-bg); color:var(--ok); }
.kv-pill.partial{ background:var(--info-bg); color:var(--info); }
.kv-pill.unpaid{ background:var(--warn-bg); color:var(--warn); }
.kv-pill.overdue{ background:var(--bad-bg); color:var(--bad); }
/* semantic aliases used by the module row builders (status, stock level, state).
   Without these a pill falls through to the neutral grey default, which is how
   "Completed"/"Pending"/negative stock were all rendering colourless. */
.kv-pill.ok{ background:var(--ok-bg); color:var(--ok); }
.kv-pill.warn{ background:var(--warn-bg); color:var(--warn); }
.kv-pill.bad{ background:var(--bad-bg); color:var(--bad); }
.kv-pill.info{ background:var(--info-bg); color:var(--info); }

/* Actions column: the buttons used to be pushed hard right (flex-end) while the
   "Actions" heading above them stayed left-aligned like every other th, so the
   label sat over the previous column's data instead of over its own buttons.
   Centre both, so heading and buttons line up as one column. The heading rule
   is guarded by :has() - it only fires when the actions cell really IS the last
   column, so a list whose last column is something else keeps normal alignment.
   Phone card layout re-declares .kv-acts as a full-width grid further down. */
.kv-acts{ display:flex; justify-content:center; align-items:center; gap:6px; }
/* Two markers because the row builders are not consistent: some emit
   <td class="kv-actscell">, others just wrap the buttons in .kv-acts (and on the
   older DataTables lists kv-mobile-labels.js adds that wrapper at runtime). */
.kv-table:has(tbody td.kv-actscell:last-child) thead th:last-child,
.kv-table:has(tbody td:last-child .kv-acts) thead th:last-child,
table.dataTable:has(tbody td.kv-actscell:last-child) thead th:last-child,
table.dataTable:has(tbody td:last-child .kv-acts) thead th:last-child{ text-align:center; }
.kv-actbtn{
	display:inline-flex; align-items:center; border:1px solid var(--line-strong); background:var(--surface); color:var(--ink);
	font-size:13px; font-weight:500; font-family:inherit; padding:3px 9px; border-radius:6px; text-decoration:none; white-space:nowrap; cursor:pointer;
}
.kv-actbtn:hover{ border-color:var(--ink); color:var(--ink); text-decoration:none; }
/* "more actions" dropdown trigger - same sizing as the other action buttons */
.kv-more{ position:relative; display:inline-block; }
.kv-more > .kv-actbtn{ padding:3px 8px; }
.kv-more .dropdown-menu{ min-width:190px; font-size:13px; font-family:inherit; }
.kv-more .dropdown-menu > li > a{ display:flex; align-items:center; gap:8px; color:var(--ink); }
/* The host theme paints hover as white text on a grey fill
   (style.css: ".dropdown-menu > li > a:hover{background:#aaa;color:#fff}").
   Overriding only the background left white text on this near-white fill -
   the label vanished on hover - so the colour has to be restated with it.
   :focus is included because Bootstrap styles the pair together, so keyboard
   navigation and the just-clicked item hit the same rule. */
.kv-more .dropdown-menu > li > a:hover,
.kv-more .dropdown-menu > li > a:focus{ background:var(--sunk); color:var(--ink); }

.kv-empty, .kv-loading{ padding:40px; text-align:center; color:var(--muted); font-size:13.5px; }
.kv-foot{ padding:12px 22px; font-size:12.5px; color:var(--muted); border-top:1px solid var(--line); }

/* Small inline tags under a row's primary name (state/tax/customer-type/
   branch) - a soft rounded badge per category so each reads as its own kind
   of fact at a glance instead of one undifferentiated grey label. Shared by
   every list page that adopts this design, not just Sales. */
.kv-subtags .qc-badge{ font-size:9.5px; font-weight:800; padding:2px 8px; border-radius:20px; letter-spacing:.2px; }
.kv-subtags .qc-badge.intra{ background:linear-gradient(135deg,#fef3c7,#fde68a); color:#92400e; }
.kv-subtags .qc-badge.inter{ background:linear-gradient(135deg,#bbf7d0,#86efac); color:#14532d; }
.kv-subtags .qc-badge.tax{ background:linear-gradient(135deg,#e0e7ff,#c7d2fe); color:#3730a3; }
.kv-subtags .qc-badge.exm{ background:linear-gradient(135deg,#f1f5f9,#e2e8f0); color:#475569; }
.kv-subtags .qc-badge.imp{ background:linear-gradient(135deg,#fed7aa,#fdba74); color:#7c2d12; }
.kv-subtags .qc-badge.dmg{ background:linear-gradient(135deg,#fecaca,#fca5a5); color:#7f1d1d; }
.kv-subtags .qc-badge.combo{ background:linear-gradient(135deg,#ede9fe,#ddd6fe); color:#5b21b6; }
.kv-subtags .qc-badge.biz{ background:linear-gradient(135deg,#fef2f2,#fecaca); color:#991b1b; }
.kv-subtags .qc-badge.branch{ background:linear-gradient(135deg,#e2e8f0,#cbd5e1); color:#334155; }
/* B2B vs B2C previously shared one plain grey .kv-tag - now distinct colors,
   same violet/cyan pairing the original card-list design used. */
.kv-subtags .qc-badge.b2b{ background:linear-gradient(135deg,#ede9fe,#ddd6fe); color:#5b21b6; }
.kv-subtags .qc-badge.b2c{ background:linear-gradient(135deg,#cffafe,#a5f3fc); color:#0e7490; }
/* restaurant table number - the field staff scan a Sales Order list for */
.kv-subtags .qc-badge.tableno{ background:linear-gradient(135deg,#ede9fe,#ddd6fe); color:#5b21b6; }

/* ---- responsive: stacked header/toolbar on narrow screens, table keeps its
   own columns and scrolls horizontally instead of squeezing/wrapping cells
   (the standard, accepted pattern for dense data tables on small screens -
   matches the reference app's own .tbl-wrap{overflow-x:auto}). ---- */
@media (max-width: 782px){
	.kv-bar{ flex-direction:column; align-items:flex-start !important; }
	.kv-newbtn{ width:100%; justify-content:center; }
	.kv-toolbar{ flex-direction:column; align-items:stretch !important; }
	.kv-seg{ width:100%; }
	.kv-seg .kv-chip{ flex:1; text-align:center; }
	.kv-search{ margin-left:0 !important; max-width:none !important; width:100%; }
}

/* ======================================================================
   kv-table ON A DATATABLE
   ----------------------------------------------------------------------
   A module list can be a DataTable (for its sort/search/export) AND carry
   .kv-table for the shared look. table-modern.css styles DataTables cells
   through ".content .dataTables_wrapper table.dataTable thead th" - four
   classes, with !important - so it beats a plainer .kv-table rule and the
   list ends up with 11px uppercase headers and no sticky row instead of
   the Sales-list styling. These selectors match that depth so .kv-table
   wins wherever it is present. Nothing here applies to a DataTable that
   is NOT a kv-table, so untouched lists keep their current look.
   ====================================================================== */
.content .dataTables_wrapper table.dataTable.kv-table thead th,
.content .dataTables_wrapper table.dataTable.kv-table thead td{
	background:var(--sunk) !important; color:var(--muted) !important;
	font-size:12.5px !important; font-weight:600 !important;
	text-transform:none !important; letter-spacing:0 !important;
	padding:9px 9px !important; border:0 !important;
	border-bottom:1px solid var(--line) !important; white-space:nowrap;
}
.content .dataTables_wrapper table.dataTable.kv-table tbody td{
	font-size:13.5px !important; color:var(--ink) !important;
	padding:11px 9px !important; border:0 !important;
	border-bottom:1px solid var(--line-2, #EAECEA) !important; vertical-align:middle;
}
.content .dataTables_wrapper table.dataTable.kv-table tbody tr:hover > td{ background:var(--sunk) !important; }
/* right-align the money/number columns the controller marks with kv-amt */
.content .dataTables_wrapper table.dataTable.kv-table tbody td.kv-amt,
.content .dataTables_wrapper table.dataTable.kv-table thead th.num{ text-align:right !important; }
.content .dataTables_wrapper table.dataTable.kv-table tbody td.kv-invno{ font-weight:700 !important; }

/* theme-lgx.css styles generic list tables via
   "body.lgx table#datatables > thead > tr > th" - an ID selector, so it beats
   any class-only rule. Repeated here with the id AND .kv-table so a converted
   list keeps the Sales-list header instead of the 11px uppercase one. */
body.lgx table.kv-table > thead > tr > th,
body.lgx table#datatables.kv-table > thead > tr > th{
	background:var(--sunk) !important; color:var(--muted) !important;
	font-size:12.5px !important; font-weight:600 !important;
	text-transform:none !important; letter-spacing:0 !important;
	padding:9px 9px !important; border:0 !important;
	border-bottom:1px solid var(--line) !important;
}
body.lgx table.kv-table > tbody > tr > td,
body.lgx table#datatables.kv-table > tbody > tr > td{
	font-size:13.5px !important; color:var(--ink) !important;
	padding:11px 9px !important; border:0 !important;
	border-bottom:1px solid var(--line-2, #EAECEA) !important;
}
body.lgx table#datatables.kv-table > tbody > tr > td.kv-amt{ text-align:right !important; }
body.lgx table#datatables.kv-table > tbody > tr > td.kv-invno{ font-weight:700 !important; }

/* DataTables' own toolbar, tuned to sit inside the kv card quietly */
.kv-root .dataTables_wrapper .dataTables_length,
.kv-root .dataTables_wrapper .dataTables_filter{ padding:8px 10px 0; }
.kv-root .dataTables_wrapper .dataTables_info,
.kv-root .dataTables_wrapper .dataTables_paginate{ padding:8px 10px 10px; }

/* product/thumbnail cell: small image beside the name, used by the Products
   list so the list keeps its visual identity inside the shared table */
.kv-prodcell{ display:flex; align-items:center; gap:9px; }
.kv-prodcell > div{ min-width:0; }
.kv-thumb{ width:34px; height:34px; border-radius:6px; object-fit:cover;
	border:1px solid var(--line); background:var(--sunk); flex:0 0 auto; }
@media (max-width:767px){ .kv-thumb{ width:40px; height:40px; } }

/* ======================================================================
   FROZEN SHELL ONLY WHEN THE CHAIN IS INTACT
   ----------------------------------------------------------------------
   body.lgx.kv-page .kv-shell fixes the column to the viewport height and
   lets ONLY .kv-tablewrap scroll. That depends on an unbroken flex chain:
   .kv-shell > .kv-root > .kv-wrap > .kv-tablewrap.

   Several lists nest the table inside a .widget / .widget-content, so
   .kv-root is NOT a direct child. The chain breaks, .kv-tablewrap never
   gets a constrained height, and the content simply overflows the fixed
   shell with NO scrollbar anywhere - rows past the fold become unreachable
   (this is what broke scrolling on the Products list).

   So: only freeze the shell when the direct-child chain exists. Otherwise
   let the column size to its content and the page scroll normally, which
   is also what the infinite-scroll handlers fall back to.
   ====================================================================== */
body.lgx.kv-page .kv-shell:not(:has(> .kv-root)):not(:has(> .tab-content > .tab-pane > .kv-root)){
	height:auto !important; max-height:none !important; display:block !important;
}
/* tabbed lists: the pane is the flex child that stretches, not .kv-root itself */
body.lgx.kv-page .kv-shell > .tab-content{ flex:1 1 auto; min-height:0; }
body.lgx.kv-page .kv-shell > .tab-content > .tab-pane.active{
	display:flex; flex-direction:column; height:100%; min-height:0;
}
body.lgx.kv-page .kv-shell > .tab-content > .tab-pane > .kv-root{
	flex:1 1 auto; min-height:0; display:flex; flex-direction:column;
}
/* ======================================================================
   NO PANEL AROUND THE TABLE
   ----------------------------------------------------------------------
   The reference list (Sales Bill List) is: one title card at the top
   carrying the heading, the Filters button and the New button - then the
   table directly on the page canvas. Most older lists instead wrap the
   table in a .widget panel that repeats the page title and adds a second
   border/background around it ("a card inside a card").

   :has() lets that be undone centrally: any .widget that CONTAINS a
   kv-table loses its own header and its panel chrome, so the table reads
   as the page's content. The separate Filters widget has no kv-table in
   it, so it keeps its panel and still works as the collapsible filter.
   ====================================================================== */
body.lgx .widget:has(.kv-table){
	background:transparent !important; border:0 !important; box-shadow:none !important;
	padding:0 !important; margin:0 0 10px !important; border-radius:0 !important;
}
body.lgx .widget:has(.kv-table) > .widget-header{ display:none !important; }
/* the separate Filters panel keeps working, but its own header bar is dropped:
   the title card already carries the Filters button, so the old strip was a
   second header for the same thing. The hidden .widget-toggle inside it is what
   that button triggers, and jQuery fires click on hidden elements fine. */
body.lgx.kv-page .lgx-head ~ .widget:has(.widget-toggle):not(:has(.kv-table)) > .widget-header{ display:none !important; }
body.lgx.kv-page .lgx-head ~ .widget:has(.widget-toggle):not(:has(.kv-table)){
	background:transparent !important; border:0 !important; box-shadow:none !important; padding:0 !important;
}
body.lgx .widget:has(.kv-table) > .widget-content{
	padding:0 !important; margin:0 !important; background:transparent !important;
	border:0 !important; box-shadow:none !important;
}


/* Scroll-to-load replaces paging on every kv list (kv-scrolllist.js), so the
   "Show N entries" selector no longer means anything - hide it wherever it sits.
   It is hidden rather than removed so DataTables still owns the element. */
.kv-dttools .dataTables_length,
body.lgx.kv-page .dataTables_length,
body.lgx .dataTables_wrapper .dataTables_length,
body.lgx .kv-dttools .dataTables_length{ display:none !important; }
/* the DataTables toolbar after kv-mobile-labels.js moves it into the Filters
   panel: laid out as one tidy row, full width on a phone */
.kv-dttools{ display:flex; flex-wrap:wrap; align-items:center; gap:10px;
	padding:10px 0 2px; width:100%; clear:both; }
.kv-dttools .dt-buttons{ float:none !important; margin:0 !important; }
.kv-dttools .dataTables_length,
.kv-dttools .dataTables_filter{ float:none !important; margin:0 !important; padding:0 !important; text-align:left !important; }
.kv-dttools .dataTables_filter{ margin-left:auto !important; }
.kv-dttools .dataTables_filter input{ margin-left:6px !important; height:34px; min-width:200px; }
@media (max-width:767px){
	.kv-dttools{ gap:8px; }
	.kv-dttools .dataTables_filter{ margin-left:0 !important; width:100%; }
	.kv-dttools .dataTables_filter label{ width:100%; display:flex; align-items:center; gap:6px; }
	.kv-dttools .dataTables_filter input{ flex:1 1 auto; min-width:0; width:100% !important; }
}

/* the "nothing here yet" row, converted from the old .lg-card-empty placeholder:
   it is always in the markup, so hide it whenever the table has real rows */
.kv-table tbody tr.kv-emptyrow:not(:only-child){ display:none !important; }
.kv-table tbody tr.kv-emptyrow > td{ color:var(--muted); padding:18px 10px !important; }

/* ======================================================================
   CELL WEIGHT  -  only the important fields are bold
   ----------------------------------------------------------------------
   custom.css sets "body{font-weight:bold}", so EVERY cell in every list
   inherits bold and nothing stands out. That global rule is left alone
   (it would restyle the whole application); instead list cells are reset
   to normal here and weight is given back only to the fields that carry
   the meaning, matching the Sales Bill List:
     - the id (invoice/jobcard/production no.)  -> 700
     - the name the row is about (customer)     -> 600
     - the headline amount (total)              -> 600
   everything else - date, category, assignee, gender, branch, sub-totals,
   tax, paid, balance - stays normal so the eye lands on the three that
   matter. Applies to every kv-table list, desktop and phone.
   ====================================================================== */
body.lgx table.kv-table > tbody > tr > td,
body.lgx table#datatables.kv-table > tbody > tr > td,
.content .dataTables_wrapper table.dataTable.kv-table tbody td{ font-weight:400 !important; }

body.lgx table.kv-table > tbody > tr > td.kv-invno,
body.lgx table#datatables.kv-table > tbody > tr > td.kv-invno,
.content .dataTables_wrapper table.dataTable.kv-table tbody td.kv-invno,
body.lgx table.kv-table > tbody > tr > td.kv-invno > a{ font-weight:700 !important; }

body.lgx table.kv-table > tbody > tr > td .kv-cust,
body.lgx table#datatables.kv-table > tbody > tr > td .kv-cust,
.content .dataTables_wrapper table.dataTable.kv-table tbody td .kv-cust,
/* the labeller puts kv-cust on the TD itself for DataTables lists */
body.lgx table.kv-table > tbody > tr > td.kv-cust,
body.lgx table#datatables.kv-table > tbody > tr > td.kv-cust,
.content .dataTables_wrapper table.dataTable.kv-table tbody td.kv-cust{ font-weight:600 !important; }

body.lgx table.kv-table > tbody > tr > td.kv-total,
body.lgx table#datatables.kv-table > tbody > tr > td.kv-total,
.content .dataTables_wrapper table.dataTable.kv-table tbody td.kv-total{ font-weight:600 !important; }

/* chips and pills keep their own weight */
body.lgx table.kv-table > tbody > tr > td .qc-badge{ font-weight:800 !important; }
body.lgx table.kv-table > tbody > tr > td .kv-pill{ font-weight:500 !important; }

/* ======================================================================
   PHONE CARD LAYOUT  -  GLOBAL, every kv-table list
   ----------------------------------------------------------------------
   Horizontal scrolling is fine for a dense table on a desktop, but on a
   phone the right-hand columns - on these lists the action buttons - sit
   off-screen behind a sideways swipe, so the main thing you came to do is
   invisible. Under 768px every row collapses into a card: the identifier
   cell is the card title, every other cell becomes a label:value line,
   and the actions become a full-width row of thumb-sized buttons.

   Applies to ANY page with body.lgx.kv-page, so a new list built on the
   standard pattern is phone-ready with no extra markup. The captions come
   from each td's data-label, which kv-mobile-labels.js fills in from the
   table's own <thead> - so no controller has to emit them and a caption
   can never drift from its column heading.

   .kv-mobilecards is kept as an alias for pages that opted in by name
   before this became the default.
   ====================================================================== */
@media (max-width: 767px){
	/* the frozen dashboard shell (fixed height, inner scroll) fights a phone:
	   let the whole page scroll under the thumb instead */
	body.lgx.kv-page .kv-shell{ height:auto !important; max-width:none !important; }
	body.lgx.kv-page .kv-root .kv-tablewrap{ overflow:visible !important; flex:none !important; }
	body.lgx.kv-page .kv-tablewrap{ overflow-x:visible; }

	body.lgx.kv-page .kv-table,
	body.lgx.kv-page .kv-table tbody,
	body.lgx.kv-page .kv-table tr,
	body.lgx.kv-page .kv-table td{ display:block; width:100%; }
	body.lgx.kv-page .kv-table thead{ display:none; }

	body.lgx.kv-page .kv-table tbody tr{
		background:var(--surface); border:1px solid var(--line); border-radius:12px;
		margin:0 0 10px; padding:10px 12px; box-shadow:0 1px 2px rgba(16,24,40,.05);
		position:relative;
	}
	body.lgx.kv-page .kv-table tbody tr:hover{ background:var(--surface); }
	body.lgx.kv-page .kv-table td{
		border:0 !important; padding:5px 0 !important;
		display:flex; align-items:center; justify-content:space-between; gap:12px;
		text-align:right; min-height:0;
	}
	body.lgx.kv-page .kv-table td::before{
		content:attr(data-label); flex:0 0 auto; text-align:left;
		font-size:10.5px; font-weight:700; letter-spacing:.4px; text-transform:uppercase;
		color:var(--muted);
	}
	/* a cell whose column had no heading (or an icon-only column) shows no caption */
	body.lgx.kv-page .kv-table td[data-label=""]::before,
	body.lgx.kv-page .kv-table td:not([data-label])::before{ display:none; }
	/* an empty cell adds nothing on a phone - drop the whole line */
	body.lgx.kv-page .kv-table td.kv-cell-empty{ display:none !important; }

	/* a cell carrying badge chips can't sit on one line with its caption - the
	   chips get squeezed into the value column and wrap raggedly. Stack it:
	   caption on its own line, value and chips underneath, left aligned. */
	body.lgx.kv-page .kv-table td:has(.kv-subtags){ display:block; text-align:left; }
	body.lgx.kv-page .kv-table td:has(.kv-subtags)::before{ display:block; margin-bottom:3px; }

	/* identifier cell = card title, full width, no caption */
	body.lgx.kv-page .kv-table td.kv-invno{
		display:block; text-align:left; font-size:16px; padding:0 0 6px !important;
		border-bottom:1px solid var(--line) !important; margin-bottom:6px;
	}
	body.lgx.kv-page .kv-table td.kv-invno::before{ display:none; }
	body.lgx.kv-page .kv-table td.kv-invno .kv-subtags{ margin-top:5px; }
	/* restaurant table number reads at a glance across a busy floor */
	body.lgx.kv-page .kv-subtags .qc-badge.tableno{
		font-size:13px; padding:4px 11px; font-weight:800; order:-1;
	}
	/* the business-name chip is low value on a phone and ate a whole line */
	body.lgx.kv-page .kv-subtags .qc-badge.biz{ font-size:9px; padding:2px 6px; }

	/* actions: full-width thumb targets, not a cramped right-aligned strip.
	   :has() finds the cell without every controller needing a marker class. */
	body.lgx.kv-page .kv-table td.kv-actscell,
	body.lgx.kv-page .kv-table td:has(.kv-acts){ display:block; padding-top:9px !important; }
	body.lgx.kv-page .kv-table td.kv-actscell::before,
	body.lgx.kv-page .kv-table td:has(.kv-acts)::before{ display:none; }
	body.lgx.kv-page .kv-acts{
		display:grid; grid-template-columns:repeat(auto-fit,minmax(64px,1fr));
		gap:6px; justify-content:stretch; width:100%;
	}
	body.lgx.kv-page .kv-acts > .kv-actbtn,
	body.lgx.kv-page .kv-more > .kv-actbtn{
		min-height:42px; display:flex; align-items:center; justify-content:center;
		font-size:13px; padding:6px 8px;
	}
	body.lgx.kv-page .kv-more{ display:block; }
	body.lgx.kv-page .kv-more > .kv-actbtn{ width:100%; }
	body.lgx.kv-page .kv-more .dropdown-menu{ right:0; left:auto; }

	/* header band + filter controls stack and go full width */
	body.lgx.kv-page .lgx-head{ flex-wrap:wrap; }
	body.lgx.kv-page .lgx-h-act{ width:100%; display:flex; flex-wrap:wrap; gap:8px; margin-top:8px; }
	body.lgx.kv-page .lgx-h-act > .btn{ flex:1 1 120px; min-height:44px; display:flex; align-items:center; justify-content:center; }
	body.lgx.kv-page .lgx-searchrow{ min-width:0 !important; flex-wrap:wrap; }
	body.lgx.kv-page .lgx-searchfld{ width:100%; }
	body.lgx.kv-page #qsearch{ min-width:0 !important; width:100% !important; height:42px; }
	/* filter panel: one field per line, full-width controls */
	body.lgx.kv-page #lgxFilterPanel .form-group{ margin-bottom:10px; }
	body.lgx.kv-page #lgxFilterPanel .form-control{ height:40px; }
	body.lgx.kv-page #lgxFilterPanel .col-md-8,
	body.lgx.kv-page #lgxFilterPanel .col-md-4{ width:100%; }
	body.lgx.kv-page .lgx-kpis{ grid-template-columns:repeat(2,1fr) !important; }
}

/* ----------------------------------------------------------------------
   Same phone card treatment for every DataTables list in the app.

   "table.dataTable" is the class DataTables itself adds on init, and in this
   codebase DataTable() is only ever called on list tables (#datatables,
   #datatables1/2/one, #paysliphistory, #advancehistory) - never on an
   entry-form line-item table (#salestable, #rowtable, #ProdTbl, #stocktable
   and friends are plain tables). So this hook is automatic and safe: any
   list, including one added later, becomes phone-ready with no page edit.

   "kv-mobiletable" stays as a manual opt-in for a list table that is NOT a
   DataTable. "kv-nomobilecards" on the table opts one back out.
   kv-mobile-labels.js labels all of these.
   ---------------------------------------------------------------------- */
@media (max-width: 767px){
	/* escape hatch for a list that must keep real columns on a phone */
	body.lgx table.kv-nomobilecards,
	body.lgx table.kv-nomobilecards tbody,
	body.lgx table.kv-nomobilecards tr,
	body.lgx table.kv-nomobilecards td{ display:revert !important; }
	body.lgx table.kv-nomobilecards thead{ display:table-header-group !important; }
	body.lgx table.kv-nomobilecards td::before{ display:none !important; }

	body.lgx table.kv-mobiletable, body.lgx table.dataTable,
	body.lgx table.kv-mobiletable tbody, body.lgx table.dataTable tbody,
	body.lgx table.kv-mobiletable tr, body.lgx table.dataTable tr,
	body.lgx table.kv-mobiletable td, body.lgx table.dataTable td, body.lgx table.dataTable td{ display:block; width:100% !important; }
	body.lgx table.kv-mobiletable thead, body.lgx table.dataTable thead{ display:none; }
	body.lgx table.kv-mobiletable tbody tr, body.lgx table.dataTable tbody tr{
		background:#fff; border:1px solid #D3D6D3; border-radius:12px;
		margin:0 0 10px; padding:10px 12px; box-shadow:0 1px 2px rgba(16,24,40,.05);
	}
	body.lgx table.kv-mobiletable td,
	/* table-modern.css styles DataTables cells through
	   ".content .dataTables_wrapper table.dataTable tbody td" (4 classes), which
	   out-specifies a plainer selector even with !important - matched here so the
	   card layout actually wins on borders and padding. */
	body.lgx .content .dataTables_wrapper table.kv-mobiletable tbody td,
	body.lgx .content .dataTables_wrapper table.dataTable tbody td{
		border:0 !important; padding:5px 0 !important;
		display:flex; align-items:center; justify-content:space-between; gap:12px;
		text-align:right; white-space:normal !important;
	}
	body.lgx table.kv-mobiletable td::before, body.lgx table.dataTable td::before{
		content:attr(data-label); flex:0 0 auto; text-align:left;
		font-size:10.5px; font-weight:700; letter-spacing:.4px; text-transform:uppercase; color:#5B636A;
	}
	body.lgx table.kv-mobiletable td[data-label=""]::before,
	body.lgx table.dataTable td[data-label=""]::before,
	body.lgx table.dataTable td:not([data-label])::before,
	body.lgx table.kv-mobiletable td:not([data-label])::before{ display:none; }
	body.lgx table.kv-mobiletable td.kv-cell-empty, body.lgx table.dataTable td.kv-cell-empty{ display:none !important; }
	/* first cell reads as the card title */
	body.lgx table.kv-mobiletable tbody td:first-child,
	body.lgx table.dataTable tbody td:first-child,
	body.lgx .content .dataTables_wrapper table.kv-mobiletable tbody td:first-child,
	body.lgx .content .dataTables_wrapper table.dataTable tbody td:first-child{
		display:block; text-align:left; font-size:15px; font-weight:700;
		padding:0 0 6px !important; border-bottom:1px solid #D3D6D3 !important; margin-bottom:6px;
	}
	body.lgx table.kv-mobiletable tbody td:first-child::before,
	body.lgx table.dataTable tbody td:first-child::before{ display:none; }
	/* DataTables' own chrome is unusable at this width */
	body.lgx .dataTables_wrapper .dataTables_length,
	body.lgx .dataTables_wrapper .dataTables_filter{ float:none !important; width:100%; text-align:left; }
	body.lgx .dataTables_wrapper .dataTables_filter input{ width:100% !important; margin:4px 0 0 !important; height:40px; }
	body.lgx .dataTables_wrapper .dataTables_scrollBody{ overflow-x:hidden !important; }
}


/* ======================================================================
   GLOBAL SHELL - sidebar/canvas/brand colors/fonts/buttons, applied to
   EVERY page via plain "body.lgx" (not gated behind a per-page class).
   This is the app-wide redesign baseline: as each page gets its content
   rewritten to the table/list patterns below, it inherits this shell
   automatically instead of needing its own copy. Reuses theme-lgx.css's
   own CSS-variable system where possible (redefining --lgx-canvas/
   --lgx-nav/--lgx-brand repaints every existing var(...) rule at once),
   and adds direct overrides only for the sidebar rules theme-lgx.css
   wrote as hardcoded hex rather than a variable. Selectors/!important are
   copied from theme-lgx.css itself so these win by source order (this
   file loads after it) at matching specificity.
   ====================================================================== */
body.lgx{
	--lgx-canvas:#E9EAE7;              /* page background, same as .kv-root --bg   */
	--lgx-nav:#22282D;                 /* sidebar background                        */
	/* NOTE: the accent (--lgx-brand / -lt / -dk / -sf / -rgb and --lgx-grad)
	   is deliberately NOT redefined here. It is defined once in
	   theme-lgx.css's :root block, which is the single place to change the
	   whole application's accent colour. This file used to redeclare it,
	   which meant the accent lived in two files and they could drift apart.
	   Only the two shell colours above are specific to this baseline. */
	/* System font stack only - no Google Fonts request. Segoe UI (Windows) /
	   -apple-system (Mac) already look clean and modern, and this removes two
	   network round-trips + a font-swap flash from every page load. */
	font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif !important;
}
body.lgx .content,
body.lgx .content-page{ font-family:inherit !important; }
/* table-modern.css's own "GLOBAL PAGE BACKGROUND" rule (body.fixed-left ... .content)
   ties theme-lgx.css's specificity and loads earlier, so --lgx-canvas alone never
   reaches these elements - restate the background directly here to win on source
   order at the same specificity. */
body.fixed-left.lgx,
body.fixed-left.lgx #wrapper,
body.fixed-left.lgx .content-page,
body.fixed-left.lgx .content-page > .content,
body.fixed-left.lgx .content{ background-color:var(--lgx-canvas) !important; }

/* sidebar text/hover/active - theme-lgx.css hardcodes these as hex instead of
   a variable, so they need a direct override rather than a --lgx-* redefine.
   Rounded, inset pill (not a full-bleed square row) - same shape/font-weight
   as the reference app's own ".nav a{padding:7px 10px;border-radius:6px;
   font-weight:500}" inside its ".nav{padding:4px 10px 12px}" rail. */
body.lgx #sidebar-menu ul li a{
	color:#C7CDD1 !important; font-weight:500 !important;
	margin:1px 10px !important; width:auto !important; border-radius:6px !important;
}
body.lgx #sidebar-menu ul li a i{ color:#88929A !important; }
body.lgx #sidebar-menu ul li a:hover{ background:#2C3339 !important; color:#ffffff !important; }
body.lgx #sidebar-menu ul li a:hover i{ color:#ffffff !important; }
body.lgx #sidebar-menu > ul > li.active > a,
body.lgx #sidebar-menu > ul > li > a.active,
body.lgx #sidebar-menu > ul > li > a.subdrop{ color:#ffffff !important; }
body.lgx #sidebar-menu > ul > li.active > a:hover,
body.lgx #sidebar-menu > ul > li > a.subdrop:hover{ background:#2C3339 !important; }
/* the one filled row: active leaf item gets a solid accent block instead of
   the old flat grey fill (the brand navy pairs with white text, unlike the
   reference app's yellow which needed its own dark-text special case). Same
   weight (500) as every other nav link - the reference app doesn't bold its
   active item either, the fill color alone marks it as current. */
body.lgx #sidebar-menu ul ul li.active > a,
body.lgx #sidebar-menu ul ul li a.active{
	background:var(--lgx-brand) !important; color:#ffffff !important; font-weight:500 !important;
}
body.lgx #sidebar-menu ul ul li.active > a:before,
body.lgx #sidebar-menu ul ul li a.active:before{ content:none !important; }
body.lgx #sidebar-menu ul ul li a{ color:#9fa6ab !important; margin:1px 10px !important; width:auto !important; border-radius:6px !important; }
body.lgx #sidebar-menu ul ul li a:hover{ background:#2C3339 !important; color:#ffffff !important; }
body.lgx .left.side-menu{ scrollbar-color:#3a414699 transparent !important; }
body.lgx .left.side-menu::-webkit-scrollbar-thumb{ background:#3a4146 !important; }

/* buttons/forms - theme-lgx.css's hover state and focus ring are hardcoded to
   the terracotta rgb values rather than a variable, so they need the same
   direct-override treatment as the sidebar above. */
body.lgx .btn-primary:hover, body.lgx .btn-info:hover,
body.lgx .btn-success:hover, body.lgx .btn-sea-green:hover{
	background:linear-gradient(135deg,#1A6099 0%,#051D35 100%) !important; border-color:transparent !important;
}
body.lgx .btn-primary, body.lgx .btn-info,
body.lgx .btn-success, body.lgx .btn-sea-green{
	box-shadow:0 2px 8px rgba(9,50,92,.28) !important;
}
body.lgx .form-control:focus{ box-shadow:0 0 0 3px rgba(9,50,92,.17) !important; }

/* ======================================================================
   PER-PAGE LAYOUT, opt-in via body class "kv-page" (added alongside "lgx",
   never in place of it). The global shell above applies everywhere now;
   these rules are the ones that depend on page-specific markup (a
   ".kv-shell" wrapper, a ".lgx-head"/".lgx-kpis" band) that not every page
   has yet, so they stay gated until that page's content is rewritten too.
   ====================================================================== */

/* Tighter vertical rhythm: topbar -> title card -> KPI strip -> table were
   14px/12px/12px apart (theme-lgx.css's own spacing, designed before this
   page had a KPI strip AND a full table below it); pulled to 8px/8px/8px so
   the table starts higher and more of it shows on one screen. */
body.lgx.kv-page .content-page .content{ padding-top:6px !important; }
body.lgx.kv-page .content-page .content > .row:first-child,
body.lgx.kv-page .content-page .content > .lgx-head:first-child{ margin-top:2px !important; }
body.lgx.kv-page .lgx-head{ margin-bottom:8px !important; }
body.lgx.kv-page .lgx-kpis{ margin-bottom:8px !important; }

/* Frozen dashboard layout (Zoho Books / Tally style): title, KPI cards, the
   (optional) filter panel and the table HEADER never move - only the table
   ROWS scroll, inside their own bounded box, instead of the whole window
   growing as infinite-scroll appends more invoices. ".kv-shell" is the
   col-sm-12 wrapper that holds all of it; everything in it is fixed-height
   by default, and only .kv-root/.kv-wrap/.kv-tablewrap are told to stretch
   and scroll. 68px = topbar(50) + this page's own tightened top/bottom
   padding (6+2+10) from the rules just above. */
body.lgx.kv-page .kv-shell{
	display:flex; flex-direction:column; height:calc(100vh - 68px);
	/* On a 4K monitor an unconstrained width:100% table also hits a real
	   browser auto-table-layout bug: with every other column's content short
	   and fixed, the browser dumps ALL the leftover horizontal space into one
	   arbitrary column (Status, here), stranding Actions far off to the
	   right instead of spreading sensibly. Capping the whole shell's width
	   sidesteps it the same way Zoho/QuickBooks/Tally do - none of them
	   stretch a transaction table bezel-to-bezel on an ultrawide monitor
	   either, since past a point extra width just hurts scannability. */
	max-width:1900px; margin-left:auto; margin-right:auto;
}
body.lgx.kv-page .kv-shell > *{ flex:0 0 auto; }
body.lgx.kv-page .kv-shell > .kv-root{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
body.lgx.kv-page .kv-root .kv-wrap{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
body.lgx.kv-page .kv-root .kv-tablewrap{ flex:1 1 auto; min-height:0; overflow-y:auto; }
body.lgx.kv-page table.kv-table thead th{ position:sticky; top:0; z-index:2; }

/* Pre-existing filter-panel bug (not introduced here): .lgx-searchrow has
   min-width:0, so when the other filter fields' real rendered width (e.g. a
   long company name in the Business Name dropdown) eats into the shared
   flex-wrap row, the flex algorithm shrinks the search row below what its
   OWN child input can go (that input has its own unconditional
   min-width:240px) - the row doesn't wrap to a new line, the input just
   silently overflows on top of the record-count/Excel button next to it.
   Giving the row a real min-width makes the wrap trigger correctly instead. */
body.lgx.kv-page .lgx-searchrow{ min-width:380px !important; }

/* KPI strip: "Collected"/"Outstanding" already get semantic ok/bad colors -
   "Invoices" and "Total Billed" were left plain grey, reading as an
   afterthought next to the other two. Invoices takes the brand navy (neutral
   count, not a status), Total Billed takes the existing warn/amber token
   (gross value - something to be aware of, not yet collected or overdue). */
body.lgx.kv-page .lgx-kpi.brand .k-l, body.lgx.kv-page .lgx-kpi.brand .k-v{ color:var(--lgx-brand); }
