:root {
  --brand-purple: #6b3fa0;
  --brand-purple-dark: #4d2b76;
  /* Task #311, 2026-08-10 — added for the sidebar/button gradient treatment below: darker
     anchor for the left edge of the sidebar's gradient, lighter anchor for the right edge of
     button gradients (same left-dark/right-light direction on both, per Scott's spec). */
  --brand-purple-darker: #3d2160;
  --brand-purple-light: #8a5cc0;
  --brand-mint: #3ecf8e;
  --brand-coral: #ff6f61;
  --ink: #2a2438;
  --ink-soft: #6b6478;
  --bg: #f7f5fb;

  /* Task #565, 2026-09-28 — theme-able surface tokens. Every "card" surface (data tables,
     dashboard widgets, calendar tiles, AR tiles, crew board columns, map container) already used
     one repeated hardcoded box-shadow string; that's now var(--card-shadow) so a Dashboard Style
     theme (set via data-theme on .app-shell, see MainLayout.razor) can swap the whole look by
     overriding these custom properties in one place instead of touching every rule. Defaults
     below are exactly today's "classic" look (#311/#384) — no visual change for any install that
     hasn't picked a different DashboardTheme. */
  --card-shadow: 0 0 0 2px rgba(107,63,160,0.32), 0 0 24px rgba(107,63,160,0.26);
  --card-border: none;
  --card-radius: 10px;
  --content-bg: var(--bg);
  --sidebar-bg: linear-gradient(90deg, var(--brand-purple-darker) 0%, var(--brand-purple) 100%);
  --sidebar-border: none;
  --sidebar-text: #ffffff;
  --sidebar-nav-text: rgba(255,255,255,0.85);
  --sidebar-nav-hover-bg: rgba(255,255,255,0.08);
  --sidebar-nav-active-bg: rgba(255,255,255,0.15);
  --sidebar-nav-active-text: #ffffff;
  --sidebar-slogan-color: rgba(255,255,255,0.7);
  --btn-primary-bg: linear-gradient(90deg, var(--brand-purple-dark) 0%, var(--brand-purple-light) 100%);
  --btn-primary-bg-hover: linear-gradient(90deg, var(--brand-purple-darker) 0%, var(--brand-purple) 100%);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: 'Segoe UI', system-ui, sans-serif;
  background: var(--bg);
  color: var(--ink);
}

/* Task #442-446, 2026-08-18 — the new record-opening links (Employee Number/Name, Property
   Address, Customer Name, Vehicle Number/VIN/Plate, Inventory Photo/Name) made the browser's
   default visited-link color (dull purple/grey) show up all over the list pages once clicked,
   which read as broken/inconsistent against the brand purple. Pinning a:visited to the same
   color as a plain link removes that color change globally.
   :not(.nav-item) is required, not just belt-and-suspenders: a:visited is a tag+pseudo-class
   selector (specificity 0,1,1), which outranks .nav-item's single class selector (0,1,0) — so
   without the exclusion this rule overrode the sidebar's white nav text on any link the user
   had already clicked, leaving dark purple text on the purple sidebar background (2026-08-18
   regression, caught live). Other more specific selectors elsewhere (.customer-search-results a,
   .form-actions a, etc.) still win over this regardless. */
a:not(.nav-item):not(.topnav-link) { color: var(--brand-purple-dark); }
a:visited:not(.nav-item):not(.topnav-link) { color: var(--brand-purple-dark); }
a:hover:not(.nav-item):not(.topnav-link) { color: var(--brand-purple); }

/* ---- Login page ---- */
.login-body {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background: linear-gradient(160deg, var(--brand-purple), var(--brand-purple-dark));
}

.login-card {
  background: white;
  padding: 40px;
  border-radius: 16px;
  width: 320px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.25);
}

.login-card h1 {
  margin: 0;
  font-size: 22px;
  color: var(--brand-purple-dark);
}

.login-card .subtitle {
  margin: 2px 0 20px;
  color: var(--ink-soft);
  font-size: 13px;
}

.login-card label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin: 12px 0 4px;
}

.login-card input {
  width: 100%;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid #ddd;
  font-size: 14px;
}

.login-card button {
  margin-top: 20px;
  width: 100%;
  padding: 11px;
  border: none;
  border-radius: 8px;
  background: var(--brand-purple);
  color: white;
  font-weight: 700;
  cursor: pointer;
}

.login-card button:hover { background: var(--brand-purple-dark); }

.login-error {
  background: #fde8e6;
  color: #a33;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13px;
  margin-bottom: 8px;
}

/* Task #253, 2026-08-26 — success counterpart to .login-error above, for the Email Invoice
   confirmation flow (New.razor) and Email Settings "Send Test Email" result. */
.login-success {
  background: #e6f7ea;
  color: #217a3a;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13px;
  margin-bottom: 8px;
}

/* ---- App shell ---- */
/* Task #266, 2026-08-03 — app-shell scroll containment: the shell itself is now pinned to
   exactly the viewport height with its own overflow hidden, so it's .content-body below (not
   the whole document) that scrolls. Before this, nothing set an overflow anywhere, so a tall
   page scrolled the whole document and dragged the sidebar NavMenu and the topbar (EN/ES,
   "Logged in as...", Log Out) out of view along with it. */
.app-shell {
  display: flex;
  height: 100vh;
  overflow: hidden;
}

/* Task #311, 2026-08-10 — sidebar now carries a left-dark/right-light horizontal gradient
   instead of a flat fill, per Scott's approved mockup concept C (reversed direction: darker
   at the left edge, lighter toward the content area on the right). */
.sidebar {
  width: 220px;
  background: var(--sidebar-bg);
  border-right: var(--sidebar-border);
  color: var(--sidebar-text);
  padding: 24px 0;
  flex-shrink: 0;
  height: 100vh;
  overflow-y: auto;
}

.brand { padding: 0 20px 20px; text-align: center; }
.brand-logo { width: 75%; height: auto; object-fit: contain; margin: 0 auto 10px; display: block; }
.brand-name { font-weight: 800; font-size: 17px; line-height: 1.15; color: var(--sidebar-text); }
.brand-slogan {
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--sidebar-slogan-color);
  margin-top: 4px;
}
.brand-sub { font-size: 12px; opacity: 0.75; }

.nav-menu { display: flex; flex-direction: column; }

.nav-item {
  padding: 12px 20px;
  color: var(--sidebar-nav-text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
}

.nav-item:hover { background: var(--sidebar-nav-hover-bg); }
.nav-item.active { background: var(--sidebar-nav-active-bg); color: var(--sidebar-nav-active-text); }
.nav-item.disabled { opacity: 0.5; }
.nav-item .soon {
  font-size: 10px;
  text-transform: uppercase;
  margin-left: 6px;
  border: 1px solid rgba(255,255,255,0.4);
  border-radius: 6px;
  padding: 1px 5px;
}

/* Task #266, 2026-08-03 — height/overflow added so .topbar stays pinned at the top of this
   column and only .content-body (below) scrolls. */
.main-content { flex: 1; display: flex; flex-direction: column; height: 100vh; overflow: hidden; }

.lang-toggle {
  display: flex;
  gap: 4px;
  margin-right: 4px;
}

/* Task #267, 2026-08-03 — padding/font-size trimmed (was 4px 10px / 12px) as part of the ~25%
   topbar height reduction, since these pill buttons were the tallest element in the row and
   governed its actual rendered height. */
.lang-btn {
  border: 1px solid #ddd;
  background: white;
  color: var(--ink-soft);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  cursor: pointer;
}

.lang-btn.active {
  background: var(--brand-purple);
  border-color: var(--brand-purple);
  color: white;
}

/* Task #267, 2026-08-03 — vertical padding cut ~25% (was 14px top/bottom) alongside the
   .lang-btn shrink above, targeting a ~25% reduction in the banner's overall rendered height. */
.topbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 24px;
  padding: 10px 24px;
  background: white;
  border-bottom: 1px solid #eee;
  font-size: 13px;
}

/* Fixed min-width + right-aligned text so switching to the longer Spanish phrase
   ("Sesion iniciada como") doesn't grow this box and shift the lang toggle / logout
   link that sit next to it in this flex-end row (added 2026-07-29). */
.signed-in-as {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}

/* Fixed width sized to fit "Sesión iniciada como" (the longer of the two language variants)
   without wrapping — this is what actually stops the EN/ES toggle from shifting sideways when
   swapping languages (see Decision Log 2026-07-29). min-width alone wasn't enough because it
   only sets a floor; the box still grew past it once the Spanish text needed more room. */
