/* Garage Management System — clean, premium ERP UI.
   Inter (self-hosted), a single blue accent, neutral grays, outline icons.
   Flat surfaces: 1px borders instead of shadows, no gradients, no glass.
   Self-contained: no CDNs. */

/* Inter variable — OFL, see /static/fonts/Inter-LICENSE.txt */
@font-face {
  font-family: "Inter";
  src: url("../fonts/Inter-Variable.woff2") format("woff2-variations"),
       url("../fonts/Inter-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Accent */
  --primary: #2563EB;
  --primary-dark: #1D4ED8;
  --primary-soft: #EFF4FF;
  /* Semantic colours */
  --success: #16A34A;
  --danger: #DC2626;
  --warning: #D97706;
  --info: #2563EB;
  /* Soft fills for chips, and a darker text cut that stays readable on them */
  --success-soft: #ECFDF3;  --success-ink: #15803D;  --success-line: #CBF0D9;
  --warning-soft: #FEF6E7;  --warning-ink: #B45309;  --warning-line: #FAE6C4;
  --danger-soft:  #FEF2F2;  --danger-ink:  #B91C1C;  --danger-line:  #FBD5D5;
  --primary-ink:  #1D4ED8;                            --primary-line: #D6E2FE;
  --neutral-soft: #F2F4F7;  --neutral-ink: #4B5563;

  /* Neutrals */
  --bg: #F7F8FA;
  --card-bg: #FFFFFF;
  --text: #1F2937;
  --muted: #6B7280;
  --border: #E8EAED;
  --border-strong: #D8DBE0;
  --hover: #F2F4F7;
  --disabled: #D1D5DB;

  /* Sidebar — white chrome against the off-white canvas */
  --sidebar-bg: #FFFFFF;
  --sidebar-text: #4B5563;
  --sidebar-active-bg: var(--primary-soft);

  /* Radii */
  --radius: 12px;      /* cards, tables, dropdowns */
  --radius-sm: 8px;    /* buttons, inputs */
  --radius-xs: 6px;    /* chips, options */
  --radius-lg: 14px;   /* modals */

  /* Shadows — only floating surfaces get one; flat surfaces use borders */
  --shadow-sm: none;
  --shadow-md: 0 4px 12px rgba(16,24,40,.07);
  --shadow-lg: 0 16px 40px rgba(16,24,40,.11);
  --shadow: var(--shadow-sm);

  --ring: 0 0 0 3px rgba(37,99,235,.12);
  --speed: 150ms;
  --ease: cubic-bezier(.4,0,.2,1);

  /* Chrome metrics — the topbar spans the full width, the sidebar sits under it */
  --topbar-h: 56px;
  --sidebar-w: 244px;
  /* Card padding, also used to pull tables out to the card edges */
  --card-pad: 1.25rem;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* Inline monochrome icons */
.icon { width: 1.15em; height: 1.15em; display: inline-block; vertical-align: -0.18em;
  stroke: currentColor; fill: none; flex: 0 0 auto; }
.page-head h1 .icon { width: 1.05em; height: 1.05em; vertical-align: -0.1em; margin-right: .2rem; color: var(--muted); }
.card-head .icon { color: var(--muted); }
.sidebar nav a .ic .icon { width: 18px; height: 18px; vertical-align: middle; }
.btn .icon { width: 1.05em; height: 1.05em; }

body {
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.55;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  /* Disable text selection, long-press callout and double-tap zoom app-wide */
  -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
  -webkit-touch-callout: none; touch-action: manipulation;
}
/* Keep editable fields fully usable */
input, textarea, select, [contenteditable="true"] {
  -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text;
}
/* Anchors that are buttons are excluded: `a:hover` outranks `.btn-primary`, so
   without this the link colour repaints a solid button's label on hover. */
a:not(.btn) { color: var(--primary); text-decoration: none; transition: color var(--speed) var(--ease); }
a:not(.btn):hover { color: var(--primary-dark); text-decoration: underline; }

/* Type scale: page title 24, section 18, body 14, small 12.
   Weights stay in the 500–600 band; only the page title goes heavier. */
h1, h2, h3, h4 { margin: 0 0 .5rem; color: var(--text); line-height: 1.3; }
h1 { font-size: 24px; font-weight: 650; letter-spacing: -.018em; }
h2 { font-size: 18px; font-weight: 600; letter-spacing: -.01em; }
h3 { font-size: 16px; font-weight: 600; }
h4 { font-size: 14px; font-weight: 600; }

/* ── Layout ────────────────────────────────────────────────────────────────
   Full-width topbar; below it a fixed sidebar and a scrolling content column. */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; height: var(--topbar-h); z-index: 1000;
  background: var(--card-bg); border-bottom: 1px solid var(--border);
  padding: 0 1.25rem; display: flex; align-items: center; gap: 1rem;
}
.topbar .brand {
  display: flex; align-items: center; gap: .55rem; flex: 0 0 auto;
  width: calc(var(--sidebar-w) - 1.25rem); min-width: 0;
  font-size: 15px; font-weight: 600; color: var(--text); letter-spacing: -.01em;
}
.topbar .brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar .hamburger { display: none; background: none; border: none; cursor: pointer;
  color: var(--muted); padding: .35rem; border-radius: var(--radius-sm); line-height: 0;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease); }
.topbar .hamburger:hover { background: var(--hover); color: var(--text); }
.topbar .hamburger .icon { width: 20px; height: 20px; }

.layout { display: flex; min-height: 100vh; padding-top: var(--topbar-h); }
.sidebar {
  width: var(--sidebar-w); background: var(--sidebar-bg); color: var(--sidebar-text);
  position: fixed; top: var(--topbar-h); bottom: 0; left: 0; overflow-y: auto; z-index: 990;
  border-right: 1px solid var(--border); transition: transform 200ms var(--ease);
}
.sidebar nav { padding: .75rem .6rem; }
.sidebar nav a {
  display: flex; align-items: center; gap: .65rem; padding: .5rem .65rem; margin: 1px 0;
  color: var(--sidebar-text); font-size: 14px; font-weight: 500; border-radius: var(--radius-sm);
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.sidebar nav a:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.sidebar nav a.active { background: var(--sidebar-active-bg); color: var(--primary); font-weight: 600; }
.sidebar nav a .ic { width: 18px; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.sidebar nav a:hover .ic { color: var(--text); }
.sidebar nav a.active .ic { color: var(--primary); }

.main { flex: 1; margin-left: var(--sidebar-w); display: flex; flex-direction: column; min-width: 0; }
/* Let flex/grid children shrink so wide tables scroll INSIDE their card
   instead of stretching the whole page. */
.content { min-width: 0; }
/* ── Detail-page column layouts ────────────────────────────────────────────
   Declared here rather than inline per template, so every detail page shares
   one set of widths and one breakpoint. `minmax(0,…)` lets a wide table shrink
   its column instead of pushing the page wider than the viewport. */
.cust-cols,
.veh-cols { display: grid; grid-template-columns: 320px minmax(0,1fr); gap: 1rem; align-items: start; }
.inv-cols { display: grid; grid-template-columns: minmax(0,2fr) minmax(0,1fr); gap: 1rem; align-items: start; }
/* Let grid children shrink so wide tables scroll INSIDE their card instead of
   stretching the whole page. */
.jc-cols > *, .cust-cols > *, .veh-cols > *, .inv-cols > *, .rep-cols > * { min-width: 0; }
@media (max-width: 1024px) {
  .cust-cols, .veh-cols { grid-template-columns: 280px minmax(0,1fr); }
}
@media (max-width: 900px) {
  .cust-cols, .veh-cols, .inv-cols { grid-template-columns: minmax(0,1fr); }
}

.search-wrap { flex: 1; max-width: 440px; position: relative; }
.search-wrap input {
  width: 100%; height: 36px; padding: 0 .85rem; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 14px; font-family: inherit; outline: none;
  color: var(--text); background: var(--bg);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background var(--speed) var(--ease);
}
.search-wrap input::placeholder { color: var(--muted); }
.search-wrap input:hover { border-color: var(--border-strong); }
.search-wrap input:focus { border-color: var(--primary); background: var(--card-bg); box-shadow: var(--ring); }
.search-results {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; background: var(--card-bg);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-md);
  max-height: 70vh; overflow-y: auto; display: none; z-index: 950; padding: 4px; overflow-x: hidden;
}
.search-results.show { display: block; animation: fade var(--speed) var(--ease); }
.search-results .sec { padding: .5rem .7rem .3rem; font-size: 11px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted); font-weight: 600; }
.search-results .item { padding: .55rem .7rem; border-radius: var(--radius-xs); cursor: pointer;
  transition: background var(--speed) var(--ease); }
.search-results .item:hover { background: var(--hover); }
.topbar .user { margin-left: auto; display: flex; align-items: center; gap: .75rem; font-size: 13px;
  color: var(--muted); flex: 0 0 auto; }
.topbar .user > span { color: var(--text); font-weight: 500; }
.badge-role { background: var(--primary-soft); color: var(--primary-ink); border-radius: var(--radius-xs);
  padding: .15rem .45rem; font-size: 11px; font-weight: 600; letter-spacing: .01em; }

.content { padding: .4rem; flex: 1; animation: pageIn 180ms var(--ease) both; }
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; flex-wrap: wrap; gap: .6rem; }
.page-head h1 { font-size: 24px; font-weight: 650; margin: 0; letter-spacing: -.018em; }
.page-head .flex { gap: .4rem; flex-wrap: wrap; }

