/* AccountsERP — glassmorphic violet design system (Phase 3 redesign).
   Same custom-property NAMES as the original token layer throughout —
   only their values changed — so every class rule and every inline
   `style="...var(--x)..."` across the app picks up the new look without
   any usage site needing to be touched. New variables (marked below)
   were added for things the old system didn't have: the page-background
   gradient, glass blur, and a properly-defined --accent-100 (previously
   referenced in ~12 files but never defined, so those always silently
   rendered their hardcoded fallback instead). */

:root {
  --surface-0: #FFFFFF; --surface-50: #F5F3FC; --surface-100: #EFEBFA; --surface-200: rgba(108,92,231,.16);
  --ink-900: #181428; --ink-700: #5B5470; --ink-500: #948DA8;
  --brand-700: #4B3FC9; --brand-600: #6C5CE7; --brand-100: #EFECFF;
  --danger-600: #D8394B; --danger-100: #FDEAEC;
  --success-600: #12925B; --success-100: #E7F9EF;
  --warning-600: #B4740E; --warning-100: #FCF0DA;
  --radius-sm: 10px; --radius-md: 14px; --radius-lg: 22px;
  --shadow-md: 0 16px 40px -14px rgba(108,92,231,.24), 0 6px 16px -8px rgba(108,92,231,.12);
  --font-display: -apple-system, "Segoe UI Semibold", "Segoe UI", sans-serif;
  --font-body: -apple-system, "Segoe UI", sans-serif;

  /* New in Phase 3 */
  --accent-100: #EFECFF;
  --shadow-shell: 0 30px 70px -24px rgba(90,70,190,.28), 0 10px 26px -12px rgba(90,70,190,.14);
  --bg-1: #EEF1FF; --bg-2: #F6EEFC; --bg-3: #EAF4FF;
  --blur: 20px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-body);
  color: var(--ink-900);
  background:
    radial-gradient(1200px 700px at 15% 10%, var(--bg-1), transparent 60%),
    radial-gradient(1000px 700px at 85% 20%, var(--bg-2), transparent 55%),
    radial-gradient(1200px 800px at 50% 100%, var(--bg-3), transparent 60%),
    var(--bg-1);
}

/* §dialog-double-scrollbar — showModal() puts a <dialog> in the top
   layer and blocks interaction with the rest of the page, but it does
   NOT stop the page behind it from still being scrollable — the
   background page's own scrollbar stayed visible and usable the whole
   time a dialog was open, alongside the dialog's own .modal-body
   scrollbar, showing as two separate scrollbars at once (worst on
   Quick Invoice, where the background Invoices list is a long table).
   :has() lets one rule cover every dialog.modal in the app — opened
   from dozens of different onclick handlers across many files — without
   adding open/close bookkeeping to each of them. */
body:has(dialog[open]) { overflow: hidden; }

.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface-0);
  border: 1px solid var(--surface-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-shell);
  padding: 32px 28px;
}

.auth-brand {
  text-align: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 16px;
  margin-bottom: 22px;
}
.auth-brand .dot { display:inline-block; width:9px; height:9px; border-radius:50%; background:var(--brand-600); margin-right:7px; }

.field { margin-bottom: 14px; display: flex; flex-direction: column; gap: 5px; }
.field label { font-size: 12px; font-weight: 700; color: var(--ink-700); }
.field input {
  border: 1px solid var(--surface-200);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink-900);
  background: var(--surface-0);
}
.field input:focus { outline: 2px solid var(--brand-600); outline-offset: 1px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%; padding: 10px 14px; border-radius: var(--radius-md);
  border: 1px solid var(--brand-600); background: var(--brand-600); color: #fff;
  font-weight: 700; font-size: 14px; font-family: inherit; cursor: pointer;
}
.btn:hover { background: var(--brand-700); border-color: var(--brand-700); }
.btn.secondary { background: var(--surface-0); color: var(--ink-900); border-color: var(--surface-200); }

.auth-links { text-align: center; font-size: 12.5px; margin-top: 16px; display:flex; flex-direction:column; gap:8px; }
.auth-links a { color: var(--brand-700); font-weight: 600; text-decoration: none; }
.auth-links a:hover { text-decoration: underline; }

.error-banner {
  background: var(--danger-100); color: var(--danger-600); border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 12.5px; margin-bottom: 14px; line-height: 1.5;
}
.status-banner {
  background: var(--success-100); color: var(--success-600); border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 12.5px; margin-bottom: 14px; line-height: 1.5;
}

/* Global flash-message toast (layouts/app.blade.php) — replaces the old
   always-inline status/error banner, which pages then duplicated with
   their own copy. Fixed to the viewport so it never fights page layout,
   auto-dismisses after data-toast-timeout ms (default 15s) or on click. */
.toast-stack {
  position: fixed; top: 20px; right: 20px; z-index: 1000;
  display: flex; flex-direction: column; gap: 10px; max-width: 360px;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: var(--radius-md); font-size: 13px; line-height: 1.5;
  box-shadow: var(--shadow-md); animation: toast-in .18s ease-out;
}
.toast-success { background: var(--success-100); color: var(--success-600); }
.toast-error { background: var(--danger-100); color: var(--danger-600); }
.toast span { flex: 1; }
.toast-close {
  background: none; border: none; cursor: pointer; font-size: 16px; line-height: 1;
  color: inherit; opacity: .6; padding: 0 2px;
}
.toast-close:hover { opacity: 1; }
.toast-hide { animation: toast-out .2s ease-in forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } }
@keyframes toast-out { from { opacity: 1; } to { opacity: 0; transform: translateX(16px); } }

.otp-input { display:flex; gap:8px; justify-content:center; }
.otp-input input {
  width: 40px; height: 46px; text-align: center; font-size: 20px; font-weight:700;
  border: 1px solid var(--surface-200); border-radius: var(--radius-sm);
}

.method-tabs { display:flex; gap:6px; margin-bottom:16px; justify-content:center; }
.method-tabs button {
  font-size: 11.5px; font-weight: 700; padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--surface-200); background: var(--surface-0); color: var(--ink-700); cursor:pointer;
}
.method-tabs button.active { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }

.company-grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(160px,1fr)); gap:12px; }
.company-card {
  border: 1px solid var(--surface-200); border-radius: var(--radius-md); padding: 16px;
  text-align:center; background: var(--surface-0); text-decoration:none; color:var(--ink-900); cursor:pointer;
}
.company-card:hover { border-color: var(--brand-600); }
.company-card.create { border-style: dashed; color: var(--ink-500); }

.recovery-codes { display:grid; grid-template-columns: 1fr 1fr; gap:8px; font-family: ui-monospace, monospace; font-size:13px; background:var(--surface-100); border-radius:var(--radius-sm); padding:14px; margin-bottom:16px; }

.qr-box { text-align:center; margin-bottom: 16px; }
.qr-box img { border: 1px solid var(--surface-200); border-radius: var(--radius-sm); }
.secret-key { font-family: ui-monospace, monospace; font-size: 12px; text-align:center; color: var(--ink-500); margin-bottom:16px; word-break:break-all; }