.signed-in-label {
  display: inline-block;
  width: 172px;
  white-space: nowrap;
  text-align: right;
}

/* Fixed width (not min-width), same reasoning as .signed-in-label above — "Cerrar Sesión" is
   wider than "Log Out", and since this is the last element in a flex-end row, letting IT grow
   was dragging every earlier element (including the EN/ES toggle) left when switching to
   Spanish. The min-width-only version (2026-07-29, task #132) missed this half of the shift.
   See Decision Log 2026-07-29, task #138. */
.logout-link {
  color: var(--brand-coral);
  font-weight: 700;
  text-decoration: none;
  width: 92px;
  display: inline-block;
  white-space: nowrap;
  text-align: right;
}

/* Task #266, 2026-08-03 — this is now the app's only scrolling region (see .app-shell above).
   Top padding halved (was 24px all around) plus a heading-margin reset just below, together
   cutting the blank space above every page's title roughly in half — see that rule's comment
   for the full before/after math. */
.content-body { padding: 12px 24px 24px 24px; overflow-y: auto; flex: 1; min-height: 0; background: var(--content-bg); }

/* Task #266, 2026-08-03 — every page's title is a bare, unwrapped <h2> (Home.razor's
   "Dashboard", Customers/Employees/etc.'s own titles), so the un-reset browser default
   top margin (~0.83em of the h2's own 1.5em font-size, ~20px at a 16px base) was stacking on
   top of .content-body's own padding above it. Zeroing it here, combined with the padding cut
   above, is what actually halves the visible gap — halving the padding alone wouldn't have
   touched this default-margin component of the gap at all. */
.content-body h2:first-child { margin-top: 0; }

.hint { color: var(--ink-soft); font-size: 13px; }

/* ---- Buttons / tables / forms ---- */
/* Task #311, 2026-08-10 — buttons now carry the same left-dark/right-light gradient direction
   as the sidebar, per Scott's approved mockup ("reverse intensity on buttons like NavMenu"). */
.btn-primary {
  background: var(--btn-primary-bg);
  color: white;
  border: none;
  border-radius: 8px;
  padding: 9px 16px;
  font-weight: 700;
  cursor: pointer;
  margin-bottom: 16px;
}
.btn-primary:hover { background: var(--btn-primary-bg-hover); }
.btn-primary:disabled { opacity: 0.6; cursor: default; }

/* Task #311, 2026-08-10 — same doubled purple glow ring as the dashboard widgets, extended to
   every list page (Vehicles/Employees/Customers/Properties/Inventory/Equipment/Financials/
   Company all share this one class). */
.data-table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  border-radius: var(--card-radius);
  overflow: hidden;
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}

.data-table th, .data-table td {
  text-align: left;
  padding: 10px 12px;
  font-size: 13px;
  border-bottom: 1px solid #f0eef5;
}

.data-table th {
  background: #efe8f7;
  color: var(--brand-purple-dark);
  font-weight: 700;
}

/* Tasks #226/#227/#228, 2026-08-01 — clickable sortable column headers on Employees/
   Customers/Inventory. */
.data-table th.sortable-th {
  cursor: pointer;
  user-select: none;
}
.data-table th.sortable-th:hover {
  background: #e2d6f2;
}

/* Task #231, 2026-08-01 — permanent sort-direction arrow: faint on columns that are
   sortable but not currently the active sort, full-opacity + correct direction on the
   active column. Signals "this header is clickable" even before the user has sorted. */
.sort-arrow {
  display: inline-block;
  margin-left: 5px;
  font-size: 10px;
  opacity: 0.35;
}
.sort-arrow-active {
  opacity: 1;
}

.form-row { margin-bottom: 14px; max-width: 400px; }
.form-row label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.form-row input, .form-row select {
  width: 100%;
  padding: 9px;
  border-radius: 8px;
  border: 1px solid #ddd;
  font-size: 14px;
}

.form-actions { display: flex; align-items: center; gap: 16px; margin-top: 8px; }
.form-actions a { color: var(--ink-soft); font-size: 13px; }

/* Checkboxes inside a .form-row default to the same 100%-width/block-label rules meant for
   text inputs above, which stretches the box and misaligns the label. This puts the box
   back to its natural size, sitting to the left of and vertically centered with its text. */
.form-row .checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 13px;
  margin-bottom: 0;
  cursor: pointer;
}
.form-row .checkbox-label input[type="checkbox"] {
  width: auto;
  margin: 0;
}

/* Task #321, 2026-08-06 — "price per unit" hint shown next to a Property Details quantity field
   (Square Footage/Floor-Level Windows/Ladder-Access Windows), reusing the same muted-hint look as
   .hint elsewhere on this page. */
.price-per-unit {
  display: block;
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 400;
  margin-top: 4px;
}

/* Task #320 (corrected), 2026-08-06 — Week of (Monday) + New Crew Name now share one row instead
   of stacking (Scott's correction to the first pass, which had them stacked and in the wrong
   order), so the crew board starts higher on the page. */
/* Task #333, 2026-08-06 — align-items changed from flex-end to flex-start: with flex-end, the
   New Crew Name column (label + input + button, taller overall than the Week of column) pushed
   its label 11px above the Week of label, so the field boxes and button all sat higher than the
   Week of date box. flex-start top-aligns both labels (they share identical font-size/margin, so
   this lines them up exactly), which puts both field boxes at the same vertical position. Gap
   inside .crew-toolbar-inline raised from 8px to 24px to match the gap between the two columns,
   per Scott's request. */
/* Task #333 correction, 2026-08-06 — the label-level fix above didn't fully fix it: the New Crew
   Name <input> still sat 7px below the Week of date box and the Add Crew button, because
   .crew-toolbar-inline used align-items: center, which centers the 36px-tall input against the
   34px-tall button instead of top-aligning either of them to the row above. Switched to
   flex-start so the input and button both start at the same y as the Week of box. Also: heading
   margin-bottom and toolbar-row margin-bottom both cut 25% (h2's default ~19.92px -> 15px, row's
   14px -> 10.5px) per Scott's request to tighten the dead space above/below this toolbar. */
.crews-heading {
  margin-bottom: 15px;
}
.crew-toolbar-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 24px;
  margin-bottom: 10.5px;
}
.crew-toolbar-field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 4px;
}
.crew-toolbar-inline {
  display: flex;
  align-items: flex-start;
  gap: 24px;
}
/* Task #342, 2026-08-06 — Scott flagged that even top-aligned, the Week of box (210x39),
   New Crew Name input (200x36), and Add Crew button (105x34) were all visibly different
   sizes. Locked all three to the same 210x39 footprint: input width raised 200->210 and an
   explicit height added (was relying on padding alone, which native <input type=date>
   renders ~3px taller than a text input at identical padding/font-size — see .week-picker-input
   below, same issue). The button gets its own rule further down since fixed-size buttons need
   flex centering instead of relying on padding to size themselves. */
.crew-toolbar-inline input {
  padding: 9px;
  border-radius: 8px;
  border: 1px solid #ddd;
  font-size: 14px;
  width: 210px;
  height: 39px;
}
/* Task #342, 2026-08-06 — Add Crew button locked to the same 210x39 box as the two inputs
   above it. margin-bottom:0 overrides .btn-primary's default 16px (meant for standalone
   buttons, not one sitting in this flex row); display:flex centers the label inside the
   now-fixed box instead of letting padding alone size it. */
