/* ============================================
   SAHAJ COOLING ERP — Design System
   Light sidebar, Shadcn/Radix-inspired layout
   ============================================ */

:root {
  /* shadcn CSS variables (used by login page) */
  --radius: 0.65rem;
  --background: hsl(0 0% 100%);
  --foreground: hsl(222.2 84% 4.9%);
  --primary: hsl(221.2 83.2% 53.3%);
  --primary-foreground: hsl(210 40% 98%);
  --muted-foreground: hsl(215.4 16.3% 46.9%);
  --accent: hsl(210 40% 96.1%);
  --accent-foreground: hsl(222.2 47.4% 11.2%);
  --destructive: hsl(0 84.2% 60.2%);
  --border: hsl(214.3 31.8% 91.4%);
  --input: hsl(214.3 31.8% 91.4%);
  --ring: hsl(221.2 83.2% 53.3%);

  /* Brand Colors */
  --s-primary: #2C4190;
  --s-primary-light: #3a56b0;
  --s-primary-dark: #1e2d66;
  --s-accent: #E42626;
  --s-accent-light: #ff4444;
  --s-accent-dark: #b81e1e;

  /* Semantic Colors */
  --s-success: #2e7d32;
  --s-success-light: #e8f5e9;
  --s-warning: #ed6c02;
  --s-warning-light: #fff3e0;
  --s-error: #d32f2f;
  --s-error-light: #ffebee;
  --s-info: #0288d1;
  --s-info-light: #e1f5fe;

  /* Neutrals */
  --s-bg: #f5f5f5;
  --s-surface: #ffffff;
  --s-border: #e0e0e0;
  --s-text: #212121;
  --s-text-secondary: #666666;
  --s-text-disabled: #9e9e9e;
  --s-hover: #f0f0f0;
  --s-selected: #e8eaf6;

  /* Sidebar — light theme tokens */
  --s-sidebar-width: 16rem;
  --s-sidebar-collapsed: 3rem;
  --s-sidebar-bg: #E3F2FD;
  --s-sidebar-text: #222222;
  --s-sidebar-text-muted: rgba(34,34,34,0.5);
  --s-sidebar-accent: #d6e4f0;
  --s-sidebar-accent-fg: #1e3a5f;
  --s-sidebar-border: #e8e8e8;
  --s-sidebar-ring: rgba(44,65,144,0.3);
  --s-header-height: 4rem;

  /* Spacing */
  --s-xs: 4px;
  --s-sm: 8px;
  --s-md: 16px;
  --s-lg: 24px;
  --s-xl: 32px;
  --s-2xl: 48px;

  /* ---- Safe area and the end-of-page gutter ----
     --s-safe-bottom is whatever the device reserves at the bottom of the screen: the
     home indicator on an iPhone, the gesture bar on Android. It reads 0 on a desktop and
     on any phone without one, so everything built on it is inert there. It only reports a
     real value because App.razor asks for viewport-fit=cover.

     --s-page-bottom-gutter is the single place the "room after the last thing on the page"
     is set. .s-content spends it, the phone breakpoint below raises it, and any future
     scroll container that ends a page should use it rather than inventing its own number.
     Deliberately *not* spacing between components — only after the whole page. */
  --s-safe-bottom: env(safe-area-inset-bottom, 0px);
  --s-page-bottom-gutter: calc(var(--s-md) + var(--s-safe-bottom));

  /* Typography */
  --s-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --s-font-mono: 'JetBrains Mono', 'Consolas', monospace;

  /* Shadows */
  --s-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --s-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
  --s-shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --s-shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
  --s-shadow-inset: 0 1px 3px rgba(0,0,0,0.06), 0 0 0 1px rgba(0,0,0,0.04);

  /* Radius */
  --s-radius: 0.625rem;
  --s-radius-sm: 0.375rem;
  --s-radius-md: 0.625rem;
  --s-radius-lg: 0.75rem;
  --s-radius-xl: 1rem;
}

/* ---- Reset ---- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { margin: 0; height: 100%; overflow: hidden; }
html { font-size: 16px; -webkit-tap-highlight-color: transparent; }
body {
  font-family: var(--s-font); color: var(--s-text); background: var(--s-bg);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: var(--s-primary); text-decoration: none; transition: color 0.15s ease; }
a:hover { text-decoration: underline; }

/* ---- Typography ---- */
h1 { font-size: 1.75rem; font-weight: 600; }
h2 { font-size: 1.5rem; font-weight: 600; }
h3 { font-size: 1.25rem; font-weight: 600; }
h4 { font-size: 1.125rem; font-weight: 600; }
h5 { font-size: 1rem; font-weight: 600; }
h6 { font-size: 0.875rem; font-weight: 600; }
.text-caption { font-size: 0.75rem; color: var(--s-text-secondary); }
.text-body2 { font-size: 0.875rem; }
.text-overline { font-size: 0.625rem; text-transform: uppercase; letter-spacing: 1px; color: var(--s-text-secondary); }

/* ---- Grid System (12-col) ---- */
.s-grid { display: grid; gap: var(--s-md); }
.s-grid-2 { grid-template-columns: repeat(2, 1fr); }
.s-grid-3 { grid-template-columns: repeat(3, 1fr); }
.s-grid-4 { grid-template-columns: repeat(4, 1fr); }
.s-grid-6 { grid-template-columns: repeat(6, 1fr); }
.s-grid-12 { grid-template-columns: repeat(12, 1fr); }
.s-col-1 { grid-column: span 1; }
.s-col-2 { grid-column: span 2; }
.s-col-3 { grid-column: span 3; }
.s-col-4 { grid-column: span 4; }
.s-col-6 { grid-column: span 6; }
.s-col-8 { grid-column: span 8; }
.s-col-12 { grid-column: span 12; }
@media (max-width: 768px) {
  .s-grid-2, .s-grid-3, .s-grid-4, .s-grid-6 { grid-template-columns: 1fr; }
  .s-col-1, .s-col-2, .s-col-3, .s-col-4, .s-col-6, .s-col-8 { grid-column: span 1; }
}

/* ---- Flex Utilities ---- */
.s-stack { display: flex; flex-direction: column; gap: var(--s-md); }
.s-row { display: flex; flex-direction: row; gap: var(--s-md); align-items: center; }
.s-row-between { display: flex; justify-content: space-between; align-items: center; }
.s-gap-sm { gap: var(--s-sm); }
.s-gap-xs { gap: var(--s-xs); }
.s-wrap { flex-wrap: wrap; }

/* ---- Spacing ---- */
.s-p-sm { padding: var(--s-sm); }
.s-p-md { padding: var(--s-md); }
.s-p-lg { padding: var(--s-lg); }
.s-p-xl { padding: var(--s-xl); }
.s-mb-sm { margin-bottom: var(--s-sm); }
.s-mb-md { margin-bottom: var(--s-md); }
.s-mb-lg { margin-bottom: var(--s-lg); }
.s-mt-md { margin-top: var(--s-md); }

/* ---- Buttons ---- */
.s-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px; border: 1px solid transparent;
  font-family: var(--s-font); font-size: 0.875rem; font-weight: 500;
  cursor: pointer; transition: background 0.15s, color 0.15s; line-height: 1.4;
  background: var(--s-primary); color: #fff; border-radius: var(--s-radius);
}
.s-btn:hover { background: var(--s-primary-light); }
.s-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.s-btn--outline { background: transparent; color: var(--s-primary); border-color: var(--s-primary); }
.s-btn--outline:hover { background: var(--s-selected); }
.s-btn--text { background: transparent; color: var(--s-primary); border: none; padding: 8px 12px; }
.s-btn--text:hover { background: var(--s-hover); }
.s-btn--danger { background: var(--s-error); }
.s-btn--danger:hover { background: var(--s-accent-dark); }
.s-btn--text.s-btn--danger { background: transparent; color: var(--s-error); }
.s-btn--text.s-btn--danger:hover { background: color-mix(in srgb, var(--s-error) 10%, transparent); color: var(--s-error); }
.s-btn--success { background: var(--s-success); }
.s-btn--sm { padding: 4px 10px; font-size: 0.8125rem; }
.s-btn--icon { padding: 6px; min-width: 32px; justify-content: center; }