/* Page-local content width/heading styles — now rendered inside
   .app-content (the sidebar app shell below, Component-Library §17). */
.page-shell { max-width: 760px; margin: 0 auto; padding: 40px 24px 80px; }
.page-shell h1 { font-family: var(--font-display); font-size: 22px; margin: 0 0 4px; }
.page-shell .sub { color: var(--ink-500); font-size: 13px; margin: 0 0 28px; }

/* Breadcrumb-style back link, e.g. "← Customers" atop a form/show page —
   used across ~45 views but had no rule at all before Phase 3. */
.topnav { display:flex; align-items:center; gap:12px; margin-bottom:14px; font-size:12.5px; }
.topnav a { color: var(--ink-500); text-decoration:none; font-weight:600; }
.topnav a:hover { color: var(--brand-700); }

/* Sidebar app shell (Component-Library §17, Sitemap.md §4/§5) — a
   floating glass card, per the approved design, not edge-to-edge. */
.app-shell {
  display: flex;
  min-height: calc(100vh - 40px);
  max-width: 1400px;
  margin: 20px auto;
  background: rgba(255,255,255,.78);
  backdrop-filter: blur(var(--blur));
  -webkit-backdrop-filter: blur(var(--blur));
  border: 1px solid var(--surface-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-shell);
  overflow: hidden;
}

.app-sidebar {
  width: 240px; flex-shrink: 0; background: rgba(108,92,231,.05);
  border-right: 1px solid var(--surface-200); padding: 20px 14px;
  overflow-y: auto;
}
.app-brand {
  font-family: var(--font-display); font-weight: 800; font-size: 15px;
  margin-bottom: 20px; padding: 0 8px; display: flex; align-items: center;
}
.app-brand .dot {
  display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px;
  border-radius: 8px; background:var(--brand-600); margin-right:9px; color:#fff; font-size:12px; font-weight:800;
}

.nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.nav-single, .nav-group summary, .nav-group a {
  display: flex; align-items:center; gap:8px; padding: 8px 10px; border-radius: var(--radius-sm);
  color: var(--ink-700); text-decoration: none; font-size: 13.5px;
  cursor: pointer;
}
.nav-single svg, .nav-group summary svg { width:15px; height:15px; flex-shrink:0; color: var(--ink-500); }
.nav-group summary { font-weight: 700; list-style: none; }
.nav-group summary::-webkit-details-marker { display: none; }
.nav-group summary::after { content: '▸'; margin-left:auto; font-size:10px; color: var(--ink-500); }
.nav-group[open] summary::after { content: '▾'; }
.nav-group ul { list-style: none; margin: 2px 0 4px; padding: 0 0 0 20px; display: flex; flex-direction: column; gap: 1px; }
.nav-group ul a { font-size: 13px; padding: 6px 10px; }
.nav-single:hover, .nav-group ul a:hover { background: var(--surface-100); }
.nav-single.active, .nav-group ul a.active {
  background: var(--brand-600); color: #fff; font-weight: 700;
}
.nav-single.active svg { color: #fff; }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 28px; border-bottom: 1px solid var(--surface-200);
  background: rgba(255,255,255,.5); gap: 16px;
}
.app-topbar-title { font-family: var(--font-display); font-weight: 700; font-size: 15px; }
.app-topbar-right { display: flex; align-items: center; gap: 14px; font-size: 13px; }
.app-topbar-company { color: var(--ink-500); }

.user-menu { position: relative; }
.user-menu summary { list-style: none; cursor: pointer; font-weight: 700; color: var(--ink-900); }
.user-menu summary::-webkit-details-marker { display: none; }
/* .user-menu-portal is added by action-menu.js — same box, but once the
   panel is moved to <body> on open (see that file for why), `position`
   is overridden inline to `fixed` with JS-computed coordinates, which
   beats this rule's `absolute` regardless of source order/specificity.
   The plain `.user-menu ul` selector stays as the no-JS fallback. */
.user-menu ul, .user-menu-portal {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  list-style: none; margin: 0; padding: 6px; min-width: 180px;
  background: var(--surface-0); border: 1px solid var(--surface-200);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 1px;
}
.user-menu-portal { z-index: 500; }
.user-menu ul li a, .user-menu ul li .link-button,
.user-menu-portal li a, .user-menu-portal li .link-button {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 7px 9px;
  border-radius: var(--radius-sm); color: var(--ink-900); text-decoration: none;
  font-size: 13px; border: none; background: none; cursor: pointer; font-family: inherit;
}
.user-menu ul li a svg, .user-menu ul li .link-button svg,
.user-menu-portal li a svg, .user-menu-portal li .link-button svg { width: 15px; height: 15px; flex-shrink: 0; color: var(--ink-500); }
.user-menu ul li a:hover, .user-menu ul li .link-button:hover,
.user-menu-portal li a:hover, .user-menu-portal li .link-button:hover { background: var(--surface-100); }
.user-menu ul li .link-button.active, .user-menu-portal li .link-button.active { color: var(--brand-700); font-weight: 700; }

.app-content { flex: 1; padding: 0; }
.app-content .status-banner, .app-content .error-banner { margin: 20px 28px 0; }

/* Tile grid — Settings Home (S-T01) and Dashboard quick links share this */
.tile-grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap:12px; }
.tile { display:block; background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-lg);
  padding:16px; text-decoration:none; color:var(--ink-900); box-shadow: var(--shadow-md); }
.tile:hover { border-color:var(--brand-600); }
.tile b { display:block; font-family:var(--font-display); font-size:13.5px; margin-bottom:3px; }
.tile span { font-size:11.5px; color:var(--ink-500); }
.tile-cat { font-size:11px; text-transform:uppercase; letter-spacing:.05em; color:var(--brand-700); font-weight:700; margin:22px 0 8px; }

.card-block { background: var(--surface-0); border: 1px solid var(--surface-200); border-radius: var(--radius-lg); padding: 24px; margin-bottom: 20px; }
.card-block h2 { font-family: var(--font-display); font-size: 15px; margin: 0 0 16px; }

/* AI Invoice Inbox — chat composer (§ai-async-invoice): a single
   always-visible send bar, no persisted message history. */
