/* Split from app2.css purely to stay under ~64KB per file — php -S's
   built-in static-file serving on this machine truncates any response
   past that size and resets the connection (see app2.css's own docblock
   on app.css, the first time this was hit) — app2.css itself grew past
   that same limit again since. Loaded directly after app2.css's own
   <link> tag and before app3.css's, so this is a straight continuation
   of app2.css in the middle of the split chain, not a separate concern;
   order matters (later rules can depend on earlier ones), so this tag
   must stay exactly where app2.css's layout.php <link> puts it. */

/* Rich pickers (leads-grid.js's buildRichStatusSelect()/
   buildRichSourceSelect()/buildRichUserSelect()) — colored popovers
   replacing the Status/Source/Assigned To cells' own plain <select>s,
   since each already has its own color (badge class, or a deterministic
   Core\AvatarColor hash) a native dropdown can't show at all. Each
   trigger is the same compact height as .cell-display's own content, so
   editing one of these cells never stretches the row the way a taller
   control would.

   The popover itself is portaled onto document.body the instant it
   opens (see openRichPopover()) rather than living inside the table
   cell — position:fixed here, coordinates set inline in viewport units
   by that same function, is what makes that possible; a table row
   otherwise reliably clips or paints over an absolutely-positioned
   dropdown trying to escape it. z-index only matters relative to
   whatever else sits directly on <body> (nothing normally does), so a
   modest value is enough once it's portaled. */
.rich-status-select { position: relative; display: inline-flex; align-items: center; gap: 4px; cursor: pointer; outline: none; }
.rich-status-select .rich-status-trigger,
.rich-status-select .rich-source-trigger,
.rich-status-select .rich-user-trigger { animation: none; }
.rich-status-select:focus .rich-status-trigger,
.rich-status-select.open .rich-status-trigger { box-shadow: 0 0 0 3px rgba(14, 164, 114, 0.28); }
.rich-status-chevron { font-size: 0.7rem; color: var(--text-muted); transition: transform 0.15s var(--ease); flex-shrink: 0; }
.rich-status-select.open .rich-status-chevron { transform: rotate(180deg); }
.rich-status-popover {
  position: fixed; z-index: 500;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius, 12px);
  box-shadow: var(--shadow-lg); padding: 10px; display: grid;
  grid-template-columns: repeat(2, minmax(112px, 1fr)); gap: 6px;
  width: max-content; max-width: 380px; max-height: 60vh; overflow-y: auto;
  animation: fadeInUp 0.15s var(--ease);
}
.rich-status-option {
  font: inherit; text-align: left; cursor: pointer; border: 1.5px solid transparent;
  transition: border-color 0.12s var(--ease), transform 0.12s var(--ease);
}
.rich-status-option:hover { border-color: currentColor; transform: translateY(-1px); }
.rich-status-option.selected { box-shadow: 0 0 0 2px currentColor inset; }

/* Source/Assigned To pickers — a single column (unlike Status's 2-col
   grid: names here vary a lot more in length, and there's no fixed set
   of colors to arrange evenly), each option a colored dot (Source) or
   the same .assignee-avatar circle (Assigned To) used everywhere else
   in this app, plus the name as plain text. */
.rich-status-popover.rich-source-popover,
.rich-status-popover.rich-user-popover { display: flex; flex-direction: column; grid-template-columns: none; min-width: 190px; max-width: 260px; }
.rich-source-trigger, .rich-user-trigger {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px 2px 2px; border-radius: 999px;
  background: #eef2f0; font-size: 0.82rem; font-weight: 600; color: var(--text);
}
.rich-source-option, .rich-user-option {
  display: flex; align-items: center; gap: 8px; text-align: left; font: inherit; font-size: 0.85rem;
  padding: 7px 10px; border: 1.5px solid transparent; border-radius: var(--radius-sm); cursor: pointer;
  background: none; color: var(--text); transition: background 0.12s var(--ease), border-color 0.12s var(--ease);
}
.rich-source-option:hover, .rich-user-option:hover { background: rgba(14, 164, 114, 0.1); }
.rich-source-option.selected, .rich-user-option.selected { border-color: var(--brand); background: var(--brand-light); font-weight: 700; }
.rich-source-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; background: var(--brand); }
.rich-source-dot-none { background: #cbd5e1; }
.rich-user-select .assignee-avatar, .rich-user-select .assignee-unassigned { margin-right: 0; }

/* Dashboard hero + stat icons */
.dash-hero {
  background: var(--brand-gradient);
  background-size: 180% 180%;
  animation: gradientDrift 10s ease infinite, fadeInUp 0.35s var(--ease);
  border-radius: var(--radius);
  padding: 28px 30px;
  margin-bottom: 22px;
  color: #fff;
  box-shadow: var(--shadow);
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.dash-hero::after {
  content: "";
  position: absolute; top: -60px; right: -60px; width: 220px; height: 220px;
  background: radial-gradient(circle, rgba(255,255,255,0.18), transparent 70%);
  border-radius: 999px;
}
.dash-hero-text { position: relative; z-index: 1; }
.dash-hero-text h2 { margin: 0 0 6px; font-size: 1.5rem; }
.dash-hero-text p { margin: 0; opacity: 0.92; font-size: 0.92rem; }

/* Live clock card — glassmorphism panel sitting on the hero gradient,
   ticked every second by assets/js/dashboard-clock.js. */
.dash-hero-clock {
  position: relative; z-index: 1;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.28);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 16px;
  padding: 14px 22px;
  text-align: right;
  flex-shrink: 0;
}
.dash-hero-clock-time {
  font-size: 1.9rem; font-weight: 800; letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums; line-height: 1.1;
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;
}
.dash-hero-clock-ampm { font-size: 0.85rem; font-weight: 700; opacity: 0.8; margin-left: 4px; letter-spacing: 0.04em; }
.dash-hero-clock-meta {
  margin-top: 4px; font-size: 0.78rem; opacity: 0.88;
  display: flex; align-items: center; justify-content: flex-end; gap: 6px;
}
.dash-hero-clock-dot {
  width: 7px; height: 7px; border-radius: 999px; background: #4ade80;
  box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.6);
  animation: dashClockPulse 1.8s ease infinite;
  flex-shrink: 0;
}
@keyframes dashClockPulse {
  0% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.55); }
  70% { box-shadow: 0 0 0 6px rgba(74, 222, 128, 0); }
  100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0); }
}
@media (max-width: 640px) {
  .dash-hero { flex-direction: column; align-items: flex-start; }
  .dash-hero-clock { text-align: left; align-self: stretch; }
  .dash-hero-clock-meta { justify-content: flex-start; }
}

/* One universal translucent-white circle, not a per-accent pastel chip —
   now that .stat-card itself is the solid color (app.css's own base
   rule), a plain white-on-white-ish badge reads correctly against any of
   them, so there's no need for the old per-position/per-accent
   background swap this used to need. */
.stat-card-icon {
  width: 42px; height: 42px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.22); color: #fff;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 12px;
  box-shadow: 0 4px 10px -2px rgba(0, 0, 0, 0.15);
}