/* ---- Autocomplete ---- */
.s-autocomplete { position: relative; }
.s-autocomplete-dropdown {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 1050;
  max-height: 220px; overflow-y: auto;
  background: #fff; border: 1px solid var(--s-border);
  border-radius: var(--s-radius); box-shadow: var(--s-shadow-md);
  margin-top: 2px;
}
.s-autocomplete-option {
  padding: 8px 12px; font-size: 0.84rem; cursor: pointer;
  transition: background 0.1s ease; background: #fff;
}
.s-autocomplete-option:hover, .s-autocomplete-option--active { background: #e8f0fe; }

/* Allow dropdowns/autocomplete to escape parent clipping — only when open */
td:has(.s-autocomplete), td:has(.s-multiselect), td:has(.s-custom-date) { overflow: visible !important; }
.s-table:has(.s-autocomplete), .s-table-edit:has(.s-autocomplete), .s-table:has(.s-multiselect), .s-table-edit:has(.s-multiselect) { overflow: visible !important; }
/* SSelect (`.s-custom-select-*`) renders its dropdown as position:fixed with
   JS-computed coordinates, so it doesn't need overflow:visible on ancestors.
   Removing the s-custom-select-backdrop entries here keeps the list page's
   sticky pagination contained when an inline status SSelect opens in a row. */
/* SAutocomplete now positions its list the same way, so .s-autocomplete-dropdown is
   deliberately NOT in the two rules below any more.
   Flipping a scroll pane to overflow:visible resets its scrollLeft to 0. On a phone that
   was a real bug: swipe a BOD line grid right to reach the IDU picker, tap it, and the
   pane jumped back to column one — carrying the list that had just opened off screen with
   it (measured: scrollLeft 220 → 0, dropdown at left 452 in a 360px viewport). Harmless on
   a desktop only because the grid fits and there is no scroll position to lose.
   .s-multiselect stays: its dropdown is still absolutely positioned, and it is the same
   trap waiting for whoever puts one in a wide grid. */
.s-table-wrap:has(.s-multiselect-backdrop) { overflow: visible !important; }
.s-card:has(.s-multiselect-backdrop) { overflow: visible !important; }
.lead-panel:has(.s-autocomplete), .lead-panel:has(.s-multiselect), .lead-panel:has(.s-custom-date) { overflow: visible !important; }
.lead-panel-body:has(.s-autocomplete), .lead-panel-body:has(.s-multiselect), .lead-panel-body:has(.s-custom-date) { overflow: visible !important; }
/* ---- Custom Themed Dropdowns ---- */
/* The scrollbar is hidden three ways because no single property covers every engine:
   scrollbar-width (Firefox, and Safari 18.2+), -ms-overflow-style (legacy Edge) and the
   ::-webkit-scrollbar rule below (Chrome, older Safari, Samsung Internet). Linters flag
   scrollbar-width as unsupported in Safari — the webkit rule is what covers it there, so
   don't drop either one. */
.s-multiselect, .s-custom-select { position: relative; }
.s-multiselect-dropdown, .s-custom-select-dropdown { position: absolute; top: 100%; left: 0; right: 0; z-index: 1050; max-height: 200px; overflow-y: auto; background: #fff; border: 1px solid var(--s-border); border-radius: var(--s-radius); box-shadow: var(--s-shadow-md); margin-top: 2px; padding: 0; scrollbar-width: none; -ms-overflow-style: none; }
.s-multiselect-dropdown::-webkit-scrollbar, .s-custom-select-dropdown::-webkit-scrollbar { display: none; }
.s-multiselect-backdrop, .s-custom-select-backdrop { position: fixed; inset: 0; z-index: 1049; }
.s-custom-select-trigger { display: flex; align-items: center; justify-content: space-between; gap: 6px; cursor: pointer; padding: 8px 12px; border: 1px solid var(--s-border); font-family: var(--s-font); font-size: 0.875rem; background: var(--s-surface); color: var(--s-text); border-radius: var(--s-radius); transition: border-color 0.15s, box-shadow 0.15s; width: 100%; -webkit-user-select: none; user-select: none; min-height: 38px; }
.s-custom-select-trigger > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.s-custom-select-trigger:hover { border-color: #bbb; }
.s-custom-select-trigger.open { border-color: var(--s-primary); box-shadow: 0 0 0 2px rgba(44,65,144,0.15); }
.s-custom-select-trigger svg { width: 14px; height: 14px; color: var(--s-text-secondary); flex-shrink: 0; transition: transform 0.15s; }
.s-custom-select-trigger.open svg { transform: rotate(180deg); }
.s-custom-select-option { display: flex; align-items: center; gap: 8px; padding: 7px 12px; cursor: pointer; font-size: 0.84rem; color: var(--s-text); transition: background 0.1s; }
.s-custom-select-option:hover { background: var(--s-hover); }
.s-custom-select-option.selected { background: var(--s-selected); color: var(--s-primary); font-weight: 500; }
.s-custom-select-option .check-icon { width: 14px; height: 14px; color: var(--s-primary); }

/* ---- Custom Themed Date Picker ---- */
.s-custom-date { position: relative; }
.s-custom-date-trigger { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 8px 12px; border: 1px solid var(--s-border); font-family: var(--s-font); font-size: 0.875rem; background: var(--s-surface); color: var(--s-text); border-radius: var(--s-radius); transition: border-color 0.15s, box-shadow 0.15s; width: 100%; -webkit-user-select: none; user-select: none; }
.s-custom-date-trigger:hover { border-color: #bbb; }
.s-custom-date-trigger.open { border-color: var(--s-primary); box-shadow: 0 0 0 2px rgba(44,65,144,0.15); }
.s-custom-date-trigger svg { width: 16px; height: 16px; color: var(--s-text-secondary); flex-shrink: 0; }
.s-custom-date-trigger .date-text { flex: 1; }
.s-custom-date-trigger .date-placeholder { color: var(--s-text-disabled); }
.s-custom-date-popup { position: absolute; top: 100%; left: 0; z-index: 1050; background: #fff; border: 1px solid var(--s-border); border-radius: var(--s-radius); box-shadow: var(--s-shadow-md); margin-top: 2px; padding: 12px; min-width: 280px; }
.s-cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.s-cal-header span { font-size: 0.875rem; font-weight: 600; color: var(--s-text); }
.s-cal-nav { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: none; background: none; cursor: pointer; border-radius: var(--s-radius-sm); color: var(--s-text-secondary); font-size: 16px; }
.s-cal-nav:hover { background: var(--s-hover); color: var(--s-text); }
/* Month + year as dropdowns between the arrows. Native <select>s on purpose: the browser draws
   and scrolls the option list itself, which is what makes reaching 1990 one gesture instead of
   430 clicks on the ‹ arrow. They are styled to read as the old static "January 2026" label. */
.s-cal-period { display: flex; align-items: center; gap: 4px; min-width: 0; }
.s-cal-period-select {
  border: 1px solid transparent; background: none; cursor: pointer;
  font-family: var(--s-font); font-size: 0.875rem; font-weight: 600; color: var(--s-text);
  padding: 2px 4px; border-radius: var(--s-radius-sm); max-width: 104px;
}
.s-cal-period-select:hover { background: var(--s-hover); }
.s-cal-period-select:focus { outline: none; border-color: var(--s-primary); }
.s-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; text-align: center; }
.s-cal-dow { font-size: 0.7rem; font-weight: 600; color: var(--s-text-disabled); padding: 4px 0; }
.s-cal-day { font-size: 0.8rem; padding: 6px 0; border-radius: var(--s-radius-sm); cursor: pointer; color: var(--s-text); transition: background 0.1s; border: none; background: none; }
.s-cal-day:hover { background: var(--s-hover); }
.s-cal-day.other-month { color: var(--s-text-disabled); }
.s-cal-day.today { font-weight: 700; color: var(--s-primary); }
.s-cal-day.selected { background: var(--s-primary); color: #fff; font-weight: 600; }
.s-cal-day.selected:hover { background: var(--s-primary-light); }
.s-cal-today-btn { display: block; width: 100%; margin-top: 8px; padding: 6px; border: none; background: none; color: var(--s-primary); font-size: 0.78rem; font-weight: 500; cursor: pointer; border-radius: var(--s-radius-sm); }
.s-cal-today-btn:hover { background: var(--s-selected); }
/* .lead-panel-head { background: var(--s-sidebar-bg) !important; } */

/* ---- 12-hour Time Picker (STimePicker / SDateTimePicker) ---- */
/* Three dropdowns rather than <input type="time">: the native control follows the OS
   locale and renders 24-hour on many machines, and no CSS reaches it. */
.s-time-picker { display: flex; align-items: center; gap: 4px; }
.s-time-picker select.s-input { width: auto; min-width: 62px; padding-left: 8px; padding-right: 22px; font-variant-numeric: tabular-nums; }
.s-time-picker__meridiem { margin-left: 4px; }
.s-time-picker__sep { color: var(--s-text-secondary); font-weight: 600; }
.s-time-picker__clear { color: var(--s-text-secondary); }
.s-datetime-picker { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.s-datetime-picker .s-custom-date { min-width: 150px; }
.s-datetime-picker .s-field { margin-bottom: 0; }
@media (max-width: 640px) {
    .s-time-picker select.s-input { min-width: 58px; padding-right: 18px; }
    .s-datetime-picker { width: 100%; }
    .s-datetime-picker > * { flex: 1 1 auto; }
}

/* ---- Shared Panel / Form Layout ---- */
.lead-form { display: flex; flex-direction: column; gap: 12px; }
.lead-panel { margin-bottom: 6px; background: #fff; border: 1px solid var(--s-border); border-radius: 8px; overflow: hidden; }
.lead-panel-head { background: #f8f9fb; padding: 8px 16px; border-bottom: 1px solid var(--s-border); font-size: 0.8rem; font-weight: 600; color: var(--s-primary); text-transform: uppercase; letter-spacing: 0.4px; }
.lead-panel-body { padding: 12px 16px; }
.lead-panel-body .s-grid { gap: 8px 12px; }
.lead-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.lead-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 10px 16px; background: #f8f9fb; border: 1px solid var(--s-border); border-radius: 8px; }
.detail-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid #f0f1f3; font-size: 0.85rem; }
.detail-label { color: var(--s-text-secondary); font-weight: 500; }
.detail-value { color: var(--s-text); font-weight: 600; text-align: right; }
@media (max-width: 900px) { .lead-two-col { grid-template-columns: 1fr; } }

/* ---- Form Controls ---- */
.s-field { display: flex; flex-direction: column; gap: 6px; }
.s-label { font-size: 0.8125rem; font-weight: 500; color: var(--s-text-secondary); }
.s-input, .s-select, .s-textarea {
  padding: 8px 12px; border: 1px solid var(--s-border);
  font-family: var(--s-font); font-size: 0.875rem;
  background: var(--s-surface); color: var(--s-text);
  transition: border-color 0.15s, box-shadow 0.15s; width: 100%;
  border-radius: var(--s-radius);
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.s-input:focus, .s-select:focus, .s-textarea:focus {
  outline: none; border-color: var(--s-primary); box-shadow: 0 0 0 2px rgba(44,65,144,0.15);
}
.s-input:disabled, .s-select:disabled, .s-textarea:disabled {
  background: #fff; cursor: not-allowed; color: var(--s-text-disabled);
}
.s-input:read-only, .s-textarea:read-only {
  background: #fff; cursor: default;
}
.s-btn:focus-visible, .s-tab:focus-visible, .s-pagination button:focus-visible, .s-nav-link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--s-surface), 0 0 0 4px rgba(44,65,144,0.3);
  transition: box-shadow 0.15s;
}
@keyframes s-shake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-4px); }
  40%, 80% { transform: translateX(4px); }
}
.s-input--error { border-color: var(--s-error); animation: s-shake 0.3s ease; }
.s-input--changed { background-color: #fef3c7; border-color: #f59e0b; font-weight: 800; color: #78350f; letter-spacing: 0.01em; }
.s-helper { font-size: 0.75rem; color: var(--s-text-secondary); }
.s-error-text { font-size: 0.75rem; color: var(--s-error); }
.s-textarea { min-height: 80px; resize: vertical; }
.s-checkbox-row { display: flex; align-items: center; gap: 8px; }
.s-section-title {
  font-size: 0.9375rem; font-weight: 600; color: var(--s-text);
  margin: 0; padding-bottom: 0.25rem;
}
.s-switch { position: relative; width: 40px; height: 22px; }

/* ---- Cards / Paper ---- */
.s-card, .s-paper {
  background: var(--s-surface); border: 1px solid var(--s-border);
  padding: var(--s-md); box-shadow: var(--s-shadow-sm);
  border-radius: var(--s-radius);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.s-card--elevated { box-shadow: var(--s-shadow); }
.s-card--elevated:hover { box-shadow: var(--s-shadow-md); transform: translate3d(0, -1px, 0); }
.s-card--form { padding: var(--s-lg); }

.s-list-page > .s-card { padding: 0; border-radius: var(--s-radius); }

/* ---- Editable Table (Excel-like) ---- */
.s-table-edit { width: 100%; border-collapse: collapse; font-size: 0.82rem; table-layout: auto; }
.s-table-edit th {
  padding: 10px 10px; background: #f0f0f0; border: 1px solid #d0d0d0;
  font-weight: 600; font-size: 0.75rem; color: var(--s-text-secondary);
  text-align: center; white-space: nowrap; -webkit-user-select: none; user-select: none;
}
.s-table-edit td {
  padding: 4px 6px; border: 1px solid #d0d0d0; vertical-align: middle;
  position: relative; text-align: center; min-height: 40px;
}
.s-table-edit td .s-input,
.s-table-edit td .s-textarea,
.s-table-edit td input,
.s-table-edit td textarea {
  border: none; border-radius: 0; background: transparent;
  padding: 8px 10px; width: 100%; font-size: 0.82rem;
  outline: none; box-shadow: none; text-align: center;
}
.s-table-edit td .s-select,
.s-table-edit td select {
  border: none; border-radius: 0; background: #fff;
  padding: 8px 10px; width: 100%; font-size: 0.82rem;
  outline: none; box-shadow: none; text-align: center;
  -webkit-appearance: menulist; appearance: menulist;
}
.s-table-edit td select option { background: #fff; color: #333; }
.s-table-edit td input:focus,
.s-table-edit td select:focus,
.s-table-edit td .s-input:focus {
  background: #e8f0fe; outline: 2px solid var(--s-primary); outline-offset: -2px;
}
.s-table-edit td .s-field { gap: 0; }
.s-table-edit td .s-label { display: none; }
.s-table-edit td .s-custom-select-trigger { min-height: unset; padding: 4px 8px; font-size: 0.82rem; border: none; border-radius: 0; box-shadow: none; }
.s-table-edit td .s-custom-select-trigger:hover { border: none; }
.s-table-edit td .s-custom-select-trigger.open { border: none; box-shadow: none; outline: none; }
.s-table-edit td span { padding: 8px 10px; display: block; font-size: 0.82rem; }
.s-table-edit td .s-autocomplete { position: relative; }
.s-table-edit td .s-autocomplete .s-input { border: none; border-radius: 0; }
.s-table-edit td input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; accent-color: var(--s-primary); }
.s-table-edit td .s-btn--icon { padding: 4px; }
.s-table-edit tbody tr:hover { background: #f8f9fa; }
.s-table-edit tbody tr:nth-child(even) { background: #fafbfc; }
.s-table-edit tbody tr:nth-child(even):hover { background: #f0f1f2; }
.s-table-edit tfoot td { background: #f0f0f0; font-weight: 600; padding: 6px 8px; text-align: center; }
.s-table-edit--hs th { background: #E3F2FD; color: #1565C0; }
.s-table-edit--hs tfoot td { background: #E3F2FD; }
.s-table-edit--ls th { background: #FFF3E0; color: #E65100; }
.s-table-edit--ls tfoot td { background: #FFF3E0; }

/* ---- Tables ---- */
.s-table-wrap { overflow-x: auto; }
.s-table { width: 100%; border-collapse: collapse; font-size: 0.84rem; table-layout: auto; border: 1px solid var(--s-border); }
.s-table th {
  text-align: center; padding: 9px 12px; background: #fafafa;
  border: 1px solid var(--s-border); font-weight: 700;
  color: #000000; font-size: 0.8rem;
  white-space: nowrap; cursor: default; -webkit-user-select: none; user-select: none;
}
.s-table td { padding: 8px 12px; border: 1px solid var(--s-border); text-align: center; vertical-align: middle; color: var(--s-text); font-size: 0.78rem; font-weight: 400; }

/* Actions column (last) centered */
.s-table th:last-child,
.s-table td:last-child { text-align: center; }

.s-table tbody tr { transition: background 0.15s ease; cursor: pointer; }
.s-table tbody tr:hover { background: var(--s-hover); }
.s-table tbody tr.selected { background: var(--s-selected); }
.s-table--empty { padding: 40px; text-align: center; color: var(--s-text-secondary); }
.s-table .s-actions { display: flex; flex-direction: row; align-items: center; gap: var(--s-xs); justify-content: center; }

/* ---- Row actions ----
   Icon-only View / Edit / Delete on a single line. See RowActions.razor for why icons.

   The nowrap rule below is the blunt half of the fix and it is deliberate: action buttons are
   scattered through ~40 list pages as bare children of a <td>, so a cell with three of them wrapped
   and one row of data rendered three rows tall. Every one of those cells is now a single line
   without touching a single page. Pages that adopt RowActions get the icons as well. */
.s-row-actions { display: flex; flex-direction: row; align-items: center; gap: 4px; justify-content: center; white-space: nowrap; }
.s-table td:last-child { white-space: nowrap; }
.s-table td:last-child > .s-btn + .s-btn { margin-left: 4px; }

.s-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  /* 30px, not the 44px touch minimum: a data grid is a pointer surface and three 44px targets in
     a cell is taller than the row. The phone layouts stack these pages into cards instead. */
  width: 30px; height: 30px; padding: 0; flex-shrink: 0;
  border: 1px solid var(--s-border); border-radius: 6px;
  background: var(--s-surface, #fff); color: var(--s-text-secondary);
  cursor: pointer; text-decoration: none; transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.s-icon-btn:hover { background: var(--s-hover); color: var(--s-primary); border-color: var(--s-primary); }
.s-icon-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.s-icon-btn:disabled:hover { background: var(--s-surface, #fff); color: var(--s-text-secondary); border-color: var(--s-border); }
/* Colour only on the one action that cannot be undone. */
.s-icon-btn--danger:hover { background: var(--s-error-light); color: var(--s-error); border-color: var(--s-error); }

/* ---- Pagination ---- */
.s-pagination { display: flex; align-items: center; gap: 4px; justify-content: center; padding: var(--s-sm) 0; }
.s-pagination button {
  min-width: 32px; height: 32px; border: 1px solid var(--s-border);
  background: var(--s-surface); cursor: pointer; font-size: 0.8125rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--s-radius); transition: background 0.15s ease, color 0.15s ease;
}
.s-pagination button:hover { background: var(--s-hover); }
.s-pagination button.active { background: var(--s-primary); color: #fff; border-color: var(--s-primary); }
.s-pagination button:disabled { opacity: 0.4; cursor: not-allowed; }

/* ---- Badges / Chips ---- */
.s-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 10px; font-size: 0.75rem; font-weight: 600;
  border-radius: var(--s-radius-sm); text-transform: uppercase; letter-spacing: 0.5px;
}
.s-badge--success { background: var(--s-success-light); color: var(--s-success); }
.s-badge--warning { background: var(--s-warning-light); color: var(--s-warning); }
.s-badge--error { background: var(--s-error-light); color: var(--s-error); }
.s-badge--info { background: var(--s-info-light); color: var(--s-info); }
.s-badge--default { background: #f5f5f5; color: var(--s-text-secondary); }
.s-badge--primary { background: var(--s-selected); color: var(--s-primary); }

/* ---- Tabs ---- */
.s-tabs { display: flex; flex-direction: column; }
.s-tabs-header { display: flex; border-bottom: 2px solid var(--s-border); gap: 0; }
.s-tab-btn {
  padding: 10px 20px; cursor: pointer; font-size: 0.875rem; font-weight: 500;
  color: var(--s-text-secondary); border: none; background: none;
  border-bottom: 2px solid transparent; margin-bottom: -2px;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  font-family: var(--s-font); white-space: nowrap;
}
.s-tab-btn:hover { color: var(--s-primary); background: #f5f5f5; }
.s-tab-btn--active { color: var(--s-primary); border-bottom-color: var(--s-primary); font-weight: 600; }
.s-tabs-content { padding: var(--s-md) 0; }

/* ---- Dialogs / Modals ---- */
.s-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000; animation: fadeIn 0.2s ease-out;
  overflow: hidden;
}
.s-dialog {
  background: var(--s-surface); min-width: 400px; max-width: 90vw;
  max-height: 90vh; max-height: 90dvh; display: flex; flex-direction: column;
  box-shadow: var(--s-shadow-lg); border-radius: var(--s-radius-lg);
  animation: zoomIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  /* Note: do NOT add `will-change: transform` or `transform` here — it would
     turn .s-dialog into the containing block for position:fixed children
     (e.g. SSelect dropdowns), pinning their popovers to the dialog corner. */
}
.s-dialog-title {
  padding: 16px 20px; font-size: 1.125rem; font-weight: 600;
  border-bottom: 1px solid var(--s-border);
}

/* Size modifiers. .s-dialog is shrink-to-fit with only a 90vw ceiling, so a dialog holding a
   12-column grid stretches to fill the screen: the New Employee form measured 1145px on a 1366px
   viewport, which put ~530px behind every half-width control and left "Gender" and a 10-digit
   mobile marooned in whitespace. --lg was already being asked for in the markup and had never
   been defined anywhere, so it was inert.

   820px is a two-column form: ~370px per field, wide enough for the helper lines under Role and
   Work Location without turning a short input into a runway. */
.s-dialog--sm { width: 460px; max-width: 94vw; }
.s-dialog--md { width: 640px; max-width: 94vw; }
.s-dialog--lg { width: 820px; max-width: 94vw; }

/* Grouped form sections inside a dialog — "Role", "Contact", "Login", "Shift & leave".
   These were bold text floating in whitespace, which is what made a busy form read as sparse:
   with nothing dividing them the eye cannot tell a heading from a field label. The rule does the
   grouping so the gaps do not have to, which buys back vertical space too. */
.s-form-section { margin-top: 2px; padding-top: 10px; border-top: 1px solid var(--s-border); }
.s-form-section:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.s-form-section-title { font-size: 0.875rem; font-weight: 600; color: var(--s-text); }
.s-form-section-hint { font-size: 0.75rem; color: var(--s-text-secondary); margin-top: 2px; }

/* Tighter rows inside a dialog than on a full page. The column gap stays at 16px — that one
   separates two controls sitting side by side and still has to. */
.s-dialog-content .s-grid, .s-dialog-body .s-grid { gap: 12px 16px; }

/* Confirmation dialogs (SConfirmDialog, ~101 of the hosted dialogs in the app). The component
   renders inside the host's .s-dialog-content, so its actions bar has to be pulled back out to
   the dialog edges for its top rule to actually divide the question from the answer. */
/* ---- Card header: tabs + toolbar as one band ----
   A tabbed list page used to stack three separate strips down the screen — a tab row, a toolbar
   row, then the table card — so the controls read as floating debris above the content rather than
   as part of it. This makes them the card's own header: tabs on the left, search and column
   controls on the right, one rule under both, and the table directly beneath. The tab strip keeps
   its own border off; .s-card-head carries it, and .s-tab-btn's existing -2px margin lets the
   active underline sit on top of it. */
.s-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; flex-shrink: 0;
  /* Vertical padding, not zero: the tabs are outlined chips now, and a chip touching the table's
     grey header below it is what made them look like they were sliding under the table. */
  padding: 8px 12px; background: var(--s-surface);
  border-bottom: 1px solid var(--s-border);
  border-top-left-radius: var(--s-radius); border-top-right-radius: var(--s-radius);
}
.s-card-head > .s-tabs-header { border-bottom: none; gap: 6px; }

/* The open tab is outlined, not merely underlined. On an approval queue the tab you are on decides
   what Approve and Reject will act on, so which one is open has to be unmistakable rather than a
   two-pixel colour change.

   It is a self-contained chip with a border on all four sides, NOT a folder tab. The folder trick
   — paint the bottom edge in the surface colour and pull it down over the header rule — needs the
   panel directly beneath to be that colour, and here it is the table's grey sticky header. The tab
   read as though it were sliding underneath the table. A closed shape has nothing to blend into
   and cannot go wrong whatever sits below it.

   Scoped to .s-card-head on purpose: nine other screens use .s-tab-btn and were not part of this
   change. Any page adopting the card-head pattern gets this for free. */
.s-card-head .s-tab-btn {
  border: 1px solid transparent;
  border-radius: var(--s-radius-sm);
  margin-bottom: 0;
  padding: 7px 16px;
}
.s-card-head .s-tab-btn:hover { background: var(--s-hover); }
.s-card-head .s-tab-btn--active {
  border-color: var(--s-primary);
  background: var(--s-selected);
  color: var(--s-primary);
  font-weight: 600;
}
.s-card-head__tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.s-card-head__tools .s-toolbar { margin-bottom: 0; }

/* The table is no longer the top of the card, so it must not round its top corners or the header
   above it would appear to float on a notched edge. */
.s-list-page > .s-card > .s-card-head ~ .s-table-wrap { border-top-left-radius: 0; border-top-right-radius: 0; }
.s-list-page > .s-card > .s-card-head ~ .s-table-wrap > .s-table thead tr:first-child th:first-child,
.s-list-page > .s-card > .s-card-head ~ .s-table-wrap > .s-table thead tr:first-child th:last-child {
  border-radius: 0;
}

@media (max-width: 720px) {
  /* The search has a 250px floor, so on a phone the tools drop to their own full-width line
     under the tabs rather than squeezing the tab labels to nothing. */
  .s-card-head { padding: 0 10px; }
  .s-card-head__tools { width: 100%; padding-bottom: 10px; }
  .s-card-head__tools .s-search { flex: 1 1 0; min-width: 0; }
}

/* A table whose declared column widths are binding. Auto layout treats a th width as a suggestion
   and lets cell content override it, which is how the approval queues ended up 35–94px wider than
   their own scroll container and clipped the Approve/Reject column off the right edge — the widest
   offender being the Decision cell, whose reviewer name and timestamp simply refused to shrink.
   With fixed layout the widths hold and the text wraps instead. Columns left without a width share
   whatever is left, so exactly one free column (the remark/reason) absorbs the slack.

   overflow-wrap is the necessary companion: a single long unbroken token would otherwise still
   spill out of its cell. */
.s-table--fixed { table-layout: fixed; width: 100%; }
.s-table--fixed td, .s-table--fixed th { overflow-wrap: anywhere; }

/* The base sheet sets `white-space: nowrap` on the last cell and on every th, which suits a row of
   action buttons and short headers. It does not suit a Decision cell carrying a reviewer's name, a
   timestamp and a quoted note: that is one unbreakable line, and it was the whole reason only the
   Reviewed tab overflowed while Pending sat exactly on the container width. Let both wrap here. */
.s-table--fixed td:last-child, .s-table--fixed th { white-space: normal; }

/* ...but a button label is not prose. Letting the cell wrap made "Approve" break as "Approv e",
   which is worse than the overflow it was fixing. Text wraps, controls do not. */
.s-table--fixed td:last-child .s-btn { white-space: nowrap; }

/* ---- Frozen actions column (pointer screens only) ----
   The last column is the actions column by convention across ~40 list pages — the base sheet
   already assumes it, with `td:last-child { white-space: nowrap }` and the button-spacing rule
   beside it. On a wide table the reader scrolls right to read the data and loses View / Edit /
   Delete off the left, or scrolls back to reach them. Pinning that column keeps the actions under
   the cursor at every scroll position.

   Desktop only, deliberately: the phone layout already pins the FIRST column (see the mobile
   block near the end of this file) so the row stays identifiable while scrolling, and pinning both
   edges of a 360px screen would leave almost nothing between them.

   The pattern mirrors that mobile rule exactly, because the same three things have to be true:
   an opaque background, the row's hover/selected state repainted on the pinned cell (its own
   background would otherwise cut the row's short), and an edge line saying the table continues.

   `.s-list-page` carries it automatically because that convention holds there. A report page has
   no such guarantee — its last column is usually data (Vehicles, Locations, a note) and pinning
   that would be nonsense — so those opt in per table with `.s-table--sticky-actions`. */
@media (min-width: 1024px) {
  .s-list-page .s-table tbody td:last-child,
  .s-table--sticky-actions tbody td:last-child {
    position: sticky; right: 0; z-index: 2;
    background: var(--s-surface);
    box-shadow: -1px 0 0 var(--s-border);
  }
  /* Above the sticky thead's z-index: 50, which is declared !important. */
  .s-list-page .s-table thead th:last-child,
  .s-table--sticky-actions thead th:last-child {
    position: sticky !important; right: 0; z-index: 51 !important;
    box-shadow: inset 0 -1px 0 var(--s-border), inset 0 1px 0 var(--s-border),
                -1px 0 0 var(--s-border) !important;
  }
  .s-list-page .s-table tbody tr:hover td:last-child,
  .s-table--sticky-actions tbody tr:hover td:last-child { background: var(--s-hover); }
  .s-list-page .s-table tbody tr.selected td:last-child,
  .s-table--sticky-actions tbody tr.selected td:last-child { background: var(--s-selected); }

  /* The "nothing matched" row spans every column, so pinning it would park the message on the
     right-hand edge instead of across the table. */
  .s-list-page .s-table tbody td.s-table--empty,
  .s-table--sticky-actions tbody td.s-table--empty { position: static; box-shadow: none; }
}

/* Empty state for a list/queue with nothing in it. Distinct from .s-table--empty, which is the
   "nothing matched your filter" row *inside* a table — an empty queue and a filtered-out one are
   different facts and the reader has to be able to tell which they are looking at. */
.s-empty-state { padding: 2.5rem 1.5rem; text-align: center; }
.s-empty-state__icon { font-size: 1.75rem; line-height: 1; margin-bottom: 10px; opacity: 0.75; }
.s-empty-state__title { font-size: 0.95rem; font-weight: 600; color: var(--s-text); margin-bottom: 4px; }
.s-empty-state__hint {
  font-size: 0.8125rem; color: var(--s-text-secondary);
  max-width: 46ch; margin: 0 auto; line-height: 1.5;
}

.s-confirm-message { margin: 0; line-height: 1.55; color: var(--s-text); font-size: 0.9rem; }
.s-confirm-actions { margin: 20px -20px -20px; }
@media (max-width: 640px) {
  .s-confirm-actions { margin: 16px -16px -16px; }
}
.s-dialog-inner { display: flex; flex-direction: column; max-height: 90vh; max-height: 90dvh; }
.s-dialog-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--s-border); }
/* min-height:0 is load-bearing, not decoration: a flex child defaults to min-height:auto, which
   refuses to shrink below its content, so `flex: 1; overflow-y: auto` alone lets a tall form push
   the dialog past its max-height instead of scrolling inside it. Pages were each patching this
   with an inline style; it belongs here. */
.s-dialog-body { padding: 20px; overflow-y: auto; flex: 1; min-height: 0; }
.s-dialog-content { padding: 20px; overflow-y: auto; flex: 1; min-height: 0; }
.s-dialog-actions { padding: 12px 20px; display: flex; gap: 8px; justify-content: flex-end; border-top: 1px solid var(--s-border); }

/* ---- Utility ---- */
.s-form { width: 100%; }
.text-danger { color: var(--s-error); }

/* ---- Toasts (Sonner richColors) ---- */
.s-toast-container {
  position: fixed;
  bottom: 32px;
  right: 32px;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 356px;
  max-width: calc(100vw - 32px);

  /* The container is a 356px-wide fixed box pinned to the bottom-right corner at z-index 2000 —
     exactly where every form page puts its save button (.lead-actions, .prof-panel-actions and
     the dialog action rows are all bottom-right). Without this it stays in the hit-test tree even
     while empty of anything the user wants to click, so any click landing inside those 356px is
     swallowed by the container instead of reaching the button underneath.

     That made the commonest recovery in the app silently fail: get a validation error, fix the
     field, click Save again while the error toast is still on screen — and nothing happens. The
     button looks fine and answers no clicks, which reads as a dead form rather than a covered one.

     pointer-events:none lets clicks fall straight through the container; .s-toast puts them back
     on for the toasts themselves, so their own dismiss button still works. */
  pointer-events: none;
}
.s-toast {
  position: relative;
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 16px;
  padding-right: 36px;
  width: 100%;
  border-radius: 8px;
  border: 1px solid;
  font-family: var(--s-font);
  font-size: 13px;
  line-height: 1.5;
  box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  animation: s-toast-in 0.35s cubic-bezier(0.21,1.02,0.73,1) forwards;
  will-change: transform, opacity;
}
.s-toast--exiting {
  animation: s-toast-out 0.2s ease-in forwards;
}

/* richColors per severity */
.s-toast--success {
  background: #fff;
  border-color: hsl(0 0% 89.8%);
  color: #0a0a0a;
}
.s-toast--error {
  background: hsl(359, 100%, 97%);
  border-color: hsl(359, 100%, 94%);
  color: hsl(360, 100%, 45%);
}
.s-toast--warning {
  background: hsl(49, 100%, 97%);
  border-color: hsl(49, 91%, 91%);
  color: hsl(31, 92%, 45%);
}
.s-toast--info {
  background: hsl(208, 100%, 97%);
  border-color: hsl(221, 91%, 91%);
  color: hsl(210, 92%, 45%);
}

/* Icon */
.s-toast__icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}
.s-toast__icon svg { display: block; }

/* Content */
.s-toast__content { flex: 1; min-width: 0; }
.s-toast__title {
  font-size: 13px;
  font-weight: 600;
}
.s-toast__desc {
  font-size: 13px;
  font-weight: 400;
  opacity: 0.9;
  margin-top: 2px;
}

/* Action button */
.s-toast__action {
  flex-shrink: 0;
  padding: 2px 10px;
  height: 24px;
  font-size: 12px;
  font-weight: 500;
  font-family: var(--s-font);
  background: rgba(0,0,0,0.15);
  color: inherit;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.15s;
  white-space: nowrap;
  align-self: center;
}
.s-toast__action:hover { background: rgba(0,0,0,0.25); }

/* Close button — always visible, top-right */
.s-toast__close {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  color: inherit;
  cursor: pointer;
  border-radius: 50%;
  opacity: 0.5;
  transition: opacity 0.15s;
  padding: 0;
}
.s-toast__close:hover { opacity: 1; }

/* Loading spinner */
.s-toast__spinner {
  width: 16px;
  height: 16px;
  animation: spin 0.8s linear infinite;
}
.s-toast__spinner circle {
  stroke: currentColor;
  stroke-width: 2.5;
  fill: none;
  stroke-dasharray: 40;
  stroke-dashoffset: 12;
  stroke-linecap: round;
}

/* Animations */
@keyframes s-toast-in {
  from { opacity: 0; transform: translateY(100%) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes s-toast-out {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(100%) scale(0.96); }
}

@media (max-width: 600px) {
  .s-toast-container {
    bottom: 16px;
    right: 16px;
    left: 16px;
    width: auto;
  }
}

/* ---- Alerts ---- */
.s-alert {
  padding: 12px 16px; display: flex; align-items: flex-start; gap: 10px;
  font-size: 0.875rem; border-left: 4px solid;
  border-radius: var(--s-radius);
}
.s-alert--success { background: var(--s-success-light); border-left-color: var(--s-success); }
.s-alert--error { background: var(--s-error-light); border-left-color: var(--s-error); }
.s-alert--warning { background: var(--s-warning-light); border-left-color: var(--s-warning); }
.s-alert--info { background: var(--s-info-light); border-left-color: var(--s-info); }

/* ---- Progress ---- */
.s-progress { height: 6px; background: var(--s-border); overflow: hidden; border-radius: var(--s-radius); }
.s-progress-bar { height: 100%; background: var(--s-primary); transition: width 0.3s; }
.s-progress-bar--success { background: var(--s-success); }

/* ---- Spinner ---- */
.s-spinner {
  width: 24px; height: 24px; border: 3px solid var(--s-border);
  border-top-color: var(--s-primary); border-radius: 50%;
  animation: spin 0.7s linear infinite;
  will-change: transform;
}
.s-spinner--lg { width: 40px; height: 40px; border-width: 4px; }
.s-spinner-center { display: flex; justify-content: center; padding: var(--s-xl); }

/* ---- Divider ---- */
.s-divider { border: none; border-top: 1px solid var(--s-border); margin: var(--s-md) 0; }

/* ---- Tooltip ---- */
.s-tooltip { position: relative; display: inline-block; }
.s-tooltip::after {
  content: attr(data-tooltip); position: absolute; bottom: 100%;
  left: 50%; transform: translateX(-50%); padding: 4px 8px;
  background: #333; color: #fff; font-size: 0.75rem; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.15s;
  border-radius: var(--s-radius-sm);
}
.s-tooltip:hover::after { opacity: 1; }

/* ============================================
   App Shell — Inset Sidebar Layout
   All collapse/expand driven by .s-collapsed on .s-app
   ============================================ */
.s-app {
  display: flex;

  /* dvh, not vh. On a phone browser 100vh is the *large* viewport — the height the page
     would have if the URL bar were retracted — so the app was laid out ~110px taller than
     the screen actually showed. html/body are overflow:hidden and the shell owns the only
     scroll container, so that bottom strip could not be scrolled to by any means: the
     pagination footer, the Save row at the end of a form and the last dashboard card were
     simply gone. vh stays first as the fallback for browsers without dvh; on a desktop the
     two are identical, which is why this never showed up there. */
  height: 100vh;
  height: 100dvh;

  overflow: hidden;
  background: var(--s-sidebar-bg);
}

/* ---- Sidebar ---- */
.s-sidebar {
  width: var(--s-sidebar-width);
  background: var(--s-sidebar-bg);
  color: var(--s-sidebar-text);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 100;
  overflow: hidden;
  transition: width 200ms cubic-bezier(0.4, 0, 0.2, 1);
  will-change: width;
}

.s-collapsed .s-sidebar {
  width: var(--s-sidebar-collapsed);
}

/* Sidebar Header (logo area) */
.s-sidebar-header {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.75rem;
  min-height: var(--s-header-height);
  flex-shrink: 0;
}

.s-sidebar-logo-img {
  max-height: calc(var(--s-header-height) - 16px);
  max-width: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  transition: max-width 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

.s-collapsed .s-sidebar-header {
  padding: 0.5rem 0.25rem;
}

.s-collapsed .s-sidebar-logo-img {
  max-height: calc(var(--s-header-height) - 18px);
  max-width: calc(var(--s-sidebar-collapsed) - 16px);
  object-fit: contain;
  object-position: center;
}

.s-collapsed .s-sidebar-content {
  padding: 0.25rem 0;
}

/* Sidebar Content (scrollable nav area — hidden scrollbar like Ticket-Final) */
.s-sidebar-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0.25rem 0.5rem;
  /* Three properties for one effect — see the note above .s-custom-select. Dropping
     scrollbar-width because a linter calls it Safari-only would un-hide it on Firefox. */
  -ms-overflow-style: none;       /* legacy Edge */
  scrollbar-width: none;          /* Firefox, Safari 18.2+ */
}
.s-sidebar-content::-webkit-scrollbar {
  display: none;                  /* Chrome, older Safari, Samsung Internet */
}

/* Nav Sections */
.s-nav-section {
  padding: 0.25rem 0;
}

.s-nav-section-title {
  padding: 0.375rem 0.5rem;
  font-size: 0.875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #1e3a5f;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  height: auto;
  transition: opacity 200ms, height 200ms, padding 200ms;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  -webkit-user-select: none;
  user-select: none;
}
.s-nav-section-title:hover {
  background: rgba(0,0,0,0.06);
  border-radius: var(--s-radius-sm);
  color: #0d2340;
}
.s-nav-section-title .s-chevron {
  width: 12px; height: 12px; transition: transform 200ms; flex-shrink: 0;
}
.s-nav-section.s-section-collapsed .s-chevron { transform: rotate(-90deg); }
.s-nav-section.s-section-collapsed .s-nav-link { display: none; }

.s-collapsed .s-nav-section-title {
  display: none;
}

/* Nav Links */
.s-nav-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0.5rem;
  height: 2rem;
  color: var(--s-sidebar-text);
  font-size: 0.875rem;
  transition: background 0.15s, color 0.15s, padding 200ms;
  cursor: pointer;
  text-decoration: none;
  border-radius: var(--s-radius-sm);
  white-space: nowrap;
  overflow: hidden;
}

.s-nav-link:hover {
  background: var(--s-sidebar-accent);
  color: var(--s-sidebar-accent-fg);
  text-decoration: none;
}

.s-nav-link.active {
  background: var(--s-sidebar-accent);
  color: var(--s-sidebar-accent-fg);
  font-weight: 500;
}

.s-nav-link svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

/* Shown in place of the sidebar when a role can open nothing, so an empty sidebar always
   explains itself instead of looking broken. See NavMenu. */
.s-nav-notice {
  padding: 0.85rem 1rem;
  font-size: 0.78rem;
  color: var(--s-text-secondary);
  line-height: 1.55;
}

.s-nav-link span {
  overflow: hidden;
  opacity: 1;
  transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);
  backface-visibility: hidden;
}

/* ---- The collapsed rail was completely empty ----
   Two rules that are each correct on their own cancelled the sidebar out entirely:

     .s-nav-section.s-section-collapsed .s-nav-link { display: none }   (accordion)
     .s-collapsed .s-nav-section-title              { display: none }   (icon rail)

   Every section renders collapsed to begin with, so all 61 links are hidden and the
   expanded sidebar shows just the 10 section titles you click to open. Collapse the rail
   and those titles are hidden too — leaving 0 of 61 links and 0 of 10 titles visible, i.e.
   a blank 48px strip with nothing in it and no way to navigate.

   In the rail there is no section header to click, so a section's accordion state is
   meaningless there and must not suppress its links: the icons *are* the navigation. */
.s-collapsed .s-nav-section.s-section-collapsed .s-nav-link { display: flex; }

.s-collapsed .s-nav-link {
  justify-content: center;
  padding: 0;
  gap: 0;
  width: 100%;
  position: relative;
}

.s-collapsed .s-nav-link span {
  display: none;
}

.s-collapsed .s-nav-link:hover::after {
  content: attr(title);
  position: absolute;
  left: calc(100% + 0.5rem);
  top: 50%;
  transform: translateY(-50%);
  background: var(--s-text);
  color: var(--s-surface);
  padding: 0.25rem 0.5rem;
  border-radius: var(--s-radius-sm);
  font-size: 0.75rem;
  white-space: nowrap;
  z-index: 300;
  pointer-events: none;
  opacity: 1;
}

/* Sidebar Footer */
.s-sidebar-footer {
  padding: 0.5rem;
  border-top: 1px solid var(--s-sidebar-border);
  flex-shrink: 0;
}

/* ---- Sidebar Inset (main content wrapper) ---- */
.s-sidebar-inset {
  margin-left: var(--s-sidebar-width);
  margin-top: 0.5rem;
  margin-right: 0.5rem;
  margin-bottom: 0.5rem;
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Must track .s-app's unit exactly — see the note there. Left at vh this stayed the
     taller of the two and re-created the unreachable strip on its own. */
  height: calc(100vh - 1rem);
  height: calc(100dvh - 1rem);
  transition: margin-left 200ms cubic-bezier(0.4, 0, 0.2, 1);
  will-change: margin-left;
  background: var(--s-surface);
  border-radius: var(--s-radius-xl);
  border: 1px solid var(--s-border);
  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
  overflow: hidden;
}

.s-collapsed .s-sidebar-inset {
  margin-left: calc(var(--s-sidebar-collapsed) + 0.5rem);
}

/* ---- Header ---- */
.s-header {
  height: var(--s-header-height);
  background: #ffffff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--s-lg);
  position: sticky;
  top: 0;
  /* Must be ABOVE sticky table thead (z-index 50) so the notification panel
     descending from the header stacks above tables on every page. */
  z-index: 200;
  transition: box-shadow 200ms;
  border-bottom: 1px solid transparent;
}

.s-header.scrolled {
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
  border-bottom-color: var(--s-border);
}

/* Toggle Button */
.s-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  color: var(--s-text);
  cursor: pointer;
  border-radius: var(--s-radius-sm);
  transition: background 0.15s;
}

.s-toggle-btn:hover {
  background: var(--s-hover);
}

.s-toggle-btn svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* ---- Content Area ---- */
.s-content {
  /* The app's only page-level scroll container — html/body are overflow:hidden and every
     route renders inside this one <main>. That makes it the right and the *only* place to
     put the end-of-page gutter: set it here and every current and future page inherits it
     with no per-page markup. Do not push this onto individual pages.

     The bottom is a token rather than var(--s-md) so the phone breakpoint can raise it and
     the device's safe-area inset can ride along. On a desktop the inset is 0 and this
     computes to exactly the 16px it always was. */
  padding: var(--s-md);
  padding-bottom: var(--s-page-bottom-gutter);

  /* Landscape notch: viewport-fit=cover lets content sit under a display cutout on the
     long edge. Both are 0 in portrait and on every desktop. */
  padding-left: calc(var(--s-md) + env(safe-area-inset-left, 0px));
  padding-right: calc(var(--s-md) + env(safe-area-inset-right, 0px));

  flex: 1;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ---- Full-height list page layout ---- */
.s-list-page {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.s-list-page > .s-card {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.s-list-page > .s-card > .s-table-wrap {
  overflow-x: auto;
  flex: 1;
  border-top-left-radius: var(--s-radius);
  border-top-right-radius: var(--s-radius);
}

/* Match the rounded card on the sticky thead so its grey background doesn't
   square off the card's rounded top corners. */
.s-list-page > .s-card > .s-table-wrap > .s-table thead tr:first-child th:first-child {
  border-top-left-radius: var(--s-radius);
}
.s-list-page > .s-card > .s-table-wrap > .s-table thead tr:first-child th:last-child {
  border-top-right-radius: var(--s-radius);
}

/* Solid sticky header — applies to every list-page table */
.s-table thead,
.s-table thead tr {
  background: #dde3eb !important;
}
.s-table thead th {
  position: sticky !important;
  top: 0 !important;
  z-index: 50 !important;
  background: #dde3eb !important;
  box-shadow: inset 0 -1px 0 var(--s-border), inset 0 1px 0 var(--s-border) !important;
}


/* Report pages: the .s-card directly wraps the <table> with no .s-table-wrap,
   so the page itself is the scroll container and the sticky thead has no close
   scrolling ancestor to attach to. Adding overflow + max-height here turns the
   card into the scroll region; the .s-table thead th sticky rule above then
   keeps the header pinned to the top of the card while rows scroll. */
.s-card.s-card--scroll {
  overflow: auto;
  max-height: calc(100vh - 240px);
  max-height: calc(100dvh - 240px);
}

/* Center the filter header divs */
.s-table th > div { justify-content: center; }

/* The master pages carry no pagination — they load every row and the table scrolls — so
   .s-list-count is all that sits in the footer slot there. Both stick to the bottom of the
   card so the footer stays put while the rows scroll under it. */
.s-list-page > .s-card > .s-pagination,
.s-list-page > .s-card > .s-list-count {
  position: sticky;
  bottom: 0;
  z-index: 3;
  border-top: 1px solid var(--s-border);
  margin: 0 -0px;
  padding: 8px 12px;
  background: #fff;
  box-shadow: 0 -2px 6px rgba(0,0,0,0.04);
  border-bottom-left-radius: var(--s-radius);
  border-bottom-right-radius: var(--s-radius);
}

.s-list-count {
  font-size: 12.5px;
  color: #6b7280;
  font-family: var(--s-font);
}

/* ---- Header popup backdrop ----
   Invisible click-catcher behind the notification panel and the profile menu, so a click
   anywhere outside them dismisses them. Deliberately transparent: these are menus, not modals,
   and a dimmed page would read as "the app is blocked". It must out-rank the sticky table
   thead (z-index 30) and the sidebar (200) but stay under its own panel. */
.s-menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 99998;
  background: transparent;
}

/* ---- Mobile Overlay ---- */
.s-sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 150;
  opacity: 0;
  transition: opacity 200ms;
  pointer-events: none;
}

/* ---- Mobile Responsive ---- */
@media (max-width: 768px) {
  .s-sidebar {
    transform: translateX(-100%);
    width: var(--s-sidebar-width);
    z-index: 200;
    box-shadow: var(--s-shadow-lg);
    transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1), width 200ms cubic-bezier(0.4, 0, 0.2, 1);
  }

  .s-collapsed .s-sidebar {
    width: var(--s-sidebar-width);
    transform: translateX(-100%);
  }

  .s-mobile-open .s-sidebar {
    transform: translateX(0);
  }

  .s-mobile-open .s-sidebar-backdrop {
    display: block;
    opacity: 1;
    pointer-events: auto;
  }

  .s-sidebar-inset,
  .s-collapsed .s-sidebar-inset {
    margin-left: 0;
    margin-top: 0;
    margin-right: 0;
    margin-bottom: 0;
    border-radius: 0;

    /* ---- THE bottom-of-every-page bug ----
       This rule used to say `min-height: 100vh`, and that one declaration cut the bottom off
       every page in the app on every phone.

       On a phone `vh` is the *large* viewport — the height the page would have if the browser
       retracted its URL bar — so 100vh is roughly the visible height PLUS the URL bar, about
       110px more than you can actually see. `.s-sidebar-inset` already sets
       `height: calc(100dvh - 1rem)` and `dvh` is the correct, currently-visible height. But a
       min-height floor always beats height, so the correct dvh height was overridden and the
       main content wrapper was laid out ~110px taller than the screen.

       Nothing could reach that strip. `.s-app` is `height: 100dvh; overflow: hidden` and this
       element is `overflow: hidden` too, so the extra height was clipped rather than scrolled
       — and `.s-content`, which is `flex: 1` inside here, inherited the overshoot. Scrolling
       .s-content to its very end therefore parked the last card, the pagination footer, the
       Save row and the end-of-page gutter inside a region below the visible screen. Every
       page, every module, and no amount of padding could fix it: the padding was landing in
       the clipped strip too, which is exactly why raising it changed nothing.

       No desktop browser can reproduce this — with no retractable URL bar `vh == dvh`, the
       floor equals the height and nothing overflows. That is why it survived the emulated
       360px checks and only ever showed up on real devices.

       The margins above are zeroed here, so the inset should simply be the full shell height.
       vh first as the fallback for browsers without dvh, matching .s-app and the base rule. */
    height: 100vh;
    height: 100dvh;
  }
}

/* ---- Page Header ---- */
.s-page-header {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: var(--s-lg);
}
.s-page-title { font-size: 1.5rem; font-weight: 600; border: none; outline: none; }
.s-page-subtitle { font-size: 0.875rem; color: var(--s-text-secondary); }

/* ---- Toolbar ---- */
.s-toolbar { display: flex; gap: var(--s-sm); flex-wrap: nowrap; align-items: center; margin-bottom: var(--s-md); }
.s-toolbar .s-select { width: auto; min-width: 0; padding: 6px 8px; font-size: 0.8125rem; }

/* ---- Search Box ---- */
.s-search { position: relative !important; display: inline-flex !important; align-items: center; min-width: 250px; }
.s-search .s-input { padding-left: 36px !important; width: 100% !important; }
.s-search > svg { position: absolute !important; left: 10px !important; top: 50% !important; transform: translateY(-50%) !important; color: var(--s-text-secondary); pointer-events: none; z-index: 1; width: 16px !important; height: 16px !important; overflow: visible !important; }

/* ---- Currency ---- */
.s-currency { font-variant-numeric: tabular-nums; }
.s-currency::before { content: '\20B9'; }

/* ---- Dropdown ---- */
.s-dropdown { position: relative; display: inline-block; }
.s-dropdown-menu {
  position: absolute; top: 100%; right: 0; min-width: 180px;
  background: var(--s-surface); border: 1px solid var(--s-border);
  box-shadow: var(--s-shadow-md); z-index: 500;
  display: none; border-radius: var(--s-radius);
}
.s-dropdown.open .s-dropdown-menu {
  display: block;
  animation: zoomIn 0.15s cubic-bezier(0.16, 1, 0.3, 1);
}
.s-dropdown-item {
  padding: 8px 16px; cursor: pointer; font-size: 0.875rem;
  display: flex; align-items: center; gap: 8px;
  transition: background 0.15s ease;
}
.s-dropdown-item:hover { background: var(--s-hover); }

/* ---- Animations (GPU-accelerated with translate3d/scale3d) ---- */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes slideInRight { from { transform: translate3d(100%, 0, 0); opacity: 0; } to { transform: translate3d(0, 0, 0); opacity: 1; } }
@keyframes zoomIn { from { opacity: 0; transform: scale3d(0.95, 0.95, 1); } to { opacity: 1; transform: scale3d(1, 1, 1); } }
@keyframes zoomOut { from { opacity: 1; transform: scale3d(1, 1, 1); } to { opacity: 0; transform: scale3d(0.95, 0.95, 1); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes s-filter-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }

/* ---- Scrollbar ---- */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; transition: background 0.2s; }
::-webkit-scrollbar-thumb:hover { background: #aaa; }
.s-sidebar::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.12); }

/* ---- Login Page ---- */
.login-page,
.login-page *,
.login-page *::before,
.login-page *::after {
  box-sizing: border-box;
}
.login-page h1,
.login-page h2,
.login-page h3,
.login-page p {
  margin: 0;
}
.login-page a {
  color: inherit;
  text-decoration: none;
  transition: none;
}
.login-page a:hover {
  text-decoration: none;
}
.login-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100svh;
  gap: 1.5rem;
  background: var(--background);
  color: var(--foreground);
  padding: 1.5rem;
  font-family: var(--s-font);
}
@media (min-width: 768px) {
  .login-page { padding: 2.5rem; }
}
.login-container {
  width: 100%;
  max-width: 24rem;
}
.login-form-wrapper {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.login-fieldgroup {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.login-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  text-align: center;
}
.login-logo-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--foreground);
}
.login-logo-link:hover { text-decoration: none; }
.login-logo {
  display: flex;
  width: 2rem;
  height: 2rem;
  align-items: center;
  justify-content: center;
  border-radius: calc(var(--radius) - 2px);
  color: var(--foreground);
}
.login-logo svg { width: 1.5rem; height: 1.5rem; }
.login-logo img { height: 2.5rem; width: auto; }
.login-title {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--foreground);
  margin: 0;
}
.login-description {
  font-size: 0.875rem;
  color: var(--muted-foreground);
  margin: 0;
  text-align: center;
}
.login-description a {
  color: var(--foreground) !important;
  font-weight: 500;
  text-decoration: underline !important;
  text-underline-offset: 4px;
}
.login-description a:hover { text-decoration: underline !important; }
.login-field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.login-label {
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1;
  color: var(--foreground);
}
.login-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.login-forgot {
  font-size: 0.8125rem;
  color: var(--foreground) !important;
  text-decoration: underline !important;
  text-underline-offset: 4px;
}
.login-forgot:hover { color: var(--foreground) !important; text-decoration: underline !important; }
.login-input {
  display: flex;
  height: 2.25rem;
  width: 100%;
  border-radius: calc(var(--radius) - 2px);
  border: 1px solid var(--input);
  background: transparent;
  padding: 0.375rem 0.75rem;
  font-size: 0.875rem;
  font-family: var(--s-font);
  color: var(--foreground);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition: border-color 0.15s, box-shadow 0.15s;
  outline: none;
  box-sizing: border-box;
}
.login-input::placeholder {
  color: var(--muted-foreground);
}
.login-input:focus {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 15%, transparent);
}
.login-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  white-space: nowrap;
  border-radius: calc(var(--radius) - 2px);
  font-size: 0.875rem;
  font-weight: 500;
  font-family: var(--s-font);
  height: 2.25rem;
  padding: 0.375rem 1rem;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  text-decoration: none;
  box-sizing: border-box;
  line-height: 1.25rem;
}
.login-btn svg {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}
.login-btn--primary {
  width: 100%;
  background: var(--primary);
  color: var(--primary-foreground);
  border: none;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.login-btn--primary:hover {
  background: color-mix(in srgb, var(--primary) 90%, black);
}
.login-btn--outline {
  width: 100%;
  background: var(--background);
  color: var(--foreground);
  border: 1px solid var(--input);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.login-btn--outline:hover {
  background: var(--accent);
  color: var(--accent-foreground);
}
.login-separator {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem;
  color: var(--muted-foreground);
  text-transform: uppercase;
}
.login-separator::before,
.login-separator::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}
.login-social-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 640px) {
  .login-social-grid { grid-template-columns: 1fr 1fr; }
}
.login-password-wrapper { position: relative; }
.login-password-wrapper .login-input { padding-right: 2.5rem; }
.login-password-toggle {
  position: absolute; right: 0.75rem; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer; color: var(--muted-foreground);
  padding: 0; display: flex; align-items: center; justify-content: center;
}
.login-password-toggle:hover { color: var(--foreground); }
.login-password-toggle svg { width: 1rem; height: 1rem; }
.login-terms {
  padding: 0 1.5rem;
  text-align: center;
  font-size: 0.75rem;
  color: var(--muted-foreground);
  text-wrap: balance;
  margin: 0;
  line-height: 1.6;
}
.login-terms a {
  color: var(--foreground) !important;
  text-decoration: underline !important;
  text-underline-offset: 4px;
}
.login-terms a:hover { color: var(--foreground) !important; }
.login-alert {
  border-radius: calc(var(--radius) - 2px);
  padding: 0.625rem 0.75rem;
  font-size: 0.875rem;
}
.login-alert--error {
  background: color-mix(in srgb, var(--destructive) 10%, transparent);
  color: var(--destructive);
  border: 1px solid color-mix(in srgb, var(--destructive) 20%, transparent);
}
.login-alert--success {
  background: color-mix(in srgb, hsl(142 76% 36%) 10%, transparent);
  color: hsl(142 76% 36%);
  border: 1px solid color-mix(in srgb, hsl(142 76% 36%) 20%, transparent);
}

/* ---- Auth Pages (Login / Register) ---- */
.s-auth-page {
  /* Same trap as .s-sidebar-inset above, on the one page that does not use the app shell:
     html/body are overflow:hidden, so a 100vh floor on a phone puts ~110px of this page below
     the visible screen with no way to scroll to it — enough to hide the sign-in button on a
     short handset. dvh is the visible height; vh stays first as the fallback. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(to bottom, #eff6ff, #f3f4f6);
  padding: var(--s-md);
}
.s-auth-card {
  background: var(--s-surface); width: 100%; max-width: 28rem;
  border-radius: var(--s-radius-lg); box-shadow: var(--s-shadow-lg);
  overflow: hidden;
}
.s-auth-card-header {
  background: var(--s-primary-dark, #1e2d66); color: white;
  text-align: center; padding: 1.5rem;
}
.s-auth-card-header .s-auth-icon {
  width: 48px; height: 48px; display: flex; align-items: center;
  justify-content: center; margin: 0 auto 12px; color: white;
  background: none; border-radius: 0;
}
.s-auth-card-header .s-auth-icon svg { width: 48px; height: 48px; }
.s-auth-card-header .s-auth-heading {
  font-size: 1.5rem; font-weight: 700; text-align: center;
  margin: 0 0 var(--s-xs); color: white;
}
.s-auth-card-header .s-auth-subtitle {
  font-size: 0.875rem; text-align: center; color: rgba(255,255,255,0.7);
  margin: 0;
}
.s-auth-card-body { padding: 2rem; }
.s-auth-heading {
  font-size: 1.5rem; font-weight: 700; text-align: center;
  margin: 0 0 var(--s-xs); color: var(--s-text);
}
.s-auth-subtitle {
  font-size: 0.875rem; text-align: center; color: var(--s-text-secondary);
  margin: 0 0 var(--s-lg);
}
/* Password show/hide toggle */
.s-password-wrapper { position: relative; }
.s-password-wrapper .s-input { padding-right: 40px; }
.s-password-toggle {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer; color: #9ca3af; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.s-password-toggle:hover { color: #4b5563; }
.s-password-toggle svg { width: 16px; height: 16px; }
.s-auth-forgot {
  display: block; text-align: center; margin-top: var(--s-md);
  font-size: 0.8125rem; color: var(--s-primary); text-decoration: none;
}
.s-auth-forgot:hover { text-decoration: underline; }
.s-auth-divider {
  display: flex; align-items: center; gap: var(--s-md);
  margin: var(--s-lg) 0; font-size: 0.8125rem; color: var(--s-text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.s-auth-divider::before,
.s-auth-divider::after {
  content: ''; flex: 1; border-top: 1px solid var(--s-border);
}
.s-auth-footer {
  text-align: center; margin-top: var(--s-lg); font-size: 0.875rem;
  color: var(--s-text-secondary);
}
.s-auth-footer a { color: var(--s-primary); font-weight: 600; text-decoration: none; }
.s-auth-footer a:hover { text-decoration: underline; }
.s-auth-name-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-md); }
.s-btn--block { width: 100%; justify-content: center; }
.s-btn--outline {
  background: var(--s-surface); color: var(--s-text); border: 1px solid var(--s-border);
}
.s-btn--outline:hover { background: var(--s-hover); }
.s-btn--outline:disabled { opacity: 0.5; cursor: not-allowed; }
@media (max-width: 480px) {
  .s-auth-card-body { padding: 1.5rem; }
}

/* ===== Reports module ===== */
.s-report-hero {
  background: linear-gradient(135deg, var(--s-primary) 0%, var(--s-primary-light) 60%, #5468c7 100%);
  color: #fff;
  border-radius: var(--s-radius-lg);
  padding: 1.75rem 2rem;
  margin-bottom: 1.25rem;
  position: relative;
  overflow: hidden;
  box-shadow: 0 12px 28px -16px rgba(44,65,144,0.55);
}
.s-report-hero::after {
  content: ""; position: absolute; right: -70px; top: -70px;
  width: 280px; height: 280px; border-radius: 50%;
  background: radial-gradient(circle at center, rgba(228,38,38,0.35), transparent 70%);
  pointer-events: none;
}
.s-report-hero__inner { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.s-report-hero h1 { font-size: 1.5rem; font-weight: 700; margin: 0 0 0.35rem; letter-spacing: -0.01em; color: #fff; }
.s-report-hero h1:focus, .s-report-hero h1:focus-visible { outline: none; box-shadow: none; background: transparent; }
.s-report-hero p { margin: 0; opacity: 0.9; font-size: 0.9rem; }
.s-report-hero__meta { display: flex; gap: 1rem; font-size: 0.78rem; opacity: 0.9; align-items: center; }
.s-report-hero__meta b { font-weight: 600; }

.s-kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 1.25rem; }
.s-kpi {
  background: var(--s-surface); border: 1px solid var(--s-border);
  border-radius: var(--s-radius-md);
  padding: 1rem 1.1rem; position: relative; overflow: hidden;
  transition: transform 160ms ease, box-shadow 160ms ease;
}
.s-kpi:hover { transform: translateY(-2px); box-shadow: 0 10px 22px -12px rgba(0,0,0,0.18); }
.s-kpi::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: var(--s-primary);
}
.s-kpi--accent::before { background: var(--s-accent); }
.s-kpi--success::before { background: #2e7d32; }
.s-kpi--warn::before { background: #ed6c02; }
.s-kpi--info::before { background: #0288d1; }
/* A KPI that drills into the rows behind its number. It is a real <button> so it is
   keyboard-reachable; these resets stop the UA button styling from fighting the card. */
.s-kpi--link {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit;
  cursor: pointer; padding-right: 2rem;
}
.s-kpi--link::after {
  content: "\203A"; position: absolute; right: 0.85rem; top: 50%; transform: translateY(-50%);
  font-size: 1.4rem; line-height: 1; color: var(--s-text-muted); opacity: 0.55;
}
.s-kpi--link:hover::after, .s-kpi--link:focus-visible::after { opacity: 1; color: var(--s-primary); }
.s-kpi--link:focus-visible { outline: 2px solid var(--s-primary); outline-offset: 2px; }

/* The tile currently focusing the table below it. Needs to be unmistakable — a filtered report
   that looks unfiltered reads as missing rows. */
.s-kpi--on {
  border-color: var(--s-primary);
  box-shadow: 0 0 0 2px rgba(44, 65, 144, 0.18);
  background: rgba(44, 65, 144, 0.04);
}
.s-kpi--on::after { content: "\00D7"; opacity: 1; color: var(--s-primary); font-size: 1.1rem; }

.s-kpi__label { font-size: 0.7rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--s-text-muted); font-weight: 600; }
.s-kpi__value { font-size: 1.55rem; font-weight: 700; color: var(--s-text); margin: 0.2rem 0 0.15rem; line-height: 1.1; letter-spacing: -0.02em; }
.s-kpi__sub { font-size: 0.76rem; color: var(--s-text-muted); }
.s-kpi__icon {
  position: absolute; right: 0.8rem; top: 0.85rem;
  width: 32px; height: 32px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(44,65,144,0.08); color: var(--s-primary);
}
.s-kpi--accent .s-kpi__icon { background: rgba(228,38,38,0.1); color: var(--s-accent); }
.s-kpi--success .s-kpi__icon { background: rgba(46,125,50,0.12); color: #2e7d32; }
.s-kpi--warn .s-kpi__icon { background: rgba(237,108,2,0.12); color: #ed6c02; }
.s-kpi--info .s-kpi__icon { background: rgba(2,136,209,0.12); color: #0288d1; }

/* ── Clickable KPI tiles ───────────────────────────────────────────────────
   The hover lift is scoped to .s-kpi--clickable rather than every .s-kpi: a tile
   that lifts under the cursor and then does nothing when clicked reads as broken,
   which is what the un-wired tiles used to do.
   The arrow appears on hover/focus and sits in the padding, so it never reflows
   the number above it. */
.s-kpi--clickable { cursor: pointer; }
.s-kpi--clickable:hover { transform: translateY(-2px); box-shadow: 0 10px 22px -12px rgba(0,0,0,0.18); }
.s-kpi--clickable:focus-visible {
  outline: 2px solid var(--s-primary); outline-offset: 2px;
}
.s-kpi--clickable::after {
  content: "→";
  position: absolute; right: 0.9rem; bottom: 0.7rem;
  font-size: 0.85rem; line-height: 1; color: var(--s-primary);
  opacity: 0; transform: translateX(-3px);
  transition: opacity 160ms ease, transform 160ms ease;
}
.s-kpi--clickable:hover::after,
.s-kpi--clickable:focus-visible::after { opacity: 0.75; transform: translateX(0); }

/* Names the dashboard filter a list page was opened with, and clears it. */
.s-kpi-view-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 6px 5px 10px; border-radius: 999px;
  background: var(--s-selected); color: var(--s-primary);
  border: 1px solid rgba(44,65,144,0.22);
  font-size: 12px; font-weight: 500; flex-shrink: 0;
}
.s-kpi-view-chip b { font-weight: 700; }
.s-kpi-view-chip__clear {
  border: 0; border-radius: 999px; cursor: pointer;
  padding: 2px 9px; font: inherit; font-size: 11px; font-weight: 600;
  background: var(--s-surface); color: var(--s-text-secondary);
  border: 1px solid var(--s-border);
}
.s-kpi-view-chip__clear:hover { color: var(--s-error); border-color: var(--s-error); }

.s-report-card-title { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.15rem; display: flex; align-items: center; gap: 0.5rem; }
.s-report-card-sub { font-size: 0.78rem; color: var(--s-text-muted); margin: 0 0 0.85rem; }

/* Distribution bar */
.s-dist { display: flex; width: 100%; height: 10px; border-radius: 999px; overflow: hidden; background: var(--s-border); }
.s-dist > span { display: block; height: 100%; transition: width 300ms ease; }
.s-dist-legend { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.7rem; font-size: 0.75rem; color: var(--s-text-muted); }
.s-dist-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 0.35rem; vertical-align: middle; }
.s-dist-legend b { font-weight: 600; color: var(--s-text); margin-left: 0.2rem; }

/* Funnel */
.s-funnel { display: flex; flex-direction: column; gap: 0.55rem; }
.s-funnel__row { display: grid; grid-template-columns: 120px 1fr 70px; align-items: center; gap: 0.6rem; font-size: 0.82rem; }
.s-funnel__label { color: var(--s-text-secondary); font-weight: 500; }
.s-funnel__bar { height: 22px; background: var(--s-hover, #f5f5f5); border-radius: 6px; overflow: hidden; position: relative; }
.s-funnel__fill { height: 100%; background: linear-gradient(90deg, var(--s-primary), #5468c7); border-radius: 6px; transition: width 400ms ease; }
.s-funnel__fill--accent { background: linear-gradient(90deg, var(--s-accent), #ff6b6b); }
.s-funnel__fill--success { background: linear-gradient(90deg, #2e7d32, #66bb6a); }
.s-funnel__count { font-weight: 600; color: var(--s-text); text-align: right; font-variant-numeric: tabular-nums; }

/* Hub category card */
.s-rcat {
  position: relative; display: flex; flex-direction: column; gap: 0.6rem;
  padding: 1.25rem; background: var(--s-surface);
  border: 1px solid var(--s-border); border-radius: var(--s-radius-lg);
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
  min-height: 172px; text-decoration: none; color: inherit;
}
.s-rcat:hover { transform: translateY(-3px); box-shadow: 0 14px 28px -16px rgba(44,65,144,0.28); border-color: rgba(44,65,144,0.3); }
.s-rcat__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.5rem; }
.s-rcat__icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: rgba(44,65,144,0.08); color: var(--s-primary); flex: none; }
.s-rcat__title { font-size: 1rem; font-weight: 600; margin: 0.25rem 0 0; color: var(--s-text); }
.s-rcat__desc { font-size: 0.82rem; color: var(--s-text-muted); margin: 0; flex: 1; line-height: 1.5; }
.s-rcat__foot { display: flex; align-items: center; justify-content: space-between; margin-top: 0.25rem; }
.s-rcat__cta { color: var(--s-primary); font-weight: 600; font-size: 0.85rem; display: inline-flex; align-items: center; gap: 0.3rem; }
.s-rcat__tag { font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 0.22rem 0.55rem; border-radius: 999px; background: rgba(46,125,50,0.12); color: #2e7d32; }
.s-rcat__tag--soon { background: rgba(158,158,158,0.18); color: #616161; }
.s-rcat__tag--beta { background: rgba(237,108,2,0.14); color: #ed6c02; }
.s-rcat--accent .s-rcat__icon { background: rgba(228,38,38,0.1); color: var(--s-accent); }
.s-rcat--success .s-rcat__icon { background: rgba(46,125,50,0.12); color: #2e7d32; }
.s-rcat--warn .s-rcat__icon { background: rgba(237,108,2,0.12); color: #ed6c02; }
.s-rcat--info .s-rcat__icon { background: rgba(2,136,209,0.12); color: #0288d1; }
.s-report-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }

/* Coming-soon checklist */
.s-soon-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; margin-top: 1rem; }
.s-soon-item { display: flex; align-items: center; gap: 0.65rem; padding: 0.75rem 0.9rem; background: var(--s-surface); border: 1px dashed var(--s-border); border-radius: var(--s-radius-md); font-size: 0.85rem; color: var(--s-text-secondary); }
.s-soon-item svg { color: var(--s-text-muted); flex: none; }

/* Rank / leaderboard list */
.s-rank { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.s-rank__item { display: grid; grid-template-columns: 24px 1fr auto; align-items: center; gap: 0.6rem; font-size: 0.85rem; }
.s-rank__pos { width: 22px; height: 22px; border-radius: 6px; background: rgba(44,65,144,0.08); color: var(--s-primary); font-weight: 700; font-size: 0.72rem; display: flex; align-items: center; justify-content: center; }
/* Matches the first row whether .s-rank__item is the <li> itself or a <button> inside one —
   a drillable rank list wraps the row in a button, which would make :nth-child(1) true for
   every row. */
.s-rank > li:first-child .s-rank__pos { background: rgba(228,38,38,0.12); color: var(--s-accent); }
.s-rank__label { color: var(--s-text); font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.s-rank__value { color: var(--s-text); font-weight: 600; font-variant-numeric: tabular-nums; }
.s-rank__bar { grid-column: 2 / 4; height: 4px; background: var(--s-border); border-radius: 999px; overflow: hidden; margin-top: -0.15rem; }
.s-rank__bar > span { display: block; height: 100%; background: linear-gradient(90deg, var(--s-primary), #5468c7); }

/* Trend chart */
.s-trend { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0.4rem; align-items: end; height: 140px; padding: 0 0 0.25rem; }
.s-trend__col { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; height: 100%; justify-content: flex-end; }
.s-trend__bar { width: 100%; max-width: 28px; background: linear-gradient(180deg, var(--s-primary), #5468c7); border-radius: 6px 6px 0 0; min-height: 4px; transition: height 300ms ease; }
.s-trend__bar--accent { background: linear-gradient(180deg, var(--s-accent), #ff6b6b); }
.s-trend__label { font-size: 0.7rem; color: var(--s-text-muted); font-weight: 500; }
.s-trend__value { font-size: 0.72rem; color: var(--s-text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* A chart element that opens the rows behind it. Real <button>s, so a keyboard reaches them.
   Deliberately sets no `display` — the layout class it is combined with (.s-trend__col,
   .s-funnel__row, .s-rank__item) owns that and would be overridden. */
.s-drillable {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; width: 100%; text-align: inherit; cursor: pointer;
}
.s-drillable:focus-visible { outline: 2px solid var(--s-primary); outline-offset: 3px; border-radius: 4px; }
.s-funnel__row.s-drillable:hover .s-funnel__label,
.s-rank__item.s-drillable:hover .s-rank__label { color: var(--s-primary); }
.s-funnel__row.s-drillable:hover .s-funnel__bar { box-shadow: inset 0 0 0 1px var(--s-primary); }
.s-trend__col.s-drillable:hover .s-trend__bar { filter: brightness(1.2); }
.s-trend__col.s-drillable:hover .s-trend__label { color: var(--s-primary); font-weight: 700; }
.s-dist-legend .s-drillable { width: auto; }
.s-dist-legend .s-drillable:hover { color: var(--s-primary); text-decoration: underline; }

/* Stat grid (inside cards) */
.s-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 0.75rem; margin-top: 0.5rem; }
.s-stat { padding: 0.65rem 0.75rem; border-radius: var(--s-radius-md); background: var(--s-hover, #f7f9fc); border: 1px solid var(--s-border); }
.s-stat__label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--s-text-muted); font-weight: 600; }
.s-stat__value { font-size: 1.1rem; font-weight: 700; color: var(--s-text); margin-top: 0.2rem; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }

/* Dashboard hero overrides */
.s-dash-hero { padding: 2rem 1.75rem; min-height: 132px; display: flex; align-items: center; }
.s-dash-hero .s-report-hero__inner { align-items: center; gap: 1.25rem; flex-wrap: nowrap; width: 100%; }
.s-dash-hero__id { display: flex; align-items: center; gap: 1rem; min-width: 0; flex: 1; }
.s-dash-hero__id > div:last-child { min-width: 0; }
.s-dash-hero__id h1 { font-size: 1.4rem; margin: 0 0 0.35rem; line-height: 1.25; }
.s-dash-hero__id h1:focus, .s-dash-hero__id h1:focus-visible { outline: none; box-shadow: none; background: transparent; }
.s-dash-hero__id p { margin: 0; opacity: 0.9; font-size: 0.85rem; line-height: 1.45; }
.s-dash-hero__avatar {
  width: 52px; height: 52px; border-radius: 14px; flex: none;
  background: rgba(255,255,255,0.18);
  border: 1px solid rgba(255,255,255,0.35);
  color: #fff; display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 1.15rem; letter-spacing: 0.02em;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.s-dash-hero__date {
  text-align: right; flex: none; padding: 0.55rem 0.9rem;
  border-radius: 10px; background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.18);
}
.s-dash-hero__day { font-size: 0.78rem; opacity: 0.8; font-weight: 500; letter-spacing: 0.02em; }
.s-dash-hero__full { font-size: 0.95rem; font-weight: 600; letter-spacing: -0.005em; margin-top: 0.1rem; }
@media (max-width: 720px) {
  .s-dash-hero .s-report-hero__inner { flex-wrap: wrap; }
  .s-dash-hero__date { text-align: left; width: 100%; }
}

/* Bucket (progress segment) */
.s-bucket { display: flex; flex-direction: column; gap: 0.5rem; }
.s-bucket__row { display: grid; grid-template-columns: 90px 1fr 70px; gap: 0.6rem; align-items: center; font-size: 0.82rem; }
.s-bucket__label { color: var(--s-text-secondary); font-weight: 500; }
.s-bucket__track { height: 18px; background: var(--s-hover, #f5f5f5); border-radius: 4px; overflow: hidden; }
.s-bucket__fill { height: 100%; background: linear-gradient(90deg, var(--s-primary), #5468c7); transition: width 400ms ease; }
.s-bucket__count { text-align: right; font-weight: 600; color: var(--s-text); font-variant-numeric: tabular-nums; }

/* ---- Status & Priority badge tones ----
   Emitted by StatusBadge.razor (GetVariant), so these apply app-wide.
   Tailwind 100/700 scale so status and priority read as one system.
   .status-select--* below re-uses the same tones for the inline Status
   dropdown on the leads grid, which is a select rather than a badge. */

.s-badge--priority-high   { background: #ffedd5; color: #c2410c; }
.s-badge--priority-medium { background: #fef9c3; color: #a16207; }
.s-badge--priority-low    { background: #dcfce7; color: #15803d; }

/* Status badges — same tones as the .status-select dropdown below, for the
   lead view header and the BOD/BOQ tables in Related Documents. */
.s-badge--status-info    { background: #dbeafe; color: #1d4ed8; }
.s-badge--status-success { background: #dcfce7; color: #15803d; }
.s-badge--status-warning { background: #fef9c3; color: #a16207; }
.s-badge--status-error   { background: #fee2e2; color: #b91c1c; }
.s-badge--status-default { background: #f3f4f6; color: #4b5563; }

/* Lead-pipeline tones — one hue per LeadStatus so no two stages look alike.
   New=default grey, InProgress=info blue, Won=success, Lost=error, OnHold=warning;
   the four below fill the remaining stages. Same 100/700 scale as above. */
.s-badge--status-contacted   { background: #cffafe; color: #0e7490; } /* cyan   */
.s-badge--status-sitevisit   { background: #fce7f3; color: #be185d; } /* pink   */
.s-badge--status-quoted      { background: #ede9fe; color: #6d28d9; } /* violet */
.s-badge--status-negotiation { background: #ffedd5; color: #c2410c; } /* orange */

/* Width comes from the SSelect inside (grids pass max-width:140px; form
   fields stay full-bleed), so don't constrain it here. */
.status-select { display: block; }
.status-select .s-custom-select-trigger { font-weight: 600; border-color: transparent; }
/* Chevron defaults to grey — follow the pill's text colour instead. */
.status-select .s-custom-select-trigger svg { color: currentColor; }

/* InProgress (also Submitted/Sent on the BOD & BOQ grids) */
.status-select--info .s-custom-select-trigger    { background: #dbeafe; color: #1d4ed8; }
/* Won */
.status-select--success .s-custom-select-trigger { background: #dcfce7; color: #15803d; }
/* OnHold */
.status-select--warning .s-custom-select-trigger { background: #fef9c3; color: #a16207; }
/* Lost */
.status-select--error .s-custom-select-trigger   { background: #fee2e2; color: #b91c1c; }
/* New */
.status-select--default .s-custom-select-trigger { background: #f3f4f6; color: #4b5563; }
/* Contacted */
.status-select--contacted .s-custom-select-trigger   { background: #cffafe; color: #0e7490; }
/* SiteVisitDone */
.status-select--sitevisit .s-custom-select-trigger   { background: #fce7f3; color: #be185d; }
/* Quoted */
.status-select--quoted .s-custom-select-trigger      { background: #ede9fe; color: #6d28d9; }
/* Negotiation */
.status-select--negotiation .s-custom-select-trigger { background: #ffedd5; color: #c2410c; }

/* ---- My Profile (/my/profile) ----
   The page is built from the standard form classes (lead-form / lead-panel / s-grid /
   s-field / s-input / s-badge / lead-actions). Only the few things with no existing
   equivalent live here. Not a .razor.css because App.razor omits the scoped bundle.

   Everything is scoped under .prof-page (the wrapper around the whole page) so the
   responsive rules at the bottom of this block cannot reach the other form pages. */

/* This is a form page, not a data page: capped and centred so the fields stay a sane
   length on a wide monitor instead of a single input spanning 1600px. */
.prof-page { max-width: 1280px; margin-inline: auto; }
.prof-header { display: flex; align-items: center; justify-content: space-between;
               flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }

/* Submit row inside a panel, so the button stays attached to the fields it posts. Same
   look as .lead-actions, but as the panel's own footer rather than a detached bar. */
.prof-panel-actions { display: flex; justify-content: flex-end; gap: 10px;
                      padding: 10px 16px; background: #f8f9fb;
                      border-top: 1px solid var(--s-border); }

/* Column-flex the side-by-side panels so a footer pins to the bottom of the taller one
   instead of floating wherever its own content happens to end. */
.prof-page .lead-two-col > .lead-panel { display: flex; flex-direction: column; }
.prof-page .lead-two-col > .lead-panel > .lead-panel-body { flex: 1 1 auto; }
.prof-page .prof-panel-actions { margin-top: auto; }

/* A column of stacked panels as one half of a .lead-two-col row — Account with Username under it,
   beside Change Password. align-self:start keeps the pair at their natural height instead of the
   grid stretching them to match the taller password panel, which is what left a tall empty box
   under "Employee record" once the mobile and email rows moved into My Details. */
.prof-page .lead-two-col > .prof-col {
  display: flex; flex-direction: column; gap: 12px;
  align-self: start; min-width: 0;
}

/* Save bar for the My Details form. Deliberately NOT position:sticky.
   It was, briefly, so a change made in the top panel could be saved without scrolling — but a
   sticky element stays pinned for the whole height of its container, and that container is the
   entire five-panel form. So the bar floated over the Personal Information and Contact fields the
   moment you were anywhere above the end of the section: solid background, drawn on top, looking
   exactly like a rendering bug. A save bar belongs at the end of what it saves. */

/* My Details is one panel holding five headed groups, so each group needs a divider that reads as
   subordinate to .lead-panel-head rather than competing with it: a rule above, smaller and in the
   secondary colour. The first has no top border — it sits directly under the panel head, and a
   second line there would look like a doubled border. */
.prof-details .prof-group-head {
  margin: 18px 0 10px; padding-top: 14px;
  border-top: 1px solid var(--s-border);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--s-text-secondary);
}
.prof-details .prof-group-head:first-child {
  margin-top: 0; padding-top: 0; border-top: none;
}

.prof-page .prof-save-note {
  margin-right: auto; align-self: center;
  font-size: 0.78rem; color: var(--s-text-secondary);
}

/* Identity block at the top of the Account panel. */
.prof-identity { display: flex; align-items: center; gap: 12px; padding-bottom: 12px;
                 margin-bottom: 4px; border-bottom: 1px solid #f0f1f3; }
.prof-avatar   { width: 44px; height: 44px; flex: none; border-radius: 50%;
                 background: var(--s-primary); color: #fff; display: flex;
                 align-items: center; justify-content: center;
                 font-size: 0.95rem; font-weight: 700; }
.prof-name     { font-size: 0.95rem; font-weight: 700; color: var(--s-text); margin-bottom: 4px; }

/* Password input + reveal button. .s-field is a flex column, so this is the row that
   positions the eye over the input. */
.prof-pw          { position: relative; }
.prof-pw .s-input { padding-right: 38px; }
.prof-eye         { position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
                    background: none; border: none; cursor: pointer;
                    color: var(--s-text-secondary); padding: 2px; display: flex; }
.prof-eye:hover   { color: var(--s-text); }
.prof-eye svg     { width: 17px; height: 17px; }

/* Live requirement ticks under the new-password field. auto-fit rather than a fixed
   two columns: this box sits in a half-width panel on a laptop and a full-width one on
   a tablet, so let it find its own column count instead of guessing per breakpoint. */
.prof-rules { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
              gap: 6px 12px;
              padding: 10px 12px; background: #f8f9fb;
              border: 1px solid var(--s-border); border-radius: 8px; }
.prof-rule  { display: flex; align-items: center; gap: 7px;
              font-size: 0.75rem; color: var(--s-text-secondary); }
.prof-tick  { display: inline-flex; width: 12px; justify-content: center;
              color: var(--s-border); font-weight: 700; }
.prof-rule.is-met            { color: var(--s-success); }
.prof-rule.is-met .prof-tick { color: var(--s-success); }

/* Hidden until the confirm field actually disagrees. */
.prof-mismatch { display: none; }

/* Result banner after a POST round-trip. */
.prof-alert       { padding: 9px 14px; border-radius: 8px; font-size: 0.82rem; border: 1px solid transparent; }
.prof-alert--ok   { background: var(--s-success-light); color: var(--s-success); border-color: var(--s-success); }
.prof-alert--err  { background: var(--s-error-light);   color: var(--s-error);   border-color: var(--s-error); }

/* A long username or email is one unbroken string; without this it pushes the row wider
   than the panel instead of wrapping. min-width:0 lets the flex child actually shrink. */
.prof-page .detail-value { min-width: 0; overflow-wrap: anywhere; }

/* A grid item's automatic minimum size is min-content, and the vehicles table's min-content
   is ~680px — so without this the track grows to fit the table and drags the whole page
   (identity panel and all) out to 680px on a phone. With it the track shrinks to the
   container and the table scrolls inside its own .s-table-wrap, which is the point of that
   wrapper. */
.prof-page .s-grid > * { min-width: 0; }

/* ---- Responsive ----
   The global rule near the top of this file collapses .s-col-* to `span 1` at <=768px but
   never re-declares .s-grid-12, so a .s-col-6 inside a 12-column grid becomes one twelfth
   of the width — the password fields render as ~60px slivers. These rules restate both
   halves together, which is the only way to keep them consistent.

   Tablet keeps two columns (the panels are full width by then, so a half is still ~340px);
   phones go to one. */
@media (max-width: 768px) {
  .prof-page .s-grid-12 { grid-template-columns: repeat(2, 1fr); }
  .prof-page .s-col-6   { grid-column: span 1; }
  .prof-page .s-col-12  { grid-column: span 2; }

  /* Comfortable touch target for the reveal toggle — the icon stays 17px, the hit area grows. */
  .prof-page .prof-pw .s-input { padding-right: 44px; }
  .prof-page .prof-eye { right: 2px; width: 40px; height: 40px; padding: 0;
                         align-items: center; justify-content: center; }
}

@media (max-width: 560px) {
  .prof-page .s-grid-12 { grid-template-columns: 1fr; }
  .prof-page .s-col-12  { grid-column: span 1; }

  /* Label above value: side-by-side wraps the label onto two lines as soon as the value is
     a sentence ("Not linked — My Portal pages are unavailable"). Beats !important-ing the
     inline text-align on every long row. */
  .prof-page .detail-row   { flex-direction: column; align-items: flex-start; gap: 2px; }
  .prof-page .detail-value { text-align: left !important; }

  /* Anything under 16px makes iOS Safari zoom the viewport on focus, which then leaves the
     page scrolled sideways. */
  .prof-page .s-input { font-size: 16px; }

  .prof-page .lead-panel-body     { padding: 12px; }
  .prof-page .prof-panel-actions  { padding: 10px 12px; }
  .prof-page .prof-panel-actions .s-btn { width: 100%; justify-content: center; }

  /* The save bar stacks rather than squeezing the note and the button onto one 360px row,
     where the button would shrink to a couple of words. */
  .prof-page .prof-save-bar { flex-wrap: wrap; padding: 10px 12px; }
  .prof-page .prof-save-note { margin-right: 0; width: 100%; }
  .prof-page .prof-save-bar .s-btn { width: 100%; justify-content: center; }
}


/* ============================================================================
   SITE VISIT — photo picker and detail page
   ---------------------------------------------------------------------------
   Lives here rather than in a .razor.css because scoped styles are not wired up
   in App.razor, so a component stylesheet would be inert.

   Two photo surfaces, deliberately styled apart:
     .svd-odo   two fixed slots, the evidence behind the KM figure and the fuel
                claim. Always exactly start + end.
     .spp/.svd-gallery   a growing gallery of the work itself.
   They are never merged. Someone auditing a travel claim needs to land on the
   two readings, not hunt for them among thirty site pictures.
   ========================================================================= */

/* ---- Page header ---- */
.svd-head { display: flex; align-items: flex-start; justify-content: space-between;
    gap: 12px; margin-bottom: 14px; }
.svd-head-main { min-width: 0; }
.svd-head-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.svd-crumb { display: inline-flex; align-items: center; gap: 3px; font-size: 0.7rem;
    font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase;
    color: var(--s-text-secondary); text-decoration: none; margin-bottom: 3px; }
.svd-crumb:hover { color: var(--s-primary); }
.svd-title { margin: 0; font-size: 1.3rem; font-weight: 700; color: var(--s-text);
    letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; }
.svd-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
    font-size: 0.78rem; color: var(--s-text-secondary); margin-top: 3px; }
.svd-dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: 0.5; }

/* Equal-height panels in the two-column row. Without this the shorter panel stretches and
   leaves a dead band of whitespace under its content. */
.svd-page .lead-two-col > .lead-panel { display: flex; flex-direction: column; }
.svd-page .lead-two-col > .lead-panel > .lead-panel-body { flex: 1 1 auto; }

/* ---- Definition rows ---- */
.svd-dl { margin: 0; }
.svd-dl-row { display: grid; grid-template-columns: 108px 1fr; gap: 10px; align-items: center;
    padding: 7px 0; border-bottom: 1px solid #f0f1f4; }
.svd-dl-row:last-child { border-bottom: none; padding-bottom: 0; }
.svd-dl-row:first-child { padding-top: 0; }
.svd-dl dt { font-size: 0.75rem; color: var(--s-text-secondary); font-weight: 500; }
.svd-dl dd { margin: 0; font-size: 0.85rem; font-weight: 600; color: var(--s-text);
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.svd-time { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.svd-muted { font-weight: 400; color: var(--s-text-secondary); }

/* ---- The odometer sum, written out ---- */
.svd-calc { display: flex; align-items: stretch; gap: 8px; }
.svd-calc-term { flex: 1; min-width: 0; text-align: center; background: #f8f9fb;
    border: 1px solid var(--s-border); border-radius: 8px; padding: 9px 6px; }
.svd-calc-term--total { background: rgba(44,65,144,0.06); border-color: rgba(44,65,144,0.25); }
.svd-calc-num { font-size: 1.15rem; font-weight: 700; color: var(--s-text);
    font-variant-numeric: tabular-nums; letter-spacing: -0.02em; line-height: 1.2; }
.svd-calc-term--total .svd-calc-num { color: var(--s-primary); }
.svd-calc-lbl { font-size: 0.5625rem; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--s-text-secondary); margin-top: 2px; }
.svd-calc-op { display: flex; align-items: center; font-size: 0.95rem; font-weight: 600;
    color: var(--s-text-secondary); flex-shrink: 0; }

.svd-warn { margin-top: 8px; font-size: 0.7rem; color: #b45309; background: #fffbeb;
    border: 1px solid #fde68a; border-radius: 6px; padding: 6px 9px; }

.svd-odo-head { font-size: 0.75rem; color: var(--s-text-secondary); font-weight: 500;
    margin: 14px 0 7px; padding-top: 12px; border-top: 1px solid #f0f1f4; }
.svd-odo-note { margin: 9px 0 0; font-size: 0.6875rem; color: var(--s-text-secondary); }

.spp-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* Applied to both a <label> (gallery) and a <button> (camera) — font-family: inherit is what
   keeps them looking like one pair, since a button otherwise takes the UA's own font. */
.spp-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px;
           padding: 10px 14px; background: #f8f9fb; border: 1px dashed var(--s-border);
           border-radius: 8px; cursor: pointer; font-size: 0.8125rem; font-weight: 600;
           font-family: inherit; color: var(--s-text); min-height: 42px; }
.spp-btn:hover { border-color: var(--s-primary); color: var(--s-primary); }
.spp-btn input[type="file"] { display: none; }
.spp-btn--off { opacity: 0.5; cursor: not-allowed; }
.spp-btn--off:hover { border-color: var(--s-border); color: var(--s-text); }

.spp-note { font-size: 0.6875rem; color: var(--s-text-secondary); margin-top: 6px; }
.spp-note--warn { color: #b45309; }

.spp-grid, .svd-gallery { display: grid; gap: 8px; margin-top: 10px;
    /* auto-fill, not auto-fit: with two photos auto-fit stretches each to half the
       panel. Thumbnails keep one size and the row simply fills up. */
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }

.spp-thumb, .svd-photo { position: relative; border: 1px solid var(--s-border);
    border-radius: 8px; overflow: hidden; background: #f8f9fb;
    transition: transform 140ms ease, box-shadow 140ms ease, border-color 140ms ease; }
.svd-photo:hover { transform: translateY(-2px); border-color: var(--s-primary);
    box-shadow: 0 8px 18px -10px rgba(0,0,0,0.25); }
.spp-thumb img, .svd-photo img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
    display: block; }
.svd-photo img { cursor: zoom-in; }

.spp-del, .svd-del { position: absolute; top: 5px; right: 5px; width: 24px; height: 24px;
    border-radius: 50%; background: rgba(0,0,0,0.55); color: #fff; border: none;
    cursor: pointer; font-size: 0.7rem; line-height: 1; display: flex;
    align-items: center; justify-content: center; }

.spp-src { position: absolute; bottom: 5px; left: 5px; background: rgba(0,0,0,0.6);
    color: #fff; border-radius: 4px; padding: 1px 5px; font-size: 0.5625rem;
    font-weight: 700; letter-spacing: 0.04em; }

.svd-photo-meta { padding: 5px 7px; font-size: 0.6875rem; color: var(--s-text-secondary);
    display: flex; align-items: center; justify-content: space-between; gap: 5px; }
.svd-late { position: absolute; top: 5px; left: 5px; background: #fffbeb; color: #b45309;
    border: 1px solid #fde68a; border-radius: 4px; padding: 1px 5px;
    font-size: 0.5625rem; font-weight: 700; letter-spacing: 0.03em; }

/* The two odometer slots. Fixed pair, visually distinct from the gallery above. */
.svd-odo { display: flex; flex-wrap: wrap; gap: 14px; }
.svd-odo-slot { text-align: center; }
.svd-odo-cap { font-size: 0.625rem; font-weight: 700; letter-spacing: 0.06em;
    color: var(--s-text-secondary); margin-top: 4px; }

/* Read-only fact, shaped like an SInput so a panel of facts lines up with a panel of fields. */
.svd-fact-label { display: block; font-size: 0.75rem; font-weight: 500;
    color: var(--s-text-secondary); margin-bottom: 4px; }
.svd-fact-value { font-size: 0.9375rem; font-weight: 600; color: var(--s-text);
    font-variant-numeric: tabular-nums; }
.svd-fact-sub { font-size: 0.6875rem; color: var(--s-text-secondary); margin-top: 2px; }

.svd-note { background: #f8f9fb; border: 1px solid var(--s-border); border-radius: 6px;
    padding: 9px 11px; font-size: 0.8125rem; white-space: pre-wrap; word-break: break-word; }

.svd-empty { border: 1px dashed var(--s-border); border-radius: 8px; padding: 20px;
    text-align: center; color: var(--s-text-secondary); font-size: 0.8125rem; }

/* Marks a panel whose contents were recorded at the punch and cannot be edited. Notes and site
   photos stay open; times, coordinates and odometer readings never do. Saying which is which
   beats leaving someone hunting for an edit button that does not exist. */
.svd-locked { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
    font-size: 0.5625rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
    color: var(--s-text-secondary); background: #eef0f4; border: 1px solid var(--s-border);
    border-radius: 4px; padding: 2px 6px; }

@media (max-width: 560px) {
  .spp-grid, .svd-gallery { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
  .spp-btn { flex: 1; }
}


/* ============================================================================
   GLOBAL RESPONSIVE
   ---------------------------------------------------------------------------
   Last block in the file on purpose: these rules correct patterns defined much
   earlier, and relying on source order is clearer than an arms race of
   specificity selectors.

   Breakpoints match the rest of the app: 900px (sidebar still visible, panels
   narrow), 768px (sidebar becomes a drawer), 560px (phone).
   ========================================================================= */

/* ---- Grid children must be allowed to shrink ----
   A grid item's automatic minimum size is min-content, so one wide child — a
   table, a long unbroken string — makes its track wider than the container and
   drags the whole page sideways. This is what makes .s-table-wrap's overflow-x
   actually engage instead of being bypassed. */
.s-grid > * { min-width: 0; }

/* ---- 12-column form grids ----
   The 768px block near the top of this file collapses .s-col-* to `span 1` but
   never re-declares .s-grid-12, so a .s-col-6 inside a 12-track grid rendered at
   one twelfth width — roughly 60px, i.e. unusable. Track count and span have to
   change together, which is what these two blocks do. */
@media (max-width: 768px) {
  .s-grid-12 { grid-template-columns: repeat(2, 1fr); }
  .s-col-12  { grid-column: span 2; }
}
@media (max-width: 560px) {
  .s-grid-12 { grid-template-columns: 1fr; }
  .s-col-12  { grid-column: span 1; }
}

/* ---- Dialogs ----
   .s-dialog sets `min-width: 400px; max-width: 90vw`. min-width wins over
   max-width in CSS, so on any viewport under ~444px every dialog in the app was
   wider than the screen — and .s-overlay is overflow:hidden, so the parts that
   hung off the edge could not even be scrolled to. Several dialogs also carry an
   inline min-width (420-560px), which is why this needs !important: an inline
   style cannot be beaten any other way. */
@media (max-width: 640px) {
  .s-overlay { padding: 10px; align-items: flex-start; overflow-y: auto; }
  .s-dialog {
    min-width: 0 !important;
    width: 100%;
    max-width: 100%;
    max-height: calc(100dvh - 20px);
    margin: auto 0;
  }
  .s-dialog-inner { max-height: calc(100dvh - 20px); }

  .s-dialog-title, .s-dialog-header { padding: 14px 16px; font-size: 1rem; }
  .s-dialog-body,  .s-dialog-content { padding: 14px 16px; }

  /* Stacked action buttons get a full-width tap target rather than three
     cramped ones on a single line. */
  .s-dialog-actions { padding: 12px 16px; flex-wrap: wrap; }
  .s-dialog-actions .s-btn { flex: 1 1 130px; justify-content: center; }
}

/* ---- Toolbars, search and page headers ----
   .s-toolbar is flex-wrap:nowrap and .s-search has a 250px floor, so a toolbar
   with a search box plus two filters could not fit a phone and pushed the page
   wide. Wrapping is the whole fix; the search box just needs permission to
   shrink. */
@media (max-width: 900px) {
  .s-toolbar { flex-wrap: wrap; }
  .s-toolbar > * { min-width: 0; }
  .s-search { min-width: 0; flex: 1 1 220px; }

  .s-page-header { flex-wrap: wrap; gap: 10px; }
  .s-page-header > * { min-width: 0; }
}

/* ---- Search and Columns share the top line on a phone ----
   The search box had `flex: 1 1 100%` below 560px, so it claimed a whole line on its own
   and every list page in the app pushed Columns onto a second one — a 40px-tall strip
   holding a single right-aligned button, on 42 pages.

   Two changes make them a pair. The toggle collapses to an icon-only square, which is what
   frees the room: the label and the caret cost ~85px of the ~340px a 360px screen has, and
   the grid icon already reads as "columns" next to a magnifier. And `order` pins the pair
   to the first line regardless of source position — the Clear Filters / Clear Sort buttons
   are rendered *between* the two, so without it they would land between them on the line.
   Everything else in the toolbar keeps the old full-width stacking below the pair.

   !important throughout: every one of these properties is set inline on the component. */
@media (max-width: 640px) {
  .s-toolbar > * { order: 2; }
  .s-toolbar > .s-search { order: 0; flex: 1 1 0; min-width: 0; }
  .s-toolbar > *:not(.s-search):not(.s-coltoggle) { flex: 1 1 100%; }

  .s-coltoggle {
    order: 1;
    display: flex !important;
    margin-left: 0 !important;

    /* The width must be a definite number, not `auto` + aspect-ratio. Sizing the button
       from its own height is circular here: the height comes from `align-self: stretch`,
       which is only resolved *after* the toolbar has laid its line out, so the line budgets
       the bare icon (~21px) and the painted 38px button then hangs 7px off the right of a
       360px screen. 38px matches the search input; the stretch below keeps the two aligned
       even if that input's height changes. */
    flex: 0 0 38px;
    align-self: stretch;
  }
  .s-coltoggle-label, .s-coltoggle-caret { display: none !important; }
  .s-coltoggle-btn {
    width: 100% !important;
    height: 100% !important;
    padding: 0 !important;
    gap: 0 !important;
    justify-content: center;
  }
  .s-coltoggle-btn .s-coltoggle-icon { width: 18px !important; height: 18px !important; }
}

@media (max-width: 560px) {
  .s-page-title { font-size: 1.2rem; }

  /* Toolbar controls go full width and stack: side-by-side halves leave the
     labels of a date filter or a select clipped. The search box is the exception —
     it pairs with the Columns toggle above. */
  .s-toolbar .s-btn { flex: 1 1 auto; justify-content: center; }

  .s-pagination { flex-wrap: wrap; }

  /* iOS Safari zooms the viewport when a focused input is under 16px, and never
     zooms back out — leaving the page scrolled sideways. */
  .s-input, .s-select, .s-textarea, select, textarea,
  input[type="text"], input[type="password"], input[type="email"],
  input[type="number"], input[type="date"], input[type="tel"] { font-size: 16px; }
}

/* ---- Intrinsic form sub-grids ----
   Replaces the hand-written `style="display:grid;grid-template-columns:1fr 1fr"`
   that was scattered through the HR dialogs. auto-fit + minmax means the column
   count follows the space available, so the same markup gives two columns in a
   680px dialog and one on a phone with no breakpoint to keep in sync. */
.s-form-grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.75rem; }
.s-form-grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0.75rem; }
.s-form-grid-2 > *, .s-form-grid-3 > * { min-width: 0; }

/* ---- Page-header action clusters and pagination ----
   Both are inline-styled `display:flex` rows with no wrap, so their buttons ran
   off the right edge on a phone (measured ~490px of content in a 360px viewport
   on /sales/quotations). Only flex-wrap is set here — the inline styles own
   everything else, and none of them declare wrap. */
@media (max-width: 900px) {
  .s-page-header .s-row { flex-wrap: wrap; justify-content: flex-end; }
}
@media (max-width: 560px) {
  .s-page-header .s-row { width: 100%; }
  .s-page-header .s-row .s-btn { flex: 1 1 auto; justify-content: center; }

  .s-pagination, .s-pagination > div, .s-pagination > div > div { flex-wrap: wrap; min-width: 0; }
  .s-pagination { justify-content: center; row-gap: 6px; }

  /* Belt and braces for the 16px input rule above: a wider font must never let a
     control outgrow the column it sits in. */
  .s-input, .s-select, .s-textarea, select, textarea { max-width: 100%; }
}

/* ---- Tab strips ----
   Not inside a media query on purpose. A settings page with seven tabs overflows
   at 768px, a page with three does not until 400px, and picking one breakpoint
   gets it wrong for both. overflow-x:auto costs nothing when the tabs already
   fit — no scrollbar is shown — and turns the strip into its own scroll pane
   exactly when it does not. */
.s-tabs-header {
  overflow-x: auto;

  /* Explicit, not left to the initial `visible`. Setting overflow on one axis forces the other
     from `visible` to `auto`, so this box had become scrollable vertically too — and .s-tab-btn
     carries `margin-bottom: -2px` to sit its underline over the header's border, which is exactly
     the kind of intentional 2px overhang that then reads as vertical overflow. */
  overflow-y: hidden;

  flex-wrap: nowrap;

  /* No visible scrollbar. A tab strip is one row of controls: a scrollbar track under it adds
     height, draws a second horizontal line right below the header's border, and appears even on
     the pages whose tabs fit and never scroll. Hiding the chrome keeps the strip swipeable and
     shift-wheel scrollable without any of that. This is what tab strips normally do. */
  scrollbar-width: none;          /* Firefox, Safari 18.2+ */
  -ms-overflow-style: none;       /* legacy Edge */

  /* Swiping the tabs to their end must not then drag the page or trigger back-navigation. */
  overscroll-behavior-x: contain;
}
.s-tabs-header::-webkit-scrollbar { display: none; }   /* Chrome, older Safari, Samsung Internet */

.s-tab-btn { flex: 0 0 auto; white-space: nowrap; }

/* ── Margin summary (MarginSummary.razor) ───────────────────────────────────────────────
   Only rendered for roles holding CanViewMargin. Three cards sit in a responsive band. */
.s-margin-panel {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@media (max-width: 720px) {
  .s-margin-panel { grid-template-columns: 1fr; }
}

.s-margin-card {
  border: 1px solid var(--s-border);
  border-left-width: 3px;
  border-radius: 8px;
  padding: 10px 14px;
  background: var(--s-surface, #fff);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.s-margin-card--pos { border-left-color: #2E7D32; }
.s-margin-card--neg { border-left-color: var(--s-error); }
.s-margin-card--total { background: var(--s-surface-variant, #f5f6fa); }

.s-margin-card-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--s-text-secondary);
}
.s-margin-card-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.s-margin-card-amount {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.1;
}
.s-margin-card--pos .s-margin-card-amount { color: #2E7D32; }
.s-margin-card--neg .s-margin-card-amount { color: var(--s-error); }

.s-margin-card-pct {
  font-size: 12px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.s-margin-card--pos .s-margin-card-pct { color: #1B5E20; background: #E8F5E9; }
.s-margin-card--neg .s-margin-card-pct { color: #B71C1C; background: #FDECEA; }

.s-margin-card-sub {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 11px;
  color: var(--s-text-secondary);
  border-top: 1px dashed var(--s-border);
  padding-top: 4px;
  margin-top: 2px;
}
.s-margin-card-sub b { font-weight: 600; color: var(--s-text); }

/* Legacy inline margin rows kept for the quotation view's pricing table. */
.s-margin-pos { color: #2E7D32; }
.s-margin-neg { color: var(--s-error); }

/* ============================================================================
   Leave balance cards  —  /my/leave-balances
   ----------------------------------------------------------------------------
   These carry their own grid because .s-form-grid-2 cannot be reused here: it is
   `auto-fit minmax(180px, 1fr)`, and inside a ~250px card interior only one
   column fits, so every label/value pair collapsed onto two separate rows.
   .s-leave-row is an explicit `1fr auto` — label left, figure right, always.
   ============================================================================ */
.s-leave-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s-md);
}

.s-leave-card {
  background: var(--s-surface);
  border: 1px solid var(--s-border);
  border-radius: var(--s-radius-lg);
  padding: 1.1rem 1.15rem 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  position: relative;
  overflow: hidden;
  transition: box-shadow 160ms ease, transform 160ms ease;
}
.s-leave-card:hover { transform: translateY(-2px); box-shadow: var(--s-shadow-md); }

/* Accent rail. The tone is the leave type's health, set by a --tone variable so
   one rule serves every state instead of four near-identical modifiers. */
.s-leave-card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--tone, var(--s-primary));
}

.s-leave-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.s-leave-card__name {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--s-text-secondary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.s-leave-card__figure { display: flex; align-items: baseline; gap: 0.4rem; }
.s-leave-card__value {
  font-size: 2.125rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--tone, var(--s-primary));
}
.s-leave-card__unit { font-size: 0.8125rem; color: var(--s-text-secondary); }

.s-leave-card__meter { display: flex; flex-direction: column; gap: 0.3rem; }
.s-leave-card__meter .s-progress { border-radius: 999px; }
.s-leave-card__meter .s-progress-bar { background: var(--tone, var(--s-primary)); border-radius: 999px; }
.s-leave-card__legend {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.7rem;
  color: var(--s-text-secondary);
  font-variant-numeric: tabular-nums;
}

.s-leave-breakdown {
  border-top: 1px dashed var(--s-border);
  padding-top: 0.55rem;
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
}
.s-leave-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0.75rem;
  font-size: 0.75rem;
}
.s-leave-row > span:first-child { color: var(--s-text-secondary); }
.s-leave-row > span:last-child {
  font-weight: 600;
  color: var(--s-text);
  font-variant-numeric: tabular-nums;
}
.s-leave-row--used > span:last-child { color: var(--s-error); }
.s-leave-row--lapsed > span:last-child { color: var(--s-warning); }

/* The "unlimited / unpaid" type has no quota, so a bar and a remaining figure are
   both meaningless — it gets a plain note in their place. */
.s-leave-card__note {
  font-size: 0.72rem;
  color: var(--s-text-secondary);
  background: var(--s-hover);
  border-radius: var(--s-radius-sm);
  padding: 0.4rem 0.55rem;
  line-height: 1.35;
}

.s-leave-hint { font-size: 0.78rem; color: var(--s-text-secondary); }
.s-leave-hint a { color: var(--s-primary); font-weight: 600; text-decoration: none; }
.s-leave-hint a:hover { text-decoration: underline; }

@media (max-width: 640px) {
  .s-leave-grid { grid-template-columns: 1fr; }
}


/* ============================================================================
   MOBILE — app shell, list tables, dashboard hero
   ---------------------------------------------------------------------------
   Last block in the file, same reason as the GLOBAL RESPONSIVE section above:
   these rules correct patterns declared much earlier and source order is easier
   to follow than an escalating specificity war.

   Desktop is deliberately untouched. The one rule outside a media query is inert
   at laptop widths — it only bites on a box that is already being squeezed.
   ========================================================================= */

/* ---- Never squeeze a block whose height comes from its content ----
   .s-content is `display:flex; flex-direction:column`, so every page-level block
   is a flex item and gets shrunk before the column agrees to scroll. The
   dashboard hero lost 78px that way on both a phone and a tablet:
   `min-height:132px` became the floor it was crushed to and `overflow:hidden`
   swallowed the rest, so "Good morning, QA DIRECTOR" rendered as "DIRECTOR" with
   the greeting cropped off above it and the date chip sliced in half below.

   .s-list-page is the deliberate exception — it is the one child that *should*
   absorb the leftover height and scroll its table inside itself. */
.s-content > *:not(.s-list-page) { flex-shrink: 0; }

@media (max-width: 640px) {
  /* ---- List tables ----
     The table already scrolls inside .s-table-wrap, so nothing was unreachable,
     but it read as broken for two reasons. Every cell wrapped: "Project
     Engineer" over two lines, a date over three, which made an attendance row
     73px tall so four records filled the screen. And once you swiped right the
     name column was gone, leaving columns of numbers belonging to nobody.

     One line per cell plus a pinned identity column fixes both. The table gets
     *wider* as a result, which is the right trade — it is a scroll pane, and
     vertical space is what is scarce on a phone. */
  .s-table th, .s-table td { white-space: nowrap; }
  .s-table th { padding: 8px 10px; }
  .s-table td { padding: 7px 10px; font-size: 0.75rem; }

  /* First column stays put while the rest swipes under it. thead th is already
     `position: sticky !important` for the vertical stick, so this only adds the
     horizontal half; its z-index has to clear the 50 that rule uses. */
  .s-table tbody td:first-child {
    position: sticky; left: 0; z-index: 2;
    background: var(--s-surface);
  }
  /* !important is not decoration here: the sticky-thead rule sets `z-index: 50 !important`
     on every th, so the pinned cell tied with the ones scrolling past it and lost on paint
     order — it stayed in place but was covered, and the header read "Check Out" above a
     column of names. The body cells never had this problem: their siblings have no z-index
     at all. */
  .s-table thead th:first-child { left: 0; z-index: 51 !important; }

  /* The edge line is the affordance: it is what says the table continues past it,
     since a touch device shows no scrollbar until you drag. The header cell has to
     restate the two insets it already carried — that declaration is !important, so
     a plain box-shadow here would be ignored rather than merged. */
  .s-table tbody td:first-child { box-shadow: 1px 0 0 var(--s-border); }
  .s-table thead th:first-child {
    box-shadow: inset 0 -1px 0 var(--s-border), inset 0 1px 0 var(--s-border),
                1px 0 0 var(--s-border) !important;
  }

  /* Row states have to be repainted on the pinned cell — it carries its own
     opaque background, so the row's would otherwise stop at the second column. */
  .s-table tbody tr:hover td:first-child { background: var(--s-hover); }
  .s-table tbody tr.selected td:first-child { background: var(--s-selected); }

  /* Cells that opted into truncation keep it. /hr/site-visits sets its own
     max-width + ellipsis on the Location column; nowrap above is what makes that
     ellipsis appear rather than the text simply being cut. */
  .s-table td[style*="ellipsis"] { max-width: 160px; }

  /* ---- Pagination footer ----
     Three lines on a phone with the middle one half empty. The footer is
     space-between with two children — the record count, and a group holding both
     the page buttons and the Rows selector. That group wraps as a unit, so Rows
     was dragged onto a third line while the space beside "Page 1 of 3 | 54
     records" sat empty.

     display:contents dissolves the group: its two halves become items of the
     footer itself and can be placed independently. Rows fills the empty space,
     the buttons take the line below and centre. Two lines, no hole.
     :has() picks them by what they contain rather than by position, so a page
     that orders the two differently still lands right. */
  .s-pagination { flex-wrap: wrap; justify-content: space-between; row-gap: 8px; column-gap: 10px; }
  /* !important because that group carries an inline `display:flex` on all 12 HR pages
     plus the Settings lists, and an inline declaration cannot be beaten any other way.
     Without it the rule silently no-ops and the `order` values below sort the two halves
     *within* the group instead of within the footer — which moved the record count to the
     bottom and made the footer worse, not better. */
  .s-pagination > div:nth-child(2):has(select) { display: contents !important; }
  .s-pagination > div:first-child { order: 1; }
  .s-pagination > div > div:has(> select) { order: 2; margin-left: auto; }
  .s-pagination > div > div:has(> button) { order: 3; flex: 1 0 100%; justify-content: center; }
}

/* ---- Horizontal strips that must scroll rather than overflow ----
   Not in a media query, and not new behaviour: this is exactly what .s-tabs-header
   already does. A strip written as a plain flex div (RoleEditor's tab row) has no
   class to hang it on, so it gets this one. Costs nothing when the strip fits — no
   scrollbar is drawn — and turns it into its own scroll pane when it does not. */
.s-scroll-x {
  overflow-x: auto;
  overflow-y: hidden;
  flex-wrap: nowrap;
  scrollbar-width: none;          /* Firefox, Safari 18.2+ */
  -ms-overflow-style: none;       /* legacy Edge */
  overscroll-behavior-x: contain;
}
.s-scroll-x::-webkit-scrollbar { display: none; }   /* Chrome, older Safari, Samsung Internet */
.s-scroll-x > * { flex: 0 0 auto; }

@media (max-width: 640px) {
  /* ---- Detail-page headers ----
     `<div style="display:flex; align-items:center; justify-content:space-between;
     margin-bottom:12px">` with an action cluster inside it, repeated verbatim across
     ~35 view and create/edit pages in Sales, Service, Settings, Projects and Masters.
     Neither div declares flex-wrap, so the buttons ran off the right edge — 319px off
     on the quotation view, where Back, Download PDF, Create Revision and Edit share a
     row that will not break.

     Matching on the inline style is not pretty. It is also the only hook those 35
     copies share, and threading a class through all of them would be a far larger and
     riskier edit than the three declarations it would buy. Nothing but wrapping is set
     here; the inline styles still own layout, spacing and alignment. */
  .s-content > div[style*="space-between"][style*="margin-bottom"],
  .s-content > div[style*="space-between"][style*="margin-bottom"] > div { flex-wrap: wrap; }
  .s-content > div[style*="space-between"][style*="margin-bottom"] > div > .s-btn {
    flex: 1 1 auto; justify-content: center;
  }

  /* ---- Tables written without a .s-table-wrap ----
     The related-documents tables on the lead view sit straight inside .lead-panel,
     which is overflow:hidden — so 406px of BOD and BOQ columns, including the Grand
     Total, could not be reached at all. Only tables whose direct parent is not a wrap
     are touched, so every list page keeps the pane it already has.

     `display: block` on a <table> makes the table box itself the scroll container while
     its rows still lay out as a table. It is the one way to give a table a pane without
     wrapping it in new markup.

     Any unwrapped table, not only .s-table: the quotation's Pricing Summary is a bare
     <table> inside a panel that sets overflow:hidden explicitly, and it clipped too. */
  :not(.s-table-wrap) > table { display: block; overflow-x: auto; max-width: 100%; }
}

@media (max-width: 768px) {
  /* ---- Room at the end of a page ----
     There is deliberately NO phone-specific number here. The gutter is
     `var(--s-md) + env(safe-area-inset-bottom)` at every width — see the token in :root.

     This block used to raise it to 40px, and that was compensation for a bug rather than a
     layout decision: `min-height: 100vh` on .s-sidebar-inset was pushing the foot of every
     page below the screen, and padding was the wrong lever for it — the padding landed in the
     clipped strip too. With the shell fixed, the extra was pure white band, and on a list page
     it never scrolled away: .s-list-page is `flex: 1`, so the card always stretches to the
     padding edge and the gap sits under it permanently. Measured ~56px on a gesture-nav phone
     (40 + a 16px inset).

     What is left is the two things that are actually part of the layout: --s-md, the same
     16px this page already uses for its other gutters, and the device's own reserved strip,
     which is not breathing room but compensation and is 0 where there is none. Nothing else
     is anchored to the bottom of the viewport — this app has no bottom navigation and no
     fixed footer, and the header is a flex sibling above .s-content rather than an overlay,
     so neither needs paying for. */

  /* The list page hands its height to the card, so the gutter has to come off the card
     rather than after it — otherwise the sticky pagination footer is what sits against
     the edge. */
  .s-list-page { padding-bottom: 0; }

  /* The drawer is fixed to bottom:0, so its last nav link is the one thing the gutter
     above cannot reach. */
  .s-sidebar-content { padding-bottom: calc(0.25rem + var(--s-safe-bottom)); }

  /* Toasts and dialogs are the other two boxes anchored near the bottom edge. */
  .s-toast-container { bottom: calc(32px + var(--s-safe-bottom)); }
  .s-dialog-actions { padding-bottom: calc(12px + var(--s-safe-bottom)); }
}

@media (max-width: 560px) {
  /* Reclaim the gutter — 8px per side is a 5% wider table on a 360px screen. */
  .s-content {
    padding: 12px 10px var(--s-page-bottom-gutter);
    padding-left: calc(10px + env(safe-area-inset-left, 0px));
    padding-right: calc(10px + env(safe-area-inset-right, 0px));
  }

  /* ---- Dashboard / report hero ----
     Sized for a laptop: 2rem padding, a 132px floor and a row that refuses to
     wrap until 720px. On a phone that leaves the greeting fighting the date chip
     for 270px. */
  .s-report-hero { padding: 1.25rem 1.1rem; }
  .s-dash-hero { padding: 1.25rem 1.1rem; min-height: 0; }
  .s-dash-hero .s-report-hero__inner { flex-wrap: wrap; gap: 0.85rem; }
  .s-dash-hero__avatar { width: 44px; height: 44px; font-size: 1rem; border-radius: 12px; }
  .s-dash-hero__id { gap: 0.7rem; }
  .s-dash-hero__id h1 { font-size: 1.1rem; }
  .s-dash-hero__id p { font-size: 0.8rem; }
  .s-report-hero h1 { font-size: 1.25rem; }

  /* Stat and KPI strips: one 180px floor is wider than half a phone, so the
     tiles were already stacking one-up and wasting the row. 150px gives two. */
  .s-kpi-strip { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
  .s-kpi { padding: 0.8rem 0.85rem; }
  .s-kpi__value { font-size: 1.3rem; }

  /* The icon is absolutely positioned in the tile's corner — free space at 240px,
     directly on top of the label at 150px. Reserving the corner instead only moved
     the problem: "PENDING QUOTATIONS" and "OPEN SERVICE CALLS" then wrapped to two
     lines, which made every tile in that row taller and left the short-labelled
     ones half empty.

     So the icon goes at phone width. It is decoration that repeats what the label
     already says, and the coloured bar down the left edge still tells the tiles
     apart. Every label fits one line with it gone. */
  .s-kpi__icon { display: none; }
  .s-kpi__label { font-size: 0.68rem; }
  .s-kpi__sub { font-size: 0.72rem; }
  .s-stat-grid { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 0.5rem; }

  /* Fixed label columns inside cards — 120px of a 300px card left the value with
     no room, so long values wrapped under a half-empty label. */
  .s-funnel__row { grid-template-columns: 92px 1fr 46px; gap: 0.45rem; font-size: 0.78rem; }
  .s-bucket__row { grid-template-columns: 76px 1fr 46px; gap: 0.45rem; font-size: 0.78rem; }
}

/* ============================================================================
   In-page camera (sahajCamera.capture)
   ----------------------------------------------------------------------------
   A live getUserMedia viewfinder, used everywhere a photo is taken: check-in
   odometer, site-visit odometers, expense receipts, vehicle number plate, site
   photos. It replaced <input type=file capture> as the primary path because a
   picked file arrives in the vendor's format — an iPhone hands over HEIC, which
   no engine decodes through an <img> tag, and the odometer preview silently
   never appeared. A frame off the video stream is drawn to a canvas and encoded
   by us, so every device produces the same JPEG.

   Built in plain DOM on document.body, outside Blazor's render tree: it must
   open on the tap without a circuit round-trip, and a re-render must not be
   able to detach the node the shutter handler is bound to.
   ========================================================================= */
.scam {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 3000;
  background: #000;
  flex-direction: column;
  align-items: stretch;
}
.scam--on { display: flex; }

.scam-video {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  /* contain, not cover: cropping a viewfinder hides the edge of the odometer
     the employee is trying to frame. */
  object-fit: contain;
  background: #000;
}

.scam-msg {
  color: #fff;
  font-family: var(--s-font);
  font-size: 0.875rem;
  text-align: center;
  padding: 0.75rem 1.25rem;
  min-height: 1.25rem;
}

.scam-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  /* The home indicator sits over the bottom strip on an iPhone, and the shutter
     is exactly what lands under it. */
  padding-bottom: calc(1rem + var(--s-safe-bottom));
  background: #000;
}

.scam-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.4);
  color: #fff;
  font-family: var(--s-font);
  font-size: 0.8125rem;
  border-radius: 999px;
  padding: 0.5rem 1rem;
  cursor: pointer;
  /* 44px is the smallest target a thumb hits reliably on a phone. */
  min-height: 44px;
  min-width: 72px;
}

.scam-shutter {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: #fff;
  border: 4px solid rgba(255, 255, 255, 0.35);
  background-clip: padding-box;
  cursor: pointer;
  flex: 0 0 auto;
}
.scam-shutter:active { opacity: 0.7; }

/* The escape hatch: shown only when the stream will not start (permission
   refused, no camera, or a plain-HTTP origin where getUserMedia is blocked).
   A real label wrapping a real input, so the tap is a genuine user gesture —
   a programmatic click after an awaited rejection is not, and iOS blocks it. */
.scam-file {
  display: none;
  margin: 0 1.25rem 1.5rem;
  padding: 0.875rem 1rem;
  text-align: center;
  border: 1px dashed rgba(255, 255, 255, 0.5);
  border-radius: var(--s-radius);
  color: #fff;
  font-family: var(--s-font);
  font-size: 0.875rem;
  cursor: pointer;
}
.scam--failed .scam-file { display: block; }
.scam--failed .scam-shutter { opacity: 0.35; pointer-events: none; }
.scam-file input { display: none; }

/* Landscape on a phone leaves almost no vertical room — put the controls beside
   the viewfinder rather than stealing half the height from it. */
@media (max-height: 480px) and (orientation: landscape) {
  .scam { flex-direction: row; }
  .scam-video { order: 1; }
  .scam-bar {
    order: 2;
    flex-direction: column-reverse;
    padding: 1rem;
    padding-bottom: 1rem;
    padding-right: calc(1rem + env(safe-area-inset-right, 0px));
  }
  .scam-msg { display: none; }
  .scam-shutter { width: 56px; height: 56px; }
}

/* ============================================================================
   Machine filter bar (MachineFilterBar.razor)
   ----------------------------------------------------------------------------
   Sits above a service call's machine table on the job sheet and the call view.
   One AMC contract on a hotel puts every unit in the building on one call, so
   the strip is what turns "read down 32 rows" into "3rd floor, room 204".
   ========================================================================= */
.mfb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--s-border);
  background: var(--s-surface-variant, #fafbfc);
}

/* The search box takes the slack; the pickers stay at a readable fixed width so
   a long product name cannot push the row into a second line on a laptop. */
.mfb-search { flex: 1 1 220px; min-width: 0; }
.mfb-pick { flex: 0 1 160px; min-width: 0; }

.mfb-count {
  font-size: 0.75rem;
  color: var(--s-text-secondary);
  white-space: nowrap;
}

.mfb-clear { flex-shrink: 0; }

/* On a phone the four controls stack rather than shrink to unusable widths —
   this strip is most useful in the field, on the smallest screen. */
@media (max-width: 640px) {
  .mfb { gap: 6px; }
  .mfb-search { flex: 1 1 100%; }
  .mfb-pick { flex: 1 1 calc(50% - 3px); }
}