.ai-composer { padding: 14px 16px; border: 2px dashed transparent; border-radius: var(--radius-md); transition: border-color .1s, background .1s; }
.ai-composer.is-dragover { border-color: var(--brand-600); background: var(--brand-100); }
.ai-composer-row { display: flex; align-items: flex-end; gap: 8px; }
.ai-composer-row textarea {
  flex: 1; border: 1px solid var(--surface-200); border-radius: var(--radius-sm); padding: 10px 12px;
  font-size: 13.5px; font-family: inherit; color: var(--ink-900); background: var(--surface-0);
  resize: none; max-height: 120px; min-height: 40px;
}
.ai-composer-row textarea:focus { outline: 2px solid var(--brand-600); outline-offset: 1px; }
.ai-composer-row .btn-inline, .ai-composer-row .icon-btn { flex-shrink: 0; }
.ai-composer-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ai-composer-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  background: var(--brand-100); color: var(--brand-700); border-radius: 999px; font-size: 12px; font-weight: 600;
}
.ai-composer-chip button { border: none; background: none; color: inherit; cursor: pointer; font-size: 14px; line-height: 1; }
.ai-composer-status { font-size: 12px; color: var(--ink-500); margin: 8px 0 0; min-height: 1em; }
.ai-composer-status.is-active { color: var(--brand-700); font-weight: 600; }
.ai-composer-status.is-error { color: var(--danger-600, #c0392b); font-weight: 600; }

.form-grid { display:grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-grid .full { grid-column: 1 / -1; }
/* :not([type=radio|checkbox]) — those render as native small controls;
   without this exclusion they inherit width:100%+padding+border meant
   for text-like inputs and stretch into an unusable full-width box. */
.form-grid .field input:not([type="radio"]):not([type="checkbox"]), .form-grid .field select, .form-grid .field textarea {
  width: 100%; border: 1px solid var(--surface-200); border-radius: var(--radius-sm);
  padding: 8px 10px; font-size: 13.5px; font-family: inherit; color: var(--ink-900); background: var(--surface-0);
}
.form-grid .field textarea { resize: vertical; min-height: 60px; }
.btn-row { display:flex; gap: 10px; margin-top: 18px; }
.btn-inline { display:inline-flex; align-items:center; padding: 7px 13px; border-radius: var(--radius-md);
  font-size: 12.5px; font-weight:700; border:1px solid var(--surface-200); background: var(--surface-0); color: var(--ink-900); cursor:pointer; }
.btn-inline.primary { background: var(--brand-600); border-color: var(--brand-600); color: #fff; }
.btn-inline.danger { border-color: var(--danger-600); color: var(--danger-600); }
.btn-inline:disabled { opacity:.5; cursor:not-allowed; }

/* §contra-two-column - the "Invoices to Settle"/"Purchases to Settle"
   pair on the Contra create screen (resources/views/accounting/contra/
   form.blade.php). Explicit min-width:0 on children is required so an
   unconstrained table.dt inside a grid child can't push past its
   column share and spill into its neighbor - same fix .split-detail
   already documents above, applied to a plain two-up layout instead
   of the split-shell's list/detail pair. */
.contra-allocations { display:grid; grid-template-columns: 1fr 1fr; gap:20px; margin-top:16px; }
.contra-allocations .card-block { min-width:0; }
@media (max-width: 860px) { .contra-allocations { grid-template-columns: 1fr; } }

table.dt { width:100%; border-collapse: collapse; font-size: 13px; background: var(--surface-0); border: 1px solid var(--surface-200); border-radius: var(--radius-md); overflow:hidden; }
table.dt th { text-align:left; font-size: 10.5px; text-transform:uppercase; letter-spacing:.04em; color: var(--ink-500); background: var(--surface-100); padding: 9px 12px; }
table.dt td { padding: 9px 12px; border-top: 1px solid var(--surface-200); }
table.dt td.num, table.dt th.num { text-align: right; }
.pill { display:inline-flex; align-items:center; gap:4px; padding:2px 9px; border-radius:999px; font-size:10.5px; font-weight:700; white-space:nowrap; }
.pill.active { background: var(--success-100); color: var(--success-600); }
.pill.archived { background: var(--surface-100); color: var(--ink-500); }
.pill.warning { background: var(--warning-100); color: var(--warning-600); }
.pill.danger { background: var(--danger-100); color: var(--danger-600); }
.pill.pending { background: var(--brand-100); color: var(--brand-700); }

/* §repeat-last-order — a quantity carried over from a past invoice, not yet reviewed by the user (invoice-form.js's invLoadLastOrder()/submitAs()). */
.qty-needs-review { border-color: var(--warning-600) !important; background: var(--warning-100); }

/* §line-rows-ssel-width-fix — see the comment above <colgroup> in sales/invoices/_form_fields.blade.php: without this, .ssel-trigger's global 160px min-width can exceed a narrow fixed-layout column and visually spill into the next one. */
#line-rows .ssel-trigger { min-width: 0; width: 80%; }
/* Unit specifically keeps a small floor so a very narrow column never squeezes it down to an unreadable sliver — 100px stays well under the column's own width, unlike the removed 160px global default. */
#line-rows td[data-label="Unit"] .ssel-trigger { min-width: 100px; }

/* §combined-ledger — marks a ledger entry's narration as clickable
   (opens #closing-entry-detail-modal via closing-entry-detail.js). */
.entry-detail-trigger { cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
.entry-detail-trigger:hover { color: var(--brand-700); }

/* Small square icon-only row actions (Users list, S-T04) — a compact
   sibling of .btn-inline for rows with several actions where text
   labels would crowd the table; title="" attribute carries the a11y
   label instead of visible text. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--radius-sm);
  border: 1px solid var(--surface-200); background: var(--surface-0);
  color: var(--ink-700); cursor: pointer; flex-shrink: 0;
}
.icon-btn svg { width: 15px; height: 15px; }
.icon-btn:hover { background: var(--surface-100); }
.icon-btn.danger { border-color: var(--danger-600); color: var(--danger-600); }
.icon-btn.danger:hover { background: var(--danger-100); }
.icon-btn.success { border-color: var(--success-600); color: var(--success-600); }
.icon-btn.primary { border-color: var(--brand-600); background: var(--brand-600); color: #fff; }
.icon-btn.primary:hover { background: var(--brand-700); border-color: var(--brand-700); }

/* §customer-last-price - custom hover tooltip for the invoice line
   "last price for this customer" icon-btn. Deliberately NOT the native
   title="" attribute other icon-btns use: the browser's own tooltip has
   a built-in OS hover delay that can't be shortened, and this one needs
   to appear the instant the mouse arrives (the underlying data is
   preloaded, so there's no reason left to wait).
   Shown/positioned by invoice-form.js's initLastPriceTooltips(), not
   plain CSS :hover - table.dt sets overflow:hidden (for its
   border-radius), which clips any position:absolute descendant,
   exactly the bug action-menu.js already fixed for the row "..." menu.
   Same fix here: on hover, JS portals this element to document.body
   with position:fixed computed from the button's real coordinates. */
.line-last-price-wrap { display: inline-flex; flex-shrink: 0; }
.last-price-tooltip {
  display: none;
  background: var(--surface-0); border: 1px solid var(--surface-200);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
  padding: 8px 10px; font-size: 11.5px; line-height: 1.5; font-weight: 400;
  color: var(--ink-900); white-space: pre-line; text-align: left;
  width: max-content; max-width: 220px; z-index: 500; pointer-events: none;
}
.icon-btn.success:hover { background: var(--success-100); }

/* Product Units grid (S-N02) — Unit Type badges, the Unit Rules
   checklist sidebar, and the Conversion Preview strip. */
.unit-type-badge { display:inline-flex; align-items:center; gap:5px; padding:3px 9px; border-radius:999px; font-size:11px; font-weight:700; white-space:nowrap; }
.unit-type-badge.primary_sku { background: var(--brand-100); color: var(--brand-700); }
.unit-type-badge.secondary_unit { background: #E0EAFF; color: #3554C9; }
.unit-type-badge.additional_unit { background: var(--warning-100); color: var(--warning-600); }

.units-layout { display:grid; grid-template-columns: 1fr 300px; gap:18px; align-items:start; }
@media (max-width: 860px) { .units-layout { grid-template-columns: 1fr; } }

.rule-check-list { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:14px; }
.rule-check-list li { display:flex; gap:10px; align-items:flex-start; font-size:12.5px; }
.rule-check-list .rule-icon { flex-shrink:0; width:18px; height:18px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; margin-top:1px; }
.rule-check-list .rule-icon.ok { background: var(--success-100); color: var(--success-600); }
.rule-check-list .rule-icon.pending { background: var(--surface-100); color: var(--ink-500); }
.rule-check-list .rule-title { font-weight:700; color: var(--ink-900); display:block; }
.rule-check-list .rule-desc { color: var(--ink-500); }

.conversion-preview { display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin-top:12px; }
.conversion-chip { display:flex; align-items:center; gap:10px; background: var(--surface-0); border:1px solid var(--surface-200); border-radius: var(--radius-md); padding:10px 16px; }
.conversion-chip .cc-icon { width:34px; height:34px; border-radius: var(--radius-sm); display:flex; align-items:center; justify-content:center; font-size:16px; flex-shrink:0; }
.conversion-chip.primary_sku .cc-icon { background: var(--brand-100); color: var(--brand-700); }
.conversion-chip.secondary_unit .cc-icon { background: #E0EAFF; color: #3554C9; }
.conversion-chip.additional_unit .cc-icon { background: var(--warning-100); color: var(--warning-600); }
.conversion-chip .cc-value { font-family: var(--font-display); font-size: 17px; font-weight:700; color: var(--ink-900); line-height:1.1; }
.conversion-chip .cc-unit { font-size:11.5px; color: var(--ink-500); }
.conversion-eq { font-size:16px; color: var(--ink-500); font-weight:700; }

/* Product Images field — drag & drop / click / paste-from-clipboard. */
.image-dropzone {
  border: 2px dashed var(--surface-200); border-radius: var(--radius-md);
  padding: 28px 16px; text-align:center; cursor:pointer; color: var(--ink-700);
  background: var(--surface-0);
}
.image-dropzone:hover, .image-dropzone:focus, .image-dropzone.is-dragover {
  border-color: var(--brand-600); background: var(--brand-100); outline: none;
}
.image-dropzone .image-dropzone-icon { font-size:22px; color: var(--brand-600); margin-bottom:6px; }

.ai-info-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  border-radius: 50%; color: var(--ink-500); font-size: 14px; cursor: pointer; flex-shrink: 0;
}
.ai-info-btn:hover { background: var(--surface-100); color: var(--brand-700); }

/* Compact inline add/edit rows used by simple master-list screens
   (Categories & Brands, Units) — a lightweight sibling of .form-grid for
   forms that live inside a table row or a toolbar strip rather than a
   full field grid. */
.inline-edit-form { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.inline-edit-form input[type="text"], .inline-edit-form select {
  border: 1px solid var(--surface-200); border-radius: var(--radius-sm);
  padding: 7px 9px; font-size: 13px; font-family: inherit; color: var(--ink-900); background: var(--surface-0);
}
.inline-edit-form input[type="text"]:focus, .inline-edit-form select:focus {
  outline: none; border-color: var(--brand-600);
}

.logo-preview { display:flex; align-items:center; gap:12px; margin-bottom: 10px; }
.logo-preview img { width:48px; height:48px; object-fit:contain; border:1px solid var(--surface-200); border-radius:var(--radius-sm); background:var(--surface-50); }

/* Shared utility classes — Phase 3B sweep replaces per-file inline
   `style="..."` totals blocks and muted empty-state text with these. */
.totals-block { background: var(--accent-100); border-radius: var(--radius-sm); padding: 14px; margin-top: 14px; max-width: 320px; margin-left: auto; }
.totals-row { display:flex; justify-content:space-between; }
.totals-row.grand { font-weight: 700; border-top: 1px solid var(--surface-200); margin-top: 6px; padding-top: 6px; }
.muted { color: var(--ink-500); }

/* §33 Installer (S-I01…S-I09) */
.install-card { max-width: 480px; }
.progress-rail { display:flex; flex-wrap:wrap; gap: 4px 10px; margin-bottom: 22px; padding-bottom: 16px; border-bottom: 1px solid var(--surface-200); }
.progress-rail .step { font-size: 10.5px; font-weight: 700; color: var(--ink-500); white-space: nowrap; }
.progress-rail .step.active { color: var(--brand-700); }
.progress-rail .step.done { color: var(--success-600); }

.check-list { display:flex; flex-direction:column; gap:10px; margin-bottom:20px; }
.check-row { display:flex; align-items:flex-start; gap:9px; font-size:13px; line-height:1.4; }
.check-row .icon { flex-shrink:0; font-weight:800; }
.check-row.pass .icon { color: var(--success-600); }
.check-row.fail .icon { color: var(--danger-600); }
.check-row .remediation { color: var(--ink-500); font-size:12px; margin-top:2px; }

.skip-note { font-size:12px; color: var(--ink-500); margin: 4px 0 16px; line-height:1.5; }
.test-result { font-size:12.5px; margin: 8px 0; padding: 8px 10px; border-radius: var(--radius-sm); display:none; }
.test-result.ok { display:block; background: var(--success-100); color: var(--success-600); }
.test-result.error { display:block; background: var(--danger-100); color: var(--danger-600); }

.review-block { margin-bottom: 14px; }
.review-block h3 { font-size: 11px; text-transform:uppercase; letter-spacing:.04em; color: var(--ink-500); margin: 0 0 6px; }
.review-block dl { display:grid; grid-template-columns: auto 1fr; gap: 3px 10px; font-size: 13px; margin:0; }
.review-block dt { color: var(--ink-500); }
.review-block dd { margin:0; }

.install-progress { display:flex; flex-direction:column; gap:8px; margin: 16px 0; }
.install-progress .step-row { font-size:13px; color: var(--ink-500); display:flex; align-items:center; gap:8px; }
.install-progress .step-row .icon { color: var(--success-600); font-weight:800; }

.complete-warning { background: var(--danger-100); color: var(--danger-600); border-radius: var(--radius-sm); padding: 12px 14px; font-size: 12.5px; line-height:1.5; margin: 16px 0; }

/* §5 Executive Dashboard — stat tiles, list cards, inline SVG graphs.
   Colors reuse the existing brand/success/danger/ink tokens rather than
   a new palette; each graph card is single-hue, so cross-series CVD
   distinction doesn't apply — the card title carries the identity. */
.stat-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap:12px; margin: 16px 0; }
.stat-card { background: var(--surface-0); border:1px solid var(--surface-200); border-radius: var(--radius-lg); padding: 16px 18px; box-shadow: var(--shadow-md); }
.stat-card .stat-label { font-size: 11px; text-transform:uppercase; letter-spacing:.04em; color: var(--ink-500); margin-bottom: 6px; }
.stat-card .stat-value { font-family: var(--font-display); font-size: 22px; font-weight: 700; color: var(--ink-900); }
.stat-card.accent-brand .stat-value { color: var(--brand-700); }
.stat-card.accent-success .stat-value { color: var(--success-600); }
.stat-card.accent-danger .stat-value { color: var(--danger-600); }
.stat-card.accent-warning .stat-value { color: var(--warning-600); }

.dash-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap:16px; margin: 16px 0; }
.dash-card { background: var(--surface-0); border:1px solid var(--surface-200); border-radius: var(--radius-lg); padding: 16px 18px; }
.dash-card h3 { margin: 0 0 12px; font-size: 13px; text-transform:uppercase; letter-spacing:.03em; color: var(--ink-500); }
.dash-list { display:flex; flex-direction:column; gap:0; }
.dash-list .row { display:flex; justify-content:space-between; align-items:center; padding: 8px 0; border-top: 1px solid var(--surface-100); font-size: 13px; }
.dash-list .row:first-child { border-top:none; }
.dash-list .row .name { color: var(--ink-900); }
.dash-list .row .meta { color: var(--ink-500); font-size: 11.5px; }
.dash-list .empty { color: var(--ink-500); font-size: 13px; padding: 8px 0; }
.dash-chart svg { display:block; }
.dash-chart text { font-family: var(--font-body); }

/* Customer module UI redesign — list+detail split view, Add/Edit modal,
   invoice drawer, empty state. Composed entirely from existing tokens,
   no new palette. Not a .page-shell (760px) page — a split view needs
   more room, so this gets its own wider container, scoped to this one
   screen rather than raising the cap app-wide. */
.customers-page { max-width: 1320px; margin: 0 auto; padding: 32px 24px 60px; }
.customers-page-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:18px; gap:16px; flex-wrap:wrap; }
.customers-page-head h1 { font-family: var(--font-display); font-size: 22px; margin: 0 0 4px; }
.customers-page-head .sub { color: var(--ink-500); font-size: 13px; margin: 0; }

.customers-toolbar { display:flex; align-items:center; gap:10px; margin-bottom:16px; flex-wrap:wrap; }
.customers-toolbar .search-field { flex:1; min-width:220px; }
.customers-toolbar input[type="search"], .customers-toolbar input[type="date"], .customers-toolbar select {
  border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:8px 11px; font-size:13.5px; font-family:inherit; color:var(--ink-900); background:var(--surface-0);
}
.customers-toolbar input[type="search"] { width:100%; }

/* Product detail — Stock Ledger toolbar (date range + type filter +
   column picker), stock-level bar, insight cards, and the balance
   trend sparkline, all inside the analytics block above the product's
   own identity strip. */
.ledger-toolbar { display:flex; align-items:center; gap:10px; margin-bottom:16px; flex-wrap:wrap; }
.ledger-toolbar .dp-range { display:flex; align-items:center; gap:6px; }
.ledger-toolbar .dp-wrapper { flex: 0 0 auto; }
.ledger-toolbar .dp-wrapper .dp-display { min-width:120px; }
.ledger-toolbar select { border:1px solid var(--surface-200); border-radius:var(--radius-sm); padding:8px 11px; font-size:13px; font-family:inherit; background:var(--surface-0); color:var(--ink-900); }

.stock-level-bar { margin-bottom:20px; }
.stock-level-track { position:relative; height:10px; border-radius:999px; background:var(--surface-100); overflow:hidden; }
.stock-level-fill { height:100%; border-radius:999px; background:var(--success-600); transition:width .2s ease; }
.stock-level-fill.low { background:var(--danger-600); }
.stock-level-marker { position:absolute; top:-3px; width:2px; height:16px; background:var(--ink-700); }
.stock-level-labels { position:relative; display:flex; justify-content:space-between; margin-top:6px; font-size:11px; color:var(--ink-500); }
.stock-level-labels .reorder-label { position:absolute; transform:translateX(-50%); white-space:nowrap; }

.insights-row { display:grid; grid-template-columns:repeat(auto-fit, minmax(160px, 1fr)); gap:12px; margin-bottom:20px; }
.insight-card { display:flex; align-items:center; gap:10px; background:var(--surface-50); border:1px solid var(--surface-200); border-radius:var(--radius-md); padding:12px 14px; }
.insight-icon { font-size:18px; }
.insight-label { font-size:11px; color:var(--ink-500); text-transform:uppercase; letter-spacing:.03em; }
.insight-value { font-family:var(--font-display); font-size:15px; font-weight:700; color:var(--ink-900); margin-top:2px; }

.balance-trend h3 { font-family:var(--font-display); font-size:13px; margin:0 0 8px; }
.balance-trend h3 .muted { font-weight:400; font-size:12px; }

/* Small KPI-style chips atop a list view (e.g. Invoices' Outstanding/
   Overdue/Draft summary) — hidden once the split view opens, per
   §invoice-actions ("not when entering the invoice detail view"). */
.stat-chip-row { display:flex; gap:12px; margin-bottom:16px; flex-wrap:wrap; }
.stat-chip {
  display:flex; flex-direction:column; gap:2px; background:var(--surface-0);
  border:1px solid var(--surface-200); border-radius:var(--radius-md); padding:10px 16px; min-width:140px;
}
.stat-chip-label { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--ink-500); }
.stat-chip-value { font-family:var(--font-display); font-size:18px; font-weight:700; font-variant-numeric:tabular-nums; }
.stat-chip-danger .stat-chip-value { color:var(--danger-600); }

/* Small round-square initials badge — list rows and the detail header
   share one treatment (single brand tint) rather than a new categorical
   palette this app's token system doesn't otherwise have. */
.avatar {
  display:flex; align-items:center; justify-content:center; flex-shrink:0;
  border-radius: 8px; background: var(--brand-100); color: var(--brand-700);
  font-weight:800; font-family: var(--font-display);
  width:32px; height:32px; font-size:12px;
}
.avatar.lg { width:48px; height:48px; font-size:16px; border-radius: var(--radius-md); }

.empty-state { text-align:center; padding:90px 20px; background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-lg); }
.empty-state .icon { font-size:40px; margin-bottom:14px; }
.empty-state h2 { font-family:var(--font-display); font-size:17px; margin:0 0 6px; }
.empty-state p { color:var(--ink-500); font-size:13.5px; margin:0 0 22px; }

.split-shell { display:grid; grid-template-columns: minmax(260px, 30%) 1fr; gap:16px; align-items:start; }
.split-shell.split-shell-3col { grid-template-columns: minmax(220px, 22%) minmax(220px, 26%) 1fr; }

.split-list-header { display:flex; align-items:center; padding:10px 12px; border-bottom:1px solid var(--surface-100); }

.split-list {
  background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-lg);
  max-height: calc(100vh - 230px); overflow-y:auto; min-width:0;
}
.split-list .customer-row {
  display:flex; align-items:center; gap:10px; width:100%; text-align:left; padding:12px 16px;
  border:none; border-bottom:1px solid var(--surface-100); background:none; cursor:pointer; font-family:inherit; color:var(--ink-900);
}
.split-list .customer-row:last-child { border-bottom:none; }
.split-list .customer-row:hover { background:var(--surface-50); }
.split-list .customer-row.active { background: var(--brand-100); }
.split-list .customer-row .row-body { display:flex; flex-direction:column; min-width:0; flex:1; }
.split-list .customer-row .row-name { font-weight:700; font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.split-list .customer-row .row-meta { font-size:11.5px; color:var(--ink-500); margin-top:2px; }
/* §purchases-list-row — an optional first line above .row-name, for a
   date + status pill (Purchases' 3-line row layout). */
.split-list .customer-row .row-top { display:flex; align-items:center; gap:8px; font-size:11px; color:var(--ink-500); margin-bottom:2px; }

/* min-width:0 overrides the flex/grid-item default of "never shrink below
   content's intrinsic width" — without it, a wide child (the Transactions
   table below) forces this column past its grid track and the whole page
   scrolls horizontally instead of the table scrolling within its own box. */
.split-detail { display:flex; flex-direction:column; gap:16px; min-width:0; }

/* Wraps any wide table.dt inside the detail column so it scrolls within
   its own box instead of forcing .split-detail (and the page) wider. */
.table-scroll { overflow-x:auto; }
.table-scroll table.dt { min-width:600px; }
.tbl-paginate-nav { display:flex; align-items:center; justify-content:center; gap:14px; padding:12px 0 4px; }
.tbl-paginate-nav .btn-inline:disabled { opacity:.4; cursor:not-allowed; }
.tbl-paginate-info { font-size:12.5px; color:var(--ink-500); font-weight:600; }
.detail-header { display:flex; align-items:flex-start; justify-content:space-between; background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-lg); padding:20px 24px; gap:16px; flex-wrap:wrap; }
.detail-header .identity { display:flex; align-items:center; gap:14px; }
.detail-header .name-row { display:flex; align-items:center; gap:10px; }
.detail-header h2 { margin:0; font-family:var(--font-display); font-size:18px; }
.detail-header .location { color:var(--ink-500); font-size:12.5px; margin-top:3px; }
.detail-header .head-actions { display:flex; gap:8px; }

.detail-info-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px 24px; }
.detail-info-grid dt { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--ink-500); margin-bottom:3px; }
.detail-info-grid dd { margin:0; font-size:13.5px; white-space:pre-line; }