/* Reports index — each card carries its own deliberate --report-color/
   --report-bg (Core\Reports\ReportPresentation), not the nth-child cycle
   above, since the point is "this color always means this report", not
   "these four colors just rotate through whatever's next in the grid".
   The extra .stat-grid.reports-grid compound is what actually wins over
   the nth-child rules' specificity — a plain .report-card::before would
   still lose to them despite coming later in the file. */
.reports-group-title { margin: 28px 0 12px; font-size: 1rem; color: var(--text-muted); }
.reports-group-title:first-of-type { margin-top: 0; }
.reports-grid { margin-bottom: 8px; }
/* Explicit position/size, not inherited from .stat-card::before's own
   full-cover darkening veil (app.css) — this is a genuine thin top accent
   bar, the one piece of the old pre-redesign .stat-card look this picker
   tile keeps on purpose (see .report-card's own docblock just below for
   why it opts out of the rest). Without its own geometry here it would
   otherwise inherit the veil's inset:0 and paint as a solid block over
   the whole card instead of a 3px strip. */
.stat-grid.reports-grid .stat-card.report-card::before {
  content: ""; background: var(--report-color);
  position: absolute; inset: auto 0 auto 0; top: 0; height: 3px;
  z-index: 0;
}
/* .report-card also carries the shared .stat-card class (for the same
   grid/spacing/hover-lift plumbing) but is a report PICKER tile, not a
   KPI number — it needs its own per-report --report-color/--report-bg
   identity untouched by .stat-card's own new solid-gradient/white-text
   redesign (see that class's base rule), so this resets background/color
   straight back to the plain white-card look every report-card's own
   title/desc/link colors were actually designed against.
   Real bug, reported live: a plain .report-card selector (1 class) has
   LOWER specificity than .stat-grid .stat-card:nth-child(4n+2/3/4) in
   app.css (2 classes + a pseudo-class), so the position-based color
   cycle was winning for 3 of every 4 cards regardless of this rule —
   exactly the "same colors repeating, dark text unreadable on a bright
   card" pattern reported. Same compound selector this rule's own ::before
   neighbor above already had to use for the identical reason. */
.stat-grid.reports-grid .stat-card.report-card {
  display: block; text-decoration: none; color: inherit;
  background: var(--surface); box-shadow: var(--shadow-sm);
  text-shadow: none;
}
.stat-grid.reports-grid .stat-card.report-card:hover { box-shadow: var(--shadow); }
.report-card-icon { background: var(--report-bg); color: var(--report-color); }
.report-card-title { font-weight: 700; font-size: 0.98rem; color: var(--text); }
.report-card-desc { color: var(--text-muted); font-size: 0.82rem; margin: 6px 0 12px; line-height: 1.45; }
.report-card-link { display: inline-flex; align-items: center; gap: 5px; font-size: 0.82rem; font-weight: 700; color: var(--report-color); }
.report-card:hover .report-card-link svg { transform: translateX(2px); }
.report-card-link svg { transition: transform 0.15s var(--ease); }

/* Report detail page — header/note/table all pick up --report-color from
   the same Core\Reports\ReportPresentation mapping the index cards use,
   so a report looks like the same thing whether you're looking at its
   tile or its own page. */
.report-header-title { display: flex; align-items: center; gap: 12px; }
.report-header-title .report-card-icon { width: 42px; height: 42px; border-radius: 11px; }
.report-header-title h2 { margin: 0; }
.report-note { border-left: 4px solid var(--report-color); padding: 14px 18px; }
.report-note p { margin: 0; color: var(--text-muted); }
.report-table-card { padding: 0; overflow: hidden; }
.report-table-card > div { padding: 4px 20px 20px; }
.report-table { width: 100%; }
.report-table thead th {
  background: var(--report-bg, #fafcfb);
  color: var(--report-color, var(--text-muted));
  border-bottom: 2px solid var(--report-color, var(--border));
}
.report-table tbody tr:nth-child(even) { background: #fafcfb; }
.report-table tbody tr:hover { background: var(--brand-light); }
.report-row-count { color: var(--text-muted); font-size: 0.82rem; margin: 14px 20px 20px; }
.report-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 48px 20px; color: var(--text-muted); text-align: center; }
.report-empty svg { color: var(--border); }
.report-empty p { margin: 0; font-size: 0.9rem; }

/* Payments Outstanding report's own row template (core/views/partials/
   _payments_outstanding_table.php) — Name and Paid Amount render as
   plain-looking buttons so they read as clickable without competing with
   the invoice-number/status text around them. */
.payments-outstanding-table td { vertical-align: middle; }
.po-name-link, .po-paid-link {
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--report-color, var(--brand-dark)); font-weight: 700; text-decoration: none;
  border-bottom: 1px dashed currentColor;
}
.po-name-link:hover, .po-paid-link:hover { text-decoration: none; opacity: 0.75; }
.po-balance-cell { font-weight: 800; color: var(--danger); }
/* .po-name-link/.po-paid-link set their own `color` directly (see just
   above), which wins over an ancestor .po-balance-cell's inherited color
   regardless of specificity — this reasserts red on the button/link
   itself for every Balance cell that renders as one (most of them). */
.po-balance-cell .po-paid-link, .po-balance-cell.po-paid-link { color: var(--danger); }

/* Payments Pending by Agent's own invoice-auto-matched hint (see
   _payments_pending_by_agent_table.php and PaymentsPendingByAgentReport::
   ORPHAN_INSTALL_INVOICE_SUBQUERY) — small enough to sit inline next to
   the invoice number without pushing the column wider. */
.ppa-auto-matched { cursor: help; opacity: 0.7; font-size: 0.85em; }

/* Payments Pending by Agent/Customer/Invoice's own mode-picker modal
   (core/views/partials/_payments_pending_view_picker.php) — three big
   colorful cards, same family as .sales-lost-reason-pill above but
   roomier since each one carries an icon + a description line, not just
   a one-word pill. */
.ppa-picker-dialog { max-width: 640px; }
.ppa-picker-icon {
  width: 46px; height: 46px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--brand-light); color: var(--brand-dark); margin-bottom: 12px;
}
.ppa-picker-dialog h3 { margin: 0 0 4px; }
.ppa-picker-sub { color: var(--text-muted); font-size: 0.86rem; margin: 0 0 20px; }
.ppa-picker-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
@media (max-width: 640px) { .ppa-picker-grid { grid-template-columns: 1fr; } }
.ppa-picker-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px; text-align: left;
  padding: 16px; border: 1.5px solid var(--border); border-radius: var(--radius); background: var(--surface);
  cursor: pointer; transition: border-color 0.16s var(--ease), box-shadow 0.16s var(--ease), transform 0.16s var(--ease);
}
.ppa-picker-card:hover { border-color: var(--brand); box-shadow: var(--shadow); transform: translateY(-2px); }
.ppa-picker-card-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.ppa-picker-card-title { font-weight: 700; font-size: 0.95rem; color: var(--text); }
.ppa-picker-card-desc { font-size: 0.8rem; color: var(--text-muted); line-height: 1.4; }

/* Payments Pending by Agent/Customer/Invoice's own grouped view
   (_payments_pending_by_agent_table.php) — one colorful section header
   per bucket (an agent, a customer, or — view_mode 'invoice' — a single
   invoice), its own subtotal chip riding on the same --report-color this
   report already themes everything else with. */