.crew-toolbar-inline button {
  width: 210px;
  height: 39px;
  margin-bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* Date box shows the full Monday-Friday range ("08/10/2026 - 08/14/2026") directly in the field
   instead of the single date a native date input normally displays: the input's own text is made
   transparent (the control stays fully functional — clicking anywhere in the box, including the
   calendar icon, still opens the native picker) and an absolutely-positioned overlay span on top
   renders the formatted range. */
.week-picker {
  position: relative;
  width: 210px;
}
.week-picker-input {
  width: 100%;
  height: 39px;
  padding: 9px;
  border-radius: 8px;
  border: 1px solid #ddd;
  font-size: 14px;
  color: transparent;
  background: #fff;
  position: relative;
  z-index: 1;
  box-sizing: border-box;
}
.week-picker-input::-webkit-datetime-edit,
.week-picker-input::-webkit-datetime-edit-fields-wrapper,
.week-picker-input::-webkit-datetime-edit-text,
.week-picker-input::-webkit-datetime-edit-month-field,
.week-picker-input::-webkit-datetime-edit-day-field,
.week-picker-input::-webkit-datetime-edit-year-field {
  color: transparent;
}
/* The overlay span sits ABOVE the input in stacking order (z-index 2 > 1) so its text isn't
   painted over by the input's own opaque white background — the first pass had this backwards
   (span behind the input), which silently hid the range text entirely even though the code was
   otherwise correct. pointer-events:none lets clicks fall through to the input beneath it, so the
   native picker still opens normally. */
.week-picker-display {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  z-index: 2;
  font-size: 14px;
  color: var(--ink);
  white-space: nowrap;
}

/* Task #317, 2026-08-06 — Left/Right month-navigation arrows flanking the dashboard's middle
   mini-calendar header.
   Task #348, 2026-08-06 — Scott: "Center all names of the month in it's widget." The plain
   `justify-content: space-between` version below only centered the month label by coincidence
   (it happens to work when both nav arrows are exactly the same width) — switched to a 3-column
   grid (arrow | label | arrow) with the two side columns pinned to the arrows' own natural
   width and the center column taking the remaining space, so the label is truly centered
   regardless of arrow sizing. The two non-middle months (no arrows, just a plain div) get
   `text-align: center` on the shared base `.mini-cal-header` rule below instead. */
.mini-cal-header.cal-header-with-nav {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 6px;
  /* Task #358, 2026-08-08 — bumped from the base 13px so the centered month name doesn't look
     lost next to the now-doubled 36px arrows; scoped to this nav header only (not past/future
     month headers, which have no arrows and stay at 13px). */
  font-size: 20px;
}
/* Task #358, 2026-08-08 — arrows doubled from 18px to 36px per Scott's request; the header's
   month-name text gets a matching bump (13px -> 20px, base .mini-cal-header rule further down)
   scoped to just this nav-containing header so the plain past/future month headers (no arrows)
   stay at their original size — see the .mini-cal-header.cal-header-with-nav override below. */
.cal-nav-arrow {
  background: none;
  border: none;
  font-size: 36px;
  font-weight: 700;
  line-height: 1;
  color: var(--brand-purple-dark);
  cursor: pointer;
  padding: 0 4px;
  justify-self: start;
}
.cal-nav-arrow:last-of-type {
  justify-self: end;
}
.cal-nav-arrow:hover {
  color: var(--brand-purple);
}

/* ---- Home dashboard: revenue chart + AR widgets (added 2026-07-27) ---- */
/* Task #311, 2026-08-10 — same doubled purple glow ring as the other dashboard/list widgets,
   per Scott's follow-up request extending the treatment to the Revenue chart. */
.chart-bars {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 220px;
  padding: 12px 8px 0;
  background: white;
  border-radius: var(--card-radius);
  margin-bottom: 20px;
  overflow-x: auto;
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}

.bar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  flex: 1 0 34px;
  height: 100%;
  min-width: 34px;
}

.bar-value { font-size: 10px; color: var(--ink-soft); margin-bottom: 2px; white-space: nowrap; }

.bar-track {
  width: 18px;
  flex: 1;
  display: flex;
  align-items: flex-end;
}

.bar-fill { width: 100%; border-radius: 4px 4px 0 0; }
.bar-fill.bar-yearly { background: var(--brand-coral); }
.bar-fill.bar-monthly { background: var(--brand-purple); }

.bar-label { font-size: 10px; color: var(--ink-soft); margin-top: 4px; white-space: nowrap; }

.ar-summary {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.ar-tile {
  background: white;
  border-radius: var(--card-radius);
  padding: 14px 20px;
  cursor: pointer;
  min-width: 130px;
  /* Task #311, 2026-08-10 — replaced the flat drop shadow with a soft purple glow ring
     (concept B from Scott's approved mockup, doubled intensity per his follow-up request:
     2px/32% spread ring + 24px/26% blur, up from the original mockup's 1px/16% + 14px/12%).
     Task #565, 2026-09-28 — now theme-able via --card-shadow/--card-border (see :root). */
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}

.ar-tile:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.12); }