/* §party-ledger — lightweight secondary-metadata strip (Purchase/
   Invoice Count, Last Transaction, Last Ledger Reviewed, Net Position,
   etc.) below the primary stat-grid KPI row on the Supplier and
   Customer detail panels. A plain .detail-info-grid card, deliberately
   not styled as heavy .stat-cards — keeping the KPI row to just its 3
   primary numbers avoids the uneven 4-then-3 wrapped layout a 6-7 card
   grid was producing. */
.party-meta-strip { background: var(--surface-0); border:1px solid var(--surface-200); border-radius: var(--radius-lg); padding: 14px 18px; margin: 0 0 16px; }
.party-meta-strip dd .pill { margin-left: 6px; vertical-align: middle; }

/* Customer/Invoice detail tabs — plain show/hide panels, no routing. */
.detail-tabs { display:flex; gap:4px; background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-lg); padding:6px; }
.detail-tab {
  flex:1; border:none; background:none; cursor:pointer; font-family:inherit; font-weight:700; font-size:12.5px;
  color:var(--ink-500); padding:9px 14px; border-radius:var(--radius-md);
  text-decoration:none; display:inline-flex; align-items:center; justify-content:center; text-align:center;
}
.detail-tab:hover { background:var(--surface-50); color:var(--ink-900); }
.detail-tab.active { background:var(--brand-100); color:var(--brand-700); }
.tab-panel { display:none; }
.tab-panel.active { display:block; }