.ppa-group { margin-bottom: 22px; }
.ppa-group:last-child { margin-bottom: 0; }
.ppa-group-header {
  display: flex; align-items: center; justify-content: flex-start; gap: 12px 22px; flex-wrap: wrap;
  padding: 10px 16px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--report-bg, var(--brand-light)); border: 1px solid var(--report-color, var(--border)); border-bottom: none;
}
.ppa-group-title { font-weight: 800; font-size: 0.95rem; color: var(--report-color, var(--brand-dark)); }
/* Invoice view's own ribbon title is a button (straight to the same
   preview modal as its Invoice # cell below) whenever there's an actual
   invoice to preview — same dashed-underline affordance as .po-name-link,
   just inheriting this ribbon's own bigger bold size instead of its own. */
.ppa-group-title-link { background: none; border: none; padding: 0; font: inherit; cursor: pointer; border-bottom: 1.5px dashed currentColor; }
.ppa-group-title-link:hover { opacity: 0.75; }
.ppa-group-count { font-weight: 600; font-size: 0.78rem; color: var(--text-muted); margin-left: 8px; }
.ppa-group-subtotal { font-weight: 800; font-size: 0.95rem; color: var(--report-color, var(--brand-dark)); }
.ppa-group-table-wrap { border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius-sm) var(--radius-sm); overflow-x: auto; }
.ppa-group-table-wrap .report-table { margin: 0; }

/* Payments Pending by Agent/Customer/Invoice's own always-visible lens
   switcher (modules/reports/views/show.php, scoped to this report key) —
   three pill tabs above the filter bar, present regardless of which
   view_mode is currently active, so switching never means hunting for a
   dropdown. Same --report-color theming as everything else on this page. */
.ppa-view-tabs { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.ppa-view-tab {
  display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: 999px;
  border: 1.5px solid var(--border); background: var(--surface); color: var(--text); font-weight: 700; font-size: 0.88rem;
  text-decoration: none; transition: border-color 0.16s var(--ease), box-shadow 0.16s var(--ease), transform 0.16s var(--ease), background 0.16s var(--ease), color 0.16s var(--ease);
}
.ppa-view-tab:hover { text-decoration: none; border-color: var(--report-color, var(--brand)); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.ppa-view-tab.active {
  background: var(--report-color, var(--brand)); border-color: var(--report-color, var(--brand)); color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--report-color, var(--brand)) 40%, transparent);
}

/* Overdue-row highlight — universal across all three lenses' own mini
   tables (ppa_render_table() sets class="is-overdue" per row wherever
   overdue_days > 0, Agent/Customer/Invoice alike), not just Invoice —
   the same at-a-glance urgency signal everywhere this report shows a
   balance, and a bolder colored Balance figure to match. */
.payments-pending-by-agent-table tbody tr.is-overdue { background: #fef2f2; box-shadow: inset 3px 0 0 var(--danger); }
.payments-pending-by-agent-table .ppa-balance-cell { font-weight: 800; color: var(--report-color, var(--brand-dark)); }
.payments-pending-by-agent-table tbody tr.is-overdue .ppa-balance-cell { color: var(--danger); }

/* Payments Made's own Amount column — same bold-emphasis treatment as
   Payments Pending's own Balance column above, just on a different table
   class/column (see core/views/partials/_payments_made_table.php). */
.payments-made-table .ppa-balance-cell { font-weight: 800; color: var(--report-color, var(--brand-dark)); }

/* Payments Made's own "All companies" grouping — one colored section per
   company (see _payments_made_table.php's own docblock), the header bar
   itself filled solid with that company's color (Core\CompanyScope::
   colorMap() — same palette Inventory's own company pills use) so each
   company reads as a genuinely distinct block, not just a differently-
   colored ribbon title inside an otherwise-identical section. Every
   .ppa-group ribbon nested inside picks up that same color automatically
   via --report-color set on the wrapping div just below the header. */
.pm-company-section { margin-bottom: 30px; }
.pm-company-section:last-child { margin-bottom: 0; }
.pm-company-header {
  display: flex; align-items: center; justify-content: flex-start; gap: 12px 22px; flex-wrap: wrap;
  padding: 12px 18px; border-radius: var(--radius-sm); margin-bottom: 14px;
  background: var(--pm-company-color, var(--brand)); color: #fff;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--pm-company-color, var(--brand)) 35%, transparent);
}
.pm-company-name { font-weight: 800; font-size: 1.02rem; }
.pm-company-meta { font-weight: 600; font-size: 0.85rem; opacity: 0.92; }

/* Invoice details / payment history modals (core/views/partials/
   _invoice_details_modal.php) — the details one reuses the wide dialog
   variant since it carries a full line-items table, the payments one
   stays at the plain default width. */
.invoice-details-dialog { max-height: 85vh; overflow-y: auto; }
.invoice-details-meta { margin: 16px 0 18px; }
.invoice-payments-dialog table { margin-top: 4px; }

/* App-wide confirm() replacement (assets/js/confirm-dialog.js) — a
   compact centered card, not the full-page slide-over .panel-modal
   pattern used elsewhere, since a confirmation is a quick yes/no, not a
   form to fill in. */
.confirm-dialog-backdrop {
  position: fixed; inset: 0; z-index: 400;
  background: rgba(6, 20, 15, 0.55); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none; transition: opacity 0.18s var(--ease);
}
.confirm-dialog-backdrop.open { opacity: 1; pointer-events: auto; }
.confirm-dialog {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); width: min(380px, calc(100% - 40px));
  padding: 28px 26px 22px; text-align: center;
  transform: scale(0.94) translateY(6px); transition: transform 0.2s var(--ease);
}
.confirm-dialog-backdrop.open .confirm-dialog { transform: scale(1) translateY(0); }
.confirm-dialog-icon {
  width: 52px; height: 52px; border-radius: 999px; margin: 0 auto 16px;
  display: flex; align-items: center; justify-content: center; color: #fff;
  box-shadow: 0 6px 16px rgba(8, 30, 22, 0.22);
}
.confirm-dialog-title { font-size: 1.1rem; font-weight: 800; letter-spacing: -0.01em; margin-bottom: 6px; }
.confirm-dialog-message { color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; margin-bottom: 22px; }
.confirm-dialog-type-check { display: none; text-align: left; margin: -8px 0 20px; }
.confirm-dialog-type-check label { display: block; font-size: 0.78rem; font-weight: 600; color: var(--text-muted); margin-bottom: 6px; }
.confirm-dialog-type-check label strong { color: var(--danger); font-family: monospace; letter-spacing: 0.03em; }
.confirm-dialog-type-input {
  width: 100%; padding: 9px 12px; border-radius: 10px; border: 1.5px solid var(--border);
  font-size: 0.95rem; text-align: center; letter-spacing: 0.06em; box-sizing: border-box;
}
.confirm-dialog-type-input:focus { outline: none; border-color: var(--danger); box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15); }
.confirm-dialog-actions { display: flex; gap: 10px; justify-content: center; }
.confirm-dialog-actions .btn { flex: 1; }
.confirm-dialog-actions .btn:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
.confirm-dialog-btn-warning { background: var(--accent-gradient); box-shadow: 0 2px 6px rgba(180, 83, 9, 0.3); }
.confirm-dialog-btn-warning:hover { box-shadow: 0 6px 16px rgba(180, 83, 9, 0.38); }