.ar-tile-label { font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.ar-tile-value { font-size: 20px; font-weight: 800; color: var(--brand-purple-dark); margin-top: 2px; }

.data-table th.sortable { cursor: pointer; user-select: none; }
.data-table th.sortable:hover { background: #e5dbf3; }

.btn-secondary {
  background: white;
  color: var(--brand-purple-dark);
  border: 1px solid var(--brand-purple);
  border-radius: 8px;
  padding: 8px 14px;
  font-weight: 700;
  cursor: pointer;
}
.btn-secondary:hover { background: #f3ecfa; }

/* Task #335, 2026-08-06 — "Delete Work Order" button, styled distinctly from btn-primary/
   btn-secondary (both purple/brand-colored) so a destructive action doesn't visually blend in
   with everyday Save/Assign buttons. */
.btn-danger {
  background: white;
  color: #c0392b;
  border: 1px solid #c0392b;
  border-radius: 8px;
  padding: 9px 16px;
  font-weight: 700;
  cursor: pointer;
}
.btn-danger:hover { background: #fdecea; }
.btn-danger:disabled { opacity: 0.6; cursor: default; }

.row-low-stock { background: #fff6e0; }

/* Locked Annual Salary field (task #145, 2026-07-30) */
.locked-salary {
  font-family: monospace;
  letter-spacing: 1px;
  color: var(--ink-soft);
  margin-right: 8px;
}

/* ---- Dashboard layout rework (added 2026-07-28): top band = revenue (narrow) + AR (wide)
   side by side, "today's operations" band below with shopping list / crew+vehicle / calendar. ---- */
.dash-top-band {
  display: grid;
  grid-template-columns: 2fr 3fr;
  gap: 20px;
  align-items: start;
}

@media (max-width: 900px) {
  .dash-top-band { grid-template-columns: 1fr; }
}

.chart-bars-narrow { height: 180px; }
.chart-bars-narrow .bar-col { flex: 1 0 22px; min-width: 22px; }
.chart-bars-narrow .bar-track { width: 12px; }

.ar-customer-panel {
  background: white;
  border-radius: var(--card-radius);
  padding: 16px;
  margin-top: 12px;
  /* Task #311, 2026-08-10 — replaced the flat drop shadow with a soft purple glow ring
     (concept B from Scott's approved mockup, doubled intensity per his follow-up request:
     2px/32% spread ring + 24px/26% blur, up from the original mockup's 1px/16% + 14px/12%).
     Task #565, 2026-09-28 — now theme-able via --card-shadow/--card-border (see :root). */
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}
.ar-customer-panel h4 { margin: 0 0 10px; color: var(--brand-purple-dark); }
.ar-customer-panel .btn-secondary { margin-left: 8px; }

/* Task #359, 2026-08-08 — Work Order edit view (New.razor, edit mode) reworked into a 2-column
   layout so the whole page fits without scrolling: Customer/Property/Service Date/Status/crew
   summary next to Assign Crew, and Property Details next to Amount Charged/Save Changes. Each
   .ar-customer-panel inside becomes a grid item; falls back to a single stacked column below
   900px so narrower windows don't squeeze both panels unreadably thin. */
.wo-two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
.wo-two-col .ar-customer-panel {
  margin-top: 0;
}
@media (max-width: 900px) {
  .wo-two-col {
    grid-template-columns: 1fr;
  }
}

/* Task #549, 2026-09-24 — the #420 12-column .emp-grid layout (and its 900px collapse
   breakpoint) was retired when the Employee Add/Edit page reverted to a plain vertical
   serial-entry form, matching every other profile page. .emp-section-title survives (still
   used for the Pay/PTO Rates, Emergency Contacts, Mobile App Login, Documents, and Uniform
   Items section headers) — it never depended on the grid, just a tighter margin than the
   default h3 so a heading doesn't look detached from the fields immediately below it. */
.emp-section-title {
  margin: 20px 0 8px;
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.modal-card {
  background: white;
  border-radius: 10px;
  padding: 20px 24px;
  width: 360px;
  max-width: 90vw;
  box-shadow: 0 8px 24px rgba(0,0,0,0.2);
}
.modal-card h4 { margin: 0 0 10px; color: var(--brand-purple-dark); }
.modal-card .form-actions { margin-top: 14px; display: flex; gap: 10px; }
/* Task #261, 2026-08-03 — broadened from `a.btn-primary`/`a.btn-secondary` to the bare class,
   so it matches BOTH <a> and <button> elements. The narrower, element-qualified version (task
   #254) only fixed <a>-based buttons; task #255 then added real <button class="btn-primary">
   elements to this same modal (since they open a JS-free popup instead of navigating), which
   silently fell through to the base .btn-primary rule's `margin-bottom: 16px` (meant for
   standalone buttons) and re-introduced the vertical misalignment this rule exists to prevent —
   the primary button visibly sat higher than its `btn-secondary` sibling in the same row. */
.modal-card .form-actions .btn-primary,
.modal-card .form-actions .btn-secondary { text-decoration: none; display: inline-flex; align-items: center; margin-bottom: 0; }
/* Task #254, 2026-08-02 — the rule above set layout properties but never explicitly set
   color, so an <a class="btn-primary"> fell back to the browser's default anchor color
   (gray/blue) instead of inheriting .btn-primary's white — every other btn-primary button
   on the page is a real <button>, which doesn't have this problem. */
.modal-card .form-actions a.btn-primary { color: white; }

/* Task #261, 2026-08-03 — opt-in class for modal button rows that should be centered as a
   group rather than left-aligned (the default .form-actions/.modal-card .form-actions
   behavior, kept as-is everywhere else since other modals — Purchase Confirm, Supply Confirm,
   Crew Assignment — are left-aligned deliberately). Applied only to the specific rows Scott
   flagged: the "Clocked In Without a Schedule" popup and the Assign Job flow's Work-Order-picker
   step. */
.form-actions-centered { justify-content: center; }

/* Task #246, 2026-08-02 — unscheduled clocked-in popup list. */
.unscheduled-popup-list { list-style: none; margin: 12px 0 0; padding: 0; }
.unscheduled-popup-list li { padding: 6px 0; border-bottom: 1px solid var(--border-soft, #eee); font-size: 14px; }
.unscheduled-popup-list li:last-child { border-bottom: none; }

/* Task #378, 2026-08-08 — was 28px to clear the removed "Today's Operations" heading above it;
   tightened now that the heading is gone, so this band sits closer to the calendar/WO-list row
   above it instead of leaving a labeled gap. */
.dash-ops-band { margin-top: 12px; }

/* Task #378, 2026-08-08 — 3-column split: Crew & Vehicle Status (wide, room for 5 crew cards)
   · Vehicles (its own column now, moved out from the bottom of the wide column) · Shopping
   List / Requests (narrowest, furthest right). The @media collapse-to-1-column rule below still
   applies to all three unchanged. */
.dash-ops-grid {
  display: grid;
  grid-template-columns: 3fr 1.1fr 1fr;
  gap: 20px;
}
.dash-ops-col-narrow, .dash-ops-col-vehicles { min-width: 0; } /* lets <li> text wrap instead of forcing the column wide */

/* ---- Home dashboard top: 3-month calendar band (added 2026-07-29, replaces the narrow
   ops-band calendar column - moved to the top of the page, full width, larger cells). ---- */
.top-calendar-band {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 20px;
  margin-bottom: 20px;
}

@media (max-width: 1000px) {
  .top-calendar-band { grid-template-columns: 1fr; }
}

.top-calendar-band .mini-calendar {
  background: white;
  border-radius: var(--card-radius);
  padding: 16px;
  /* Task #311, 2026-08-10 — replaced the flat drop shadow with a soft purple glow ring
     (concept B from Scott's approved mockup, doubled intensity per his follow-up request:
     2px/32% spread ring + 24px/26% blur, up from the original mockup's 1px/16% + 14px/12%).
     Task #565, 2026-09-28 — now theme-able via --card-shadow/--card-border (see :root). */
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}

/* Task 2026-08-10 — Scott: "left and right calendars' font and symmetry should match the
   current month calendar; the scroll arrows added to the current month broke the symmetry."
   Root cause was two-fold: (1) this rule's 15px was winning a specificity tie against
   `.mini-cal-header.cal-header-with-nav`'s own 20px (both are two-class selectors with equal
   specificity, so the one appearing later in the file — this one — won for every header,
   silently undoing the 20px bump task #358 intended for the current month's label). Bumped to
   20px here so all three headers actually render at the same size Scott approved for the
   current month. (2) The current month's header renders taller than the side months' (36px vs
   20px) because its 36px nav arrows (task #358) push the row height up, while the plain
   past/future headers have no arrows to do the same — so the actual calendar grids below them
   started at different vertical offsets (measured live: side tables started 16px higher than
   the current month's), which is the real "broken symmetry." Fixed by giving the non-nav
   headers a matching 36px-tall flex box (centered) below, scoped with :not() so it never touches
   the arrow header's own grid layout. */
.top-calendar-band .mini-cal-header { font-size: 20px; margin-bottom: 8px; }
.top-calendar-band .mini-cal-header:not(.cal-header-with-nav) {
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.top-calendar-band .mini-cal-table td { padding: 8px 0; font-size: 13px; }

/* Task #230, 2026-08-01 — the daily work order list and the "Invoices to Process" widget sit
   side by side, same grid pattern as .dash-ops-grid below. Task #273, 2026-08-04 — a third
   placeholder panel ("Work Requests to Approve") added for Scott to evaluate the 3-up layout;
   grid-template-columns widened from 1fr 1fr accordingly. */
.top-wo-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 20px;
  margin-bottom: 28px;
}
@media (max-width: 900px) {
  .top-wo-row { grid-template-columns: 1fr; }
}

.top-wo-list-panel {
  background: white;
  border-radius: var(--card-radius);
  padding: 16px 20px;
  margin-bottom: 0;
  /* Task #311, 2026-08-10 — replaced the flat drop shadow with a soft purple glow ring
     (concept B from Scott's approved mockup, doubled intensity per his follow-up request:
     2px/32% spread ring + 24px/26% blur, up from the original mockup's 1px/16% + 14px/12%).
     Task #565, 2026-09-28 — now theme-able via --card-shadow/--card-border (see :root). */
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}
.top-wo-list-panel h3 { margin: 0 0 10px; color: var(--brand-purple-dark); }
.top-wo-list-panel ul { padding-left: 18px; margin: 6px 0; }

/* CnT runs 3 crews day-to-day, but the calendar reads live Work Orders now (see task #130/#139
   Decision Log 2026-07-29), so a date can end up with more than 3 — orange gets 4 progressively
   darker shades (3/4/5/6+) instead of one flat tone, so a genuinely overbooked day still reads
   as more alarming than a merely-full one. Placed before .cal-today/.cal-selected below, which
   now draw an inset border rather than a solid fill (task #140, 2026-07-29) so the job-count
   background color stays visible underneath either border. Green/yellow deepened 2026-07-29 per
   Scott's feedback that the original shades were too washed out to read at a glance. */
.mini-cal-table td.cal-jobs-1 { background: #a8e3b8; } /* green — 1 job */
.mini-cal-table td.cal-jobs-2 { background: #f7dd6e; } /* yellow — 2 jobs */
.mini-cal-table td.cal-jobs-3 { background: #fac387; } /* orange, level 1 — 3 jobs */
.mini-cal-table td.cal-jobs-4 { background: #f6a24f; } /* orange, level 2 — 4 jobs */
.mini-cal-table td.cal-jobs-5 { background: #ef7f2b; color: white; } /* orange, level 3 — 5 jobs */
.mini-cal-table td.cal-jobs-6 { background: #c85a12; color: white; } /* orange, level 4 — 6+ jobs */

@media (max-width: 1100px) {
  .dash-ops-grid { grid-template-columns: 1fr; }
}

.dash-ops-col {
  background: white;
  border-radius: var(--card-radius);
  padding: 16px;
  /* Task #311, 2026-08-10 — replaced the flat drop shadow with a soft purple glow ring
     (concept B from Scott's approved mockup, doubled intensity per his follow-up request:
     2px/32% spread ring + 24px/26% blur, up from the original mockup's 1px/16% + 14px/12%).
     Task #565, 2026-09-28 — now theme-able via --card-shadow/--card-border (see :root). Kept
     separate from the pulse-border keyframe below, which still animates its OWN box-shadow
     directly (unaffected by --card-shadow) so the pulsing effect stays identical across themes. */
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}
.dash-ops-col h4 { margin: 0 0 10px; color: var(--brand-purple-dark); font-size: 14px; }

/* Task #477, 2026-08-23 — optional (Company Profile checkbox) pulsating border on the Shopping
   List/Requests widget while it has pending items. Reuses the same purple glow ring .dash-ops-col
   already has (task #311) as the animation's low point, breathing out to a wider/brighter ring
   and back on a 2-second cycle.
   2026-08-24 — Scott asked for the ring to read 3x thicker without pushing the widget's outward
   footprint further into neighboring cards. The original outset ring (2px/4px) is left as-is;
   the extra 2x width is added as a matching inset shadow, so the added girth eats inward into the
   widget instead of growing outward. Total ring width: 6px rest / 12px peak (3x the original 2px/4px). */
@keyframes pulse-border-glow {
  0%, 100% {
    box-shadow: 0 0 0 2px rgba(107,63,160,0.32), inset 0 0 0 4px rgba(107,63,160,0.32), 0 0 24px rgba(107,63,160,0.26);
  }
  50% {
    box-shadow: 0 0 0 4px rgba(107,63,160,0.7), inset 0 0 0 8px rgba(107,63,160,0.7), 0 0 34px rgba(107,63,160,0.55);
  }
}
.dash-ops-col.pulse-border {
  animation: pulse-border-glow 2s ease-in-out infinite;
}

.shopping-list, .crew-list, .vehicle-status-list { list-style: none; padding: 0; margin: 0; }
.shopping-list li, .vehicle-status-list li {
  padding: 8px 0;
  border-bottom: 1px solid #f0eef5;
  font-size: 13px;
}
.shopping-list li:last-child, .vehicle-status-list li:last-child { border-bottom: none; }
.shop-vendor { font-weight: 700; color: var(--brand-purple-dark); }
/* Task #584, 2026-10-01 — "Buy N" link button to an inventory item's purchase web link, when set. */
.buy-link {
  font-weight: 600;
  color: var(--brand-purple-dark, #4b2e83);
  text-decoration: underline;
  cursor: pointer;
}
.buy-link:hover { text-decoration: none; }
.shopping-list button, .vehicle-status-list button {
  float: right;
  border: 1px solid #ddd;
  background: white;
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 12px;
  cursor: pointer;
}

.crew-list { font-size: 13px; }
.crew-list > li { margin-bottom: 10px; }
.crew-list ul { list-style: none; padding-left: 14px; margin: 4px 0 0; }
.crew-in { color: #1a7a3f; }
.crew-out { color: var(--ink-soft); }
.crew-list-item { cursor: pointer; border-radius: 6px; padding: 4px 6px; margin: -4px -6px 10px; }
.crew-list-item:hover { background: #f3ecfa; }

/* Task #377, 2026-08-08 — Crew & Vehicle Status cards, side by side instead of the old vertical
   .crew-list stack. flex-wrap so a 6th+ crew wraps to a second row rather than overflowing or
   shrinking illegibly; basis/max-width tuned so 5 fit across the widened .dash-ops-col-wide
   column at a normal window size. */
.crew-status-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.crew-status-card {
  cursor: pointer;
  border: 1px solid #e5e0ee;
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  flex: 1 1 180px;
  min-width: 160px;
  max-width: 220px;
}
.crew-status-card:hover { background: #f3ecfa; }
.crew-status-card ul { list-style: none; padding: 0; margin: 4px 0 0; }
.csc-line1 { font-size: 12px; }
.csc-line2 { color: var(--ink-soft); margin: 2px 0; }
.csc-status { font-weight: 700; color: var(--brand-purple-dark); margin: 2px 0 4px; }

/* Crew & Vehicle Status modal — drag-and-drop crew roster editor (task #174, 2026-07-30).
   Task #241, 2026-08-02: this card had no height cap at all, so a day with several crews
   (each with its own vehicle-override row per task #221/#222) or one crew with a long roster
   could push the modal taller than the viewport with no way to reach the Close button short
   of scrolling the whole page behind the overlay. Capped the card itself at 85vh and made it a
   flex column so the title/hint/error/Close button always stay in view — only the column strip
   in the middle scrolls (horizontally for many crews, and now each column body scrolls
   vertically on its own past a handful of members) instead of the modal growing without bound. */
.crew-modal-card {
  max-width: 900px;
  width: 92vw;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  padding-bottom: 16px;
}
.crew-modal-columns {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 4px;
  margin-top: 10px;
  flex: 1;
  min-height: 0;
}
.crew-modal-column {
  flex: 0 0 190px;
  border: 1px solid #e5e0ee;
  border-radius: 8px;
  background: #faf9fc;
  min-height: 120px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
}
.crew-modal-column-header {
  font-weight: 700;
  font-size: 13px;
  color: var(--brand-purple-dark);
  padding: 6px 10px;
  border-bottom: 1px solid #e5e0ee;
}
.crew-modal-column-subheader { font-weight: 400; font-size: 11px; color: var(--ink-soft); margin-top: 2px; }
.crew-modal-column-body {
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1;
  overflow-y: auto;
  min-height: 0;
}
.crew-chip {
  background: white;
  border: 1px solid #ddd;
  border-radius: 6px;
  padding: 5px 7px;
  font-size: 12px;
  cursor: grab;
}
.crew-chip:active { cursor: grabbing; }
/* role-driver/role-supervisor color chips retired 2026-07-31 (task #202) — roles are now shown
   as D -/S -/DS - text prefixes instead of color-coding, on both the Home dashboard's Crew &
   Vehicle Status modal and the Pick Crew Members board below. */

/* Task #351, 2026-08-06 — Pick Crew Members drag-drop grouping. A grouped chip in the
   Unassigned column (CSS grid, task #310) uses `grid-row: span N` set inline per-group in
   Index.razor, so the box's height comes from spanning that many grid rows (plus the grid's own
   row-gap) — naturally "double height" for a pair, and it scales for larger groups too. The
   purple-tinted background + left accent bar just makes a grouped box visually distinct from a
   plain single chip at a glance. */
.crew-chip-group {
  background: #f3ecfa;
  border: 1px solid #cbb8e8;
  border-left: 3px solid var(--brand-purple);
  border-radius: 6px;
  padding: 5px 7px;
  font-size: 12px;
  cursor: grab;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}
.crew-chip-group:active { cursor: grabbing; }

/* Same idea for a group sitting on a crew's own roster — the crew column is plain flex, not a
   grid, so this is just a bordered wrapper around each member's normal .crew-chip-assigned row
   (each member keeps their own D/S checkboxes) rather than a grid-row-span box. */
.crew-chip-group-wrapper {
  background: #f3ecfa;
  border: 1px solid #cbb8e8;
  border-left: 3px solid var(--brand-purple);
  border-radius: 6px;
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.crew-chip-group-wrapper .crew-chip-assigned {
  background: white;
}

/* Task #475, 2026-08-23 — click-select replaces drag-and-drop for assigning a name to a crew
   (Manager/Owner mobile devices can't drag reliably). A chip already sitting on a crew is no
   longer draggable at all, so its grab cursor is dropped; only the name text itself is clickable
   (Scott: "name field changes to Brand Primary Color"), everything else on the chip (D/S
   checkboxes) stays independently clickable without toggling selection. */
.crew-chip-assigned,
.crew-chip-group-wrapper {
  cursor: default;
}
.crew-chip-name {
  cursor: pointer;
  display: inline-block;
}
.crew-chip-name.selected {
  color: var(--brand-purple);
  font-weight: 700;
}

/* Right-click "Separate" menu for a grouped chip — a full-page transparent overlay (click
   anywhere outside the menu to dismiss it) plus a small fixed-position menu positioned at the
   click coordinates captured in Index.razor's OnGroupContextMenu. */
.group-context-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
}
/* Task #591, 2026-10-06 — permanent-or-temporary popup for mid-week roster changes. */
.persist-mode-dialog {
  position: fixed;
  z-index: 102;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: white;
  border: 1px solid #ccc;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.25);
  padding: 18px 22px;
  max-width: 420px;
}
.persist-mode-dialog h3 { margin-top: 0; }
.persist-mode-buttons { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.group-context-menu {
  position: fixed;
  z-index: 101;
  background: white;
  border: 1px solid #ddd;
  border-radius: 6px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.15);
  padding: 4px;
}
.group-context-menu button {
  background: none;
  border: none;
  padding: 6px 14px;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  border-radius: 4px;
  white-space: nowrap;
}
.group-context-menu button:hover {
  background: #f3ecfa;
}

/* Pick Crew Members board — Unassigned column + one column per crew, drag-and-drop
   (task #201/#202, 2026-07-31, replacing the old checkbox-table layout from task #173). */
/* Task #311 fix, 2026-08-10 — the doubled glow ring added below was getting hard-clipped on
   the left (Unassigned, the first column) and across the top of every column, because
   `overflow-x: auto` here also forces `overflow-y` to compute to `auto` (per the CSS Overflow
   spec, an axis left at its `visible` default becomes `auto` when the other axis isn't
   `visible`), and this container had zero padding on those sides — so the clip boundary sat
   right at the columns' own edges with no room for the shadow to render. Fixed with the
   standard "padding + equal negative margin" trick: padding-top/left/right give the shadow
   room inside the scrolling box's clip boundary, and the matching negative margin on those
   same sides pulls the box back so the columns land in exactly the same on-screen position as
   before — nothing about the visible layout shifts, only the shadow gets space to render. */
.crew-board {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding: 24px 24px 8px 24px;
  margin: -24px -24px 16px -24px;
}
/* Task #311, 2026-08-10 — swapped the plain 1px border for the same doubled purple glow ring
   used on dashboard widgets and list tables, per Scott's follow-up request. */
.crew-board-column {
  flex: 1 1 0;
  min-width: 160px;
  border-radius: 8px;
  background: #faf9fc;
  display: flex;
  flex-direction: column;
  box-shadow: var(--card-shadow);
}
.crew-board-unassigned {
  flex: 1.8 1 0;
  min-width: 260px;
  background: #f3ecfa;
}
/* Task #310, 2026-08-05 — 2 columns so a full Unassigned list fits on one page instead of
   scrolling. The sort-to-top-by-role behavior (RoleSortRank in Crews/Index.razor) already
   existed and is unaffected — DS/D/S/plain groups still fill the earliest grid cells first
   (row-by-row, left-to-right), same relative order as before, just wrapped into 2 columns. */
.crew-board-unassigned .crew-board-column-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-flow: row;
  gap: 6px;
  align-content: start;
}
.crew-board-column-header {
  padding: 8px 10px;
  border-bottom: 1px solid #e5e0ee;
  font-weight: 700;
  font-size: 13px;
  color: var(--brand-purple-dark);
}
.crew-board-column-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 4px;
}
.crew-board-active-toggle {
  font-weight: 400;
  font-size: 12px;
  margin-bottom: 4px;
}
.crew-board-vehicle-select {
  width: 100%;
  padding: 5px;
  border-radius: 6px;
  border: 1px solid #ddd;
  font-size: 12px;
}

/* Task #400, 2026-08-12 — Job Map page (Pages/Map.razor). */
.map-page-header {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 10px;
}
.map-page-header h2 { margin: 0; }
/* Task #586, 2026-10-04 — selected jobs' "#job — address" list, full-width row under the title/hints. */
.map-page-header { flex-wrap: wrap; }
.map-selected-list {
  flex-basis: 100%;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  max-height: 90px;
  overflow-y: auto;
  font-size: 0.9rem;
}

/* Task #413, 2026-08-14 — positioning anchor for the selection badge + assign panel overlays. */
.pbss-map-wrapper {
  position: relative;
}

.pbss-map-container {
  width: 100%;
  height: calc(100vh - 130px);
  border-radius: var(--card-radius);
  overflow: hidden;
  box-shadow: var(--card-shadow);
}

/* A pin's colored circle + job number/range label, built in JS (site.js pbssMap.addMarker) and
   dropped onto the map via a MapLibre GL marker element. Plain circle (not a teardrop) so the
   job-number text inside never has to be counter-rotated to stay upright. */
.pbss-map-pin {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid white;
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: white;
  font-weight: 700;
  font-size: 11px;
}
/* Task #413, 2026-08-14 — selected-pin state (left-click on the Job Map). Thick black ring drawn
   just outside the existing white ring so it reads clearly against any pin color. */
.pbss-map-pin.selected {
  border: 2px solid white;
  box-shadow: 0 0 0 3px #000, 0 1px 4px rgba(0,0,0,0.4);
}

/* Task #413, 2026-08-14 — the multi-job pin picker (site.js pbssMap.openPicker), a plain floating
   DOM box (not a MapLibre popup) positioned at the click coordinates. */
.pbss-map-picker {
  position: fixed;
  z-index: 2000;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.35);
  padding: 10px 12px;
  min-width: 220px;
  max-width: 320px;
  font-size: 13px;
}
.pbss-map-picker-title {
  font-weight: 600;
  margin-bottom: 6px;
}
.pbss-map-picker-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 0;
  cursor: pointer;
}
.pbss-map-picker-done {
  margin-top: 8px;
  width: 100%;
  padding: 4px 0;
  border: none;
  border-radius: 5px;
  background: #6b3fa0;
  color: white;
  cursor: pointer;
  font-size: 12px;
}

/* Task #413, 2026-08-14 — floating "N selected" badge + the batch Assign to Crew panel, both
   overlaid on the map container (Map.razor). */
.pbss-map-selection-badge {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 1500;
  background: rgba(0,0,0,0.78);
  color: white;
  padding: 8px 14px;
  border-radius: 20px;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.pbss-map-selection-badge button {
  border: none;
  background: rgba(255,255,255,0.18);
  color: white;
  border-radius: 12px;
  padding: 3px 10px;
  cursor: pointer;
  font-size: 12px;
}
.pbss-map-assign-panel {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 1500;
  background: white;
  border-radius: 10px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.35);
  padding: 16px;
  width: 300px;
}
.pbss-map-assign-panel h4 { margin: 0 0 10px 0; }
.pbss-map-assign-panel select {
  width: 100%;
  padding: 6px;
  margin-bottom: 10px;
}
.pbss-map-assign-panel .pbss-map-assign-actions {
  display: flex;
  gap: 8px;
  margin-top: 6px;
}
/* Task 2026-08-10 (corrected) — the first pass set min-height to 216px, matching Crew 1's
   content height at the moment it happened to have 5 members. Scott clarified the real intent:
   the default/empty height should have room for a grouped pair (like Nilyan/Diana, measured at
   98px) plus 4 more individual members (measured at 28px each) WITHOUT stretching to fit, plus
   extra blank space below that so a 6th member can be dragged onto open space in the list
   instead of being forced to drop directly on an existing chip. Measured live: pair (98) + 4
   singles (4x28=112) + 4 gaps between those 5 items (4x6=24) + top/bottom padding (16) = 250px
   snug fit for 6 members, then +1 more gap (6) + 1 chip-height blank drop zone (28) = 284px.
   As with the first pass, this is a floor only — flex `align-items: stretch` on `.crew-board`
   still equalizes columns to whichever is naturally tallest, so a heavily-loaded crew will grow
   taller than this on its own; this floor just guarantees every column (empty or not) starts
   with enough room for 6 members plus a drop target, not just whatever Crew 1 happens to hold
   at a given moment. */
.crew-board-column-body {
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  min-height: 284px;
}
.crew-board-column-footer { padding: 8px 10px; }
.crew-chip-assigned {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  flex-wrap: wrap;
}
.crew-role-checks { display: flex; gap: 8px; flex-shrink: 0; }
.crew-role-checks .checkbox-label { display: flex; align-items: center; gap: 3px; font-size: 11px; margin: 0; }
.btn-small { padding: 4px 8px; font-size: 12px; margin-bottom: 0; }

.veh-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  border-radius: 999px;
  padding: 2px 9px;
  margin-right: 6px;
}
.veh-inuse { background: #dff3e6; color: #1a7a3f; }
.veh-reserved { background: #fdf1cf; color: #9a6a0a; }
.veh-available { background: #eee; color: var(--ink-soft); }

.mini-calendars { display: flex; flex-direction: column; gap: 14px; }
.mini-calendar { }
.mini-cal-header { font-weight: 700; font-size: 13px; color: var(--brand-purple-dark); margin-bottom: 4px; text-align: center; }
.mini-cal-table { width: 100%; border-collapse: collapse; font-size: 11px; text-align: center; }
.mini-cal-table th { color: var(--ink-soft); font-weight: 600; padding: 2px 0; }
.mini-cal-table td { padding: 4px 0; border-radius: 6px; cursor: pointer; position: relative; }
.mini-cal-table td:hover { background: #f3ecfa; }

/* Today and "selected date" are now two independent visual states on the same cell (task #140,
   2026-07-29) — previously .cal-selected solid-filled purple over the whole cell, which hid the
   job-count background color entirely. inset box-shadow draws a border without affecting layout
   (border-collapse on the table makes a real `border` unreliable per-cell), and both rules come
   after the .cal-jobs-* rules above so the border always wins over the background fill. Today
   keeps its purple border regardless of what's selected; clicking any other date gets a black
   border instead. */
.mini-cal-table td.cal-today { box-shadow: inset 0 0 0 4px var(--brand-purple); }
.mini-cal-table td.cal-selected { box-shadow: inset 0 0 0 4px #000; }

/* When today's own date IS the current selection (task #172, 2026-07-30), neither solid color
   alone communicates both states, so the cell gets a black+purple striped border instead. A
   real two-color border can't be done with box-shadow (solid colors only), and a real `border`
   on the <td> itself is unreliable under border-collapse (see comment above) — so this uses an
   absolutely-positioned ::after with a striped border-image instead. That lives outside the
   table's border-collapse model entirely (it's not a cell border, just a box floating on top of
   one), which sidesteps the same rendering problem the box-shadow approach was chosen to avoid.
   Requires td { position: relative } above so ::after's inset:0 lines up with the cell. */
.mini-cal-table td.cal-today-selected::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 6px;
  pointer-events: none;
  border: 4px solid transparent;
  border-image: repeating-linear-gradient(45deg, var(--brand-purple) 0 6px, #000 6px 12px) 4;
}

/* Task #590, 2026-10-05 — gray slash from upper right to lower left across any date that has a
   cancelled work order. Drawn with ::before (the ::after slot is taken by .cal-today-selected's
   striped border above) as a thin diagonal band in a "to bottom right" gradient, whose 50% line
   runs corner-to-corner from top-right to bottom-left. Sits on top of the job-count color and
   leaves the today/selected borders alone; pointer-events none so clicks still reach the cell. */
.mini-cal-table td.cal-cancelled::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to bottom right,
    transparent calc(50% - 1.5px), #6b6b6b calc(50% - 1.5px), #6b6b6b calc(50% + 1.5px), transparent calc(50% + 1.5px));
}

.cal-day-panel {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid #f0eef5;
  font-size: 13px;
}
.cal-day-panel ul { padding-left: 18px; margin: 6px 0; }

/* New Work Order customer typeahead results (added 2026-07-29). */
.customer-search-results { list-style: none; padding: 0; margin: 8px 0; max-width: 400px; }
.customer-search-results li { padding: 6px 0; border-bottom: 1px solid #f0eef5; }
.customer-search-results a { color: var(--brand-purple-dark); font-weight: 600; text-decoration: none; }
.customer-search-results a:hover { text-decoration: underline; }

/* Task #579, 2026-09-29 — plain type-to-filter search box added above the Customers/Properties/
   Employees/Inventory/Vehicles/Equipment list tables. Same padding/border/radius as .form-row
   input (line ~361) but standalone (not inside a .form-row) and capped at 320px rather than
   stretching to a 400px form container's full width, since this sits above a wide table. */
.list-search-row { margin: 10px 0 14px; }
.list-search-box {
  width: 100%;
  max-width: 320px;
  padding: 9px;
  border-radius: 8px;
  border: 1px solid #ddd;
  font-size: 14px;
}

/* ============================================================================================
   Task #565, 2026-09-28 — Dashboard Style themes. MainLayout.razor sets data-theme on .app-shell
   from CompanyProfile.DashboardTheme (picked on the Company Profile page); everything below
   overrides the --card-*/--sidebar-*/--btn-primary-* custom properties declared in :root above,
   so the same markup repaints without any per-page changes. "classic" (the default/no attribute
   case) needs no block here — it's just the :root defaults already in place. Two of the five
   (top-nav, icon-rail) are structural, not just palette — their extra layout rules are grouped
   at the end of this section, after the five palette blocks.
   ============================================================================================ */

/* Option 3 — Flat minimal / Swiss: white canvas, thin hairlines, purple only as sparse accent. */
.app-shell[data-theme="flat-minimal"] {
  --card-shadow: none;
  --card-border: 1px solid #e4e1ec;
  --card-radius: 4px;
  --content-bg: #ffffff;
  --sidebar-bg: #ffffff;
  --sidebar-border: 1px solid #e4e1ec;
  --sidebar-text: #2a2438;
  --sidebar-nav-text: rgba(42,36,56,0.75);
  --sidebar-nav-hover-bg: rgba(42,36,56,0.06);
  --sidebar-nav-active-bg: var(--brand-purple);
  --sidebar-nav-active-text: #ffffff;
  --sidebar-slogan-color: rgba(42,36,56,0.55);
  --btn-primary-bg: var(--brand-purple);
  --btn-primary-bg-hover: var(--brand-purple-dark);
}

/* Option 6 — Rounded bento grid: Notion-style boxes, extra-round corners, soft flat shadow. */
.app-shell[data-theme="bento"] {
  --card-shadow: 0 1px 4px rgba(107,63,160,0.10);
  --card-border: 1px solid #ece6f5;
  --card-radius: 22px;
  --content-bg: var(--bg);
  --sidebar-bg: #ffffff;
  --sidebar-border: 1px solid #ece6f5;
  --sidebar-text: #3d2160;
  --sidebar-nav-text: rgba(61,33,96,0.75);
  --sidebar-nav-hover-bg: rgba(61,33,96,0.06);
  --sidebar-nav-active-bg: var(--brand-purple);
  --sidebar-nav-active-text: #ffffff;
  --sidebar-slogan-color: rgba(61,33,96,0.5);
  --btn-primary-bg: var(--brand-purple);
  --btn-primary-bg-hover: var(--brand-purple-dark);
}
.app-shell[data-theme="bento"] .btn-primary,
.app-shell[data-theme="bento"] .btn-secondary { border-radius: 16px; }

/* Option 8 — Compact data-dense: tighter spacing/smaller type so more rows fit on screen, dark
   solid sidebar (no gradient). Palette vars handle color; the rules below (scoped to this theme
   only) trim padding/font-size on the specific widgets Scott's mockup called out. */
.app-shell[data-theme="compact-dense"] {
  --card-shadow: none;
  --card-border: 1px solid #e4e1ec;
  --card-radius: 6px;
  --content-bg: var(--bg);
  --sidebar-bg: #3d2160;
  --sidebar-border: none;
  --sidebar-text: #ffffff;
  --sidebar-nav-text: rgba(233,223,245,0.85);
  --sidebar-nav-hover-bg: rgba(255,255,255,0.08);
  --sidebar-nav-active-bg: var(--brand-purple);
  --sidebar-nav-active-text: #ffffff;
  --sidebar-slogan-color: rgba(233,223,245,0.6);
  --btn-primary-bg: var(--brand-purple);
  --btn-primary-bg-hover: var(--brand-purple-dark);
}
.app-shell[data-theme="compact-dense"] .content-body { padding: 8px 16px 16px 16px; }
.app-shell[data-theme="compact-dense"] .nav-item { padding: 8px 20px; font-size: 13px; }
.app-shell[data-theme="compact-dense"] .top-calendar-band,
.app-shell[data-theme="compact-dense"] .top-wo-row { gap: 12px; margin-bottom: 12px; }
.app-shell[data-theme="compact-dense"] .dash-ops-grid { gap: 12px; }
.app-shell[data-theme="compact-dense"] .top-calendar-band .mini-calendar,
.app-shell[data-theme="compact-dense"] .top-wo-list-panel,
.app-shell[data-theme="compact-dense"] .dash-ops-col { padding: 10px; }
.app-shell[data-theme="compact-dense"] .top-calendar-band .mini-cal-table td { padding: 5px 0; font-size: 11px; }
.app-shell[data-theme="compact-dense"] .top-calendar-band .mini-cal-header { font-size: 15px; margin-bottom: 5px; }
.app-shell[data-theme="compact-dense"] .cal-nav-arrow { font-size: 26px; }
.app-shell[data-theme="compact-dense"] .crew-status-card { padding: 6px 8px; font-size: 11px; }
.app-shell[data-theme="compact-dense"] .shopping-list li,
.app-shell[data-theme="compact-dense"] .vehicle-status-list li { padding: 5px 0; font-size: 12px; }

/* Option 7 — Top command bar: sidebar retired for a slim horizontal nav; palette leans on the
   same gradient as classic's sidebar, just reshaped. Structural rules further down. */
.app-shell[data-theme="top-nav"] {
  --card-shadow: 0 1px 4px rgba(107,63,160,0.08);
  --card-border: 1px solid #ece6f5;
  --card-radius: 10px;
  --content-bg: var(--bg);
  --sidebar-bg: linear-gradient(90deg, var(--brand-purple-darker) 0%, var(--brand-purple) 100%);
  --sidebar-text: #ffffff;
  --sidebar-nav-text: rgba(255,255,255,0.85);
  --sidebar-nav-hover-bg: rgba(255,255,255,0.12);
  --sidebar-nav-active-bg: rgba(255,255,255,0.18);
  --sidebar-nav-active-text: #ffffff;
  --btn-primary-bg: var(--brand-purple);
  --btn-primary-bg-hover: var(--brand-purple-dark);
}

/* Option 11 — Icon rail app-shell: sidebar narrows to an icon-only rail. Palette is a dark rail
   (distinct from classic's gradient) per the shortlisted mockup; structural rules further down. */
.app-shell[data-theme="icon-rail"] {
  --card-shadow: 0 1px 4px rgba(107,63,160,0.08);
  --card-border: 1px solid #ece6f5;
  --card-radius: 10px;
  --content-bg: var(--bg);
  --sidebar-bg: #2a1a40;
  --sidebar-text: #cfc3e6;
  --sidebar-nav-text: rgba(207,195,230,0.85);
  --sidebar-nav-hover-bg: rgba(255,255,255,0.08);
  --sidebar-nav-active-bg: var(--brand-purple);
  --sidebar-nav-active-text: #ffffff;
  --btn-primary-bg: var(--brand-purple);
  --btn-primary-bg-hover: var(--brand-purple-dark);
}

/* Task #616, 2026-10-07 — "Modern crisp": the look of the #613 branded customer portal applied to the
   dashboard. Same top command bar markup as Option 7 (MainLayout.razor), but a solid (not gradient)
   purple band, no glow rings, hairline-bordered white cards, pill buttons, Noto Serif Display for
   page and widget headings and Inter for everything else (both loaded in _Host.cshtml), and purple
   used sparingly. */
.app-shell[data-theme="modern"] {
  --card-shadow: 0 1px 2px rgba(42,36,56,0.05);
  --card-border: 1px solid #e7e3ee;
  --card-radius: 14px;
  --content-bg: #f8f7fa;
  --sidebar-bg: #3d2160;
  --sidebar-text: #ffffff;
  --sidebar-nav-text: rgba(255,255,255,0.82);
  --sidebar-nav-hover-bg: rgba(255,255,255,0.10);
  --sidebar-nav-active-bg: rgba(255,255,255,0.16);
  --sidebar-nav-active-text: #ffffff;
  --btn-primary-bg: #4d2b76;
  --btn-primary-bg-hover: #3d2160;
  font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
  color: #2a2438;
}
.app-shell[data-theme="modern"] input,
.app-shell[data-theme="modern"] select,
.app-shell[data-theme="modern"] textarea,
.app-shell[data-theme="modern"] button { font-family: inherit; }
.app-shell[data-theme="modern"] .topnav-bar { padding: 12px 20px; gap: 14px; }
.app-shell[data-theme="modern"] .topnav-brand { font-family: 'Noto Serif Display', Georgia, serif; font-weight: 500; font-size: 17px; letter-spacing: 0.2px; }
.app-shell[data-theme="modern"] .topnav-links { gap: 0; }
.app-shell[data-theme="modern"] .topnav-link { font-weight: 500; border-radius: 50px; padding: 7px 9px; font-size: 13px; }
.app-shell[data-theme="modern"] .topbar { background: #ffffff; border-bottom: 1px solid #ece8f2; }
.app-shell[data-theme="modern"] .content-body h2 { font-family: 'Noto Serif Display', Georgia, serif; font-weight: 400; font-size: 32px; color: #2a2438; letter-spacing: 0.2px; }
.app-shell[data-theme="modern"] .content-body h3,
.app-shell[data-theme="modern"] .top-wo-list-panel h3 { font-family: 'Noto Serif Display', Georgia, serif; font-weight: 500; color: #2a2438; }
.app-shell[data-theme="modern"] .dash-ops-col h4,
.app-shell[data-theme="modern"] .content-body h4 { color: #4d2b76; font-weight: 600; letter-spacing: 0.2px; }
.app-shell[data-theme="modern"] .mini-cal-header { font-family: 'Noto Serif Display', Georgia, serif; font-weight: 500; color: #2a2438; }
/* Scott 2026-10-07: widget titles bold (calendar months, Daily WO / Invoices / Requests panels, Crew & Vehicle Status / Vehicles / Shopping List). */
.app-shell[data-theme="modern"] .top-wo-list-panel h3,
.app-shell[data-theme="modern"] .mini-cal-header,
.app-shell[data-theme="modern"] .dash-ops-col h4 { font-weight: 700; }
.app-shell[data-theme="modern"] .btn-primary { border-radius: 50px; padding: 9px 22px; font-weight: 600; letter-spacing: 0.2px; }
.app-shell[data-theme="modern"] .btn-secondary { border-radius: 50px; }
.app-shell[data-theme="modern"] .data-table th { background: #f4f1f8; color: #3d2160; font-weight: 600; }
.app-shell[data-theme="modern"] .form-row input,
.app-shell[data-theme="modern"] .form-row select { border-color: #dcd7e4; }
.app-shell[data-theme="modern"] .form-row input:focus,
.app-shell[data-theme="modern"] .form-row select:focus { outline: 2px solid #8e66ad; outline-offset: 0; border-color: #8e66ad; }
.app-shell[data-theme="modern"] .lang-btn.active { background: #4d2b76; border-color: #4d2b76; }

/* ---- Structural: Option 7 top command bar (also used by Modern crisp, #616) ----
   MainLayout.razor renders a different shell for this theme: .app-shell becomes a column flex
   container (top-nav-bar, then the normal .main-content column) instead of the classic row
   flex (sidebar beside main-content). No .sidebar element exists in this theme's markup. */
.app-shell[data-theme="top-nav"], .app-shell[data-theme="modern"] { flex-direction: column; }
.topnav-bar {
  display: flex;
  align-items: center;
  gap: 18px;
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  padding: 10px 22px;
  flex-shrink: 0;
}
.topnav-logo { width: 34px; height: 34px; border-radius: 6px; object-fit: contain; flex-shrink: 0; }
.topnav-brand { font-weight: 800; font-size: 15px; white-space: nowrap; }
.topnav-links { display: flex; flex-wrap: wrap; gap: 2px; flex: 1; }
.topnav-link {
  padding: 7px 12px;
  border-radius: 8px;
  color: var(--sidebar-nav-text);
  text-decoration: none;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.topnav-link:hover { background: var(--sidebar-nav-hover-bg); }
.topnav-link.active { background: var(--sidebar-nav-active-bg); color: var(--sidebar-nav-active-text); }
.app-shell[data-theme="top-nav"] .main-content, .app-shell[data-theme="modern"] .main-content { height: auto; flex: 1; min-height: 0; }

/* ---- Structural: Option 11 icon rail ----
   Reuses .sidebar/.nav-menu/.nav-item (same markup as the classic sidebar layout), just narrowed
   and re-centered — NavMenu.razor's IconOnly parameter (see that file) swaps each item's visible
   label for a 2-letter badge with a title tooltip carrying the full name. */
.app-shell[data-theme="icon-rail"] .sidebar { width: 64px; padding: 16px 0; }
.app-shell[data-theme="icon-rail"] .brand { padding: 0 0 14px; }
.app-shell[data-theme="icon-rail"] .brand-logo { width: 36px; }
.app-shell[data-theme="icon-rail"] .brand-name,
.app-shell[data-theme="icon-rail"] .brand-slogan { display: none; }
.app-shell[data-theme="icon-rail"] .nav-item.icon-only {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 10px 6px;
  padding: 8px 0;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3px;
}

/* Task #565, 2026-09-28 — Company Profile "Dashboard Style" picker. Independent of the
   .app-shell[data-theme] rules above (this renders on the plain, un-themed Company page), so it
   uses its own fixed colors rather than the --card-*/--sidebar-* custom properties. */
.theme-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 14px;
  margin: 12px 0 20px;
  max-width: 720px;
}
.theme-picker-card {
  border: 1px solid #e4e1ec;
  border-radius: 10px;
  padding: 10px;
  text-align: center;
}
.theme-picker-card-selected {
  border: 2px solid var(--brand-purple);
}
.theme-swatch {
  display: flex;
  height: 64px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid #e4e1ec;
  margin-bottom: 8px;
}
.theme-swatch-sidebar { width: 22%; flex-shrink: 0; }
.theme-swatch-cards {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px;
}
.theme-swatch-card { flex: 1; }
.theme-picker-label { font-size: 12px; font-weight: 700; color: var(--brand-purple-dark); margin-bottom: 6px; }
.theme-picker-card .checkbox-label { justify-content: center; font-size: 12px; }