/* Searchable select — progressive enhancement over a native <select>
   (see public/js/searchable-select.js). The select stays in the DOM,
   visually hidden, so forms/JS reading .value keep working unchanged;
   this only adds a styled trigger + filterable option panel in front. */
.ssel { position:relative; display:inline-block; }
.form-grid .field .ssel, .card-block .field .ssel { width:100%; }
.ssel select { position:absolute; inset:0; width:100%; height:100%; opacity:0; pointer-events:none; }
.ssel-trigger {
  display:flex; align-items:center; justify-content:space-between; gap:8px; min-width:160px;
  border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:8px 11px; font-size:13.5px; font-family:inherit; color:var(--ink-900); background:var(--surface-0);
  cursor:pointer; text-align:left;
}
.form-grid .field .ssel-trigger, .card-block .field .ssel-trigger { width:100%; }
.ssel-trigger:hover { border-color:var(--brand-600); }
.ssel-trigger.open { border-color:var(--brand-600); }
.ssel-trigger-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ssel-trigger-arrow { color:var(--ink-500); font-size:10px; flex-shrink:0; }
.ssel-panel {
  position:absolute; top:calc(100% + 4px); left:0; right:0; z-index:40; min-width:180px;
  background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-md);
  box-shadow:var(--shadow-shell); padding:8px; flex-direction:column;
  display:none;
}
/* [hidden] is an attribute selector, same specificity as a class — an
   unconditional `display:flex` above would beat the browser's default
   `[hidden]{display:none}` UA rule (author styles always win over UA
   styles), so the panel would render permanently open. Gating display
   behind :not([hidden]) is what actually lets JS's `panel.hidden = true`
   close it. */