/* Duplicate-mobile lookup modal (assets/js/mobile-duplicate-check.js) —
   shares the same backdrop treatment as .confirm-dialog-backdrop (kept
   as its own class since this one's body is a data table + two
   navigation actions, not a yes/no prompt). */
.dupe-modal-backdrop {
  position: fixed; inset: 0; z-index: 400;
  background: rgba(6, 20, 15, 0.55); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none; transition: opacity 0.18s var(--ease);
}
.dupe-modal-backdrop.open { opacity: 1; pointer-events: auto; }
.dupe-modal {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); width: min(460px, calc(100% - 40px));
  padding: 26px 26px 22px;
  transform: scale(0.94) translateY(6px); transition: transform 0.2s var(--ease);
}
.dupe-modal-backdrop.open .dupe-modal { transform: scale(1) translateY(0); }
.dupe-modal-header { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.dupe-modal-icon {
  width: 44px; height: 44px; border-radius: 999px; flex-shrink: 0;
  background: var(--accent-gradient); color: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 16px rgba(180, 83, 9, 0.25);
}
.dupe-modal-title { font-size: 1.05rem; font-weight: 800; letter-spacing: -0.01em; }
.dupe-modal-subtitle { color: var(--text-muted); font-size: 0.85rem; margin: 4px 0 16px; }
.dupe-modal-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.dupe-modal-table td { padding: 9px 12px; font-size: 0.87rem; border-bottom: 1px solid var(--border); }
.dupe-modal-table tr:last-child td { border-bottom: none; }
.dupe-modal-table td:first-child { color: var(--text-muted); font-weight: 600; width: 40%; background: #fafcfb; }
.dupe-modal-actions { display: flex; gap: 10px; }
.dupe-modal-actions .btn { flex: 1; }

/* Outstanding-invoice heads-up (assets/js/invoice-duplicate-check.js) —
   same .dupe-modal shell as the lead one above, just its own icon color
   (blue reads as "here's some information", not amber's "heads up,
   careful" or the delete flow's red) so the two are told apart at a
   glance despite sharing a shape. */
.inv-dupe-modal-icon { background: linear-gradient(135deg, #60a5fa, var(--info)); box-shadow: 0 6px 16px rgba(37, 99, 235, 0.25); }
.dupe-modal-items-label { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 8px; }
.dupe-modal-items-wrap { max-height: 160px; overflow-y: auto; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: 20px; }
.dupe-modal-items-wrap .report-table { font-size: 0.82rem; }
.dupe-modal-items-wrap .report-table thead th { position: sticky; top: 0; }

.error-page { text-align: center; padding: 80px 20px; }
.error-page h1 { font-size: 3.5rem; margin-bottom: 0; background: var(--brand-gradient); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* Login page */
.auth-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, var(--brand-darker) 0%, var(--brand-dark) 45%, var(--brand-2) 100%);
  background-size: 200% 200%;
  animation: gradientDrift 12s ease infinite;
}
.auth-page::before, .auth-page::after {
  content: ""; position: absolute; border-radius: 999px; filter: blur(60px); opacity: 0.35; pointer-events: none;
}
.auth-page::before { width: 380px; height: 380px; background: var(--accent); top: -120px; right: -100px; }
.auth-page::after { width: 320px; height: 320px; background: #60a5fa; bottom: -100px; left: -80px; }
.auth-card {
  background: rgba(255, 255, 255, 0.97);
  width: 100%;
  max-width: 380px;
  padding: 38px 34px;
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  position: relative;
  z-index: 1;
  animation: fadeInUp 0.4s var(--ease);
}
.auth-card::before {
  content: "";
  display: block; width: 44px; height: 44px; border-radius: 12px;
  background: var(--accent-gradient); margin-bottom: 16px;
  box-shadow: 0 6px 16px rgba(245, 158, 11, 0.4);
}
.auth-card h1 { font-size: 1.4rem; margin: 0 0 4px; letter-spacing: -0.02em; }
.auth-card .subtitle { color: var(--text-muted); font-size: 0.88rem; margin-bottom: 26px; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 0.85rem; font-weight: 600; margin-bottom: 6px; }
.form-group input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.form-group input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(14, 164, 114, 0.14); }
.btn-primary {
  width: 100%;
  background: var(--brand-gradient);
  color: #fff;
  border: none;
  padding: 12px;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(6, 95, 70, 0.32);
  transition: transform 0.14s var(--ease), box-shadow 0.14s var(--ease), filter 0.14s var(--ease);
}
.btn-primary:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 8px 20px rgba(6, 95, 70, 0.4); }
.btn-primary:active { transform: translateY(0) scale(0.99); }
.auth-hint { margin-top: 18px; font-size: 0.78rem; color: var(--text-muted); text-align: center; }

/* Mobile column 3-dot action menu (leads grid). The cell itself is
   nowrap so the toggle can never wrap onto its own line below the
   number (which is what let it drift out of place once more columns
   made the table tight — see the .leads-grid width fix above, which
   fixes the room this causes in the first place; this is the
   belt-and-suspenders layer). */
.leads-grid td[data-col="mobile"] { white-space: nowrap; }
.mobile-actions { position: relative; display: inline-block; margin-left: 6px; }
.mobile-actions-toggle {
  border: none; background: none; cursor: pointer; color: var(--text-muted); font-size: 1rem; line-height: 1;
  padding: 2px 4px; border-radius: 4px; transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.mobile-actions-toggle:hover { background: var(--brand-light); color: var(--brand); }
.mobile-actions-menu {
  display: none; position: absolute; left: 0; top: calc(100% + 4px); z-index: 60;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg); min-width: 150px; padding: 6px; animation: fadeInUp 0.15s var(--ease);
}
.mobile-actions.open .mobile-actions-menu { display: block; }
/* When JS has portalled the open menu out to <body> (see leads-grid.js) it's
   no longer a descendant of .mobile-actions, so visibility switches to this
   class on the menu itself instead — the portal is what actually stops the
   menu being painted underneath the row below: .leads-grid tbody tr has a
   permanent (fill-mode: both) transform from its entrance animation, which
   makes every row its own stacking context/containing block, so a z-index on
   a menu still nested inside row N can never out-rank row N+1 painting after
   it — moving the menu out to <body> sidesteps that entirely rather than
   fighting it with a bigger z-index. */
.mobile-actions-menu.open { display: block; position: fixed; z-index: 1000; }
.mobile-actions-menu a, .mobile-actions-menu button {
  display: block; width: 100%; text-align: left; padding: 7px 10px; border-radius: 6px; border: none; background: none;
  font-size: 0.82rem; font-weight: 600; color: var(--text); cursor: pointer; white-space: nowrap;
  transition: background 0.12s var(--ease);
}
.mobile-actions-menu a:hover, .mobile-actions-menu button:hover { background: var(--brand-light); color: var(--brand-dark); text-decoration: none; }

/* Standalone WhatsApp side panel */
.wa-panel-backdrop {
  position: fixed; inset: 0; background: rgba(6, 20, 15, 0.5); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: opacity 0.25s var(--ease); z-index: 210;
}
.wa-panel-backdrop.open { opacity: 1; pointer-events: auto; }
.wa-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 100%; max-width: 440px;
  background: var(--surface); box-shadow: -12px 0 40px rgba(8, 30, 22, 0.22);
  transform: translateX(100%); transition: transform 0.28s var(--ease);
  z-index: 211; display: flex; flex-direction: column;
}
.wa-panel.open { transform: translateX(0); }
.wa-panel-content { position: relative; display: flex; flex-direction: column; height: 100%; min-height: 0; animation: fadeIn 0.25s var(--ease); }
.wa-panel-header {
  padding: 20px 44px 14px 20px; border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, var(--brand-light) 0%, rgba(255,255,255,0) 100%); flex-shrink: 0;
}
.wa-panel-header h3 { margin: 0; font-size: 1.15rem; }
.wa-panel-subline { display: flex; align-items: center; gap: 10px; margin-top: 4px; font-size: 0.85rem; color: var(--text-muted); }
.wa-panel-open-lead { font-weight: 600; }
@media (max-width: 480px) { .wa-panel { max-width: 100%; } }