/* Action bar — secondary actions row below page-head */
.action-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: -.75rem 0 1.25rem; padding: .6rem .75rem;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); }
.action-bar .btn { flex-shrink: 0; }

/* ── Cards ─────────────────────────────────────────────────────────────── */
.card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: none; margin-bottom: 1.5rem; animation: pageIn 200ms var(--ease) both; }
/* Title sits beside its icon; trailing actions are pushed to the right edge.
   (space-between would strand a bare-text title in the middle of the row.) */
.card .card-head { padding: .9rem var(--card-pad); border-bottom: 1px solid var(--border);
  font-weight: 600; font-size: 16px; letter-spacing: -.01em;
  display: flex; align-items: center; justify-content: flex-start; gap: .5rem; flex-wrap: wrap; }
.card .card-head > *:last-child:not(:first-child) { margin-left: auto; }
.card .card-body { padding: .8rem; }

/* Tables sit flush with the card edges; any siblings (toolbar, pager) keep
   their own horizontal padding so only the table bleeds out. */
.card-body.table-wrap { padding: 0; }
.card-body:has(> .table-wrap) { padding-left: 0; padding-right: 0; }
.card-body:has(> .table-wrap) > :not(.table-wrap) { padding-left: var(--card-pad); padding-right: var(--card-pad); }
.card-body:has(> .table-wrap:first-child) { padding-top: 0; }
.card-body:has(> .table-wrap:last-child) { padding-bottom: 0; }

/* Collapsible card — click the head to expand/collapse the body. */
.toggle-head { cursor: pointer; user-select: none; }
.toggle-arrow { display: inline-block; width: 0; height: 0; border-left: 5px solid transparent;
  border-right: 5px solid transparent; border-top: 6px solid var(--muted);
  transition: transform .2s ease; vertical-align: middle; margin-left: .3rem; }
/* The disclosure arrow marks the trailing edge of the head, always in the
   same place. Without these two rules the generic "last child floats right"
   above hands the auto margin to whatever trailing control the head happens
   to carry — Workers' assign button, the photo card's count — and the arrow
   stays stranded beside the title, so the same stack of cards showed its
   arrows in two different columns. */
.card .card-head > .toggle-arrow { margin-left: auto; }
.card .card-head:has(> .toggle-arrow) > *:last-child:not(:first-child):not(.toggle-arrow) {
  margin-left: .5rem; }
.collapsible.collapsed .toggle-arrow { transform: rotate(-90deg); }
.collapsible .card-body { transition: max-height .25s ease, padding .25s ease, opacity .2s ease; overflow: hidden; }
.collapsible.collapsed > .card-body { max-height: 0 !important; padding-top: 0 !important;
  padding-bottom: 0 !important; opacity: 0; border-top: none; }

/* ══ Billing screen ════════════════════════════════════════════════════════
   A document-entry terminal, not a page of cards. The document fills the
   viewport exactly and never scrolls as a whole: a title bar, a bordered
   header block, ruled line-item grids that scroll in place, and a totals bar
   pinned across the bottom. Surfaces are separated by hairlines and share one
   flat plane — no elevation, no rounded panels.
   Opt in with {% block content_class %}content-fixed{% endblock %}.
   ═══════════════════════════════════════════════════════════════════════ */
.content-fixed { padding: 0; overflow: hidden; display: flex; flex-direction: column;
  min-height: 0; animation: none; }
.wb { display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--card-bg); }

/* ── Title bar ─────────────────────────────────────────────────────────── */
.wb-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; flex: 0 0 auto;
  padding: .55rem 1rem; border-bottom: 1px solid var(--border); }
.wb-doctype { font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); }
.wb-docno { font-size: 17px; font-weight: 600; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.wb-actions { margin-left: auto; display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }

/* ── Document header — a bordered form block, the way an invoice head reads ── */
/* No border-bottom here: every cell draws its own, and the block always ends
   with a full-width cell — a container border on top of that renders as a
   doubled 2px rule under the header. */
.wb-doc { flex: 0 0 auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  background: var(--card-bg); }
.wb-doc .d { padding: .5rem .9rem; min-width: 0; border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border); }
.wb-doc .d-k { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin-bottom: .1rem; }
.wb-doc .d-v { font-size: 13px; font-weight: 500; line-height: 1.45; overflow-wrap: anywhere; }
.wb-doc .d-v .sub { display: block; font-size: 12px; font-weight: 400; color: var(--muted); }
.wb-doc .d-full { grid-column: 1 / -1; border-right: none; }
.wb-doc .d-full .d-v { font-weight: 400; }
/* The block's own outer edges are drawn by the section borders around it */
.wb-doc .d:last-child { border-right: none; }
.wb-note { flex: 0 0 auto; padding: .5rem 1rem; border-bottom: 1px solid var(--border); }
.wb-note:empty { display: none; padding: 0; border: none; }

/* ── Body: line-item pane + side pane ──────────────────────────────────── */
.wb-body { flex: 1; min-height: 0; display: flex; }
.wb-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column;
  border-right: 1px solid var(--border); }
.wb-side { flex: 0 0 320px; width: 320px; min-height: 0; display: flex; flex-direction: column;
  background: var(--bg); }

/* A titled region. `.grow` shares the leftover height; its body scrolls. */
.wb-sec { display: flex; flex-direction: column; min-height: 0; flex: 0 0 auto;
  border-bottom: 1px solid var(--border); }
.wb-sec:last-child { border-bottom: none; }
/* Grows from its natural height rather than from zero, so a long parts grid
   keeps more of the pane than a single labor line instead of splitting 50/50. */