.ssel-panel:not([hidden]) { display:flex; }
.ssel-search {
  width:100%; border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:7px 10px; font-size:13px; font-family:inherit; color:var(--ink-900); margin-bottom:6px;
}
.ssel-search:focus { outline:2px solid var(--brand-600); outline-offset:1px; }
.ssel-options { list-style:none; margin:0; padding:0; overflow-y:auto; max-height:220px; }
.ssel-options li[role="option"] { padding:8px 10px; border-radius:var(--radius-sm); cursor:pointer; font-size:13px; }
.ssel-options li[role="option"]:hover, .ssel-options li[role="option"]:focus { background:var(--surface-50); outline:none; }
.ssel-options li[role="option"].active { background:var(--brand-100); color:var(--brand-700); font-weight:700; }
.ssel-empty { padding:8px 10px; color:var(--ink-500); font-size:12.5px; }
.ssel-options li.ssel-create { color:var(--brand-700); font-weight:600; border-top:1px solid var(--surface-200); margin-top:4px; padding-top:10px; }
.ssel-options li.ssel-create:hover, .ssel-options li.ssel-create:focus { background:var(--brand-100); outline:none; }

/* Column-visibility picker (see public/js/column-picker.js) — same
   trigger + floating panel language as .ssel above, and the same
   [hidden]-must-win CSS shape (display:none by default, display:flex
   only via :not([hidden])) so the panel actually closes. */