/* Shared WhatsApp chat thread (side panel + Conversations tab) */
.wa-thread { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.wa-bubbles { flex: 1; min-height: 220px; max-height: 480px; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 10px; }
.wa-bubble { max-width: 78%; padding: 9px 12px; border-radius: 12px; font-size: 0.88rem; line-height: 1.4; }
.wa-bubble-outbound { align-self: flex-end; background: var(--brand-light); color: var(--brand-dark); border-bottom-right-radius: 3px; }
.wa-bubble-inbound { align-self: flex-start; background: #eef2f0; color: var(--text); border-bottom-left-radius: 3px; }
.wa-bubble-text { white-space: pre-wrap; word-break: break-word; }
.wa-bubble-template-tag { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; opacity: 0.65; margin-bottom: 3px; }
.wa-bubble-meta { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; opacity: 0.65; }
.wa-ticks { color: #64748b; }
.wa-ticks-read { color: #2563eb; }
.wa-ticks-failed { color: var(--danger); font-weight: 800; }

/* One attachment per bubble (image/video/PDF) — see sendWhatsApp()'s own
   docblock for why this app only ever sends one at a time. */
.wa-bubble-media-link { display: block; margin-bottom: 6px; }
.wa-bubble-media-img { max-width: 100%; max-height: 240px; border-radius: 8px; display: block; }
.wa-bubble-media-video { max-width: 100%; max-height: 240px; border-radius: 8px; display: block; margin-bottom: 6px; background: #000; }
.wa-bubble-media-file {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: 6px;
  background: rgba(255, 255, 255, 0.6); border-radius: 8px; color: inherit; text-decoration: none; font-size: 0.82rem; font-weight: 600;
}
.wa-bubble-media-file:hover { background: rgba(255, 255, 255, 0.9); }

.wa-composer { flex-shrink: 0; padding: 14px 20px 20px; border-top: 1px solid var(--border); }
.wa-template-quick { width: 100%; margin-bottom: 8px; font-size: 0.82rem; }
.wa-text-form-row { display: flex; gap: 8px; align-items: flex-end; }
.wa-text-form textarea { flex: 1; resize: none; }
.wa-attach-btn {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  color: var(--text-muted); cursor: pointer; border: 1.5px solid var(--border); transition: border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.wa-attach-btn:hover { border-color: var(--brand); color: var(--brand); }
.wa-attach-hint { font-size: 0.72rem; color: var(--text-muted); margin: 6px 0 0; }
.wa-attach-chip {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px; margin-bottom: 8px; width: fit-content; max-width: 100%;
  background: var(--brand-light); color: var(--brand-dark); border-radius: 999px; font-size: 0.78rem; font-weight: 600;
}
.wa-attach-chip[hidden] { display: none; }
.wa-attach-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.wa-attach-chip-remove { background: none; border: none; cursor: pointer; font-size: 1rem; line-height: 1; color: inherit; opacity: 0.7; padding: 0; }
.wa-attach-chip-remove:hover { opacity: 1; }


/* Business Performance report — company-comparison table (see
   core/views/partials/_business_performance_report.php). Compact cells
   (small font, tight padding) since this table carries more columns
   than any other report's own — still legible, just denser, matching
   the "scorecard" density this report is meant to read at a glance. The
   Combined row gets a light violet wash (this report's own accent,
   Core\Reports\ReportPresentation's 'business_performance' entry) so it
   reads as the summary row it is, not just one more company. */
.business-performance-table th, .business-performance-table td { font-size: 0.78rem; padding: 7px 8px; white-space: nowrap; }
.business-performance-table th { line-height: 1.25; }
.business-performance-combined-row td { background: #ede9fe; border-top: 2px solid #4c1d95; }

/* Create/Edit Quotation's "Payment QR Code" picker (see
   assets/js/payment-qr-picker.js, modules/quotation/views/form.php) —
   named tiles the agent picks from instead of re-uploading a QR every
   time, plus an inline "upload new" row. */
.payment-qr-grid { display: flex; flex-wrap: wrap; gap: 10px; min-height: 1.4em; margin-bottom: 10px; }
.payment-qr-hint { font-size: 0.82rem; color: var(--text-muted); margin: 0; }
.payment-qr-tile {
  position: relative; width: 84px; padding: 8px 6px 6px; border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); cursor: pointer; text-align: center;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), transform 0.15s var(--ease);
}
.payment-qr-tile:hover { border-color: var(--brand); transform: translateY(-1px); }
.payment-qr-tile.selected { border-color: var(--brand); background: var(--brand-light); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent); }
.payment-qr-tile img { width: 100%; height: 64px; object-fit: contain; border-radius: 4px; background: #fff; display: block; margin-bottom: 4px; }
.payment-qr-tile-name { display: block; font-size: 0.72rem; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.payment-qr-tile-remove {
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px; border-radius: 50%; border: none;
  background: var(--danger); color: #fff; font-size: 0.85rem; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s var(--ease);
}
.payment-qr-tile:hover .payment-qr-tile-remove { opacity: 1; }
.payment-qr-upload { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.payment-qr-upload input[type="text"] { flex: 1; min-width: 180px; }
.payment-qr-upload-btn { cursor: pointer; white-space: nowrap; }

/* ===== Product Master (modules/inventory/views/product-master/index.php)
   — hero banner, card-ified Add Product form, colored per-category
   sections (Enzyme green / Tank blue / anything else violet), and a
   richer per-product table (thumbnail, toggle switch, colored centered
   headers, unified "Specs" pill, icon delete). ===== */
.pm-hero {
  position: relative; overflow: hidden; display: flex; align-items: center; gap: 16px;
  background: linear-gradient(120deg, #eaf3ff 0%, #f5fbff 55%, #ffffff 100%);
  border: 1px solid #dbeafe; border-radius: var(--radius); padding: 20px 26px; margin-bottom: 18px;
}
.pm-hero-icon {
  flex-shrink: 0; width: 48px; height: 48px; border-radius: 14px;
  background: linear-gradient(135deg, #60a5fa, #2563eb); color: #fff;
  display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 10px -2px rgba(37, 99, 235, 0.4);
}
.pm-hero-text { flex: 1; min-width: 200px; }
.pm-hero-text h2 { margin: 0 0 4px; font-size: 1.35rem; color: #1e3a5f; }
.pm-hero-text p { margin: 0; color: var(--text-muted); font-size: 0.86rem; max-width: 560px; line-height: 1.45; }
.pm-hero-deco { position: absolute; right: 150px; top: -14px; color: #bfdbfe; opacity: 0.7; }
.pm-hero-tagline {
  flex-shrink: 0; margin: 0; text-align: right; font-style: italic; font-weight: 700;
  color: var(--brand-dark); font-size: 0.92rem; line-height: 1.35;
}
.pm-hero-back { flex-shrink: 0; }
@media (max-width: 900px) {
  .pm-hero { flex-wrap: wrap; }
  .pm-hero-deco { display: none; }
  .pm-hero-tagline { text-align: left; }
}

.pm-add-card { padding-top: 18px; }
.pm-add-card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.pm-add-card-icon {
  width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
  background: var(--brand-gradient); color: #fff; display: flex; align-items: center; justify-content: center;
}
.pm-add-card-head h3 { margin: 0; font-size: 1.02rem; }
.pm-add-photo-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 8px 14px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--surface); font-size: 0.85rem; font-weight: 600; color: var(--text);
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
  max-width: 170px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.pm-add-photo-btn:hover { border-color: var(--brand); background: var(--brand-light); }
.pm-add-submit-btn { display: inline-flex; align-items: center; gap: 6px; }

.pm-category-card { padding-top: 0; overflow: hidden; }
.pm-category-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 -18px 16px; padding: 16px 18px; border-bottom: 1px solid var(--border);
}
.pm-category-icon {
  width: 32px; height: 32px; border-radius: 10px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; color: #fff;
}
.pm-category-head h3 { margin: 0; font-size: 1.05rem; }
.pm-category-count {
  font-size: 0.74rem; font-weight: 700; padding: 3px 10px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
}
.pm-category-add { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }

.pm-category-head-green { background: linear-gradient(90deg, #ecfdf5, #f5fffb); }
.pm-category-head-green .pm-category-icon { background: var(--brand-gradient); }
.pm-category-head-green h3 { color: var(--brand-dark); }
.pm-category-head-green .pm-category-count { color: var(--brand-dark); }

.pm-category-head-blue { background: linear-gradient(90deg, #eff6ff, #f7fbff); }
.pm-category-head-blue .pm-category-icon { background: linear-gradient(135deg, #60a5fa, #2563eb); }
.pm-category-head-blue h3 { color: #1d4ed8; }
.pm-category-head-blue .pm-category-count { color: #1d4ed8; }

.pm-category-head-violet { background: linear-gradient(90deg, #f5f3ff, #faf8ff); }
.pm-category-head-violet .pm-category-icon { background: linear-gradient(135deg, #a78bfa, #7c3aed); }
.pm-category-head-violet h3 { color: #6d28d9; }
.pm-category-head-violet .pm-category-count { color: #6d28d9; }

/* Table — colored, centered headers (matching whichever theme this
   category's own .pm-category-head just above picked), a numbered #
   column, and a compact Actions column for the per-row delete button. */
.pm-table thead th { text-align: center; font-weight: 800; white-space: nowrap; }
.pm-table-green thead th { background: #ecfdf5; color: var(--brand-dark); }
.pm-table-blue thead th { background: #eff6ff; color: #1d4ed8; }
.pm-table-violet thead th { background: #f5f3ff; color: #6d28d9; }
.pm-table td { vertical-align: middle; text-align: center; }
.pm-table td input[type="text"], .pm-table td input[type="number"] { text-align: left; margin: 0 auto; }
.pm-col-num { width: 34px; color: var(--text-muted); font-weight: 700; }
.pm-col-actions { width: 110px; }
.pm-col-specs-value { max-width: 220px; font-size: 0.8rem; color: var(--text); white-space: normal; text-align: left; }
.pm-specs-empty { color: var(--text-muted); }

/* Active toggle switch — same on/off semantics as the plain checkbox it
   replaces (still name="is_active[...]" posted to the bulk Save All
   form), just styled as a track+thumb instead of a bare box. */
.pm-toggle { position: relative; display: inline-block; width: 38px; height: 21px; vertical-align: middle; }
.pm-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.pm-toggle-track {
  position: absolute; inset: 0; background: #d1d5db; border-radius: 999px; cursor: pointer;
  transition: background 0.18s var(--ease);
}
.pm-toggle-track::before {
  content: ""; position: absolute; left: 2px; top: 2px; width: 17px; height: 17px;
  background: #fff; border-radius: 50%; transition: transform 0.18s var(--ease); box-shadow: var(--shadow-sm);
}
.pm-toggle input:checked + .pm-toggle-track { background: var(--brand); }
.pm-toggle input:checked + .pm-toggle-track::before { transform: translateX(17px); }
.pm-toggle input:disabled + .pm-toggle-track { opacity: 0.5; cursor: not-allowed; }

/* Specs pill — unified label ("+ Add specs"/"Edit specs") AND unified
   look for both Tank Specs and Enzyme Details: same filled brand-tinted
   pill either way now (per explicit request — "+ Add specs" used to sit
   in a plain neutral-gray outline, reading as a lesser, less-clickable
   control next to "Edit specs"'s own colored one). pm-specs-pill-set is
   kept as a class (still added once a product actually has specs saved)
   purely so anything else that might key off it later still can, but it
   no longer carries any styling of its own — both states are visually
   identical now. */
.pm-specs-pill {
  display: inline-block; padding: 5px 12px; border-radius: 999px; font-size: 0.78rem; font-weight: 700;
  background: var(--brand-light); color: var(--brand-dark); border: 1px solid transparent; text-decoration: none;
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease);
}
.pm-specs-pill:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); text-decoration: none; }

.pm-row-delete {
  width: 28px; height: 28px; border-radius: 50%; border: none; cursor: pointer;
  background: var(--danger-light); color: var(--danger); display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.15s var(--ease), transform 0.15s var(--ease);
}
.pm-row-delete:hover { background: var(--danger); color: #fff; transform: scale(1.08); }

/* Actions column now carries both the specs-edit pill AND the delete
   button side by side (the pill used to sit in its own "Specs" column,
   which now shows the actual spec values as text instead — see
   pm_specs_summary() in the view). */
.pm-actions-cell { display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; }

/* Trendy, colored-border inputs across the whole Product Master page
   (Add Product form + every bulk-edit field in each category table) —
   scoped to .pm-page so it never leaks into other pages' plain inputs.
   Each category's own table tints its input borders to match its theme
   (green/blue/violet), same color language as its header/pills; the Add
   Product form and the Tank/Enzyme spec modals use the neutral violet
   default since they aren't inside a themed table. */
.pm-page input[type="text"],
.pm-page input[type="number"],
.pm-page input[type="date"],
.pm-page select {
  border: 2px solid #ddd6fe; border-radius: 8px; padding: 7px 10px; font-size: 0.85rem;
  color: var(--text); background: #fff;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.pm-page input[type="text"]:hover,
.pm-page input[type="number"]:hover,
.pm-page input[type="date"]:hover,
.pm-page select:hover { border-color: #a78bfa; }
.pm-page input[type="text"]:focus,
.pm-page input[type="number"]:focus,
.pm-page input[type="date"]:focus,
.pm-page select:focus { outline: none; border-color: #7c3aed; box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.15); }
.pm-page input[disabled] { background: var(--bg); border-color: var(--border); color: var(--text-muted); }
.pm-table-green td input[type="text"], .pm-table-green td input[type="number"] { border-color: #6ee7b7; }
.pm-table-green td input:hover { border-color: #34d399; }
.pm-table-green td input:focus { border-color: var(--brand-dark); box-shadow: 0 0 0 3px rgba(6, 95, 70, 0.14); }
.pm-table-blue td input[type="text"], .pm-table-blue td input[type="number"] { border-color: #93c5fd; }
.pm-table-blue td input:hover { border-color: #60a5fa; }
.pm-table-blue td input:focus { border-color: #1d4ed8; box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.14); }

/* New/Edit Stock Item — corporate 3-section redesign (Basic Details/
   Purchase and Pricing/Uploads, modules/inventory/views/form.php's own
   .inv-create-page) — narrower, colored-border inputs instead of the
   plain full-width ones .form-grid gives every OTHER page in the app
   (scoped to .inv-create-page so those stay untouched), each of the 3
   sections its own tinted panel (not just a colored header icon) so
   the eye reads Basic Details/Purchase and Pricing/Uploads as three
   distinct zones, per explicit request. :not(:disabled) on the border/
   shadow rules so this never fights the existing "look calculated" pill
   styling .calc-result-field input:disabled already gives the Enzyme
   Per Kg/Gm Cost readouts, or the plain grey read-only look Company/GST
   Number already have once locked/auto-filled. Border colors are set
   per-panel further down, not here — .inv-panel-blue/-violet/-green
   each override border-color for their own inputs. */
.inv-create-page .inv-section {
  border-top: none; margin-top: 16px; padding: 18px 22px; border-radius: 14px;
}
.inv-create-page .inv-section:first-of-type { margin-top: 0; }
/* #inventory-form-card (an id, so this wins outright over app3.css's own
   .inv-section:first-of-type { padding-top: 0; } — that rule and this
   page's own plain-class .inv-section { padding: 18px 22px; } above tie
   in specificity, and app3.css happens to load AFTER this file, so
   without the id bump here "Basic Details" rendered flush against the
   card's own top accent bar instead of leaving the same 18px every
   other section already gets). */
#inventory-form-card .inv-section:first-of-type { padding-top: 18px; }
.inv-create-page .inv-panel-blue { background: linear-gradient(180deg, #eff6ff 0%, #fff 65%); border: 1px solid #bfdbfe; }
.inv-create-page .inv-panel-violet { background: linear-gradient(180deg, #f5f3ff 0%, #fff 65%); border: 1px solid #ddd6fe; }
.inv-create-page .inv-panel-green { background: linear-gradient(180deg, #ecfdf5 0%, #fff 65%); border: 1px solid #a7f3d0; }
/* The blank space between fields wasn't the inputs themselves — it was
   capping input width (max-width:300px) INSIDE .form-grid's own plain
   1fr/1fr columns, which stayed wide, so a narrow input just left dead
   space to its right and a wide gutter before the next column. Fixed at
   the grid level instead: real multi-column flow (as many ~190px
   columns as fit, no fixed 1fr/1fr), tight gaps, and each .form-group's
   own margin-bottom zeroed out (the grid's row-gap alone now spaces
   rows — the two used to stack on top of each other, doubling the
   vertical gap too). Inputs fill their now-narrow column at width:100%,
   no separate max-width needed. */
.inv-create-page .form-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 18px; }
.inv-create-page .form-group { margin-bottom: 0; }
.inv-create-page .form-group input[type="text"],
.inv-create-page .form-group input[type="number"],
.inv-create-page .form-group input[type="date"],
.inv-create-page .form-group select {
  width: 100%; border-radius: 8px; padding: 8px 11px; font-size: 0.85rem;
  background: #fff; transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.inv-create-page .form-group input:not(:disabled),
.inv-create-page .form-group select:not(:disabled) { border: 2px solid #cbd5e1; }
.inv-panel-blue .form-group input:not(:disabled), .inv-panel-blue .form-group select:not(:disabled) { border-color: #60a5fa; }
.inv-panel-blue .form-group input:not(:disabled):hover, .inv-panel-blue .form-group select:not(:disabled):hover { border-color: #3b82f6; }
.inv-panel-blue .form-group input:not(:disabled):focus, .inv-panel-blue .form-group select:not(:disabled):focus {
  outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18);
}
.inv-panel-violet .form-group input:not(:disabled), .inv-panel-violet .form-group select:not(:disabled) { border-color: #a78bfa; }
.inv-panel-violet .form-group input:not(:disabled):hover, .inv-panel-violet .form-group select:not(:disabled):hover { border-color: #8b5cf6; }
.inv-panel-violet .form-group input:not(:disabled):focus, .inv-panel-violet .form-group select:not(:disabled):focus {
  outline: none; border-color: #7c3aed; box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.18);
}
.inv-create-page .form-grid textarea { max-width: none; }
.inv-create-page .qty-with-unit, .inv-create-page .discount-cell { max-width: none; }
/* An empty leftover hint <small> (telecaller-cost-hint blank outside
   Enzyme, or an error slot with nothing in it) shouldn't still eat a
   line of vertical space — a big part of why this page read as
   "lengthy" before. */
.inv-create-page .form-group small:empty { display: none; }

/* Purchase and Pricing — several separate .form-grid sub-blocks
   (Enzyme Purchase's own enzyme-quickcalc-grid, Tank Purchase's own
   tank-purchase-group, the plain generic grid, Purchasing, Tank Specs)
   used to each lay out its own 2-3 fields as its OWN independent grid
   row, so the section visibly broke into several short, misaligned rows
   instead of one continuous line — per explicit request, this panel is
   now ONE shared grid instead: every sub-block's own grid-ness is
   switched off (display:contents — its .form-group children flatten
   straight into THIS panel's grid, in the same DOM order the category-
   switch script in this file already relies on), and every non-field
   element in between (a "🛢️ Tank Purchase"-style label, an error alert,
   the Purchasing hint) spans the full row instead of eating a column, so
   it still reads as a section break, just without breaking the actual
   field flow around it. display:contents strips an element's own box
   entirely — including how [hidden]'s UA display:none normally wins on
   its own box — so [hidden] needs restating as !important right after,
   same fix this app already applies wherever an author display rule
   would otherwise beat [hidden] (see .btn[hidden] in app.css). */
.inv-create-page .inv-panel-violet {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 18px; align-items: start;
}
.inv-create-page .inv-panel-violet > .inv-section-head,
.inv-create-page .inv-panel-violet > .form-section-title,
.inv-create-page .inv-panel-violet > .alert,
.inv-create-page .inv-panel-violet > #purchasing-hint {
  grid-column: 1 / -1;
}
.inv-create-page .inv-panel-violet > .form-grid { display: contents; }
.inv-create-page .inv-panel-violet > .form-grid[hidden] { display: none !important; }

/* Section header icon — "Basic Details" reuses the existing blue/violet
   pairing (.inv-section-head-blue/-violet, app3.css); "Uploads" gets its
   own green (matches the brand color, and Enzyme's own section theming
   on Product Master) — app5.css already defines
   .inv-section-head-green .inv-section-icon's own background for the
   Invoice redesign kit, reused here rather than redefined. */
.inv-upload-grid { gap: 16px; }
.inv-upload-btn {
  display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 8px;
  border: 2px dashed #6ee7b7; background: var(--brand-light); color: var(--brand-dark);
  font-size: 0.82rem; font-weight: 700; cursor: pointer; max-width: 300px;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
.inv-upload-btn:hover { border-color: var(--brand); background: #d1fae5; }
.inv-upload-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* New Stock Item's own "Add Stock Preview" sidebar (form.php's own
   create-only <aside>, right of the 3-section form) — live client-side
   readout of the fields entered so far, same spirit as the Quotation
   wizard's own .qt-summary-card (app6.css) but its own amber theme
   rather than reusing that card's brand-green — the panels to its left
   already used blue/violet/green, and this needs to read as its own
   distinct fourth zone per explicit request, not a 4th variation on one
   already-used color. Edit mode never renders the sidebar at all (see
   form.php's own $isEdit gate) — .inv-create-layout-noside keeps that
   case a plain single column instead of leaving a permanent empty gap
   where the sidebar column would have been. */
.inv-create-layout { display: grid; grid-template-columns: 1fr 280px; gap: 20px; align-items: start; }
/* Defense-in-depth alongside app.css's own global .content min-width:0
   fix — a grid item's default min-width is `auto`, not 0, so without
   this too, any future wide content added inside .inv-create-layout's
   own left column could still stretch this grid track directly, one
   level before .content would even get a chance to contain it. Same
   fix as .inv-invoice-layout > * in app8.css. */
.inv-create-layout > * { min-width: 0; }
.inv-create-layout-noside { display: block; }
.inv-create-sidebar { display: flex; flex-direction: column; gap: 14px; position: sticky; top: 16px; }
.inv-preview-card {
  background: linear-gradient(180deg, #fffbeb 0%, #fff 60%); border: 1px solid #fde68a; border-radius: var(--radius);
  padding: 18px 20px; box-shadow: var(--shadow-sm);
}
.inv-preview-card h3 {
  margin: 0 0 14px; font-size: 0.95rem; display: flex; align-items: center; gap: 8px; color: #92400e;
}
.inv-preview-row { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; font-size: 0.85rem; color: var(--text); border-bottom: 1px solid #fef3c7; }
.inv-preview-row:last-child { border-bottom: none; }
.inv-preview-row span:first-child { color: var(--text-muted); flex-shrink: 0; }
.inv-preview-row span:last-child { font-weight: 600; text-align: right; }
.inv-preview-row[hidden] { display: none; }
.inv-preview-grand { margin-top: 4px; padding-top: 12px; border-top: 1.5px solid #fde68a; font-weight: 800; font-size: 1.05rem; color: #92400e; }
.inv-preview-words { font-size: 0.76rem; color: var(--text-muted); font-style: italic; text-align: right; margin-top: 2px; }
.inv-preview-words:empty { display: none; }
.inv-preview-tip {
  display: flex; align-items: flex-start; gap: 8px; background: #fffbeb; color: #92400e;
  border: 1px solid #fde68a; border-radius: var(--radius); padding: 12px 14px; font-size: 0.8rem; line-height: 1.4;
}
.inv-preview-tip svg { flex-shrink: 0; margin-top: 1px; }
/* Same 980px breakpoint as the app's own main sidebar drawer (app.css)
   — see that rule's own comment on why 860px wasn't wide enough to
   cover a phone in landscape orientation. */
@media (max-width: 1100px) {
  .inv-create-layout { grid-template-columns: 1fr; }
  .inv-create-sidebar { position: static; }
}

/* Invoice create/edit — same compact, colored-border treatment as
   Inventory's own New/Edit Stock Item redesign above, scoped to
   .inv-invoice-page (modules/invoicing/views/form.php's own card class)
   so Sales Order/Purchase Order/Quotation — which share the exact same
   .qt-details-grid-5/.line-items-table markup — are never touched by
   this. Invoice Details' own inputs get a blue border (matching its
   section header's own blue), Line Items' get violet (matching ITS
   header) — same "each section's fields echo its own header color"
   convention the 3-panel Inventory redesign already established. */
.inv-invoice-page .qt-details-grid-5 { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 18px; }
.inv-invoice-page .qt-details-grid-5 .form-group { margin-bottom: 0; }
.inv-invoice-page .qt-details-grid-5 input[type="text"],
.inv-invoice-page .qt-details-grid-5 input[type="date"],
.inv-invoice-page .qt-details-grid-5 select,
.inv-invoice-page .cs-input {
  border: 2px solid #93c5fd; border-radius: 8px; padding: 8px 11px; font-size: 0.85rem; background: #fff;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.inv-invoice-page .qt-details-grid-5 input:hover,
.inv-invoice-page .qt-details-grid-5 select:hover,
.inv-invoice-page .cs-input:hover { border-color: #60a5fa; }
.inv-invoice-page .qt-details-grid-5 input:focus,
.inv-invoice-page .qt-details-grid-5 select:focus,
.inv-invoice-page .cs-input:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16); }
/* Restated with enough extra specificity to beat the base rule above
   (its own [type="text"] attribute selector already makes it more
   specific than a plain ".qt-field-icon input" override) — .qt-field-
   icon input's own padding-left:32px (app5.css) makes room for the
   little icon glyph; the base rule's plain padding:8px 11px would
   otherwise flatten that back down and the icon would sit on top of the
   typed text. */
.inv-invoice-page .qt-details-grid-5 .qt-field-icon input[type="text"] { padding-left: 32px; }

.inv-invoice-page .line-items-table input,
.inv-invoice-page .line-items-table select {
  border: 2px solid #ddd6fe; border-radius: 7px;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.inv-invoice-page .line-items-table input:hover,
.inv-invoice-page .line-items-table select:hover { border-color: #a78bfa; }
.inv-invoice-page .line-items-table input:focus,
.inv-invoice-page .line-items-table select:focus { border-color: #7c3aed; box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.14); }
.inv-invoice-page .line-items-table tbody tr:nth-child(even) { background: #faf8ff; }
.inv-invoice-page .line-items-table tbody tr:hover { background: #f3effe; }
.inv-invoice-page .richtext-editor { border: 2px solid #ddd6fe; border-radius: 7px; transition: border-color 0.15s var(--ease); }
.inv-invoice-page .richtext-editor:focus { outline: none; border-color: #7c3aed; box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.14); }