.wb-sec.grow { flex: 1 1 auto; }
.wb-sec-head { display: flex; align-items: center; gap: .45rem; flex: 0 0 auto;
  padding: .4rem 1rem; background: var(--bg); border-bottom: 1px solid var(--border);
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
.wb-sec-head .icon { width: 13px; height: 13px; }
.wb-sec-head .wb-sec-act { margin-left: auto; display: flex; align-items: center; gap: .4rem; }
.wb-sec-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
.wb-sec-body.pad { padding: .7rem 1rem; }
/* Thumbnail strip — capped so the media section never crowds out the grids */
.wb-photos { display: flex; flex-wrap: wrap; gap: .5rem; max-height: 148px; }
/* Per-grid subtotal line — outside the scroll box, so it is always in view */
.wb-sec-sub { flex: 0 0 auto; display: flex; align-items: center; gap: 1rem;
  padding: .35rem 1rem; border-top: 1px solid var(--border); background: var(--bg);
  font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.wb-sec-sub .s-count { margin-right: auto; }
.wb-sec-sub .s-v { color: var(--text); font-weight: 600; }

/* ── Line-item grid ────────────────────────────────────────────────────────
   Ruled columns, row numbers and right-aligned money — a spreadsheet the
   operator reads down a column, not a web table. */
table.dgrid { width: 100%; border-collapse: collapse; font-size: 13px;
  font-variant-numeric: tabular-nums; }
table.dgrid th, table.dgrid td { padding: .4rem .6rem; text-align: left; white-space: nowrap;
  border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
table.dgrid th:last-child, table.dgrid td:last-child { border-right: none; }
table.dgrid th { position: sticky; top: 0; z-index: 1; background: var(--bg);
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
  box-shadow: inset 0 -1px 0 var(--border); border-bottom: none; }
table.dgrid tbody tr { transition: background var(--speed) var(--ease); }
table.dgrid tbody tr:hover { background: var(--hover); }
table.dgrid .num { text-align: right; }
table.dgrid .rn { width: 40px; text-align: right; color: var(--muted); padding-right: .5rem; }
table.dgrid .act { width: 38px; text-align: center; padding: .2rem; }
table.dgrid td.empty { text-align: center; color: var(--muted); padding: 1.6rem 1rem;
  white-space: normal; border-right: none; }
table.dgrid .desc { white-space: normal; min-width: 180px; }

/* ── Side pane ─────────────────────────────────────────────────────────── */
.wb-panels { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain; }
.wb-panels .wb-sec { background: var(--card-bg); }
.wb-panels .wb-sec-body { flex: 0 0 auto; overflow: visible; }
.wb-panels .empty { padding: .9rem .25rem; font-size: 13px; }

/* ── Totals bar — pinned across the foot, grand total set apart ────────── */
.wb-foot { flex: 0 0 auto; display: flex; align-items: stretch; flex-wrap: wrap;
  border-top: 1px solid var(--border); background: var(--bg); }
.wb-foot .f-item { padding: .4rem 1rem; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; justify-content: center; min-width: 0; }
.wb-foot .f-k { font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; }
.wb-foot .f-v { font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wb-foot .f-grand { margin-left: auto; border-right: none; border-left: 1px solid var(--border);
  background: var(--card-bg); padding: .4rem 1.25rem; align-items: flex-end; }
.wb-foot .f-grand .f-k { color: var(--text); }
.wb-foot .f-grand .f-v { font-size: 21px; font-weight: 650; letter-spacing: -.02em; }

/* Quiet scrollbars inside the terminal */
.wb-sec-body, .wb-panels { scrollbar-width: thin; scrollbar-color: var(--disabled) transparent; }
.wb-sec-body::-webkit-scrollbar, .wb-panels::-webkit-scrollbar { width: 9px; height: 9px; }
.wb-sec-body::-webkit-scrollbar-track, .wb-panels::-webkit-scrollbar-track { background: transparent; }
.wb-sec-body::-webkit-scrollbar-thumb, .wb-panels::-webkit-scrollbar-thumb {
  background: var(--disabled); border-radius: 5px; border: 2px solid var(--card-bg); background-clip: padding-box; }

/* Below the tablet breakpoint the fixed shell is abandoned: a 100vh page that
   cannot scroll is unusable on a phone. */
@media (max-width: 900px) {
  .content-fixed { height: auto; overflow: visible; padding: 0; }
  .wb { height: auto; }
  .wb-doc { grid-template-columns: minmax(0,1fr); }
  .wb-doc .d { border-right: none; }
  .wb-body { flex-direction: column; }
  .wb-main { border-right: none; }
  .wb-side { flex: 0 0 auto; width: auto; border-top: 1px solid var(--border); }
  .wb-sec.grow { flex: 0 0 auto; }
  .wb-sec-body { overflow: auto; }
  .wb-panels { overflow: visible; }
  /* The totals bar stops being a bar and stacks into rows */
  .wb-foot .f-item { flex: 1 1 40%; border-bottom: 1px solid var(--border); }
  .wb-foot .f-grand { margin-left: 0; flex: 1 1 100%; border-left: none; align-items: flex-start; }
  .wb-main, .wb-side, .wb-sec, .wb-body { min-height: 0; }
}

/* ── Detail facts grid ─────────────────────────────────────────────────────
   Label-over-value pairs for "record details" panels. Replaces stacks of
   <p><strong>Key:</strong> value</p>, which run one fact per line and make a
   detail card several hundred pixels tall. */
.dl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: .8rem 1.25rem; }
.dl-grid > * { min-width: 0; }
.dl-grid .dl-k { font-size: 12px; font-weight: 500; color: var(--muted); margin-bottom: .1rem; }
.dl-grid .dl-v { font-size: 14px; font-weight: 500; line-height: 1.45; overflow-wrap: anywhere; }
.dl-grid .dl-v .text-muted { font-weight: 400; }
/* Long free text (a complaint, an address) spans the row instead of forcing a
   tall narrow column. */
.dl-grid > .dl-full { grid-column: 1 / -1; }
.dl-grid > .dl-full .dl-v { font-weight: 400; }

/* ── Sticky rail ───────────────────────────────────────────────────────────
   A column that holds position while its neighbour scrolls. Requires
   align-self:start — the grid default stretches the item to the row height,
   leaving sticky no travel room. max-height is a safety valve so a rail taller
   than the viewport scrolls internally instead of hiding its own bottom. */
.sticky-rail { position: sticky; top: calc(var(--topbar-h) + 1rem); align-self: start;
  max-height: calc(100vh - var(--topbar-h) - 2rem);
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--disabled) transparent; }
.sticky-rail::-webkit-scrollbar { width: 8px; }
.sticky-rail::-webkit-scrollbar-track { background: transparent; }
.sticky-rail::-webkit-scrollbar-thumb { background: var(--disabled); border-radius: 4px;
  border: 2px solid var(--bg); background-clip: padding-box; }
/* Rail cards sit closer together than page-level cards, and the last one must
   not add trailing space inside the sticky box. */
.sticky-rail > .card { margin-bottom: 1rem; }
.sticky-rail > .card:last-child { margin-bottom: 0; }

/* ── KPI / stat cards ──────────────────────────────────────────────────── */
/* Fixed 4 columns, not auto-fit: auto-fit's own sizing decision landed on 3
   columns at some viewport widths, stranding the 4th KPI card alone on a
   second row with two empty cells next to it. A flexible auto-fit grid can't
   know the dashboard always renders exactly 4 (or 3) cards; 4 fixed columns
   plus the narrower breakpoints below (2 columns) does. */
.kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
.kpi { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1.1rem 1.2rem; position: relative; box-shadow: none;
  transition: border-color var(--speed) var(--ease);
  animation: pageIn 200ms var(--ease) both; }
.kpi:hover { border-color: var(--border-strong); }
.kpi .label { color: var(--muted); font-size: 12px; font-weight: 500; letter-spacing: 0; }
.kpi .value { font-size: 28px; font-weight: 600; margin-top: .35rem; letter-spacing: -.025em; line-height: 1.2; }
/* Optional trend line under a value, e.g. "↑ 4 from yesterday" */
.kpi .delta { font-size: 12px; color: var(--muted); margin-top: .3rem; }
.kpi .delta.up { color: var(--success-ink); }
.kpi .delta.down { color: var(--danger-ink); }
.kpi .ic { position: absolute; right: 1.1rem; top: 1.05rem; color: var(--muted); line-height: 0; }
.kpi .ic .icon { width: 18px; height: 18px; }
/* Semantic accent lives in the icon, not a stripe or a fill */
.kpi.primary .ic { color: var(--primary); }
.kpi.success .ic { color: var(--success); }
.kpi.warning .ic { color: var(--warning); }
.kpi.danger  .ic { color: var(--danger); }

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: 0 .9rem; height: 34px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font-size: 13.5px; font-weight: 500; font-family: inherit; cursor: pointer;
  background: var(--card-bg); color: var(--text); white-space: nowrap; letter-spacing: -.005em;
  text-decoration: none;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease), color var(--speed) var(--ease); }
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--primary); }
/* Solid variants restate their label colour on hover — only the fill shifts. */
.btn-primary, .btn-primary:hover { color: #fff; }
.btn-primary { background: var(--primary); }
.btn-primary:hover { background: var(--primary-dark); }
.btn-success, .btn-success:hover { color: #fff; }
.btn-success { background: var(--success); }
.btn-success:hover { background: #15803D; }
.btn-light:hover, .btn-outline:hover { color: var(--text); }
/* Danger reads as text until hovered — destructive actions shouldn't shout */
.btn-danger { background: transparent; border-color: transparent; color: var(--danger); }
.btn-danger:hover { background: var(--danger-soft); border-color: #FBD5D5; color: var(--danger-ink); }
/* When a destructive action is the confirmed primary action of a dialog */
.btn-danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger-solid:hover { background: #B91C1C; border-color: #B91C1C; color: #fff; }
.btn-light   { background: var(--card-bg); border-color: var(--border); color: var(--text); }
.btn-light:hover { background: var(--hover); border-color: var(--border-strong); }
.btn-outline { background: transparent; border-color: var(--border); color: var(--text); }
.btn-outline:hover { background: var(--hover); border-color: var(--border-strong); }
.btn-sm { padding: 0 .6rem; height: 28px; font-size: 12px; gap: .3rem; }
.btn-icon-del, .btn-icon { background: transparent; border: none; color: var(--muted); padding: 0 .4rem; height: 28px; }
.btn-icon-del:hover { color: var(--danger); background: var(--danger-soft); }
.btn-icon:hover { color: var(--primary); background: var(--primary-soft); }
.btn[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }
.disc-toggle { min-width: 34px; padding: 0; border-color: var(--border); }
.disc-toggle.active { background: var(--primary); color: #fff; border-color: var(--primary); }

/* ── Tables ────────────────────────────────────────────────────────────────
   No zebra striping, bottom borders only, row hover. `.tbl` is canonical; a
   bare <table> in a .table-wrap gets identical treatment. */
.table-wrap { overflow-x: auto; }
table.tbl, .table-wrap > table { width: 100%; border-collapse: collapse; font-size: 14px; }
table.tbl th, table.tbl td, .table-wrap > table th, .table-wrap > table td {
  padding: .7rem .85rem; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
/* Outer edges line up with the card's own padding; inner gutters stay tight. */
table.tbl th:first-child, table.tbl td:first-child,
.table-wrap > table th:first-child, .table-wrap > table td:first-child { padding-left: var(--card-pad); }
table.tbl th:last-child, table.tbl td:last-child,
.table-wrap > table th:last-child, .table-wrap > table td:last-child { padding-right: var(--card-pad); }
table.tbl th, .table-wrap > table th {
  position: sticky; top: 0; background: var(--card-bg); font-weight: 500; color: var(--muted);
  font-size: 12px; letter-spacing: 0; z-index: 1; }
table.tbl tbody tr, .table-wrap > table tbody tr { transition: background var(--speed) var(--ease); cursor: default; }
table.tbl tbody tr:hover, .table-wrap > table tbody tr:hover { background: var(--hover); }
/* Last row's border would double up with the card's own bottom edge */
table.tbl tbody tr:last-child td, .table-wrap > table tbody tr:last-child td { border-bottom: none; }
table.tbl tbody tr:last-child td:first-child { border-bottom-left-radius: calc(var(--radius) - 1px); }
table.tbl tbody tr:last-child td:last-child { border-bottom-right-radius: calc(var(--radius) - 1px); }
table.tbl td.num { text-align: left; font-variant-numeric: tabular-nums; }
table.tbl th.num { text-align: left; font-variant-numeric: tabular-nums; }
/* An open-ended text cell that must not get a say in the column's width. A
   list that grows with the data (every action granted to a user, say) will
   otherwise widen the table past the card and push the row's own buttons out
   of reach behind a horizontal scrollbar. One line, ellipsis, full value on
   hover via the title attribute. */
.cell-clamp { display: inline-block; max-width: 22rem; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* A short explanation under a cell's value or input. Table cells are nowrap,
   so without this the sentence runs on and drags the column wide. */
table.tbl td .cell-note {
  white-space: normal; max-width: 21ch; margin-top: .2rem;
  font-size: 12px; line-height: 1.35; color: var(--muted); }
table.tbl td .cell-note.warn { color: var(--warning-ink); }

/* Wide tables scroll inside .table-wrap, but the native hairline scrollbar sits
   at the bottom of a tall table and reads as "no scrolling here". Give it real
   weight so the affordance is visible without hunting for it. */
.table-wrap { scrollbar-width: thin; scrollbar-color: var(--disabled) transparent; }
.table-wrap::-webkit-scrollbar { height: 10px; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb { background: var(--disabled); border-radius: 5px;
  border: 3px solid var(--card-bg); background-clip: padding-box; }
.table-wrap::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: padding-box; }

/* Horizontal only — the wrap grows to the table's full height so it never
   scrolls vertically; the page itself handles that. */
.table-wrap { overscroll-behavior-x: contain; }

/* Equivalency-group pin + remove controls */
.grp-pin { color: var(--primary); vertical-align: middle; margin-left: .2rem; cursor: help; }
.grp-remove { background: none; border: none; padding: 0; margin: 0; cursor: pointer; color: inherit;
  display: inline-flex; align-items: center; line-height: 1; }
.grp-remove .icon { width: .8em; height: .8em; }

/* ── Custom select (replaces native browser selects) ───────────────────── */
.cs-wrap { position: relative; width: 100%; }
.cs-trigger {
  display: block; width: 100%; text-align: left; cursor: pointer;
  height: 36px; padding: 0 2rem 0 .75rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 14px; font-family: inherit; background: var(--card-bg); color: var(--text);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .65rem center; background-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 34px;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.cs-trigger:hover { border-color: var(--border-strong); }
.cs-trigger.disabled { opacity: .5; cursor: not-allowed; }
.cs-trigger:focus-visible { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.cs-wrap.open .cs-trigger { border-color: var(--primary); box-shadow: var(--ring); }
.cs-wrap.open .cs-trigger {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%232563EB' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m18 15-6-6-6 6'/%3E%3C/svg%3E");
}
.cs-dd {
  position: absolute; left: 0; right: 0; top: 100%;
  margin-top: 4px; background: var(--card-bg); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-md);
  max-height: 240px; overflow-y: auto; z-index: 1600; padding: 4px;
  opacity: 0; transform: scale(.98); transform-origin: top;
  pointer-events: none; transition: opacity 120ms var(--ease), transform 120ms var(--ease);
}
.cs-wrap.open .cs-dd { opacity: 1; transform: scale(1); pointer-events: auto; }
.cs-opt {
  padding: .45rem .65rem; font-size: 13.5px; cursor: pointer;
  border-radius: var(--radius-xs); margin: 1px 0; position: relative;
  transition: background 80ms var(--ease), color 80ms var(--ease);
}
.cs-opt:hover { background: var(--hover); }
.cs-opt.active { background: var(--primary-soft); color: var(--primary-ink); font-weight: 500; }
.cs-opt.active::after { content: "\2713"; position: absolute; right: .65rem; top: 50%; transform: translateY(-50%); font-size: 12px; color: var(--primary); }
.cs-status { width: auto; display: inline-block; min-width: 160px; }
.cs-status .cs-trigger { width: auto; min-width: 160px; height: 34px; line-height: 32px; font-size: 13.5px; font-weight: 500; padding: 0 2rem 0 .75rem; }
/* Status tint — same four soft fills as the .pill chips, so a status reads
   identically whether it's shown as a chip or picked from this dropdown. */
.cs-trigger[data-status="open"] { background-color: var(--primary-soft); color: var(--primary-ink); border-color: var(--primary-line); }
.cs-trigger[data-status="work_done"] { background-color: var(--warning-soft); color: var(--warning-ink); border-color: var(--warning-line); }
.cs-trigger[data-status="billed"],
.cs-trigger[data-status="delivered"] { background-color: var(--success-soft); color: var(--success-ink); border-color: var(--success-line); }
.cs-trigger[data-status="cancelled"] { background-color: var(--danger-soft); color: var(--danger-ink); border-color: var(--danger-line); }
.cs-mini { width: auto; display: inline-block; }
.cs-mini .cs-trigger { width: auto; min-width: 60px; padding: 0 1.6rem 0 .5rem; height: 32px; line-height: 30px; font-size: 13px; }
.cs-mini .cs-dd { min-width: 100px; }

/* Shared date-picker input */
.rep-date { height: 34px; padding: 0 .6rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px; background: var(--card-bg); color: var(--text); }

/* Reminder configurator (modal) */
.cfg-section { border: 1px solid var(--border); border-radius: var(--radius); padding: 1.1rem 1.15rem; margin-bottom: 1rem; background: var(--card-bg); }
.cfg-section:last-child { margin-bottom: 0; }
.cfg-section > h4 { margin: -1.1rem -1.15rem 1rem; padding: .75rem 1.15rem; font-size: 14px; font-weight: 600;
  background: var(--bg); border-bottom: 1px solid var(--border); border-radius: var(--radius) var(--radius) 0 0;
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.cfg-section > h4 .cfg-en { margin-left: auto; }
.cfg-section .field + .field { margin-top: .9rem; }
.cfg-en { height: 32px; padding: 0 .55rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px; background: var(--card-bg); color: var(--text); }
.cfg-hint { display: block; margin-top: .35rem; font-size: 12px; color: var(--muted); }
.chk-chips { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: .35rem; }
.chk-chips label { display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .7rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; font-size: 13px;
  background: var(--card-bg); user-select: none; transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease), color var(--speed) var(--ease); }
.chk-chips label:hover { border-color: var(--border-strong); background: var(--hover); }
.chk-chips input { accent-color: var(--primary); margin: 0; }
.chk-chips label:has(input:checked) { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-ink); font-weight: 500; }

/* ── Status chips ──────────────────────────────────────────────────────────
   Soft fill, dark ink. Never saturated. The colour carries the status; the
   chips used to also render a small leading dot, which was dropped as noise.
   The `gap` still applies to chips that hold an icon or a remove button. */
.pill, .badge { display: inline-flex; align-items: center; gap: .4rem;
  padding: .2rem .5rem; border-radius: var(--radius-xs);
  font-size: 12px; font-weight: 500; letter-spacing: 0; line-height: 1.5;
  background: var(--neutral-soft); color: var(--neutral-ink); }
/* Active / in-flight → light blue */
.pill.open, .pill.in_progress, .pill.derived { background: var(--primary-soft); color: var(--primary-ink); }
/* Awaiting the next step → light amber */
.pill.work_done, .pill.partial, .pill.due_soon, .pill.warn { background: var(--warning-soft); color: var(--warning-ink); }
/* Settled → light green */
.pill.billed, .pill.delivered, .pill.paid, .pill.ok, .pill.scheduled { background: var(--success-soft); color: var(--success-ink); }
/* Needs attention → light red */
.pill.cancelled, .pill.unpaid, .pill.low, .pill.overdue { background: var(--danger-soft); color: var(--danger-ink); }
/* Inert → neutral */
.pill.unscheduled { background: var(--neutral-soft); color: var(--neutral-ink); }

/* ── Forms ─────────────────────────────────────────────────────────────── */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
/* Positioned so an absolutely-placed .search-results anchors to its own field */
.field { position: relative; }
.field label { display: block; font-size: 12px; color: var(--text); margin-bottom: .35rem; font-weight: 500; }
.field input, .field select, .field textarea {
  width: 100%; height: 36px; padding: 0 .75rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 14px; font-family: inherit; background: var(--card-bg); color: var(--text);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.field input:hover, .field textarea:hover { border-color: var(--border-strong); }
.field select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none; cursor: pointer;
  padding-right: 2.2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .65rem center; background-size: 16px;
}
.field select:hover { border-color: var(--border-strong); }
.field select option { padding: .5rem .75rem; font-size: 14px; }
.field textarea { height: auto; padding: .55rem .75rem; line-height: 1.5; }
.field input::placeholder, .field textarea::placeholder { color: #9CA3AF; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.field .req { color: var(--danger); }
.field.full { grid-column: 1 / -1; }
/* dd/mm/yyyy skin over a native <input type="date"> — see window.wireDateInputs
   in app.js for why this exists (the native control's displayed digit order
   follows the browser's own UI language, not anything the page can set). The
   real date input stays in the DOM, invisible but still rendered (opacity,
   not display:none — HTMLInputElement.showPicker() throws on an element with
   no box), so its name/value/min/max keep working for every existing caller. */
.dmy-wrap { display: flex; align-items: center; gap: 0; }
/* Filter rows / report toolbars lay date pickers out inline next to selects
   and buttons — there the wrap must shrink to its content, not stretch full
   width the way it does inside a stacked .field. */
.dmy-wrap:has(.rep-date) { display: inline-flex; vertical-align: middle; }
/* .dmy-display replaces the native date input in the layout, but it's a plain
   sibling now, not a child of whatever container (.field, .filter-row,
   .rep-toolbar) used to style that input directly — so it needs its own
   complete default look instead of relying on ancestor selectors that no
   longer match it. */
.dmy-display {
  height: 36px; width: 100%; padding: 0 .75rem; border: 1px solid var(--border);
  font-size: 14px; font-family: inherit; background: var(--card-bg); color: var(--text);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.dmy-display:hover { border-color: var(--border-strong); }
.dmy-display:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.dmy-wrap .dmy-display {
  flex: 1; border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm);
  border-top-right-radius: 0; border-bottom-right-radius: 0; }
.dmy-wrap .dmy-native {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; border: 0; padding: 0; }
.dmy-btn {
  flex-shrink: 0; height: 36px; width: 36px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-left: none; border-top-right-radius: var(--radius-sm);
  border-bottom-right-radius: var(--radius-sm); background: var(--card-bg); color: var(--muted); cursor: pointer; }
.dmy-btn:hover { color: var(--primary); background: var(--primary-soft); }
.dmy-btn .icon { width: 16px; height: 16px; }
/* Compact 34px control used in filter rows and report toolbars — matches the
   plain .rep-date input height/padding it wraps, so it lines up with the
   type="time" field sitting right next to it in the same From/To pair. */
.dmy-wrap:has(.rep-date) .dmy-display { height: 34px; width: auto; padding: 0 .6rem; font-size: 13px; }
.dmy-wrap:has(.rep-date) .dmy-btn { height: 34px; width: 34px; }
/* Optional extra fields tucked behind a native <details> toggle — used on the
   quick customer/vehicle create flow and elsewhere so the fast path stays
   fast and the extra fields are opt-in, not just visually de-emphasised. */
.more-details { margin-top: .25rem; }
.more-details summary {
  cursor: pointer; font-size: 13px; font-weight: 500; color: var(--primary);
  list-style: none; display: inline-flex; align-items: center; gap: .3rem; }
.more-details summary::-webkit-details-marker { display: none; }
.more-details summary::before { content: "▸"; font-size: 11px; transition: transform var(--speed) var(--ease); }
.more-details[open] summary::before { transform: rotate(90deg); }
.more-details > .form-grid { margin-top: .75rem; padding-top: .75rem; border-top: 1px dashed var(--border); }
/* Inline checkbox row — the label sits beside the box, not above it */
.field > label.check { display: flex; align-items: center; gap: .5rem; margin: 0; cursor: pointer; }
.field > label.check input[type="checkbox"],
.holding-check input[type="checkbox"] {
  width: auto; height: auto; flex: none; accent-color: var(--primary); cursor: pointer; }
.holding-check { display: flex; align-items: center; gap: .4rem; cursor: pointer; }
/* Live character counter (see charCounter in app.js) */
.char-count { display: block; font-size: 11px; color: var(--muted); margin-top: .25rem; text-align: right; line-height: 1.2; font-variant-numeric: tabular-nums; }
.char-count.warn { color: var(--warning-ink); }
/* maxlength on the input already blocks typing past the limit, so "full"
   fires on exactly-at-capacity input — a 10/10 phone number, a 15/15 GSTIN —
   which is valid, not an error. Bold instead of red says "you're at the
   limit" without reading as "something is wrong". */
.char-count.full { color: var(--text); font-weight: 600; }
.form-actions { display: flex; gap: .6rem; margin-top: 1.25rem; flex-wrap: wrap; }

/* ── Tabs ──────────────────────────────────────────────────────────────── */
/* The baseline is an inset shadow, not a border: it paints inside the box, so
   the active tab's 2px underline can sit flush on it without a negative margin
   poking 1px outside — which would turn into a vertical scroll once
   overflow-x is set for the mobile strip. */
.subtabs { display: flex; gap: .25rem; flex-wrap: wrap; margin-bottom: 1.5rem;
  box-shadow: inset 0 -1px 0 var(--border); }
.subtabs button { background: none; border: none; padding: .5rem .1rem; margin: 0 .6rem 0 0; cursor: pointer;
  font-size: 13.5px; color: var(--muted); border-bottom: 2px solid transparent; font-weight: 500;
  font-family: inherit; transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease); }
.subtabs button:hover { color: var(--text); }
.subtabs button.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }
.subtabs button .cnt { display: inline-block; margin-left: .35rem; min-width: 1.1rem; padding: .05rem .35rem;
  border-radius: var(--radius-xs); background: var(--neutral-soft); color: var(--muted); font-size: 11px; font-weight: 600; line-height: 1.4; }
.subtabs button.active .cnt { background: var(--primary-soft); color: var(--primary-ink); }

/* ── DataTable — shared sort/filter/paginate/totals component ───────────── */
.dt-toolbar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: 1rem; }
.dt-search { height: 36px; padding: 0 .75rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  min-width: 240px; font-size: 14px; font-family: inherit; outline: none; color: var(--text); background: var(--card-bg);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
.dt-search::placeholder { color: #9CA3AF; }
.dt-search:hover { border-color: var(--border-strong); }
.dt-search:focus { border-color: var(--primary); box-shadow: var(--ring); }
.dt-count { color: var(--muted); font-size: 12px; margin-left: auto; }
table.tbl th.sortable { cursor: pointer; user-select: none; }
table.tbl th.sortable:hover { color: var(--text); }
table.tbl th .dt-arrow { font-size: 10px; opacity: .9; color: var(--primary); }
table.tbl tfoot td { font-weight: 600; background: var(--bg); border-top: 1px solid var(--border); border-bottom: none; }
.dt-pager { display: flex; align-items: center; justify-content: flex-end; gap: .5rem; margin-top: 1rem; font-size: 13px; color: var(--muted); }
.dt-pager button { padding: 0 .7rem; height: 30px; border: 1px solid var(--border); background: var(--card-bg); border-radius: var(--radius-sm);
  cursor: pointer; font-size: 12.5px; font-family: inherit; color: var(--text); font-weight: 500;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease); }
.dt-pager button:hover:not(:disabled) { background: var(--hover); border-color: var(--border-strong); }
.dt-pager button:disabled { opacity: .45; cursor: not-allowed; }

/* DEV-only bulk delete toolbar on list pages */
.dev-tools { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: 1rem;
  padding: .5rem .75rem; border: 1px dashed #FBD5D5; background: var(--danger-soft); border-radius: var(--radius-sm);
  font-size: 12px; color: var(--danger-ink); }
.dev-tools label { display: inline-flex; align-items: center; gap: .3rem; color: var(--danger-ink); }
.dev-tools .spacer { flex: 1; }

/* ── Customer history timeline ─────────────────────────────────────────── */
.timeline { list-style: none; margin: 0; padding: 0; position: relative; }
.timeline .tl-item { position: relative; display: flex; gap: .85rem; padding: 0 0 1.25rem .25rem; }
.timeline .tl-item:not(:last-child)::before { content: ""; position: absolute; left: 1rem; top: 2rem;
  bottom: -0.1rem; width: 1px; background: var(--border); }
.timeline .tl-dot { flex: 0 0 auto; width: 2rem; height: 2rem; border-radius: 999px; background: var(--bg);
  border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; font-size: 14px; z-index: 1; }
.timeline .tl-body { flex: 1; padding-top: .15rem; }
.timeline .tl-title { font-weight: 600; font-size: 14px; }
.timeline .tl-detail { font-size: 13px; color: var(--muted); margin-top: .15rem; }
.timeline .tl-time { font-size: 12px; color: var(--muted); margin-top: .25rem; }
.timeline .tl-payment .tl-dot { background: var(--success-soft); border-color: #CBF0D9; }
.timeline .tl-invoice .tl-dot { background: var(--primary-soft); border-color: #D6E2FE; }
.timeline .tl-part .tl-dot { background: var(--warning-soft); border-color: #FAE6C4; }

/* SMS confirmation preview */
.sms-preview { margin-top: .75rem; padding: .9rem 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); white-space: pre-wrap; word-break: break-word; font-size: 13.5px; line-height: 1.55; color: var(--text); }
.sms-meta { font-size: 12px; color: var(--muted); margin-top: .5rem; }

/* ── Toast ─────────────────────────────────────────────────────────────────
   Minimal flat: hairline border, no shadow, no colour stripe — the leading
   icon is the only thing that carries the type. Bordered with
   --border-strong rather than --border because without a shadow to lift it,
   a white toast landing on top of a white card needs the extra contrast to
   still read as a separate surface. */
.toast-wrap { position: fixed; bottom: 1.25rem; right: 1.25rem; z-index: 2000;
  display: flex; flex-direction: column; gap: .5rem; }
.toast { display: flex; align-items: center; gap: .55rem;
  background: var(--card-bg); color: var(--text);
  padding: .5rem .5rem .5rem .7rem; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  min-width: 240px; max-width: 22rem; font-size: 13.5px; font-weight: 500;
  animation: toastin 200ms var(--ease); transition: opacity 250ms var(--ease); }
.toast-ico { width: 16px; height: 16px; flex: 0 0 auto; color: var(--muted); }
.toast.success .toast-ico { color: var(--success); }
.toast.error .toast-ico { color: var(--danger); }
.toast.info .toast-ico { color: var(--primary); }
/* Long server error strings wrap instead of forcing the card wider. */
.toast-msg { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.toast-x { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; color: var(--muted);
  padding: .2rem; border-radius: var(--radius-xs); line-height: 0;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease); }
.toast-x:hover { color: var(--text); background: var(--hover); }
.toast-x .icon { width: 14px; height: 14px; }
@keyframes toastin { from { transform: translateY(6px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
/* Content settles in with opacity only — no sliding panels */
@keyframes pageIn { from { opacity: 0; } to { opacity: 1; } }

/* ── Install banner ────────────────────────────────────────────────────────
   Offers the PWA install that Chrome otherwise buries in its ⋮ menu. Shown on
   the login page only (see app.js), so it never competes with the sidebar or
   topbar for space — it just needs to clear the page content, and stay under
   the toast layer (2000) so a toast fired from its own button is still
   readable. The bottom inset clears the iOS home indicator: login.html sets
   viewport-fit=cover, so without it the buttons sit under the bar. */
.install-banner { position: fixed; z-index: 970;
  left: 1rem; right: 1rem; bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  margin: 0 auto; max-width: 34rem;
  display: flex; align-items: center; gap: .75rem;
  background: var(--card-bg); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: .7rem .8rem; box-shadow: var(--shadow-lg);
  animation: toastin 200ms var(--ease); }
.install-banner-ico { width: 20px; height: 20px; flex: 0 0 auto; color: var(--primary); }
.install-banner-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.install-banner-text strong { font-size: 13.5px; font-weight: 600; }
.install-banner-text span { font-size: 12.5px; color: var(--muted); }
.install-banner-actions { flex: 0 0 auto; display: flex; gap: .4rem; }
/* Narrow phones: stack so neither button is squeezed to an unreadable width. */
@media (max-width: 480px) {
  .install-banner { flex-wrap: wrap; }
  .install-banner-text { flex-basis: calc(100% - 2.75rem); }
  .install-banner-actions { width: 100%; justify-content: flex-end; }
}

/* ── Modal ─────────────────────────────────────────────────────────────── */
.modal-backdrop { position: fixed; inset: 0; background: rgba(31,41,55,.4); display: none;
  align-items: flex-start; justify-content: center; z-index: 1500; padding: 3rem 1rem; overflow-y: auto; }
.modal-backdrop.show { display: flex; animation: fade var(--speed) var(--ease); }
/* Mobile/tablet only (matches the app's own hamburger-nav breakpoint —
   min-width:901px keeps the full sidebar) — a classic desktop scrollbar
   takes up layout space, so hiding it here reflows the whole page a few
   pixels wider the instant a modal opens. Touch devices' overlay scrollbars
   don't reserve space, so the lock is free there and worth keeping: a
   modal that covers most/all of a small screen still shouldn't let the
   page behind it scroll underneath. */
@media (max-width: 900px) {
  body.modal-open { overflow: hidden; }
}
.modal { background: var(--card-bg); border-radius: var(--radius-lg); width: 100%; max-width: 640px;
  max-height: calc(100dvh - 6rem); display: flex; flex-direction: column;
  border: 1px solid var(--border); box-shadow: var(--shadow-lg);
  animation: modalin var(--speed) var(--ease); }
@supports not (height: 1dvh) { .modal { max-height: calc(100vh - 6rem); } }
.modal.lg { max-width: 900px; }
/* Near-viewport size for bulk-entry grids (600+ PO line items) that need
   real room and no drag-to-move (see the (hover) block below and the
   matching opts.full skip of wireModalDrag in app.js). The backdrop itself
   centers every modal inside `padding: 3rem 1rem` (see .modal-backdrop
   above) — fine for a modal narrower than the viewport, but a modal sized
   to 100dvh *inside* that padding overflows past the bottom of the screen
   by exactly the padding amount. Full-screen needs the backdrop's own
   padding suppressed (below), not just a tall modal. */
.modal.full { max-width: 100vw; width: 100vw; max-height: 100dvh; height: 100dvh;
  border-radius: 0; }
@supports not (height: 1dvh) { .modal.full { max-height: 100vh; height: 100vh; } }
.modal-backdrop:has(.modal.full) { padding: 0; align-items: stretch; }
.modal .modal-head { padding: 1.1rem 1.4rem; border-bottom: 1px solid var(--border); display: flex;
  justify-content: space-between; align-items: center; flex-shrink: 0; }
/* Drag-to-move (see wireModalDrag in app.js) is desktop-only — match the
   same input-capability check there so the grab cursor only appears where
   the feature actually runs. A full-viewport modal has nowhere meaningful
   to be dragged to, so it's excluded here too. */
@media (hover: hover) and (pointer: fine) {
  .modal:not(.full) .modal-head { cursor: grab; touch-action: none; }
  .modal .modal-head.dragging { cursor: grabbing; }
}
.modal .modal-head .close-x { cursor: pointer; }
.modal .modal-head h3 { margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -.01em; }
.modal .modal-body { padding: 1.4rem; overflow-y: auto; overflow-x: hidden; flex: 1 1 auto; }
/* Bounded-height scroll container for the bulk-entry grid inside a
   .modal.full. Unlike .table-wrap (which grows to the table's full height so
   it never itself scrolls — see the comment near its own rules — and so
   never engages table.tbl th's position:sticky), this wrapper is capped to
   its available space, making IT the scroll container the sticky header
   resolves against. */
.po-grid-scroll { overflow: auto; max-height: 100%; }
/* Bulk-entry grid layout: modal-body becomes the flex column, the grid is
   the only item that grows/shrinks (min-height:0 so flex actually lets it
   shrink below its content size and scroll internally), everything else —
   header row, grand total, the part-search bar — stays flex-shrink:0 so it
   never gets pushed out of view. That's what makes the search bar "pinned":
   it isn't position:fixed, it just never has anything left to scroll past
   it once the grid is doing all the scrolling on its own. */
.modal.full .modal-body { display: flex; flex-direction: column; }
.modal.full .modal-body > * { flex-shrink: 0; }
.modal.full .modal-body > .po-grid-scroll { flex: 1 1 auto; min-height: 0; max-height: none; }
/* The part-search field sits at the very bottom of that column, hard against
   modal-body's own bottom edge — there's no room below it for its results
   dropdown to open downward (see .search-results above), so it opens upward
   over the grid instead, the same way a chat composer's @-mention list does. */
.po-pinned-search .search-results { top: auto; bottom: calc(100% + 6px); }
.modal .modal-body > .field > .cs-wrap.open { position: static; }
.modal .modal-body > .field > .cs-wrap.open .cs-dd { position: static; margin-top: 4px; opacity: 1;
  transform: none; box-shadow: none; border: 1px solid var(--border); max-height: none; }
/* .table-wrap only sets overflow-x, but per spec that silently turns
   overflow-y into a clipping context too — so a custom-select dropdown
   inside a table cell (e.g. the Suggested Vendor picker in Reorder
   Suggestions) got cut off by the table's own scroll boundary instead of
   floating over the rows below it, same underlying problem the modal rule
   above already works around the same way. */
.table-wrap .cs-wrap.open { position: static; }
.table-wrap .cs-wrap.open .cs-dd { position: static; margin-top: 4px; opacity: 1;
  transform: none; box-shadow: none; border: 1px solid var(--border); max-height: none; }
.modal .modal-foot { padding: 1rem 1.4rem; border-top: 1px solid var(--border); display: flex;
  justify-content: flex-end; gap: .6rem; flex-shrink: 0; }
.close-x { background: none; border: none; font-size: 1.2rem; cursor: pointer; color: var(--muted); line-height: 1;
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); transition: background var(--speed) var(--ease), color var(--speed) var(--ease); }
.close-x:hover { color: var(--text); background: var(--hover); }
/* Fade + scale, no slide */
@keyframes modalin { from { transform: scale(.98); opacity: 0; } to { transform: none; opacity: 1; } }

/* ── Utilities ─────────────────────────────────────────────────────────── */
.muted { color: var(--muted); }
.text-danger { color: var(--danger-ink); }
.text-success { color: var(--success-ink); }
.text-warning { color: var(--warning-ink); }
.text-muted { color: var(--muted); }
.text-right { text-align: right; }
.mt { margin-top: 1rem; } .mb { margin-bottom: 1rem; }
.flex { display: flex; gap: .5rem; align-items: center; } .flex-between { justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.grow { flex: 1; }
.empty { text-align: center; color: var(--muted); padding: 2.5rem 1rem; }
/* `table.tbl td` is more specific than `.empty`, so the placeholder row needs
   its own rule or it silently left-aligns. */
table.tbl td.empty, .table-wrap > table td.empty { text-align: center; white-space: normal; }

/* Inline notice / callout — one set of tinted panels instead of per-page hexes */
.notice { padding: .7rem .85rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--text); font-size: 13.5px; line-height: 1.5; }
.notice .icon { color: var(--muted); }
.notice-info    { background: var(--primary-soft); border-color: var(--primary-line); }
.notice-info .icon    { color: var(--primary); }
.notice-success { background: var(--success-soft); border-color: var(--success-line); }
.notice-success .icon { color: var(--success); }
.notice-warn    { background: var(--warning-soft); border-color: var(--warning-line); }
.notice-warn .icon    { color: var(--warning); }
.notice-danger  { background: var(--danger-soft); border-color: var(--danger-line); }
.notice-danger .icon  { color: var(--danger); }
/* A card whose whole body is a notice, e.g. the job-card lock banner */
.card.card-success { background: var(--success-soft); border-color: var(--success-line); }
.card.card-danger  { border-color: var(--danger-line); }
.card.card-danger > .card-head { color: var(--danger-ink); }
.card.card-danger > .card-head .icon { color: var(--danger); }

/* ── Login ─────────────────────────────────────────────────────────────── */
.login-page { min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: var(--bg); padding: 1rem; }
.login-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: none; width: 100%; max-width: 400px; padding: 2.25rem; }
.login-card h1 { text-align: center; font-size: 20px; font-weight: 600; color: var(--text); letter-spacing: -.015em; }
.login-card .sub { text-align: center; color: var(--muted); margin-bottom: 1.75rem; font-size: 13.5px; }

/* Off-canvas sidebar backdrop (mobile) */
.sidebar-backdrop { position: fixed; inset: 0; background: rgba(31,41,55,.35); z-index: 980;
  opacity: 0; pointer-events: none; transition: opacity 200ms var(--ease); }
.sidebar-backdrop.show { opacity: 1; pointer-events: auto; }
body.no-scroll { overflow: hidden; touch-action: none; }

/* Smooth momentum scrolling for wide content on touch devices */
.table-wrap, .modal .modal-body, .search-results { -webkit-overflow-scrolling: touch; }

/* ── Tablet ────────────────────────────────────────────────────────────── */
@media (max-width: 1024px) {
  :root { --sidebar-w: 216px; }
  .kpi-grid { gap: .85rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .content { padding: 1.25rem; }
  /* The line-item grid is 8 columns wide — give it back the space the side
     pane doesn't need before the layout stacks at 900px. */
  .wb-side { flex-basis: 272px; width: 272px; }
}

/* ── Mobile — off-canvas nav, denser layout, comfortable touch targets ─── */
@media (max-width: 900px) {
  :root { --card-pad: 1rem; --sidebar-w: 260px; }
  .sidebar { transform: translateX(-100%); box-shadow: var(--shadow-lg); border-right: none; }
  .sidebar.open { transform: none; }
  .main { margin-left: 0; }
  .topbar { padding: 0 .85rem; gap: .6rem; }
  .topbar .hamburger { display: block; }
  .topbar .brand { display: none; }
  .search-wrap { max-width: none; }
  .content { padding: 1rem; }
  .page-head { margin-bottom: 1rem; }
  .page-head h1, h1 { font-size: 20px; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); gap: .75rem; }
  .kpi { padding: .9rem 1rem; }
  .kpi .value { font-size: 22px; }
  .kpi .ic { right: .9rem; top: .85rem; }
  /* Cells stay on one line; .table-wrap scrolls horizontally as the fallback */
  table.tbl th, table.tbl td,
  .table-wrap > table th, .table-wrap > table td { white-space: nowrap; padding: .6rem .6rem; }
  .page-head .flex { width: 100%; justify-content: space-between; }
  .action-bar { margin: -.5rem 0 1rem; display: grid; grid-template-columns: 1fr 1fr; }
  .action-bar .btn, .action-bar select { width: 100%; height: auto; min-height: 36px;
    white-space: normal; text-align: center; padding: .5rem .6rem; min-width: 0 !important; }
  .action-bar > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  .action-bar-spacer { display: none; }
  .dt-toolbar { gap: .5rem; }
  .dt-search { min-width: 0; flex: 1; }
  .dt-count { width: 100%; margin-left: 0; }
  /* Horizontal strip only — overflow-y is stated explicitly because leaving it
     `visible` makes the browser compute it to `auto` alongside overflow-x. */
  .subtabs { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
  .subtabs button { white-space: nowrap; flex: 0 0 auto; }
  /* Cards must not exceed viewport */
  .card { max-width: 100%; }
  /* Single column — a pinned rail would just cover the content below it */
  .sticky-rail { position: static; max-height: none; overflow: visible; }
  .sticky-rail > .card { margin-bottom: 1.5rem; }
  /* Table footer: keep totals on one line via scroll */
  table.tbl tfoot td { white-space: nowrap; font-size: 13px; padding: .6rem var(--card-pad); }
  .dt-pager { flex-wrap: wrap; font-size: 12px; gap: .4rem; }
  .dt-pager button { padding: 0 .6rem; height: 32px; font-size: 12px; }
  /* Taller touch targets */
  .cs-trigger { height: 42px; line-height: 40px; font-size: 15px; }
  .cs-opt { padding: .55rem .75rem; font-size: 15px; }
  .cs-mini .cs-trigger { height: 36px; line-height: 34px; }
  .cs-status .cs-trigger { height: 38px; line-height: 36px; }
  .btn { height: 36px; font-size: 13.5px; }
  .btn-sm { height: 28px; font-size: 12px; }
  .field input, .field textarea, .field select, .dt-search { height: 42px; }
  .field textarea { height: auto; }
  .modal .modal-body { padding: 1.15rem; }
  .modal .modal-foot { padding: .85rem 1.15rem; }
  .form-grid { gap: .6rem; }
  .field label { margin-bottom: .25rem; }
  .char-count { font-size: 11.5px; margin-top: .15rem; }
}
@media (min-width: 901px) { .topbar .hamburger { display: none; } }

/* ── Small phones ──────────────────────────────────────────────────────── */
@media (max-width: 560px) {
  .topbar .user span:first-child { display: none; }      /* hide long name, keep bell + logout */
  .page-head { align-items: flex-start; }
  .page-head h1, h1 { font-size: 19px; }
  .form-grid { grid-template-columns: 1fr; gap: .5rem; }
  .field label { margin-bottom: .2rem; font-size: 11.5px; }
  .field input, .field select { padding: 0 .6rem; }
  .cs-trigger { height: 44px; line-height: 42px; font-size: 16px; }
  .cs-opt { padding: .65rem .75rem; font-size: 16px; }
  .btn { height: 38px; font-size: 14px; }
  .btn-sm { height: 30px; font-size: 12.5px; }
  .field input, .field textarea, .field select, .dt-search { height: 44px; }
  .field textarea { height: auto; }
  .modal-backdrop { padding: 0; align-items: flex-end; }
  .modal { max-width: 100%; max-height: 85vh; max-height: 90dvh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; border: none; }
  .modal .modal-head { padding: .85rem 1rem; }
  .modal .modal-head h3 { font-size: 16px; }
  .modal .modal-body { padding: 1rem; }
  .modal .modal-body .field { margin-bottom: .5rem; }
  .modal .modal-foot { padding: .75rem 1rem; }
  .form-actions .btn, .modal-foot .btn { flex: 1; }      /* full-width action buttons */
  .dt-pager { justify-content: space-between; }
}

/* ── Print ─────────────────────────────────────────────────────────────── */
@media print {
  .sidebar, .topbar, .no-print, .form-actions { display: none !important; }
  .layout { padding-top: 0; }
  .main { margin-left: 0; }
  body { background: #fff; }
  .content { padding: 0; animation: none; }
  .card { break-inside: avoid; }

  /* The workbench is a fixed-height app shell whose panes scroll internally —
     printed as-is it would emit one screenful and clip the rest. Unpin it so
     every section flows onto the page. */
  .content-fixed { height: auto; overflow: visible; display: block; }
  .wb, .wb-body, .wb-main, .wb-side, .wb-sec, .wb-panels { display: block; height: auto;
    min-height: 0; max-height: none; overflow: visible; border-right: none; width: auto; }
  .wb-sec { break-inside: avoid; }
  .wb-photos { max-height: none; }
  /* Controls don't belong on paper */
  .wb-actions, .wb-sec-act, table.dgrid .act { display: none !important; }
  table.dgrid th { position: static; }
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; }
}

/* ─── Aliases for the second markup dialect ─────────────────────────────────
   Newer pages (reminders, others, expenses, assets, payroll) were written with
   a different class vocabulary — .page-header / .tabs / .form-group /
   .form-control / .badge — than the original pages, which use
   .page-head / .card-head / .subtabs / .field / .pill.
   Each alias below renders *identically* to its canonical counterpart.
   ───────────────────────────────────────────────────────────────────────── */

/* .page-header ≡ .page-head */
.page-header { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 1.25rem; flex-wrap: wrap; gap: .6rem; }
.page-header h1 { font-size: 24px; font-weight: 650; margin: 0; letter-spacing: -.018em; }
.page-header h1 .icon { width: 1.05em; height: 1.05em; vertical-align: -0.1em; margin-right: .2rem; color: var(--muted); }

.card .card-head h2 { margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -.01em; }

/* .tabs/.tab-btn ≡ .subtabs — underlined tabs, not filled pills */
.tabs { display: flex; gap: .25rem; flex-wrap: wrap; margin-bottom: 1.5rem;
  box-shadow: inset 0 -1px 0 var(--border); }
.tab-btn { background: none; border: none; padding: .5rem .1rem; margin: 0 .6rem 0 0; cursor: pointer;
  font-size: 13.5px; color: var(--muted); border-bottom: 2px solid transparent; font-weight: 500;
  font-family: inherit; transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease); }
.tab-btn:hover { color: var(--text); }
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; background: none; }
.tab-btn .cnt { display: inline-block; margin-left: .35rem; min-width: 1.1rem; padding: .05rem .35rem;
  border-radius: var(--radius-xs); background: var(--neutral-soft); color: var(--muted); font-size: 11px; font-weight: 600; line-height: 1.4; }
.tab-btn.active .cnt { background: var(--primary-soft); color: var(--primary-ink); }

/* .badge-* ≡ .pill.* — same shape, same soft palette (base .badge set above) */
.badge-success { background: var(--success-soft); color: var(--success-ink); }
.badge-danger  { background: var(--danger-soft); color: var(--danger-ink); }
.badge-warning { background: var(--warning-soft); color: var(--warning-ink); }

/* .btn-xs ≡ .btn-sm — one small-button size app-wide */
.btn-xs { padding: 0 .6rem; height: 28px; font-size: 12px; gap: .3rem; }

/* Forms — .form-group/.form-control ≡ .field + its inputs */
.form-group { margin-bottom: 1rem; position: relative; }
.form-group:last-child { margin-bottom: 0; }
.form-group > label { display: block; font-size: 12px; color: var(--text); margin-bottom: .35rem; font-weight: 500; }
.form-group > label input[type="checkbox"] { margin-right: .4rem; accent-color: var(--primary); vertical-align: -.1em; }
.form-control {
  width: 100%; height: 36px; padding: 0 .75rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 14px; font-family: inherit; background: var(--card-bg); color: var(--text);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease); }
input.form-control:hover, textarea.form-control:hover, select.form-control:hover { border-color: var(--border-strong); }
select.form-control {
  -webkit-appearance: none; -moz-appearance: none; appearance: none; cursor: pointer;
  padding-right: 2.2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .65rem center; background-size: 16px; }
textarea.form-control { height: auto; padding: .55rem .75rem; line-height: 1.5; }
.form-control::placeholder { color: #9CA3AF; }
.form-control:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.form-group small { display: block; margin-top: .3rem; font-size: 12px; color: var(--muted); }

/* Entity-picker dropdown (.sr-item ≡ .search-results .item) */
.search-results .sr-item { padding: .55rem .7rem; border-radius: var(--radius-xs); cursor: pointer;
  font-size: 13.5px; transition: background var(--speed) var(--ease); }
.search-results .sr-item:hover { background: var(--hover); }
/* Arrow-key highlight for a wirePicker opted into keyboardNav — same
   treatment as .search-results .item.hl below, mirrored for .sr-item. */
.search-results .sr-item.hl { background: var(--hover); }

/* Stat strip */
.stat-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.stat-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: .9rem 1.1rem; min-width: 140px; flex: 1 1 140px; box-shadow: none;
  transition: border-color var(--speed) var(--ease); }
.stat-card:hover { border-color: var(--border-strong); }
.stat-label { font-size: 12px; color: var(--muted); font-weight: 500; margin-bottom: .25rem; }
.stat-value { font-size: 22px; font-weight: 600; color: var(--text); letter-spacing: -.02em; line-height: 1.25; }
/* Optional caption under a stat — e.g. what a figure excludes. */
.stat-sub { font-size: 12px; margin-top: .2rem; line-height: 1.35; }

/* Text + layout utilities */
small.text-muted { display: block; font-size: 12px; margin-top: .2rem; }
.center { text-align: center; }
td.center, th.center { text-align: center; }
.filter-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
/* Controls sitting directly in a filter row had no styling of their own and
   fell back to the browser's, so a date picker here looked nothing like the
   same picker inside a modal. Match .field, at the 34px filter-row height. */
.filter-row > input, .filter-row > select, .filter-row .filter-pair > input {
  height: 34px; padding: 0 .75rem; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 13px; font-family: inherit;
  background: var(--card-bg); color: var(--text);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
/* A From/To (or similar) label glued to its input so the pair moves and wraps
   together as one unit in a flex-wrap filter row — without this, a narrow
   (mobile) screen could wrap the label onto a different line than its input,
   or drop the label's only visual tie to which field it describes. The
   input's own title="" tooltip carries the same text, but that only reaches
   a mouse hover — nothing on a touch screen — so this is the one visible
   indicator on mobile. */
.filter-row .filter-pair { display: inline-flex; align-items: center; gap: .35rem; }
.filter-row .flabel { font-size: 12px; color: var(--muted); white-space: nowrap; }
.filter-row > select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none; cursor: pointer;
  padding-right: 2.2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .65rem center; background-size: 16px;
}
.filter-row > input:hover, .filter-row > select:hover, .filter-row .filter-pair > input:hover { border-color: var(--border-strong); }
.filter-row > input:focus, .filter-row > select:focus, .filter-row .filter-pair > input:focus {
  outline: none; border-color: var(--primary); box-shadow: var(--ring); }

/* Date and month pickers — one look everywhere, in filter rows and in forms. */
input[type="date"], input[type="month"] { cursor: pointer; }
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
  cursor: pointer; opacity: .45; padding: .15rem; margin-left: .15rem; border-radius: 3px;
  transition: opacity var(--speed) var(--ease), background var(--speed) var(--ease);
}
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="month"]:hover::-webkit-calendar-picker-indicator,
input[type="date"]:focus::-webkit-calendar-picker-indicator,
input[type="month"]:focus::-webkit-calendar-picker-indicator {
  opacity: 1; background: var(--hover); }

.filter-row .cs-wrap { width: auto; display: inline-block; min-width: 150px; }
.filter-row .cs-wrap .cs-trigger { width: auto; min-width: 150px; height: 34px; line-height: 32px; font-size: 13px; padding: 0 2rem 0 .75rem; }
.filter-row .cs-wrap .cs-dd { min-width: 170px; }

/* A <select> sitting next to a button inside a table cell (e.g. Others ->
   Staff & Roles "Add Staff"). .cs-wrap defaults to width:100%, which fights
   the sibling button for space in a nowrap flex row and crowds/overflows the
   cell — shrink it to its content instead, same fix as .filter-row above. */
.inline-add .cs-wrap { width: auto; display: inline-block; min-width: 150px; flex: 1 1 auto; }
.inline-add .cs-wrap .cs-trigger { width: 100%; min-width: 150px; height: 32px; line-height: 30px; font-size: 13px; padding: 0 1.8rem 0 .6rem; }
.inline-add .cs-wrap .cs-dd { min-width: 170px; }
.w-auto { width: auto; }
.mb-2 { margin-bottom: .75rem; }

/* Toggle switch */
.switch { position: relative; display: inline-block; width: 40px; height: 22px; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background: var(--disabled);
  border-radius: 999px; transition: background 200ms var(--ease); }
.slider::before { content: ""; position: absolute; height: 16px; width: 16px; left: 3px; bottom: 3px;
  background: #fff; border-radius: 50%; transition: transform 200ms var(--ease); }
.switch input:checked + .slider { background: var(--primary); }
.switch input:checked + .slider::before { transform: translateX(18px); }

/* Notification bell */
.notif-bell { position: relative; cursor: pointer; padding: .35rem; display: inline-flex; align-items: center;
  border-radius: var(--radius-sm); color: var(--muted); line-height: 0;
  transition: background var(--speed) var(--ease), color var(--speed) var(--ease); }
.notif-bell:hover { background: var(--hover); color: var(--text); }
.notif-bell .icon { width: 18px; height: 18px; }
.notif-badge { position: absolute; top: 0; right: 0; background: var(--danger); color: #fff;
  border-radius: 999px; font-size: 10px; font-weight: 600; min-width: 15px; height: 15px;
  line-height: 15px; text-align: center; border: 1.5px solid var(--card-bg); }
.notif-panel { position: absolute; top: calc(var(--topbar-h) - 6px); right: 1.25rem; width: 320px;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); z-index: 2000; max-height: 400px; overflow-y: auto; }
.notif-panel-header { padding: .7rem 1rem; font-weight: 600; font-size: 13px; border-bottom: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: center; }
.notif-close { background: none; border: none; cursor: pointer; font-size: 13px; color: var(--muted);
  padding: 0 .2rem; border-radius: 4px; }
.notif-close:hover { color: var(--text); background: var(--hover); }
.notif-list { padding: .35rem; }
.notif-item { padding: .55rem .65rem; font-size: 12.5px; border-radius: var(--radius-xs); }
.notif-item + .notif-item { border-top: 1px solid var(--border); border-radius: 0; }
.notif-item.overdue { color: var(--danger-ink); }

/* ── Keyboard navigation ───────────────────────────────────────────────────
   .hl is the *keyboard cursor* — where Enter would land. It is deliberately
   not the same signal as .active (the value currently held) or :hover, because
   all three can sit on different rows at once. The accent bar carries the
   cursor so it stays legible on top of either of the other two. */
.cs-opt.hl { background: var(--hover); }
.cs-opt.active.hl { background: var(--primary-soft); }
.cs-opt.hl::before {
  content: ""; position: absolute; left: 0; top: 3px; bottom: 3px; width: 3px;
  background: var(--primary); border-radius: 0 3px 3px 0;
}
.search-results .item.hl { background: var(--hover); }

/* Row focus. The dialog container itself takes focus when a modal has nothing
   worth landing on, and must not draw a ring for it. */
table.tbl tbody tr[data-rc]:focus, .table-wrap > table tbody tr[data-rc]:focus { outline: none; background: var(--hover); }
table.tbl tbody tr[data-rc]:focus td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
.modal:focus { outline: none; }

/* Clicked row in a manual picker (stock lot, vendor) — e.g. job card Add Part */
table.tbl tbody tr.selected, .table-wrap > table tbody tr.selected { background: var(--hover); }
table.tbl tbody tr.selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }

/* Shortcut cheat sheet (?) */
.kbd-table { width: 100%; border-collapse: collapse; }
.kbd-table td { padding: .55rem .25rem; font-size: 13.5px; vertical-align: middle;
  border-bottom: 1px solid var(--border); }
.kbd-table tr:last-child td { border-bottom: none; }
.kbd-table td:first-child { white-space: nowrap; width: 1%; padding-right: 1.25rem; }
.kbd-table td:last-child { color: var(--muted); }
kbd { display: inline-block; min-width: 24px; text-align: center; padding: .1rem .4rem;
  font-family: inherit; font-size: 12px; font-weight: 500; line-height: 1.6;
  color: var(--text); background: var(--bg); border: 1px solid var(--border);
  border-bottom-width: 2px; border-radius: var(--radius-xs); }

/* Alias dialect — responsive parity with the canonical classes */
@media (max-width: 900px) {
  .page-header { margin-bottom: 1rem; }
  .page-header h1 { font-size: 20px; }
  .tabs { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
  .tab-btn { white-space: nowrap; flex: 0 0 auto; }
  .form-control { height: 42px; }
  textarea.form-control { height: auto; }
  .btn-xs { height: 28px; font-size: 12px; }
  .notif-panel { right: .85rem; width: calc(100vw - 1.7rem); max-width: 340px; }
}
@media (max-width: 560px) {
  .page-header { align-items: flex-start; }
  .page-header h1 { font-size: 19px; }
  .form-control { height: 44px; }
  textarea.form-control { height: auto; }
  .btn-xs { height: 30px; font-size: 12.5px; }
}