.colpick { position:relative; display:inline-block; }
.colpick-panel {
  position:absolute; top:calc(100% + 4px); right:0; z-index:40; min-width:190px;
  background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-md);
  box-shadow:var(--shadow-shell); padding:8px; flex-direction:column; gap:1px;
  display:none;
}
.colpick-panel:not([hidden]) { display:flex; }
.colpick-option { display:flex; align-items:center; gap:8px; padding:7px 8px; border-radius:var(--radius-sm); font-size:13px; cursor:pointer; }
.colpick-option:hover { background:var(--surface-50); }
.colpick-option input[type="checkbox"] { accent-color:var(--brand-600); }
.colpick-empty { padding:7px 8px; color:var(--ink-500); font-size:12.5px; }

/* Date picker (see public/js/date-picker.js) — replaces Safari's native
   type="date" UI (which has no direct month/year jump, only single-step
   prev/next) with Month/Year <select>s + a day grid. Same trigger +
   floating panel language and [hidden]-must-win CSS shape as .ssel/
   .colpick above. */
.dp-wrapper { position:relative; display:inline-block; }
/* Full-width only inside a stacked form field — inside a flex-wrap
   toolbar (search/filters row) a 100%-wide item forces itself onto its
   own row and stretches edge-to-edge, which is the opposite of what a
   From/To pair should do; there it just sizes like the other filter
   controls via .dp-display's own min-width below. */
.form-grid .field .dp-wrapper { width:100%; }
.customers-toolbar .dp-wrapper { flex: 0 0 auto; }
.customers-toolbar .dp-wrapper .dp-display { min-width:120px; }
.date-range-group { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
.date-range-sep { color:var(--ink-500); font-size:12.5px; }
.dp-display {
  display:flex; align-items:center; min-width:150px;
  border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:8px 11px; font-size:13.5px; font-family:inherit; color:var(--ink-900); background:var(--surface-0);
  cursor:pointer; text-align:left;
}
.form-grid .field .dp-display { width:100%; }
.dp-display:hover { border-color:var(--brand-600); }
.dp-display.open { border-color:var(--brand-600); }
.dp-display.dp-empty { color:var(--ink-500); }
.dp-panel {
  position:absolute; top:calc(100% + 4px); left:0; z-index:40; width:260px;
  background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-md);
  box-shadow:var(--shadow-shell); padding:10px; flex-direction:column; gap:8px;
  display:none;
}
.dp-panel:not([hidden]) { display:flex; }
.dp-header { display:flex; align-items:center; gap:4px; }
.dp-nav {
  border:1px solid var(--surface-200); border-radius:var(--radius-sm); background:var(--surface-0);
  width:26px; height:26px; flex-shrink:0; cursor:pointer; font-size:14px; color:var(--ink-700);
}
.dp-nav:hover { border-color:var(--brand-600); color:var(--brand-700); }
.dp-month-select, .dp-year-select {
  flex:1; border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:5px 6px; font-size:12.5px; font-family:inherit; color:var(--ink-900); background:var(--surface-0);
}
.dp-year-select { flex:0 0 84px; }
.dp-grid { display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
.dp-day-label { text-align:center; font-size:11px; color:var(--ink-500); padding:4px 0; }
.dp-day {
  border:none; background:none; border-radius:var(--radius-sm); padding:6px 0;
  font-size:12.5px; color:var(--ink-900); cursor:pointer; text-align:center;
}
.dp-day:hover { background:var(--surface-50); }
.dp-day.today { font-weight:700; color:var(--brand-700); }
.dp-day.active { background:var(--brand-600); color:#fff; }
.dp-footer { display:flex; gap:6px; border-top:1px solid var(--surface-100); padding-top:8px; }

/* Month picker (public/js/month-picker.js) — same trigger + floating
   panel language as .dp-* above, just a 12-month grid with a Year
   prev/next header instead of a day grid with Month/Year selects. */
.mp-wrapper { position:relative; display:inline-block; }
.mp-display {
  display:flex; align-items:center; min-width:150px;
  border:1px solid var(--surface-200); border-radius:var(--radius-sm);
  padding:8px 11px; font-size:13.5px; font-family:inherit; color:var(--ink-900); background:var(--surface-0);
  cursor:pointer; text-align:left;
}
.mp-display:hover { border-color:var(--brand-600); }
.mp-display.open { border-color:var(--brand-600); }
.mp-display.mp-empty { color:var(--ink-500); }
.mp-panel {
  position:absolute; top:calc(100% + 4px); left:0; z-index:40; width:220px;
  background:var(--surface-0); border:1px solid var(--surface-200); border-radius:var(--radius-md);
  box-shadow:var(--shadow-shell); padding:10px; flex-direction:column; gap:8px;
  display:none;
}
.mp-panel:not([hidden]) { display:flex; }
.mp-header { display:flex; align-items:center; justify-content:space-between; gap:4px; }
.mp-nav {
  border:1px solid var(--surface-200); border-radius:var(--radius-sm); background:var(--surface-0);
  width:26px; height:26px; flex-shrink:0; cursor:pointer; font-size:14px; color:var(--ink-700);
}
.mp-nav:hover { border-color:var(--brand-600); color:var(--brand-700); }
.mp-year-label { font-weight:700; font-size:13.5px; color:var(--ink-900); }
.mp-grid { display:grid; grid-template-columns:repeat(4, 1fr); gap:4px; }
.mp-month {
  border:none; background:none; border-radius:var(--radius-sm); padding:8px 0;
  font-size:12.5px; color:var(--ink-900); cursor:pointer; text-align:center;
}
.mp-month:hover { background:var(--surface-50); }
.mp-month.current { font-weight:700; color:var(--brand-700); }
.mp-month.active { background:var(--brand-600); color:#fff; }
.mp-footer { display:flex; gap:6px; border-top:1px solid var(--surface-100); padding-top:8px; }

/* Add/Edit Customer modal — native <dialog> for free focus-trap/Esc-to-
   close/backdrop, styled to match the card language elsewhere. */
dialog.modal {
  border:none; border-radius:var(--radius-lg); padding:0; width:min(640px, 92%);
  box-shadow:var(--shadow-shell); background:var(--surface-0); color:var(--ink-900);
}
/* §purchase-edit-popup — wider variant for forms with a multi-column
   line-items table (Purchases, Supplier Payments, Quick Invoice, AI
   Invoice review) that don't fit the default 640px modal width.
   max-height is raised close to the viewport (not just wider than the
   base .modal-body's 65vh) because the same wide, multi-column
   line-items table also needs real vertical room — squeezed into a
   short box it forced a second, cramped vertical scrollbar on top of
   the table's own horizontal one from .table-scroll.

   §vw-scrollbar-gutter-bug — width was previously capped with `92vw`/
   `95vw`. `vw` is defined relative to the initial containing block's
   width, and on a classic (non-overlay) scrollbar — the macOS default
   the instant a mouse instead of a trackpad is used, and the Windows
   default everywhere — that ICB width includes the ~15-17px scrollbar
   gutter, while the *visible* viewport does not. The dialog (a
   position:fixed top-layer box) ends up sized a scrollbar's-width wider
   than what's actually visible, which is exactly what shows up as an
   outer horizontal scrollbar on the whole page while the dialog is
   open. `%` doesn't have this gap — percentage width on a
   position:fixed box resolves against the containing block the CSS
   Position spec defines for it, which correctly excludes the gutter. */
dialog.modal.modal-lg { width:min(1200px, 95%); }
dialog.modal.modal-lg .modal-body { max-height:88vh; }
dialog.modal::backdrop { background: rgba(24,20,40,.45); backdrop-filter: blur(2px); }

/* §non-blocking-source-preview — AI Invoice review's "View source"
   dialog is opened via .show(), not .showModal() (see _review.blade.php),
   specifically so it never blocks interaction with the invoice form
   behind it. A non-modal dialog gets no ::backdrop and no top-layer
   promotion, but it still inherits dialog.modal's default UA-centered
   position — which would visually sit on top of the form even though
   it's no longer blocking it. Docked to a side panel instead, so the
   source (photo/PDF/voice note + transcript) and the editable invoice
   fields are genuinely visible and usable at the same time.
   §source-panel-fixed-trimming — a real live production report:
   position:fixed here was cutting the panel off (viewport-relative
   fixed positioning nested inside a top-layer dialog is exactly the
   kind of thing real browsers — mobile Safari especially — get wrong).
   position:absolute instead: its containing block is #ai-review-modal
   itself (native <dialog> elements are UA-styled position:absolute,
   so the outer review dialog already qualifies as one — no extra CSS
   needed to make it a valid anchor), so top/right below are now
   relative to the review dialog's own box rather than the viewport.
   Drag-to-move and the resize handle are known not to work correctly
   under this change (the drag JS still does viewport-relative math) —
   left as-is for now, not fixed in this pass.
   §source-panel-always-visible-bug — a real live regression: `display`
   here was set unconditionally, and an author-stylesheet rule always
   wins over the UA stylesheet's own `dialog:not([open]) { display:none }`
   regardless of specificity — so the panel rendered visible at all
   times regardless of its actual open state, .close() removed the
   `open` attribute but nothing visually changed, and every fresh
   dialog (even before "View source" was ever clicked) showed up
   immediately. Scoped to [open] so the UA's own open/close visibility
   toggling is only overridden once the dialog is actually open. */
dialog.modal.source-panel[open] {
  position:absolute; top:90px; right:24px; left:auto; bottom:auto; margin:0;
  width:min(420px, 92vw); height:min(520px, calc(100vh - 120px));
  min-width:300px; min-height:220px; max-width:92vw; max-height:calc(100vh - 40px);
  resize:both; overflow:auto;
  display:flex; flex-direction:column;
}
dialog.modal.source-panel .modal-header { cursor:move; flex:0 0 auto; }
dialog.modal.source-panel .modal-body { flex:1 1 auto; max-height:none; overflow-y:auto; }

/* §source-panel-reserved-space — a real live report: once the review
   popup's own body was scrolled down, the fixed-position source panel
   sat ON TOP of the Line Items table's right-hand columns (Price,
   Total), permanently hiding them behind the panel rather than merely
   sitting beside the form. Reserves the panel's own width as padding on
   the form's content area instead, so the table (already wrapped in
   .table-scroll) reflows/scrolls within its own narrower box — nothing
   ever renders underneath the panel. :has() keys off the dialog's own
   `open` attribute, which the browser keeps in sync with .show()/
   .close() — no JS bookkeeping needed.
   §source-panel-body-id-bug — a real live regression: this used to
   target `.modal-body` by class, which ALSO matched the source panel's
   own internal `.modal-body` (itself a descendant of #ai-review-modal
   once portalled), squeezing its content down to near-zero width —
   audio controls collapsed, images stopped rendering. Scoped to the
   outer review body's own unique id instead, which the source panel's
   internal body never carries. */
#ai-review-modal:has(> dialog.source-panel[open]) #ai-review-modal-body {
  padding-right: 460px;
}
.modal-header { display:flex; align-items:center; justify-content:space-between; padding:18px 24px; border-bottom:1px solid var(--surface-200); }
.modal-header h2 { margin:0; font-family:var(--font-display); font-size:16px; }
.modal-close { border:none; background:none; font-size:20px; color:var(--ink-500); cursor:pointer; line-height:1; padding:4px; }
.modal-close:hover { color:var(--ink-900); }
.modal-body { padding:22px 24px; max-height:65vh; overflow-y:auto; }
.modal-footer { display:flex; justify-content:flex-end; gap:10px; padding:16px 24px; border-top:1px solid var(--surface-200); }

/* §delivery-actions-picker — a checklist of claimable line items (one
   invoice can have several), each with its own editable quantity, so
   Delivery Refused/Pending/Credit Note can be applied to more than one
   line in a single confirm instead of forcing the modal to be reopened
   per line. */
.delivery-picker-items-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
.delivery-picker-select-all { display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:600; color:var(--ink-700); }
.delivery-picker-qty-head { font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.03em; color:var(--ink-500); }
.delivery-picker-items { display:flex; flex-direction:column; gap:6px; max-height:240px; overflow-y:auto; border:1px solid var(--surface-200); border-radius:var(--radius-md); padding:8px; }
.delivery-picker-item-row { display:flex; align-items:center; gap:10px; padding:6px 4px; border-radius:var(--radius-sm); }
.delivery-picker-item-row:hover { background:var(--surface-50); }
.delivery-picker-item-row .dpi-name { flex:1 1 auto; font-size:13px; color:var(--ink-900); }
.delivery-picker-item-row .dpi-qty { width:90px; flex:0 0 auto; }
.delivery-picker-item-row .dpi-qty:disabled { opacity:.5; }
.delivery-picker-settlement-options { display:flex; gap:14px; flex-wrap:wrap; }
.delivery-picker-settlement-options label { display:flex; align-items:center; gap:5px; font-size:13px; font-weight:500; color:var(--ink-700); }

/* §global-tooltip — dark rounded bubble, portalled to body by
   tooltip.js (position:fixed) so it's never clipped by an
   overflow:hidden ancestor. Any element with data-tooltip="..." gets
   this automatically once initGlobalTooltips() runs. */
.gt-tooltip {
  position: fixed;
  z-index: 3000;
  pointer-events: none;
  background: var(--ink-900);
  color: #fff;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.4;
  padding: 6px 11px;
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  max-width: 240px;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity .12s ease, transform .12s ease;
}
.gt-tooltip.visible { opacity: 1; transform: translateY(0); }
.gt-tooltip::after {
  content: '';
  position: absolute;
  left: var(--gt-arrow-x, 50%);
  margin-left: -5px;
  width: 10px; height: 10px;
  background: var(--ink-900);
  transform: rotate(45deg);
  border-radius: 2px;
}
.gt-tooltip[data-placement="top"]::after { bottom: -4px; }
.gt-tooltip[data-placement="bottom"]::after { top: -4px; }

