/* RPM Filter portal — aligned with marketing-site brand palette.
 * Same color tokens + utility bar + logo + italicized-emphasis pattern
 * as /site/. Visually consistent across portal + admin + marketing.
 */
:root {
  --rpm-navy:       #0a2540;
  --rpm-navy-deep:  #061629;
  --rpm-navy-light: #16365b;
  --rpm-blue:       #0075bf;
  --rpm-blue-dark:  #045cb4;
  --rpm-blue-soft:  #e8f1f8;
  --rpm-bg:         #ffffff;
  --rpm-bg-alt:     #f8fafc;
  --rpm-text:       #1a1a1a;
  --rpm-text-soft:  #475569;
  --rpm-text-muted: #6b7280;
  --rpm-border:     #d1d5db;
  --rpm-border-soft:#e5e7eb;
  --rpm-radius:     4px;
  --rpm-radius-lg:  8px;
  --rpm-danger:     #b91c1c;
  --rpm-danger-dark:#991b1b;

  /* ── layout ────────────────────────────────────────────────────────
   * One fluid page column. --page-max is the cap on a large monitor;
   * --gutter grows with the viewport so the page breathes at 1920 and
   * stays tight on an iPhone. Breakpoints in this file: 640 (phone),
   * 1024 (two-column), 1440 (large monitor). Prefer those three over
   * adding a new one. */
  --page-max:        1600px;
  --page-max-narrow: 520px;
  --gutter:          clamp(1rem, 3vw, 2.5rem);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  color: var(--rpm-text);
  background: var(--rpm-bg-alt);
  min-height: 100vh;
  display: flex; flex-direction: column;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: var(--rpm-blue); text-decoration: none; transition: color .15s ease; }
a:hover { color: var(--rpm-navy); text-decoration: underline; }
h1, h2, h3, h4 {
  color: var(--rpm-navy); line-height: 1.2;
  margin: 0 0 .5em; font-weight: 800;
  letter-spacing: -0.015em;
}
h1 { font-size: 2rem; }
h1 em, h2 em, h3 em { color: var(--rpm-blue); font-style: italic; font-weight: 800; }
h3 { font-size: 1.125rem; }
p { margin: 0 0 1em; }
.text-muted { color: var(--rpm-text-muted); }
.muted { color: var(--rpm-text-muted); }
.small { font-size: .8125rem; }
/* Figures that sit in a column. The UI stack defaults to proportional
   digits, which spread five-digit numbers over ~12px of width -- wider than
   a character, so the column reads visibly ragged. */
.num { font-variant-numeric: tabular-nums; }
small { font-size: .8125rem; }
.eyebrow {
  display: inline-block;
  font-size: .75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--rpm-blue);
  margin-bottom: .75rem;
}

/* Page column. `.container-wide` is kept as an explicit alias because 34
   templates plus the chrome in both base templates write
   `container container-wide`; keeping it identical means content and chrome
   stay aligned. `.container-narrow` is declared last so it wins over both. */
.container, .container-wide {
  width: 100%; max-width: var(--page-max); margin: 0 auto; padding: 0 var(--gutter);
}
.container-narrow { max-width: var(--page-max-narrow); }

/* utility bar — top strip with phone + email, matches marketing site */
.utility-bar {
  background: var(--rpm-navy-deep);
  color: rgba(255, 255, 255, 0.85);
  font-size: .8125rem;
  padding: .5rem 0;
  letter-spacing: 0.01em;
}
.utility-bar .container, .utility-bar .container-wide {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: .5rem;
}
.utility-bar a { color: white; }
.utility-bar a:hover { color: #93c5fd; text-decoration: none; }

/* main header — white bar w/ logo + nav, matches marketing site */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: white;
  border-bottom: 1px solid var(--rpm-border-soft);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
  padding: 0;
}
.topbar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  padding: .875rem 0;
}
.brand {
  display: flex; align-items: center; gap: .625rem;
  color: var(--rpm-navy); font-weight: 800;
  letter-spacing: -0.01em;
}
.brand:hover { color: var(--rpm-navy); text-decoration: none; }
.brand img { height: 44px; width: auto; display: block; }
/* Signed-in customer's logo next to ours, separated by a hairline. */
.brand-customer {
  display: flex; align-items: center;
  padding-left: .875rem; border-left: 1px solid var(--border, #d8dde6);
}
.brand-customer img { max-height: 40px; max-width: 150px; width: auto; object-fit: contain; display: block; }
.brand-tagline {
  display: none;
  font-size: .6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--rpm-text-muted);
  padding-left: .625rem;
  margin-left: .375rem;
  border-left: 1px solid var(--rpm-border);
  line-height: 1.2;
}
/* The header search box took the tagline's room; it comes back on wide
   screens only. */
@media (min-width: 1200px) { .brand-tagline { display: inline-block; } }
.brand-suffix {
  display: inline-block;
  font-size: .6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--rpm-text-muted);
  padding: .125rem .5rem;
  border: 1px solid var(--rpm-border-soft);
  border-radius: 3px;
  margin-left: .25rem;
}

/* admin-specific topbar variant — accent line so admins know they're in admin */
.topbar--admin { border-bottom: 3px solid var(--rpm-danger); }
.brand-suffix--admin {
  color: var(--rpm-danger);
  border-color: var(--rpm-danger);
  background: #fef2f2;
}

/* nav links + sign-out
   ─────────────────────────────────────────────────────────────────────
   Single-line top-nav. Adding "Settings" pushed the row over its width
   budget at 1120px; the fix is tighter spacing + nowrap on every child
   so links don't break inside themselves, and a more compact role tag
   so the user identity slot doesn't wrap. */
.topnav {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: nowrap;
}
.topnav-link {
  color: var(--rpm-text);
  font-weight: 600;
  font-size: .875rem;
  padding: .375rem 0;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color .15s ease, border-color .15s ease;
}
.topnav-link:hover, .topnav-link.is-active {
  color: var(--rpm-navy);
  border-bottom-color: var(--rpm-blue);
  text-decoration: none;
}
.topnav-user {
  color: var(--rpm-navy);
  font-size: .8125rem;
  font-weight: 700;
  padding-left: .875rem;
  border-left: 1px solid var(--rpm-border-soft);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: .375rem;
}
.topnav-user strong { color: var(--rpm-navy); }
/* Role tag — small uppercase pill so it reads as intentional metadata
   rather than a clarifying parenthetical that wraps onto its own line. */
.topnav-user small {
  color: var(--rpm-blue);
  background: var(--rpm-blue-soft, #e8f1f8);
  font-size: .625rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: 1pt 5pt;
  border-radius: 3px;
  white-space: nowrap;
}
.topnav-logout { margin: 0; }
.topnav-logout .btn-link { white-space: nowrap; }
.btn-link {
  background: none; border: none; color: var(--rpm-blue);
  font: inherit; cursor: pointer; padding: 0;
  text-decoration: none; font-weight: 700;
  font-size: .9375rem;
}
.btn-link:hover { color: var(--rpm-navy); text-decoration: underline; }

/* cross-portal nav link — "Customer →" / "Admin →" */
.topnav-cross {
  font-size: .8125rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--rpm-text-muted);
  padding: .375rem .625rem;
  border: 1px solid var(--rpm-border-soft);
  border-radius: 3px;
  background: var(--rpm-bg-alt);
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.topnav-cross:hover {
  background: var(--rpm-navy);
  color: white;
  border-color: var(--rpm-navy);
  text-decoration: none;
}
.topnav-cross-label { font-weight: 800; }
.topnav-cross { white-space: nowrap; }

/* header stop search — the pill in the top-right of every signed-in page.
   Icon is an inline SVG (the CSP allows no background images) and the
   submit button is visually hidden: Enter submits, the button only exists
   for no-JS clarity and screen readers. */
.topsearch { position: relative; display: flex; align-items: center; }
.topsearch input[type="search"] {
  appearance: none; -webkit-appearance: none;
  width: 13rem; padding: .45rem .75rem .45rem 2.1rem;
  border: 1.5px solid var(--rpm-border); border-radius: 999px;
  font: inherit; font-size: .875rem; color: var(--rpm-text);
  background: #f8fafc;
  transition: width .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.topsearch input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
.topsearch input[type="search"]:focus {
  outline: none; width: 17rem; background: white;
  border-color: var(--rpm-blue); box-shadow: 0 0 0 3px rgba(0, 117, 191, 0.15);
}
.topsearch-icon {
  position: absolute; left: .75rem; top: 50%; transform: translateY(-50%);
  width: 1rem; height: 1rem; color: var(--rpm-text-muted); pointer-events: none;
}
.topsearch-submit {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
@media (max-width: 1100px) {
  .topsearch input[type="search"] { width: 10rem; }
  .topsearch input[type="search"]:focus { width: 13rem; }
  .topbar .topnav-user { display: none; }
}
@media (max-width: 640px) {
  .topbar .topbar-right { flex-wrap: wrap; width: 100%; margin-left: 0; gap: .5rem; }
  .topsearch { order: 3; flex: 1 1 100%; }
  .topsearch input[type="search"],
  .topsearch input[type="search"]:focus { width: 100%; min-height: 44px; }
}

/* Store number under a stop name in the dashboard table. */
.store-no {
  display: block; font-size: .75rem; font-weight: 600;
  color: var(--rpm-text-muted); margin-top: .125rem;
}

/* main */
main { flex: 1; padding: 2.5rem 0; }
@media (min-width: 1440px) { main { padding: 3.25rem 0; } }
.page { padding: 1rem 0; }
.lead { font-size: 1.125rem; color: var(--rpm-text-soft); margin-bottom: 2rem; }

/* cards */
.card {
  background: white;
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius-lg);
  padding: 1.75rem;
  margin-bottom: 1.25rem;
}
.card h3 { margin-bottom: .5rem; }
.card-meta { background: transparent; border: none; padding: .5rem 0; }

/* kv list */
.kv {
  display: grid; grid-template-columns: 10rem 1fr; gap: .375rem .75rem;
  margin: .5rem 0 1rem;
}
.kv dt { color: var(--rpm-text-muted); font-size: .875rem; }
.kv dd { margin: 0; font-size: .9375rem; }
@media (max-width: 640px) {
  .kv { grid-template-columns: 1fr; gap: 0 0; }
  .kv dt { margin-top: .5rem; }
}

/* login page — vertical center on a navy gradient background to match marketing hero feel */
.login-page {
  flex: 1;
  padding: 3rem 0;
  display: flex; align-items: center;
  background: linear-gradient(135deg, var(--rpm-navy-deep) 0%, var(--rpm-navy) 60%, var(--rpm-blue-dark) 100%);
}
.login-card {
  background: white;
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius-lg);
  padding: 2.5rem;
  box-shadow: 0 20px 40px rgba(6, 22, 41, 0.25);
}
.login-card h1 { font-size: 1.625rem; margin-bottom: .25rem; }
.login-help { font-size: .9375rem; margin-top: 1.25rem; }

/* forms — same input styling as marketing site */
.form { margin-top: 1.5rem; }
.form-row { margin-bottom: 1.25rem; }
.form-row label {
  display: block; margin-bottom: .375rem;
  font-weight: 700; color: var(--rpm-navy); font-size: .9375rem;
}
.form-row input {
  width: 100%; padding: .8125rem .875rem;
  border: 1.5px solid var(--rpm-border); border-radius: var(--rpm-radius);
  font-family: inherit; font-size: 1rem; color: var(--rpm-text);
  background: white; transition: border-color .15s ease, box-shadow .15s ease;
}
.form-row input:focus {
  outline: none; border-color: var(--rpm-blue);
  box-shadow: 0 0 0 3px rgba(0, 117, 191, 0.15);
}

/* buttons — match marketing site exactly */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .8125rem 1.625rem;
  border-radius: var(--rpm-radius);
  font-weight: 700; font-size: 1rem;
  border: 2px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: var(--rpm-blue); color: white; border-color: var(--rpm-blue);
}
.btn-primary:hover {
  background: var(--rpm-navy); border-color: var(--rpm-navy); color: white;
}


.btn-outline-on-light {
  background: white; color: var(--rpm-navy); border-color: var(--rpm-navy);
}
.btn-outline-on-light:hover {
  background: var(--rpm-navy); color: white;
}
.btn-block { width: 100%; }
.btn-lg { padding: 1rem 2rem; font-size: 1.0625rem; }

/* alerts */
.alert {
  padding: 1rem 1.25rem; border-radius: var(--rpm-radius); margin: 1rem 0 1.5rem;
  font-size: .9375rem; font-weight: 500;
}
.alert strong { font-weight: 700; }
.alert-error { background: #fef2f2; color: var(--rpm-danger-dark); border: 1px solid #fecaca; }
.alert-success { background: #ecfdf5; color: #065f46; border: 1px solid #a7f3d0; }
.alert-warning { background: #fffbeb; color: #92400e; border: 1px solid #fde68a; }

/* back-link arrow row above page-head */
.back-link { margin: 0 0 1rem; font-size: .9375rem; }
.back-link a { color: var(--rpm-text-muted); font-weight: 600; }
.back-link a:hover { color: var(--rpm-blue); text-decoration: none; }

/* form helper text + better card padding for forms */
.form-help {
  margin-top: .375rem; font-size: .8125rem; color: var(--rpm-text-muted);
}
.form-help code {
  background: white; border: 1px solid var(--rpm-border-soft);
  font-size: .8125rem;
}

/* footer — dark navy w/ small print, matches marketing site footer-bottom */
.footer {
  background: var(--rpm-navy-deep);
  color: rgba(255, 255, 255, 0.65);
  padding: 1.5rem 0;
  text-align: center;
  font-size: .875rem;
}
.footer a { color: rgba(255, 255, 255, 0.85); }
.footer a:hover { color: #93c5fd; text-decoration: none; }

/* rich footer — multi-column brand + contact + nav, matches marketing site density */
.footer--rich {
  padding: 2.5rem 0 1rem;
  text-align: left;
}
.footer-grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .footer-grid { grid-template-columns: 2fr 1fr 1fr; }
}
.footer-col h4 {
  color: white;
  font-size: .75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin: 0 0 .75rem;
}
.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col li { padding: .25rem 0; font-size: .9375rem; color: rgba(255, 255, 255, 0.75); }
.footer-col a { color: rgba(255, 255, 255, 0.85); }
.footer-col a:hover { color: #93c5fd; text-decoration: none; }

.footer-brand img {
  height: 36px; width: auto; display: block;
  margin-bottom: .75rem;
  filter: brightness(0) invert(1);  /* render the dark logo as white on the navy footer */
  opacity: 0.95;
}
.footer-tagline {
  color: #93c5fd;
  font-size: .75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin: 0 0 .5rem;
}
.footer-blurb {
  color: rgba(255, 255, 255, 0.7);
  font-size: .9375rem;
  line-height: 1.5;
  max-width: 360px;
  margin: 0;
}
.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding-top: 1rem;
  margin-top: 2rem;
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
  font-size: .8125rem;
}

/* (.container-wide is defined with .container above) */


/* ─── iPhone polish (≤640px) ─────────────────────────────────────────
 * Login pages are John's first portal touch from a text message.
 * Tighten padding, stack the topnav, scale down the hero card so
 * the form feels right at iPhone SE (375px) through iPhone Pro Max (430px).
 */
@media (max-width: 640px) {
  /* utility bar: stack rows so phone/email line + service-area line
     don't fight for horizontal space */
  .utility-bar {
    font-size: .75rem;
    padding: .5rem 0;
  }
  .utility-bar .container,
  .utility-bar .container-wide {
    flex-direction: column;
    align-items: flex-start;
    gap: .25rem;
    text-align: left;
  }

  /* top header: drop the brand-suffix wordmark to a smaller chip,
     give the brand logo + cross-portal pill room to breathe */
  .topbar-inner {
    flex-wrap: wrap;
    gap: .5rem;
    padding: .625rem 0;
  }
  .brand img { height: 36px; }
  .brand-customer img { max-height: 32px; max-width: 110px; }
  .brand-suffix { font-size: .625rem; padding: .0625rem .375rem; margin-left: .125rem; }
  .topnav {
    gap: .5rem;
    flex-wrap: wrap;
    margin-left: auto;
  }
  .topnav-link { font-size: .8125rem; padding: .25rem 0; }
  .topnav-user {
    display: none; /* hide "Signed in as <name>" verbose chip — name is in the dashboard */
  }
  .topnav-cross {
    font-size: .6875rem;
    padding: .3125rem .5rem;
    letter-spacing: 0.06em;
  }
  /* The label used to be dropped here, leaving a bare "→" button beside
     Sign out with nothing saying where it goes. "Customer" is two words
     shorter than the ambiguity it removes. */
  .topnav-cross { padding: .5rem .625rem; min-height: 44px; display: inline-flex; align-items: center; }

  /* login page: reduce vertical padding so the card pulls up into the
     viewport without scrolling on iPhone SE */
  .login-page { padding: 1.25rem 0; min-height: auto; }
  .login-card {
    padding: 1.5rem 1.25rem;
    border-radius: var(--rpm-radius-lg);
    /* keep the lifted-card feel but soften shadow on smaller display */
    box-shadow: 0 8px 24px rgba(6, 22, 41, 0.20);
  }
  .login-card h1 { font-size: 1.375rem; }

  /* form spacing tighter; input touch target stays ≥44pt via padding */
  .form { margin-top: 1.25rem; }
  .form-row { margin-bottom: 1rem; }
  .form-row input {
    /* font-size already 17px from base — preserved for iOS auto-zoom safety */
    padding: .8125rem .75rem;
  }

  /* button block on mobile gets a touch-friendly height */
  .btn { padding: .875rem 1.25rem; min-height: 44px; }
  .btn-lg { padding: 1rem 1.5rem; font-size: 1rem; }

  /* alert margins tighter */
  .alert { padding: .75rem 1rem; font-size: .875rem; margin: .75rem 0 1.25rem; }

  /* h1/h2/h3 scale down so dashboard headers don't blow out the viewport */
  h1 { font-size: 1.5rem; }
  h2 { font-size: 1.25rem; }
  h3 { font-size: 1.0625rem; }

  /* rich footer: reduce padding + tagline size */
  .footer--rich { padding: 1.75rem 0 .75rem; }
  .footer-grid { gap: 1.5rem; }
  .footer-brand img { height: 32px; }
  .footer-blurb { font-size: .875rem; }
  .footer-bottom { margin-top: 1.25rem; padding-top: .75rem; font-size: .75rem; }

  /* main padding tighter */
  main { padding: 1.5rem 0; }

  /* ─── Data tables → stacked cards on mobile ──────────────────────
   * Desktop tables (Date / Stop / Service / Photos / View) crush at
   * iPhone widths — the View button bleeds off the right edge and
   * Date/Photos columns wrap onto two lines. Convert each <tr> into a
   * stacked card: <thead> hidden, <td>s stack vertically, View button
   * goes full-width at the bottom as the primary tap target. */
  .data-table {
    border: none;
    margin-top: .5rem;
  }
  .data-table thead { display: none; }
  .data-table tbody { display: block; }
  .data-table tr {
    display: block;
    background: white;
    border: 1px solid var(--rpm-border-soft);
    border-radius: var(--rpm-radius-lg);
    padding: .875rem 1rem;
    margin-bottom: .625rem;
    box-shadow: 0 1px 2px rgba(10, 37, 64, 0.04);
  }
  .data-table tr:hover { background: white; } /* drop the desktop hover */
  .data-table tr:last-child td { border-bottom: 1px solid #f3f4f6; } /* re-arm the last cell border in stacked mode */
  .data-table td {
    display: block;
    padding: .25rem 0;
    border: none;
    font-size: .9375rem;
    line-height: 1.4;
  }
  /* Cells that have data-label show "Label: value" inline */
  .data-table td[data-label]::before {
    content: attr(data-label) ': ';
    color: var(--rpm-text-muted);
    font-weight: 600;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-right: .375rem;
  }
  /* The View → button (last cell) becomes full-width at the bottom of the card */
  .data-table td:last-child { padding-top: .625rem; margin-top: .375rem; border-top: 1px solid var(--rpm-border-soft); }
  .data-table td:last-child::before { content: none; }
  .data-table td .btn-mini {
    display: block;
    text-align: center;
    padding: .625rem 1rem;
    font-size: .875rem;
    min-height: 44px;
    line-height: 1.4;
  }
}

/* stat grid */
.stat-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;
  margin-bottom: 1.5rem;
}
@media (max-width: 480px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }
.stat-card {
  background: white; border: 1px solid #e5e7eb; border-radius: 8px;
  padding: 1.25rem 1.5rem; text-align: left;
}
.stat-card .stat-num {
  font-size: 2rem; font-weight: 800; color: #0a2540;
  line-height: 1; margin-bottom: .375rem; letter-spacing: -0.02em;
}
.stat-card .stat-label { color: #6b7280; font-size: .875rem; font-weight: 500; }

/* data tables */
.data-table {
  width: 100%; border-collapse: collapse; font-size: .9375rem;
  margin-top: .5rem;
}
.data-table th {
  text-align: left; padding: .5rem .75rem;
  background: #f8fafc; color: #6b7280;
  font-size: .75rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700;
  border-bottom: 1px solid #e5e7eb;
}
.data-table td {
  padding: .75rem; border-bottom: 1px solid #f3f4f6;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover { background: #f8fafc; }
.data-table tr.row-clickable { cursor: pointer; }
.data-table .col-date { white-space: nowrap; }

.range-toggle { display: inline-flex; gap: .375rem; }
.range-toggle a {
  display: inline-block;
  padding: .3125rem .75rem; border-radius: 999px;
  background: #eef1f5; color: var(--rpm-blue);
  font-size: .8125rem; font-weight: 700;
  text-decoration: none;
}
.range-toggle a:hover { background: #dde3ea; text-decoration: none; }
.range-toggle a.active { background: var(--rpm-blue); color: white; }

.btn-mini {
  display: inline-block;
  padding: .3125rem .75rem; border-radius: 4px;
  background: var(--rpm-blue); color: white;
  font-size: .8125rem; font-weight: 700;
  text-decoration: none;
  text-align: center;
  /* Min-width so adjacent mini buttons (Edit / Deactivate, etc.) render
     the same width regardless of label length. Sized at 6.25rem (100px)
     which fits "Deactivate" / "Reactivate" with breathing room — short
     labels like "Edit" get the same footprint so the column reads
     uniform, not jittery row-to-row. */
  min-width: 6.25rem;
  box-sizing: border-box;
  line-height: 1.25;
}
.btn-mini:hover { background: #0a2540; color: white; text-decoration: none; }
button.btn-mini {
  border: 0;
  font-family: inherit;
  cursor: pointer;
}

/* Small button — between .btn (hero) and .btn-mini (inline).
   Used for page-head actions on list pages where the full hero btn
   would overshout the H1. */
.btn.btn-sm {
  padding: .5rem .875rem;
  font-size: .875rem;
  font-weight: 700;
}

/* Role pill — small uppercase chip shown in tables and (matching style)
   the topnav. Same visual language as .topnav-user small so the role
   reads consistently wherever it appears. */
.role-pill {
  display: inline-block;
  font-size: .6875rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: 2pt 7pt;
  border-radius: 3px;
  background: var(--rpm-blue-soft, #e8f1f8);
  color: var(--rpm-blue, var(--rpm-blue));
  white-space: nowrap;
}
.role-pill--superadmin {
  /* Slightly stronger fill for elevated permission; same hue family
     so it doesn't look like a different concept. */
  background: var(--rpm-navy);
  color: white;
}

/* Fixed-width data-table columns. Without these the auto layout
   distributes leftover space evenly across all cols, leaving large
   gaps next to small cells (Status, Role, Last login). Setting widths
   on the narrow columns lets Name + Email absorb the residual. */
.data-table th.col-role,        .data-table td.col-role        { width: 110px; }
.data-table th.col-status,      .data-table td.col-status      { width: 90px;  }
.data-table th.col-last-login,  .data-table td.col-last-login  { width: 150px; }
.data-table th.col-actions,     .data-table td.col-actions     { width: 200px; text-align: right; }

/* "Self" placeholder shown where a Deactivate button would go in the
   logged-in admin's own row. Same footprint as a .btn-mini so the column
   stays aligned, but visually quiet — italic, muted, no fill. */
.btn-mini--self {
  display: inline-block;
  min-width: 6.25rem;
  padding: .3125rem .75rem;
  border-radius: 4px;
  background: transparent;
  border: 1px dashed var(--rpm-border);
  color: var(--rpm-text-muted);
  font-size: .75rem;
  font-weight: 600;
  font-style: italic;
  text-align: center;
  line-height: 1.25;
  box-sizing: border-box;
}

/* Group for the row's action buttons — inline-flex with consistent gap
   so Edit + Deactivate (or Edit + Self) sit shoulder-to-shoulder cleanly
   instead of relying on default whitespace + form-block layout. Gap is
   .875rem (14px) — enough breathing room that two solid-filled adjacent
   buttons read as independent controls, not a paired button-group. */
.action-group {
  display: inline-flex;
  align-items: center;
  gap: .875rem;
}
.action-group form { margin: 0; display: inline-flex; }

/* badges */
.badge {
  display: inline-block; padding: .125rem .5rem; border-radius: 999px;
  font-size: .75rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em;
}
.badge-complete { background: #d1fae5; color: #065f46; }
.badge-scheduled { background: #dbeafe; color: #1e40af; }
.badge-cancelled { background: #fee2e2; color: var(--rpm-danger-dark); }
.badge-neutral { background: var(--rpm-bg-alt); color: var(--rpm-text-soft); border: 1px solid var(--rpm-border-soft); }
.badge-hold { background: #fffbeb; color: #92400e; }
.badge-in_progress { background: #fef3c7; color: #92400e; }

/* visit list (per-facility page) */
.visit { padding: 1.25rem 0; border-bottom: 1px solid #f3f4f6; }
.visit:last-child { border-bottom: none; }
.visit-head {
  display: flex; justify-content: space-between; align-items: baseline;
  flex-wrap: wrap; gap: .5rem;
  margin-bottom: .5rem;
}
.visit-note { color: #475569; margin: .5rem 0; }

/* photo gallery */
.photo-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: .75rem;
  margin-top: .75rem;
}
.photo {
  margin: 0; background: #f8fafc; border-radius: var(--rpm-radius-lg); overflow: hidden;
  border: 1px solid #e5e7eb;
}
.photo img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block;
}
.photo figcaption {
  padding: .5rem .75rem; font-size: .8125rem; color: #6b7280;
  background: white;
}

/* notes pre */
.notes {
  white-space: pre-wrap;
  font-family: inherit;
  background: #f8fafc;
  padding: .75rem 1rem;
  border-radius: var(--rpm-radius-lg);
  border: 1px solid #e5e7eb;
  margin: 0;
  color: #1a1a1a;
}

/* inventory groups */
.inv-group { margin: 1.25rem 0; }
.inv-group:first-of-type { margin-top: .5rem; }
.inv-building {
  color: #0a2540;
  font-size: 1rem;
  margin: 0 0 .5rem;
  padding: .375rem .625rem;
  background: #f0f5fa;
  border-left: 3px solid var(--rpm-blue);
  border-radius: 2px;
}
code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .9375em;
  background: #f8fafc;
  padding: .125rem .375rem;
  border-radius: 3px;
  color: #0a2540;
}

/* admin console — same brand chrome as the customer portal, but with a
   thin red accent under the topbar (defined above as .topbar--admin) and
   an "ADMIN" badge in the brand area. Subtle but distinguishable. */
.admin-body { background: var(--rpm-bg-alt); }

/* page-head row used in admin pages */
.page-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  flex-wrap: wrap; gap: 1rem;
  margin-bottom: 1.5rem;
}
.page-head h1 { margin-bottom: .25rem; }

/* form select + textarea — extend the input rules */
.form-row select, .form-row textarea {
  width: 100%;
  padding: .8125rem .875rem;
  border: 1.5px solid var(--rpm-border);
  border-radius: 4px;
  font-family: inherit;
  font-size: 1rem;
  color: #1a1a1a;
  background: white;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.form-row textarea { resize: vertical; min-height: 80px; }
.form-row select:focus, .form-row textarea:focus {
  outline: none; border-color: var(--rpm-blue);
  box-shadow: 0 0 0 3px rgba(0,117,191,0.15);
}

/* warn-flavored mini button (deactivate) */
.btn-mini-warn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .3125rem .75rem; font-size: .8125rem; font-weight: 600;
  min-width: 6.25rem; border-radius: var(--rpm-radius);
  cursor: pointer; text-decoration: none; border: 1px solid transparent;
  background: var(--rpm-danger); color: #fff;
}
.btn-mini-warn:hover { background: var(--rpm-danger-dark); }
.btn-mini-warn:hover { background: #7f1d1d; color: white; text-decoration: none; }

/* === Agent A additions: job + photo + schedule forms === */

/* file input — match existing form-row controls visually */
.form-row input[type="file"] {
  width: 100%;
  padding: .625rem .75rem;
  border: 1.5px dashed var(--rpm-border);
  border-radius: var(--rpm-radius);
  background: #f8fafc;
  font-family: inherit;
  font-size: .9375rem;
  color: var(--rpm-text);
}
.form-row input[type="file"]:focus {
  outline: none; border-color: var(--rpm-blue); border-style: solid;
  box-shadow: 0 0 0 3px rgba(0,117,191,0.15);
}

/* photo thumbnail grid — admin upload page */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  margin-top: .75rem;
}
@media (max-width: 640px) {
  .photo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .625rem; }
}
.photo-thumb {
  margin: 0;
  background: #f8fafc;
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius);
  overflow: hidden;
  display: flex; flex-direction: column;
}
.photo-thumb a {
  display: block;
  background: #1f2937;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .2s ease;
}
.photo-thumb a:hover img { transform: scale(1.04); }
.photo-thumb figcaption {
  padding: .5rem .625rem;
  font-size: .8125rem;
  display: flex; flex-direction: column; gap: .125rem;
  border-top: 1px solid var(--rpm-border-soft);
}
.photo-thumb .photo-caption {
  color: var(--rpm-text);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* schedule form — visually separate each repeatable row block */
.schedule-form .schedule-row:first-of-type {
  border-top: none !important;
  padding-top: 0 !important;
}
.schedule-form .schedule-row + .schedule-row { margin-top: 1.25rem; }
.schedule-delete-label {
  display: inline-flex !important;
  align-items: center; gap: .5rem;
  font-weight: 600 !important;
  color: var(--rpm-danger-dark) !important;
}
.schedule-delete-label input[type="checkbox"] {
  width: auto !important;
  transform: scale(1.1);
}

/* === Agent B additions: filter dropdowns + audit log + impersonate banner === */

/* Filter bar shown above cross-customer admin views (jobs/photos/inventory)
 * and (with .filter-bar--multi) the audit log. White card with form controls
 * laid out horizontally on desktop, wrapping to a stack on phones. */
.filter-bar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: .625rem;
  background: white;
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius-lg);
  padding: 1rem 1.25rem;
  margin-bottom: 1.25rem;
}
.filter-bar label {
  display: block;
  font-weight: 700;
  color: var(--rpm-navy);
  font-size: .8125rem;
  margin-bottom: .25rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.filter-bar input[type="text"],
.filter-bar input[type="search"],
.filter-bar input[type="date"],
.filter-bar select {
  padding: .5rem .625rem;
  border: 1.5px solid var(--rpm-border);
  border-radius: var(--rpm-radius);
  font-family: inherit;
  font-size: .9375rem;
  color: var(--rpm-text);
  background: white;
  min-width: 180px;
}
.filter-bar input[type="text"]:focus,
.filter-bar input[type="search"]:focus,
.filter-bar input[type="date"]:focus,
.filter-bar select:focus {
  outline: none;
  border-color: var(--rpm-blue);
  box-shadow: 0 0 0 3px rgba(0, 117, 191, 0.15);
}
.filter-bar .btn-mini {
  align-self: flex-end;
  padding: .5rem 1rem;
  font-size: .875rem;
}
.filter-clear {
  align-self: center;
  padding: .375rem .625rem;
  font-size: .8125rem;
  color: var(--rpm-text-muted);
  font-weight: 600;
}
.filter-clear:hover { color: var(--rpm-blue); text-decoration: none; }

/* Multi-field filter bar (audit log) — each .filter-field is a labeled
 * dropdown column. */
.filter-bar--multi { gap: .875rem 1rem; }
.filter-field { display: flex; flex-direction: column; min-width: 160px; }
.filter-field--actions { flex-direction: row; align-items: flex-end; gap: .5rem; min-width: auto; }

@media (max-width: 640px) {
  .filter-bar {
    flex-direction: column;
    align-items: stretch;
    padding: .875rem 1rem;
  }
  .filter-bar input[type="text"],
  .filter-bar input[type="search"],
  .filter-bar input[type="date"],
  .filter-bar select { min-width: 0; width: 100%; }
  .filter-bar .btn-mini { align-self: stretch; text-align: center; min-height: 44px; }
  .filter-field { min-width: 0; width: 100%; }
  .filter-field--actions { flex-direction: column; align-items: stretch; }
}

/* Make the cross-customer filter input visually distinct from its hidden
 * select sibling (which we keep mounted but visually de-emphasized so the
 * <datalist> typeahead is the primary affordance). */
.filter-bar #customer_filter { min-width: 240px; }
.filter-bar #customer_id_select { min-width: 200px; }

/* Audit-log note <details> styling — a key:value list inside a small box. */
.audit-note {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .25rem .75rem;
  margin: .5rem 0 0;
  padding: .625rem .75rem;
  background: var(--rpm-bg-alt);
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius);
  font-size: .8125rem;
}
.audit-note dt {
  color: var(--rpm-text-muted);
  font-weight: 700;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.audit-note dd { margin: 0; color: var(--rpm-text); word-break: break-word; }
.audit-note-json {
  margin: 0;
  padding: .375rem .5rem;
  background: white;
  border: 1px solid var(--rpm-border-soft);
  border-radius: 3px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: .75rem;
  white-space: pre-wrap;
  max-height: 240px;
  overflow: auto;
}

/* Audit-log details summary — make it look clickable */
.data-table details > summary {
  cursor: pointer;
  color: var(--rpm-blue);
  font-weight: 600;
  font-size: .875rem;
  list-style: revert;
}
.data-table details > summary:hover { color: var(--rpm-navy); }
.data-table details[open] > summary { color: var(--rpm-navy); margin-bottom: .25rem; }

/* Pagination row beneath the audit log */
.pagination {
  display: flex; align-items: center; justify-content: center;
  gap: 1rem;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rpm-border-soft);
}
.btn-mini--disabled {
  background: var(--rpm-border-soft);
  color: var(--rpm-text-muted);
  cursor: not-allowed;
}
.btn-mini--disabled:hover { background: var(--rpm-border-soft); color: var(--rpm-text-muted); }

/* Photos-grid in the cross-customer admin view — slightly wider tiles than
 * the customer-facing facility page so admins can spot photos faster. */
.photo-gallery--admin {
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1rem;
}

/* Notification-prefs toggle row — checkbox + label inline */
.toggle-row { display: flex; align-items: center; }
.toggle-label {
  display: flex; align-items: center; gap: .625rem;
  cursor: pointer;
  font-weight: 500;
  color: var(--rpm-text);
  font-size: 1rem;
  margin: 0;
}
.toggle-label input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--rpm-blue);
  cursor: pointer;
}

/* Impersonation banner — fixed at the very top of every customer page when
 * an admin is impersonating. Navy background, white text, full bleed.
 * On mobile the inner row stacks. */
.impersonate-banner {
  position: sticky; top: 0; z-index: 100;
  background: var(--rpm-navy);
  color: white;
  padding: .625rem 1rem;
  font-size: .875rem;
  border-bottom: 2px solid var(--rpm-danger);  /* echo the admin red accent */
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
.impersonate-banner-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
}
.impersonate-banner-text { color: white; }
.impersonate-banner-text strong {
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: .75rem;
  background: var(--rpm-danger);
  color: white;
  padding: .125rem .5rem;
  border-radius: 3px;
  margin-right: .5rem;
}
.impersonate-banner-text em { color: white; font-style: italic; font-weight: 700; }
.impersonate-banner-form { margin: 0; }
.impersonate-banner-exit {
  background: white;
  color: var(--rpm-navy);
  border: none;
  padding: .375rem .875rem;
  border-radius: var(--rpm-radius);
  font-weight: 700;
  font-size: .8125rem;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s ease, color .15s ease;
}
.impersonate-banner-exit:hover {
  background: #fef2f2;
  color: var(--rpm-danger);
}
@media (max-width: 640px) {
  .impersonate-banner { font-size: .8125rem; padding: .5rem .75rem; }
  .impersonate-banner-text strong { font-size: .6875rem; padding: .0625rem .375rem; }
  .impersonate-banner-exit { padding: .25rem .625rem; font-size: .75rem; }
}

/* === Agent C additions: reports dashboard + report PDF print styles === */
/* Note: PDF print styles for the WeasyPrint template are inlined in
 * templates/admin/_print/monthly_report.html so the report is fully
 * self-contained and doesn't depend on /static/styles.css being
 * fetchable at PDF render time. This block covers ONLY the on-screen
 * admin reports UI. */

/* Bulk-monthly card on /admin/reports */
.reports-bulk-card {
  background: white;
  border: 1px solid var(--rpm-border-soft);
  border-left: 4px solid var(--rpm-blue);
  border-radius: var(--rpm-radius-lg);
  padding: 1.25rem 1.5rem;
  margin-bottom: 1.25rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}
.reports-bulk-card h3 { margin-bottom: .25rem; }
.reports-bulk-actions {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;
}

/* Date range picker on /admin/customer/<id>/reports */
.reports-range-form { margin-top: 0; }
.reports-range-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-bottom: 1rem;
}
@media (min-width: 640px) {
  .reports-range-grid { grid-template-columns: 1fr 1fr; }
}
/* Date inputs are wider than they need to be by default */
.reports-range-form input[type="date"] {
  max-width: 240px;
}
.reports-preview {
  background: var(--rpm-blue-soft);
  border: 1px solid #cce0f0;
  border-radius: var(--rpm-radius);
  padding: .875rem 1rem;
  margin-bottom: 1.25rem;
}
.reports-preview-stats {
  list-style: none;
  padding: 0;
  margin: .5rem 0 0;
  display: flex;
  gap: 1.25rem;
  flex-wrap: wrap;
  font-size: .9375rem;
}
.reports-preview-stats li strong {
  font-size: 1.125rem;
  color: var(--rpm-navy);
}
.reports-actions {
  display: flex;
  gap: .75rem;
  flex-wrap: wrap;
  margin-bottom: 0;
}

/* Mobile: stack the bulk-card actions vertically */
@media (max-width: 640px) {
  .reports-bulk-card {
    align-items: flex-start;
  }
  .reports-bulk-actions {
    width: 100%;
  }
  .reports-bulk-actions form,
  .reports-bulk-actions .btn {
    width: 100%;
  }
  .reports-actions .btn { width: 100%; }
}


/* === Region picker (admin customer detail page) ─────────────────────
 * 2-column × 4-row grid: ALL + 7 region chips, each with a visible
 * checkbox indicator before the name. Multi-select. Clicking a chip
 * toggles its (visually-hidden) native input + auto-submits.
 *
 * Layout:
 *   [ ✓ ALL          ]  [ ☐ Austin       ]
 *   [ ☐ Central TX   ]  [ ☐ Dallas       ]
 *   [ ☐ Fort Worth   ]  [ ☐ Houston      ]
 *   [ ☐ San Antonio  ]  [ ☐ West Texas   ]
 */
.region-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));  /* exact 2 cols at any width */
  gap: .625rem;
  margin-bottom: 1rem;
}
.region-chip {
  position: relative;
  display: flex;
  align-items: center;
  gap: .75rem;
  background: var(--rpm-bg-alt);
  border: 1.5px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius);
  padding: .75rem .875rem;
  color: var(--rpm-text);
  cursor: pointer;
  user-select: none;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.region-chip:hover {
  border-color: var(--rpm-blue);
  background: white;
  box-shadow: 0 1px 3px rgba(10, 37, 64, 0.06);
}
.region-chip.is-active {
  background: var(--rpm-navy);
  border-color: var(--rpm-navy);
  color: white;
  box-shadow: 0 2px 6px rgba(10, 37, 64, 0.16);
}
.region-chip.is-active:hover { background: var(--rpm-navy-deep); border-color: var(--rpm-navy-deep); }
.region-chip-name {
  font-weight: 700;
  font-size: .9375rem;
  line-height: 1.2;
  flex: 1;
}

/* ── "ALL" master-toggle chip ─────────────────────────────────────────────
   Pulls out of the 2-col grid by spanning both columns, then sits above the
   7 per-region chips with extra breathing room + a hairline rule so John's
   eye reads it as the master toggle, not as another region. The uppercase,
   letter-spaced label reinforces "this is the umbrella option". */
.region-chip--all {
  grid-column: 1 / -1;
  margin-bottom: .5rem;
  padding-bottom: calc(.75rem + .5rem);
  border-bottom: 1px dashed var(--rpm-border);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.region-chip--all .region-chip-name {
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: .8125rem;
}
.region-chip--all.is-active {
  /* Keep the dashed bottom rule visible against the navy fill — swap to a
     translucent white so it still reads as a separator without competing
     with the chip's filled state. */
  border-bottom-color: rgba(255, 255, 255, 0.35);
}
@media (max-width: 640px) {
  .region-chip--all { padding-bottom: calc(.625rem + .5rem); }
}

/* Visible checkbox indicator — a 18px square that fills with a white check
   on the navy active background, or shows empty white-with-border on the
   inactive light-gray background. The native input stays hidden but
   accessible (Tab + Space). */
.region-chip-box {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 1.5px solid var(--rpm-border);
  border-radius: 3px;
  background: white;
  transition: background .15s ease, border-color .15s ease;
}
.region-chip-box::after {
  content: '✓';
  color: white;
  font-size: 14px;
  line-height: 1;
  font-weight: 900;
  opacity: 0;
  transition: opacity .12s ease;
}
.region-chip.is-active .region-chip-box {
  background: var(--rpm-blue);
  border-color: white;
}
.region-chip.is-active .region-chip-box::after { opacity: 1; }

/* The checkbox stays in the DOM (so form submission picks up selections)
   and stays keyboard-accessible (Tab + Space to toggle), but is visually
   hidden — the chip's navy active background is the only indicator we
   need. The :focus-within rule below handles the keyboard focus ring. */
.region-chip-cb {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
.region-chip-check { display: none; } /* unused — kept in DOM, no visual */

.region-chip:focus-within {
  outline: 2px solid var(--rpm-blue);
  outline-offset: 2px;
}

.region-filter-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding-top: .5rem;
  border-top: 1px solid var(--rpm-border-soft);
  margin-top: .25rem;
}
.region-filter-actions > *:last-child { margin-left: auto; }

@media (max-width: 640px) {
  .region-grid { gap: .5rem; }
  .region-chip { padding: .625rem .75rem; gap: .625rem; }
  .region-chip-name { font-size: .875rem; }
  .region-filter-actions { flex-direction: column; align-items: flex-start; gap: .5rem; }
  .region-filter-actions > *:last-child { margin-left: 0; }
}


/* === Inline facility region <select> on admin customer detail page === */
.facility-region-select {
  font-family: inherit;
  font-size: .875rem;
  padding: .375rem .5rem;
  border: 1.5px solid var(--rpm-border);
  border-radius: var(--rpm-radius);
  background: white;
  color: var(--rpm-text);
  min-width: 140px;
  cursor: pointer;
  transition: border-color .15s ease;
}
.facility-region-select:hover { border-color: var(--rpm-blue); }
.facility-region-select:focus {
  outline: none;
  border-color: var(--rpm-blue);
  box-shadow: 0 0 0 3px rgba(0, 117, 191, 0.12);
}
.facility-region-status {
  margin-left: .5rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  display: inline-block;
  min-width: 4ch;
  vertical-align: middle;
  transition: opacity .2s ease;
}
.facility-region-status.saving { color: var(--rpm-text-muted); }
.facility-region-status.saved  { color: #10b981; }   /* green */
.facility-region-status.error  { color: var(--rpm-danger); }   /* red */


/* === Per-user region grant checkboxes (admin user form) === */
.user-region-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: .5rem;
  margin-top: .375rem;
}
.user-region-checkbox {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .625rem;
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius);
  background: var(--rpm-bg-alt);
  cursor: pointer;
  font-size: .9375rem;
  transition: background .15s ease, border-color .15s ease;
}
.user-region-checkbox:hover { background: white; border-color: var(--rpm-blue); }
.user-region-checkbox:has(input:checked) {
  background: var(--rpm-blue-soft);
  border-color: var(--rpm-blue);
  color: var(--rpm-navy);
  font-weight: 700;
}
.user-region-checkbox input { accent-color: var(--rpm-blue); }


/* === Quality dashboard ─────────────────────────────────────────── */
.quality-headline {
  display: flex;
  align-items: center;
  gap: 2rem;
  flex-wrap: wrap;
  border-left: 6px solid var(--rpm-border);
}
.quality-headline.quality-health-healthy { border-left-color: #10b981; }
.quality-headline.quality-health-watch   { border-left-color: #d97706; }
.quality-headline.quality-health-low     { border-left-color: var(--rpm-danger); }
.quality-headline.quality-health-no-data { border-left-color: var(--rpm-border); }

.quality-kpi { min-width: 180px; }
.quality-kpi-num {
  font-size: 3rem;
  font-weight: 800;
  line-height: 1;
  color: var(--rpm-navy);
  letter-spacing: -0.03em;
}
.quality-health-healthy .quality-kpi-num { color: #047857; }
.quality-health-watch   .quality-kpi-num { color: #b45309; }
.quality-health-low     .quality-kpi-num { color: var(--rpm-danger-dark); }

.quality-kpi-label {
  font-size: .875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--rpm-text-soft);
  margin-top: .5rem;
}
.quality-kpi-detail {
  flex: 1;
  font-size: .9375rem;
  line-height: 1.6;
}

.quality-pill {
  display: inline-block;
  padding: .25rem .625rem;
  border-radius: 999px;
  font-size: .8125rem;
  font-weight: 700;
}
.quality-pill.quality-health-healthy { background: #d1fae5; color: #065f46; }
.quality-pill.quality-health-watch   { background: #fef3c7; color: #92400e; }
.quality-pill.quality-health-low     { background: #fee2e2; color: var(--rpm-danger-dark); }
.quality-pill.quality-health-no-data { background: var(--rpm-bg-alt); color: var(--rpm-text-muted); }

/* Phase-2 placeholder card — visually muted so John knows it's coming, not broken */
.quality-phase2 {
  background: var(--rpm-bg-alt);
  border: 1.5px dashed var(--rpm-border);
}
.quality-phase2 h3 { font-style: italic; }
.quality-phase2-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
  margin-top: 1rem;
}
@media (min-width: 760px) { .quality-phase2-grid { grid-template-columns: 1fr 1fr; } }
.quality-phase2-num {
  font-size: 2rem;
  font-weight: 800;
  color: var(--rpm-text-muted);
  line-height: 1;
}
.quality-phase2-label {
  font-size: .875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--rpm-text-soft);
  margin-top: .375rem;
  margin-bottom: .25rem;
}


/* === Facility photos: action buttons in customer detail table + gallery thumbs === */
.facility-photo-actions {
  white-space: nowrap;
}
.facility-photo-actions .btn-mini + .btn-mini { margin-left: .375rem; }

.btn-mini-outline {
  /* Self-contained: carries .btn-mini's layout so it works standalone —
     six call sites use it without the base class. */
  display: inline-flex; align-items: center; justify-content: center;
  padding: .3125rem .75rem; font-size: .8125rem; font-weight: 600;
  min-width: 6.25rem; border-radius: var(--rpm-radius);
  cursor: pointer; text-decoration: none;
  background: transparent; color: var(--rpm-navy);
  border: 1px solid var(--rpm-border);
}
.btn-mini-outline:hover { background: var(--rpm-bg-alt); }
.btn-mini-outline:hover {
  background: var(--rpm-navy);
  color: white;
  text-decoration: none;
}

/* Photo gallery on the admin facility-photos view */
.facility-photo-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: .625rem;
  margin-top: .75rem;
}
.facility-photo-thumb {
  display: block;
  overflow: hidden;
  border-radius: var(--rpm-radius);
  border: 1px solid var(--rpm-border-soft);
  background: var(--rpm-bg-alt);
  aspect-ratio: 4 / 3;
}
.facility-photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .2s ease;
}
.facility-photo-thumb:hover img { transform: scale(1.04); }

@media (max-width: 640px) {
  .facility-photo-actions { white-space: normal; }
  .facility-photo-actions .btn-mini { display: inline-block; margin-bottom: .25rem; }
  .facility-photo-gallery { grid-template-columns: repeat(2, 1fr); }
}


/* === Service-stop trash button (admin customer detail) ─────────────
 * Sits on the LEFT of the stop name. iOS-style thin-line outline icon
 * (Heroicons trash shape) — neutral gray default, red on hover.
 */
.facility-delete-form {
  display: inline-flex;
  vertical-align: middle;
  margin: 0 .5rem 0 0;
  padding: 0;
}
.facility-delete-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: transparent;
  border: none;
  padding: 0;
  color: var(--rpm-text-muted);
  cursor: pointer;
  border-radius: var(--rpm-radius-lg);
  transition: background .12s ease, color .12s ease;
}
.facility-delete-btn svg {
  width: 18px;
  height: 18px;
  display: block;
}
.facility-delete-btn:hover {
  background: #fee2e2;
  color: var(--rpm-danger);
}
.facility-delete-btn:focus-visible {
  outline: 2px solid var(--rpm-danger);
  outline-offset: 1px;
}


/* ═════════════════════════════════════════════════════════════════════════
   ServiceFusion data-sync card (admin customer detail)
   ─────────────────────────────────────────────────────────────────────
   Card sits near the top of the customer page. Two-column layout:
     [ title + blurb + status pill   |   Sync Now button ]
   Status pill colours communicate state at a glance:
     never  = neutral light gray
     ok     = green dot + plain text
     running = pulsing blue dot + spinner
     error  = soft red with warning glyph
   ═════════════════════════════════════════════════════════════════════════ */
.sync-card-row {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.sync-card-info {
  flex: 1;
  min-width: 0;
}
.sync-card-title {
  margin: 0 0 .25rem;
  display: flex;
  align-items: center;
  gap: .625rem;
  flex-wrap: wrap;
}
.sync-card-blurb {
  margin: 0 0 .625rem;
}
.sync-phase-badge {
  display: inline-block;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: .1875rem .5rem;
  border-radius: 999px;
  background: var(--rpm-blue-soft, #e8f1f8);
  color: var(--rpm-blue, var(--rpm-blue));
  border: 1px solid #c7dff0;
}
.sync-card-action {
  flex-shrink: 0;
}
.sync-now-btn {
  min-width: 9.5rem;
}
.sync-now-btn[disabled] {
  cursor: not-allowed;
  opacity: 0.7;
}

/* ── Status pill ─────────────────────────────────────────────────── */
.sync-status-line {
  display: block;
  line-height: 1.4;
}
.sync-pill {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .375rem .75rem;
  border-radius: 999px;
  font-size: .875rem;
  font-weight: 500;
  background: #f1f5f9;
  color: var(--rpm-text-soft, #475569);
  border: 1px solid var(--rpm-border-soft);
  max-width: 100%;
}
.sync-pill-never {
  background: #f8fafc;
  color: var(--rpm-text-muted);
  font-style: italic;
}
.sync-pill-ok {
  background: #ecfdf5;
  color: #065f46;
  border-color: #a7f3d0;
}
.sync-pill-running {
  background: var(--rpm-blue-soft);
  color: #1e3a8a;
  border-color: var(--rpm-blue-soft);
}
.sync-pill-error {
  background: #fef2f2;
  color: var(--rpm-danger-dark);
  border-color: #fecaca;
}

/* Static green dot for the ok pill */
.sync-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #10b981;
  flex-shrink: 0;
  display: inline-block;
}

/* Spinner for the running pill — borrowed pattern, no CSS-anim libs */
.sync-spinner {
  width: 12px;
  height: 12px;
  border: 2px solid rgba(30, 58, 138, 0.25);
  border-top-color: #1e3a8a;
  border-radius: 50%;
  animation: sync-spin 0.8s linear infinite;
  flex-shrink: 0;
  display: inline-block;
}
@keyframes sync-spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 640px) {
  .sync-card-row {
    flex-direction: column;
    align-items: stretch;
  }
  .sync-card-action {
    width: 100%;
  }
  .sync-now-btn {
    width: 100%;
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   Settings hub (admin/settings/index.html)
   ─────────────────────────────────────────────────────────────────────
   Four stacked sections (Accounts / Data sync / System / Activity), each
   an anchor target for the sticky in-page nav. Card grids are auto-fit so
   a section with three cards and one with five both fill the row without
   per-section column rules; wide content (the sync table) uses a plain
   full-width card rather than a grid child.
   ═════════════════════════════════════════════════════════════════════════ */
.settings-tablist {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin: 0 0 1.5rem;
  border-bottom: 1px solid var(--rpm-border-soft);
}
.settings-tab {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .75rem 1rem;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  font-size: .9375rem;
  font-weight: 500;
  color: var(--rpm-text-soft, #475569);
  text-decoration: none;
}
.settings-tab:hover {
  color: var(--rpm-blue);
}
.settings-tab[aria-selected="true"] {
  color: var(--rpm-navy, #0f172a);
  font-weight: 600;
  border-bottom-color: var(--rpm-blue);
}
.settings-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 .375rem;
  border-radius: 999px;
  background: #fef2f2;
  border: 1px solid #fecaca;
  color: var(--rpm-danger-dark);
  font-size: .75rem;
  font-weight: 700;
}
/* Only once the script has confirmed it can drive the tabs does the
   heading become redundant; without JS every panel and heading shows. */
.js-tabs .settings-section-head {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.settings-panel + .settings-panel {
  margin-top: 2.25rem;
}
.js-tabs .settings-panel + .settings-panel {
  margin-top: 0;
}
.settings-section-head {
  margin: 2.25rem 0 .25rem;
  font-size: 1.0625rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--rpm-text-muted);
}
.settings-section-head + .settings-grid,
.settings-section-head + .card,
.settings-section-note + .settings-grid {
  margin-top: .875rem;
}
.js-tabs .settings-section-head + .settings-grid,
.js-tabs .settings-section-head + .card {
  margin-top: 0;
}
.settings-section-note {
  margin: .25rem 0 0;
  max-width: 62ch;
}

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
  gap: 1.25rem;
  align-items: start;
}
/* Kept for any template still spanning a card across the grid. */
.settings-card-wide {
  grid-column: 1 / -1;
}
.settings-card h3 {
  margin-top: 0;
}
.settings-card-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
}
.settings-card-head h3 {
  margin-bottom: .25rem;
}
.settings-stat {
  margin: .75rem 0;
}
.settings-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 1rem;
  align-items: center;
  margin-bottom: 0;
}
.settings-inline-link {
  font-size: .875rem;
}
.settings-role-pill {
  display: inline-block;
  margin-left: .5rem;
  padding: .0625rem .5rem;
  border-radius: 999px;
  background: var(--rpm-blue-soft);
  color: #1e3a8a;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.settings-links {
  margin: .75rem 0 0;
  padding: 0;
  list-style: none;
}
.settings-links li + li {
  margin-top: .375rem;
}
.settings-count-strip {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}

/* Sync table — the error detail row hangs directly under its customer. */
.settings-sync-table {
  margin-top: 1rem;
}
.settings-error-row td {
  padding-top: 0;
  border-top: 0;
}

/* System config — a definition list, not a table: label + value + the env
   var to edit, stacked, so long values (origin lists, paths) wrap instead
   of forcing a horizontal scroll. */
.settings-config {
  margin: .75rem 0 0;
}
.settings-config-row {
  display: grid;
  grid-template-columns: minmax(8rem, 10rem) minmax(0, 1fr);
  gap: .5rem 1rem;
  padding: .625rem 0;
  border-top: 1px solid var(--rpm-border-soft);
}
.settings-config-row:first-child {
  border-top: 0;
}
.settings-config dt {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-weight: 500;
}
.settings-config dd {
  margin: 0;
  min-width: 0;
}
.settings-config-value {
  display: block;
  overflow-wrap: anywhere;
}
.settings-config-hint {
  display: block;
  margin-top: .125rem;
  overflow-wrap: anywhere;
}
/* State glyph. A bare colour dot failed twice over — amber hit 2.15:1 and
   grey 1.48:1 against the card, and colour was the only channel carrying
   the meaning. The glyph fixes both; these three colours are all ≥4.5:1 on
   white so the mark itself is legible as text. */
.settings-flag {
  width: 1.125rem;
  height: 1.125rem;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: .75rem;
  font-weight: 700;
  line-height: 1;
  /* Sit on the label's first line, not centred in a row the value column
     has stretched to two or three lines. */
  align-self: flex-start;
  margin-top: .0625rem;
}
.settings-flag-ok   { color: #047857; background: #ecfdf5; }
.settings-flag-warn { color: #b45309; background: #fffbeb; }
.settings-flag-off  { color: #64748b; background: #f1f5f9; }

.settings-activity {
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}
.settings-activity li {
  padding: .5rem 0;
  border-top: 1px solid var(--rpm-border-soft);
}
.settings-activity li:first-child {
  border-top: 0;
}
.settings-activity-action {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8125rem;
}

/* Screen-reader-only text — the config dots need a spoken equivalent. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 720px) {
  .settings-grid {
    grid-template-columns: 1fr;
  }
  .settings-config-row {
    grid-template-columns: 1fr;
  }
}





/* ═════════════════════════════════════════════════════════════════════════
   FOOTPRINT — "the RPM network" band on the customer dashboard.

   Two stacked layers in one coordinate space:
     · the base map, a pre-rendered static SVG served as a plain <img>,
       identical for every customer;
     · an HTML pin layer for THIS customer's own stops, positioned in
       percentages by routes/customer.py.

   Why percentages line up: the <img> carries width/height attributes equal
   to the SVG's viewBox dimensions, so with width:100%;height:auto its box is
   a pure uniform scale of the viewBox — preserveAspectRatio="xMidYMid meet"
   adds no letterboxing when the aspects match. A percentage of the box is
   therefore the same fraction of the viewBox at every viewport width, with
   no JavaScript. Do not give the <img> object-fit or drop its width/height
   attributes; either one breaks register.
   ═════════════════════════════════════════════════════════════════════════ */
.footprint-head {
  margin: 0 0 .75rem;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--rpm-text-muted);
}
.footprint {
  background: var(--rpm-navy-deep);
  border: 1px solid var(--rpm-navy-light);
  border-radius: var(--rpm-radius-lg);
  padding: 1rem 1rem .25rem;
  margin-bottom: 1.25rem;
  /* Deliberately NOT overflow:hidden — that clips the hover cards. The
     rounded corner lives on the image itself instead. */
}
.footprint-stage {
  position: relative;
  line-height: 0;
}
.footprint-stage img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--rpm-radius);
}

/* ── the pin layer ─────────────────────────────────────────────────────── */
.footprint-pins {
  position: absolute;
  inset: 0;
  /* The layer itself must never swallow a click aimed at the map. */
  pointer-events: none;
}
.pin,
.dot {
  position: absolute;
  /* Anchor the pin's TIP on the coordinate, not its centre. */
  transform: translate(-50%, -100%);
}
.dot {
  width: .5rem;
  height: .5rem;
  margin-top: .25rem;          /* dots have no tip — re-centre them */
  border-radius: 50%;
  background: #ffffff;
  border: 1px solid rgba(6, 22, 41, .85);
  opacity: .85;
}
.pin {
  pointer-events: auto;
  z-index: 1;
  line-height: 1;
  text-decoration: none;
  font-size: 1.375rem;
  animation: pin-drop .5s cubic-bezier(.2, .9, .3, 1.3) both;
  animation-delay: var(--d, 0ms);
}
.pin:hover,
.pin:focus-within,
.pin:focus-visible { z-index: 40; }
.pin:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 3px;
  border-radius: 4px;
}
.pin-glyph {
  display: block;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .55));
  transition: transform .18s ease;
}
.pin:hover .pin-glyph,
.pin:focus-visible .pin-glyph { transform: translateY(-4px) scale(1.12); }

/* A ripple at the pin's tip, so the eye catches the stop even before hover. */
.pin::after {
  content: "";
  position: absolute;
  left: 50%; bottom: 0;
  width: 10px; height: 10px;
  margin: 0 0 -5px -5px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .85);
  pointer-events: none;
  animation: rpm-ping 3.4s ease-out infinite;
  animation-delay: var(--d, 0ms);
}
.pin-count {
  position: absolute;
  top: -2px; right: -10px;
  min-width: 1rem;
  padding: 0 .2rem;
  border-radius: 999px;
  background: #ffffff;
  color: var(--rpm-navy-deep);
  font: 700 .625rem/1rem system-ui, -apple-system, sans-serif;
  text-align: center;
}
/* Dense state maps — a zoomed-in Texas carrying a few hundred stops. Every
   stop keeps its own marker, hover card and link, but the emoji is swapped
   for a compact dot: at this density a field of pins smears into one blob,
   while dots read as a constellation of real locations. Hovering restores
   the pin, so the affordance is the same one the sparse map teaches. */
.footprint-pins--dense .pin {
  width: 7px;
  height: 7px;
  margin: 0 0 -3px -3px;         /* dots have no tip; centre them instead */
  border-radius: 50%;
  background: #ff5a5a;
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, .9), 0 1px 3px rgba(0, 0, 0, .6);
  transform: none;
  transition: transform .15s ease;
}
.footprint-pins--dense .pin-glyph,
.footprint-pins--dense .pin-count,
.footprint-pins--dense .pin::after { display: none; }
.footprint-pins--dense .pin:hover,
.footprint-pins--dense .pin:focus-visible { transform: scale(1.9); }
.footprint-pins--dense .pin-card { bottom: calc(100% + 6px); }

/* Visually hidden, but read aloud — this is the link's accessible name. */
.pin-sr {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── the hover card ────────────────────────────────────────────────────── */
.pin-card {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  display: grid;
  gap: .125rem;
  width: max-content;
  max-width: 15rem;
  padding: .5rem .625rem;
  border-radius: 8px;
  background: rgba(6, 22, 41, .97);
  border: 1px solid var(--rpm-navy-light);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
  color: #e8f0f8;
  font: 400 .75rem/1.35 system-ui, -apple-system, sans-serif;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  /* Never intercept the pin's own click, and never create a hover trap. */
  pointer-events: none;
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}
/* :focus-within is what gives keyboard users hover parity, for free. */
.pin:hover .pin-card,
.pin:focus-within .pin-card {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}
.pin-card-name { font-weight: 700; color: #ffffff; }
.pin-card-name ~ .pin-card-name { margin-top: .375rem; }
.pin-card-meta { color: #9fb6cd; }
.pin-card-more { color: #7fd4ff; margin-top: .25rem; }

/* Edge flips — the server sets these when a card would fall off the frame. */
.pin--below .pin-card {
  bottom: auto;
  top: calc(100% + 4px);
}
.pin--right .pin-card {
  left: 0;
  transform: translateX(-10%) translateY(4px);
}
.pin--right:hover .pin-card,
.pin--right:focus-within .pin-card { transform: translateX(-10%) translateY(0); }
.pin--left .pin-card {
  left: auto; right: 0;
  transform: translateX(10%) translateY(4px);
}
.pin--left:hover .pin-card,
.pin--left:focus-within .pin-card { transform: translateX(10%) translateY(0); }

@keyframes pin-drop {
  from { opacity: 0; transform: translate(-50%, -170%) scale(.7); }
  to   { opacity: 1; transform: translate(-50%, -100%) scale(1); }
}
@keyframes rpm-ping {
  0%        { transform: scale(.45); opacity: .9; }
  70%, 100% { transform: scale(2.2);  opacity: 0; }
}

/* ── the note under the map ────────────────────────────────────────────── */
.footprint-note {
  margin: .875rem .25rem;
  font-size: .875rem;
  line-height: 1.5;
  color: #9fb6cd;
}
.footprint-note strong { color: #ffffff; }
.footprint-caveat { color: #6f8bab; }
/* Legend swatch — a red "live" indicator, the way a recording light reads. */
.footprint-key {
  display: inline-block;
  width: .5rem; height: .5rem;
  margin-right: .45rem;
  border-radius: 50%;
  background: #ff4d4d;
  vertical-align: baseline;
  animation: rpm-live 2s ease-in-out infinite;
}
@keyframes rpm-live {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 77, 77, .55); opacity: .75; }
  50%      { box-shadow: 0 0 0 4px rgba(255, 77, 77, 0);  opacity: 1; }
}

/* On touch there is no hover; a tap should just open the stop. */
@media (hover: none) {
  .pin-card { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .pin { animation: none; }
  .pin::after { animation: none; opacity: .5; }
  .pin-glyph { transition: none; }
  .pin:hover .pin-glyph,
  .pin:focus-visible .pin-glyph { transform: none; }
  .pin-card { transition: none; }
  .footprint-key { animation: none; }
}
@media (max-width: 640px) {
  .footprint { padding: .5rem .5rem .25rem; }
  .footprint-note { font-size: .8125rem; }
  .pin { font-size: 1.125rem; }
}

/* ═════════════════════════════════════════════════════════════════════════
   MASTER MAP — admin-only, /admin/map.

   The one view that deliberately crosses every customer. Pan/zoom is driven
   by static/js/stop-map.js, the portal's only client-side script; this
   sheet just supplies the surfaces it moves around.
   ═════════════════════════════════════════════════════════════════════════ */
.mmap-card-shell { padding: 1rem; }
.mmap-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-bottom: .875rem;
}
.mmap-filter { display: flex; align-items: center; gap: .5rem; margin: 0; }
.mmap-filter span {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--rpm-text-muted);
}
.mmap-filter select {
  padding: .375rem .5rem;
  border: 1.5px solid var(--rpm-border);
  border-radius: var(--rpm-radius);
  font: inherit;
  font-size: .875rem;
  background: white;
  max-width: 18rem;
}
.mmap-zoom {
  display: flex;
  align-items: center;
  gap: .375rem;
  margin-left: auto;
}
.mmap-readout {
  font-size: .75rem;
  color: var(--rpm-text-muted);
  font-variant-numeric: tabular-nums;
  margin-right: .25rem;
}

/* Legend. The buckets answer "what needs attention", so they are named
   ranges rather than a continuous ramp. */
.mmap-legend { display: flex; flex-wrap: wrap; gap: .5rem; }
.mmap-key {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .6875rem;
  color: var(--rpm-text-muted);
}
.mmap-key::before {
  content: "";
  width: .5rem; height: .5rem;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .7);
}
.mmap-key--fresh::before   { background: #22c55e; }
.mmap-key--recent::before  { background: #38bdf8; }
.mmap-key--stale::before   { background: #f59e0b; }
.mmap-key--overdue::before { background: #ef4444; }
.mmap-key--never::before   { background: #94a3b8; }

.mmap-viewport {
  position: relative;
  overflow: hidden;
  border-radius: var(--rpm-radius);
  background: var(--rpm-navy-deep);
  cursor: grab;
  touch-action: none;          /* the script owns panning */
  aspect-ratio: 606 / 328;
}
.mmap-viewport.is-dragging { cursor: grabbing; }
.mmap-canvas {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  /* Deliberately NOT will-change/translateZ: promoting this to its own layer
     is what caused the SVG to be rasterized once and stretched. Zoom changes
     width/height (see stop-map.js), so it must re-render each time. */
}
.mmap-canvas img {
  display: block;
  opacity: var(--heat, 1);
  transition: opacity .2s ease;
  width: 100%;
  height: 100%;
  /* The pins are positioned in percentages of this box, so the image must
     fill it exactly — no letterboxing, no object-fit games. */
  user-select: none;
  -webkit-user-drag: none;
}
.mmap-pins { position: absolute; inset: 0; }

/* Reference geography. Both layers are drawn by stop-map.js rather than
   baked into the base SVG, so their weight and type size stay constant on
   screen while the map underneath grows — the thing that made labels turn
   chunky when they were part of the image. */
.mmap-roads {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
}
.mmap-roads.is-on { opacity: .5; }
.mmap-roads path {
  fill: none;
  stroke: #7fd4ff;
  stroke-width: 0.9;          /* screen px — see vector-effect in the JS */
  stroke-linecap: round;
  stroke-linejoin: round;
}
.mmap-labels { position: absolute; inset: 0; pointer-events: none; }
.mmap-label {
  position: absolute;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  color: #cfe4f7;
  paint-order: stroke;
  -webkit-text-stroke: 3px rgba(6, 22, 41, .85);
  font-weight: 600;
  letter-spacing: .01em;
}
.mmap-label--t0 { font-size: 13px; color: #fff; }
.mmap-label--t1 { font-size: 11px; }
.mmap-label--t2 { font-size: 10px; opacity: .92; }
.mmap-label--t3 { font-size: 9px;  opacity: .82; }
.mmap-label--t4 { font-size: 8px;  opacity: .72; }
/* The stop's own name, once there is room for it. */
.mmap-stop-label {
  position: absolute;
  transform: translate(-50%, 10px);
  white-space: nowrap;
  font-size: 9px;
  font-weight: 700;
  color: #fff;
  paint-order: stroke;
  -webkit-text-stroke: 3px rgba(6, 22, 41, .9);
  pointer-events: none;
}

.mmap-pin {
  position: absolute;
  width: 11px; height: 11px;
  padding: 0;
  border: 1.5px solid rgba(255, 255, 255, .92);
  border-radius: 50%;
  background: #94a3b8;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .6);
  cursor: pointer;
  transform: translate(-50%, -50%);
  transform-origin: center;
}
.mmap-pin:hover,
.mmap-pin:focus-visible {
  z-index: 5;
  transform: translate(-50%, -50%) scale(1.5);
}
.mmap-pin:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.mmap-pin--fresh   { background: #22c55e; }
.mmap-pin--recent  { background: #38bdf8; }
.mmap-pin--stale   { background: #f59e0b; }
.mmap-pin--overdue { background: #ef4444; }
.mmap-pin--never   { background: #94a3b8; }
/* A cluster carries its count, so it needs room for the digits. */
.mmap-pin--group {
  width: auto; height: auto;
  min-width: 18px;
  padding: 2px 4px;
  border-radius: 999px;
  font: 700 10px/1 system-ui, -apple-system, sans-serif;
  color: var(--rpm-navy-deep);
}
.mmap-pin-n { pointer-events: none; }

.mmap-card {
  position: absolute;
  z-index: 20;
  display: grid;
  gap: .1rem;
  max-width: 17rem;
  padding: .5rem .625rem;
  border-radius: 8px;
  background: rgba(6, 22, 41, .97);
  border: 1px solid var(--rpm-navy-light);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .5);
  color: #e8f0f8;
  font: 400 .75rem/1.35 system-ui, -apple-system, sans-serif;
  pointer-events: none;
}
/* `display: grid` outranks the UA stylesheet's `[hidden] { display: none }`,
   so the hidden attribute alone would never dismiss this card. */
.mmap-card[hidden] { display: none; }
.mmap-card-name { font-weight: 700; color: #fff; }
.mmap-card-cust { color: #7fd4ff; }
.mmap-card-meta { color: #9fb6cd; margin-bottom: .25rem; }
.mmap-foot { margin: .75rem .125rem 0; font-size: .8125rem; }

@media (max-width: 720px) {
  .mmap-zoom { margin-left: 0; }
  .mmap-viewport { aspect-ratio: 4 / 3; }
}

/* ── customer photo lightbox ─────────────────────────────────────────── */
.photo-open, .photo-gallery a[href], .facility-photo-gallery a[href],
.photo-grid a[href], [data-gallery] a[href], a[data-photo] { cursor: zoom-in; }
.photo-open { display: block; }
.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(15, 23, 42, .92);
  display: flex; align-items: center; justify-content: center;
}
.lightbox[hidden] { display: none; }
/* Photo plus whatever sits directly under it (the comment control on admin
   pages), as one centred column. Bounded so the caption along the bottom
   always has room; the image is the part that gives, which is why opening
   the comment panel shrinks the photo instead of covering it. */
.lightbox-stage {
  display: flex; flex-direction: column; align-items: center; gap: .75rem;
  max-height: 84vh; max-width: min(92vw, 1024px);
}
.lightbox-img {
  max-width: 100%; max-height: 84vh;
  flex: 0 1 auto; min-height: 0; object-fit: contain;
  border-radius: 8px; box-shadow: 0 12px 40px rgba(0,0,0,.5);
}
/* Room for the button under the photo, and for the panel once it opens. */
.lightbox-stage:has(.lightbox-comments:not([hidden])) .lightbox-img { max-height: 72vh; }
.lightbox-stage:has(.lightbox-comment-panel:not([hidden])) .lightbox-img { max-height: 40vh; }
.lightbox-caption {
  position: absolute; bottom: 1.25rem; left: 0; right: 0;
  text-align: center; color: #e5e7eb; font-size: .875rem;
  padding: 0 4rem;
}
.lightbox-close, .lightbox-nav {
  position: absolute; border: 0; background: rgba(255,255,255,.12);
  color: #fff; font-size: 1.75rem; line-height: 1;
  width: 2.75rem; height: 2.75rem; border-radius: 999px; cursor: pointer;
}
.lightbox-close:hover, .lightbox-nav:hover { background: rgba(255,255,255,.24); }
.lightbox-close { top: 1rem; right: 1rem; }
.lightbox-prev { left: 1rem; top: 50%; transform: translateY(-50%); }
.lightbox-next { right: 1rem; top: 50%; transform: translateY(-50%); }
@media (max-width: 640px) {
  .lightbox-img { max-width: 96vw; }
  .lightbox-prev { left: .5rem; } .lightbox-next { right: .5rem; }
}

/* ── lightbox comments (admin only) ───────────────────────────────────────
   Anthony reviews a visit's photos and leaves notes on the ones that need
   one. Anchored bottom-left so it clears the caption (centred) and the next
   arrow (right); the panel grows upward rather than pushing the photo. */
.lightbox-comments {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  width: 100%; max-width: min(28rem, 90vw); min-height: 0;
}
.lightbox-comments[hidden] { display: none; }
.lightbox-comment-open { background: rgba(255,255,255,.14); color: #fff; border-color: transparent; }
.lightbox-comment-open:hover { background: rgba(255,255,255,.26); }
.lightbox-comment-panel {
  background: #fff; color: #0f172a; border-radius: 10px;
  padding: .75rem; box-shadow: 0 12px 40px rgba(0,0,0,.45);
  width: 100%; box-sizing: border-box;
  max-height: 30vh; overflow-y: auto;
}
.lightbox-comment-panel[hidden] { display: none; }
.lightbox-comment-list { list-style: none; margin: 0 0 .5rem; padding: 0; }
.lightbox-comment-list li { padding: .375rem 0; border-bottom: 1px solid #e5e7eb; font-size: .875rem; }
.lightbox-comment-list li:last-child { border-bottom: 0; }
.lightbox-comment-list li.is-resolved { opacity: .55; }
.lightbox-comment-form textarea {
  width: 100%; box-sizing: border-box; font: inherit; font-size: .875rem;
  border: 1px solid #cbd5e1; border-radius: 6px; padding: .375rem .5rem; resize: vertical;
}
.lightbox-comment-actions {
  display: flex; align-items: center; gap: .5rem; margin-top: .5rem; flex-wrap: wrap;
}
.lightbox-comment-status { font-size: .8125rem; color: #475569; }
@media (max-width: 640px) {
  .lightbox-comments { max-width: 96vw; }
  .lightbox-stage:has(.lightbox-comment-panel:not([hidden])) .lightbox-img { max-height: 32vh; }
}

/* ── invite: ready-to-send draft ─────────────────────────────────────── */
.invite-draft { margin-top: 1rem; border-top: 1px solid #e5e7eb; padding-top: .75rem; }
.invite-draft summary { cursor: pointer; margin-bottom: .5rem; }
.invite-draft textarea, .invite-draft input[type=text] {
  width: 100%; font-family: ui-monospace, Menlo, monospace; font-size: .8125rem;
}

/* ── customer QA review ──────────────────────────────────────────────── */
.qa-request { margin: .5rem 0 .75rem; }
.qa-request summary { cursor: pointer; font-size: .875rem; color: var(--rpm-blue); }
.qa-request .form { margin-top: .5rem; }
.qa-request textarea { width: 100%; }
.qa-status {
  margin: .5rem 0 .75rem; padding: .625rem .75rem; border-radius: var(--rpm-radius-lg);
  font-size: .875rem; border: 1px solid transparent;
}
.qa-status--open, .qa-status--in_review { background: #fffbeb; border-color: #fde68a; }
.qa-status--resolved { background: #ecfdf5; border-color: #a7f3d0; }
.qa-status p { margin: .375rem 0 0; }

/* ── admin QA ────────────────────────────────────────────────────────── */
.nav-badge {
  display: inline-block; min-width: 1.25rem; padding: 0 .375rem;
  background: var(--rpm-danger); color: #fff; border-radius: 999px;
  font-size: .6875rem; font-weight: 700; text-align: center; line-height: 1.25rem;
}
.qa-reason {
  padding: .75rem; background: #fffbeb; border: 1px solid #fde68a;
  border-radius: var(--rpm-radius-lg); margin: 0;
}

/* ── collapsible repeat visits ───────────────────────────────────────── */
/* Section headings inside the history card: "Most recent service",
   "Coming up", "Earlier visits". Small caps so the visit rows stay the
   loudest thing on the page. */
.history-group {
  font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--rpm-text-muted);
  margin: 1.25rem 0 .25rem;
}
.history-group:first-of-type { margin-top: .5rem; }
.history-group .muted { text-transform: none; letter-spacing: 0; font-weight: 500; }
.visit--collapsible { border-bottom: 1px solid #e5e7eb; padding: .75rem 0; }
.visit--collapsible:last-of-type { border-bottom: 0; }
.visit-summary-line {
  cursor: pointer; list-style: none; padding: .25rem 0;
  display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap;
}
.visit-summary-line::-webkit-details-marker { display: none; }
.visit-summary-line::before {
  content: "▸"; color: var(--rpm-text-muted); transition: transform .15s;
}
.visit--collapsible[open] > .visit-summary-line::before { content: "▾"; }
.visit-actions { margin: .75rem 0 .25rem; }
/* Invoice reference on a visit summary. Findable but subordinate: a visit's
   identity is its date (700), so this sits a step below at 500 and earns its
   scannability from the navy and from tabular figures lining up down the
   column. At 600 the longer string outweighed the date and inverted the
   hierarchy. */
.invoice-ref {
  font-variant-numeric: tabular-nums;
  color: var(--rpm-navy);
  font-weight: 500;
}

/* ── new service stop ────────────────────────────────────────────────── */
.page-actions { margin: .5rem 0 1rem; }
.form-row--split { display: flex; gap: .75rem; }
.form-row--split > span { flex: 1; }
.form-row--split > span:last-child { flex: 0 0 7rem; } /* ZIP-sized */
@media (max-width: 640px) {
  .form-row--split { flex-direction: column; gap: 0; }
  .form-row--split > span:last-child { flex: 1; }
}
.stop-request { padding: 1rem 0; border-bottom: 1px solid #e5e7eb; }
.stop-request:last-child { border-bottom: 0; }

/* ── next service banner ─────────────────────────────────────────────── */
.next-service {
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline;
  padding: .75rem 1rem; border-radius: 8px; margin-bottom: 1rem;
  border: 1px solid;
}
.next-service--booked { background: var(--rpm-blue-soft); border-color: var(--rpm-blue-soft); }
.next-service--estimated { background: #f8fafc; border-color: var(--rpm-border-soft); }
.next-service-label {
  font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--rpm-text-muted); font-weight: 700;
}

/* ── two-row admin header ────────────────────────────────────────────── */
.topbar-right {
  display: flex; align-items: center; gap: 1rem; margin-left: auto;
}
/* Nav row: two groups — sections flush left under the brand, queues flush
   right under the user chip, so the row balances the header row above it.
   Below 900px each group takes its own full-width centered row rather than
   leaving a ragged gap in the middle. */
.topnav--rows {
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  padding: .375rem 0 .625rem;
  gap: .25rem 1.25rem;
}
.topnav-group {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: .25rem 1.125rem; min-width: 0;
}
.topnav-group--queues { margin-left: auto; }

@media (max-width: 900px) {
  .topnav--rows { justify-content: center; gap: .125rem; }
  .topnav-group {
    flex: 1 1 100%;
    justify-content: center;
    margin-left: 0;
    gap: .25rem .875rem;
  }
  .topnav-group--queues {
    margin-left: 0;
    padding-top: .25rem;
    border-top: 1px solid var(--rpm-border-soft);
  }
}
.queue-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: .625rem; margin-top: .5rem;
}
.queue-tile {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; padding: .75rem .875rem; border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius-lg); text-decoration: none;
  color: var(--rpm-navy); font-weight: 600; font-size: .9375rem;
  background: var(--rpm-bg);
}
.queue-tile:hover { background: var(--rpm-blue-soft); border-color: var(--rpm-blue); }


/* ── keyboard focus for every button ─────────────────────────────────── */
.btn:focus-visible, .btn-mini:focus-visible, .btn-mini-outline:focus-visible,
.btn-mini-warn:focus-visible, .btn-link:focus-visible,
.lightbox-close:focus-visible, .lightbox-nav:focus-visible,
.queue-tile:focus-visible {
  outline: 3px solid rgba(0, 117, 191, .45);
  outline-offset: 2px;
}

.col-freetext { max-width: 26rem; }

.empty-state {
  color: var(--rpm-text-muted); text-align: center; padding: 1.5rem 1rem;
  border: 1px dashed var(--rpm-border-soft); border-radius: var(--rpm-radius-lg);
}

/* ── phase-4 refresh: rhythm + cards ─────────────────────────────────── */
.card { border-radius: var(--rpm-radius-lg); }
.card > h3:first-child { margin-top: 0; }
.page-head { margin-bottom: 1.25rem; align-items: flex-start; }
.page-head h1 { margin-bottom: .25rem; }
/* Title block takes the slack; the action cluster keeps its own spacing
   and wraps as a unit instead of relying on inline whitespace. */
.page-head > :first-child { flex: 1 1 20rem; min-width: 0; }
.page-head > :not(:first-child) {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .625rem; justify-content: flex-end;
}
.page-head > :not(:first-child) .btn { margin: 0; }
@media (max-width: 640px) {
  .page-head > :not(:first-child) { width: 100%; justify-content: flex-start; }
}
a.card:hover, .queue-tile:hover {
  box-shadow: 0 2px 12px rgba(10, 37, 64, .08);
}
.lead { color: var(--rpm-text-soft); }


/* ── customer service map ────────────────────────────────────────────────
   The dashboard map is the same pan/zoom component as the admin master map,
   dropped into the dark footprint panel. These rules only re-dress the
   shared .mmap-* surfaces for that panel; the behaviour is stop-map.js. */
.footprint-bar {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: .5rem 1rem;
  margin-bottom: .625rem;
}
.footprint-bar .footprint-head { margin: 0; }
.footprint-bar .mmap-zoom { margin-left: auto; }
.footprint-bar .mmap-readout { color: rgba(255, 255, 255, .6); }
.footprint-bar .btn-mini {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .22);
  color: #e6f0fa;
}
.footprint-bar .btn-mini:hover {
  background: rgba(255, 255, 255, .18);
  border-color: rgba(255, 255, 255, .38);
  color: white;
}
/* The stage is the positioning context for the viewport; the legend floats
   over it so the map keeps its full height. */
.mmap-shell { line-height: normal; }
.mmap-shell .mmap-viewport { border-radius: var(--rpm-radius); }
.mmap-shell .mmap-legend {
  margin-bottom: .5rem;
  gap: .75rem;
}
.mmap-shell .mmap-key { color: rgba(255, 255, 255, .62); }
.footprint-empty {
  color: rgba(255, 255, 255, .72);
  font-size: .9375rem;
  line-height: 1.6;
  margin: 0 0 .75rem;
}
.footprint-empty a { color: #7fd4ff; }

/* An anchor jump must not land under the sticky header. */
html { scroll-behavior: smooth; }
[id="service-map"], [id="stops"] { scroll-margin-top: 6.5rem; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}


/* === Per-customer branding ─────────────────────────────────────────
   A customer's own logo, shown where their name would otherwise sit: the
   dashboard header, and the preview on the admin customer form. Sized by
   height so wide and square marks both land on the same baseline. */
.page-head img.customer-logo {
  display: block;
  height: 34px;
  width: auto;
  max-width: 260px;
  object-fit: contain;
  /* Matches .eyebrow's bottom margin so the greeting below doesn't shift
     between a logo account and a name-only one. */
  margin: 0 0 .75rem;
}
.customer-logo-preview {
  margin: 0 0 .625rem;
  padding: .75rem;
  background: var(--rpm-bg-alt);
  border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius);
}
.customer-logo-preview img {
  display: block;
  max-height: 64px;
  max-width: 100%;
  width: auto;
  object-fit: contain;
}
/* Specificity has to beat `.form-row label { display: block }` and
   `.form-row input { width: 100% }`, or the box floats above its own text. */
.form-row label.checkbox-inline,
.checkbox-inline {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .625rem;
  font-weight: 500;
  font-size: .9375rem;
  cursor: pointer;
}
.form-row .checkbox-inline input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
  width: auto;
  margin: 0;
  accent-color: var(--rpm-blue);
}

/* === Stop access (who sees which stops) ────────────────────────────── */
.alert-info {
  background: var(--rpm-blue-soft);
  color: var(--rpm-navy);
  border: 1px solid #bfdbfe;
}
.stop-access-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .75rem;
}
.stop-access-head h3 { margin: 0; }
.stop-access-tools {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}
.stop-access-tools input[type="search"] {
  min-width: 16rem;
  padding: .4rem .625rem;
  border: 1px solid var(--rpm-border);
  border-radius: var(--rpm-radius);
  font: inherit;
  font-size: .875rem;
}
.stop-access-picker select { max-width: 32rem; }
.stop-access-scope {
  display: grid;
  gap: .5rem;
  max-width: 40rem;
}
/* Two columns on a wide screen, one on a narrow. H-E-B has 666 stops, so
   this list is long by design — the filter box above it is the real
   navigation, and the grid just stops it wasting half the page. */
.stop-access-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
  gap: .375rem;
  margin-top: .75rem;
  max-height: 34rem;
  overflow-y: auto;
}
.stop-access-row { font-weight: 500; }
#stop-list-card.is-disabled {
  opacity: .45;
  pointer-events: none;
}

/* === Skeleton loaders (photos + lightbox) ──────────────────────────────
   The portal is server-rendered, so the only data the browser still waits
   on after first paint is images: gallery thumbnails filling in, and the
   full-size photo behind the lightbox. Until an image has pixels its box
   is transparent, so a shimmer painted on the tile's own background reads
   as "loading" with no extra markup — the moment the opaque, object-fit:
   cover photo paints, it covers the shimmer entirely. That means the
   skeleton works with JS off; static/js/img-skeleton.js only ends it
   (stops the animation, fades the image in, flags failed photos). */
.photo, .photo-thumb a, .facility-photo-thumb {
  background: linear-gradient(100deg, #e6ecf2 38%, #f4f8fb 50%, #e6ecf2 62%);
  background-size: 300% 100%;
  animation: skel-shimmer 1.3s ease-in-out infinite;
}
/* The upload page's tile is a dark slab — keep its depth, shimmer darker. */
.photo-thumb a {
  background: linear-gradient(100deg, #253248 38%, #33445f 50%, #253248 62%);
  background-size: 300% 100%;
}
@keyframes skel-shimmer {
  from { background-position: 100% 0; }
  to   { background-position: 0% 0; }
}

/* JS-marked end states: shimmer off, original tile background back. */
.photo.is-loaded, .photo-thumb a.is-loaded, .facility-photo-thumb.is-loaded {
  animation: none;
}
.photo.is-loaded { background: #f8fafc; }
.photo-thumb a.is-loaded { background: #1f2937; }
.facility-photo-thumb.is-loaded { background: var(--rpm-bg-alt); }

/* Fade-in. skel-wait is only ever added by JS, so a browser without it
   never has photos held at opacity 0. The transition lives on the loaded
   state so the pre-load state snaps (nothing to animate from anyway). */
img.skel-wait { opacity: 0; }
img.skel-wait.skel-in { opacity: 1; transition: opacity .3s ease; }

/* A photo that failed to load: stop pretending it is coming. Flat muted
   tile, and the img stays visible so its alt text still tells the reader
   what should have been here. */
.photo.skel-broken, .photo-thumb a.skel-broken, .facility-photo-thumb.skel-broken {
  animation: none;
  background: var(--rpm-bg-alt);
  color: var(--rpm-text-muted);
}
.photo-thumb a.skel-broken { background: #1f2937; color: #9ca3af; }
.skel-broken img { opacity: 1; }

/* Lightbox: the full-size photo is the heaviest fetch in the portal.
   While it is in flight, dim whatever is on screen and spin — same
   functional-spinner pattern as .sync-spinner, which reduced-motion
   deliberately leaves running (unlike the decorative shimmer below). */
.lightbox.is-busy .lightbox-img { opacity: .25; }
.lightbox-img { transition: opacity .15s ease; }
.lightbox-spinner {
  position: absolute; top: 50%; left: 50%;
  width: 2.5rem; height: 2.5rem;
  margin: -1.25rem 0 0 -1.25rem;
  border: 3px solid rgba(255, 255, 255, .25);
  border-top-color: #fff;
  border-radius: 999px;
  animation: sync-spin .7s linear infinite;
  pointer-events: none;
}
.lightbox:not(.is-busy) .lightbox-spinner { display: none; }

@media (prefers-reduced-motion: reduce) {
  /* The sweep is decorative — hold the flat placeholder tone instead. */
  .photo, .photo-thumb a, .facility-photo-thumb { animation: none; }
  img.skel-wait { opacity: 1; }
  img.skel-wait.skel-in { transition: none; }
  .lightbox-img { transition: none; }
}


/* ── owner workspace (admin/workspace.html) ───────────────────────────
   One account, one screen. Big numbers, short lists, one action per line,
   and a calm empty state — built for a reader who scans, not reads. */
.ws .page-head { margin-bottom: 0; }
.ws-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.ws-head h1 { margin: .125rem 0 .25rem; }
.ws-head-id { display: flex; align-items: center; gap: 1rem; min-width: 0; }
.ws-logo { max-height: 56px; max-width: 160px; object-fit: contain; }
.ws-head-actions { display: flex; gap: .625rem; align-items: center; flex-wrap: wrap; }
.ws-head-actions form { margin: 0; }
.ws-sync { margin: 0; color: var(--rpm-text-muted); font-size: .9375rem; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.ws-dot { display: inline-block; width: .625rem; height: .625rem; border-radius: 999px; background: var(--rpm-border); }
.ws-dot--ok { background: #16a34a; }
.ws-dot--bad { background: var(--rpm-danger); }
.ws-dot--busy { background: var(--rpm-blue); animation: ws-pulse 1s ease-in-out infinite; }
@keyframes ws-pulse { 50% { opacity: .35; } }

.ws-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .875rem; margin-bottom: 1.25rem;
}
@media (max-width: 480px) { .ws-tiles { grid-template-columns: repeat(2, 1fr); } }
.ws-tile {
  display: flex; flex-direction: column; gap: .25rem;
  background: white; border: 1px solid var(--rpm-border-soft); border-radius: var(--rpm-radius-lg);
  padding: 1.125rem 1.25rem; text-decoration: none; color: var(--rpm-navy);
  border-left: 5px solid var(--rpm-blue);
}
.ws-tile:hover { box-shadow: 0 2px 12px rgba(10, 37, 64, .08); text-decoration: none; }
.ws-tile--attention { border-left-color: var(--rpm-danger); background: #fff7f7; }
.ws-tile-num { font-size: 2.5rem; font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.ws-tile-label { font-size: .9375rem; font-weight: 600; color: var(--rpm-text); }
.ws-tile-label small { display: block; font-weight: 500; color: var(--rpm-text-muted); margin-top: .125rem; }

.ws-columns { display: grid; grid-template-columns: 1fr; gap: 1.25rem; align-items: start; }
@media (min-width: 1024px) { .ws-columns { grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); } }
@media (min-width: 1440px) { .ws-columns { gap: 1.75rem; grid-template-columns: minmax(0, 1fr) minmax(340px, 22rem); } }
.ws-main, .ws-side { min-width: 0; }
.ws-card h3 { font-size: 1.125rem; }
.ws-list { list-style: none; margin: .5rem 0 0; padding: 0; }
.ws-item {
  display: flex; align-items: center; gap: .875rem; padding: .75rem 0;
  border-bottom: 1px solid var(--rpm-border-soft);
}
.ws-item:last-child { border-bottom: 0; }
.ws-item-icon {
  flex: 0 0 2rem; height: 2rem; border-radius: 999px; display: inline-flex;
  align-items: center; justify-content: center; font-weight: 800;
  background: var(--rpm-blue-soft); color: var(--rpm-blue);
}
.ws-item--sync .ws-item-icon, .ws-item--photos .ws-item-icon { background: #fff1f0; color: var(--rpm-danger); }
.ws-item-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .125rem; }
.ws-item-text strong { color: var(--rpm-navy); }
.ws-empty {
  margin: .5rem 0 0; padding: 1rem; border-radius: var(--rpm-radius-lg);
  background: #f0fdf4; color: #166534; font-weight: 600;
}
.ws-warn { color: var(--rpm-danger); font-weight: 700; }
.ws-row-actions { white-space: nowrap; }
.ws-row-actions form { margin: 0 0 0 .375rem; }
.ws-table td, .ws-table th { padding-left: .5rem; padding-right: .5rem; }
.ws-table td:first-child { white-space: nowrap; }

.ws-search { display: flex; gap: .5rem; margin-top: .5rem; }
.ws-search input[type="search"] {
  flex: 1 1 auto; min-width: 0; padding: .5rem .75rem; border: 1.5px solid var(--rpm-border);
  border-radius: var(--rpm-radius); font: inherit; font-size: .9375rem;
}
.ws-search input[type="search"]:focus { outline: none; border-color: var(--rpm-blue); box-shadow: 0 0 0 3px rgba(0,117,191,.15); }
.ws-stops { list-style: none; margin: .75rem 0 0; padding: 0; }
.ws-stops li { padding: .625rem 0; border-bottom: 1px solid var(--rpm-border-soft); }
.ws-stops li:last-child { border-bottom: 0; }
.ws-stops .muted { display: block; }
.ws-stop-links { display: flex; gap: .875rem; margin-top: .25rem; font-size: .875rem; font-weight: 600; }

.ws-actions { display: grid; gap: .5rem; margin-top: .5rem; }
.ws-actions form { margin: 0; }
.ws-action {
  display: flex; flex-direction: column; gap: .125rem; width: 100%; text-align: left;
  padding: .75rem .875rem; border: 1px solid var(--rpm-border-soft); border-radius: var(--rpm-radius-lg);
  background: var(--rpm-bg); color: var(--rpm-navy); text-decoration: none; font: inherit; cursor: pointer;
}
.ws-action:hover { background: var(--rpm-blue-soft); border-color: var(--rpm-blue); text-decoration: none; }
.ws-action-title { font-weight: 700; }
.ws-action-sub { font-size: .8125rem; color: var(--rpm-text-muted); }
.ws-more { margin-top: 1rem; }
.topnav-inbox-label {
  font-size: .6875rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--rpm-text-muted); margin-right: .25rem;
}


/* ── admin stop page ─────────────────────────────────────────────────── */
.stop-meta { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; align-items: center; margin: .25rem 0 0; color: var(--rpm-text-muted); }
/* Region picker: an iOS Settings-style row -- label on the left, the current
   value on the right with a stacked chevron, a caption underneath. The
   control is still a native <select> (keyboard, screen readers, no-JS all
   work); only its chrome is replaced. */
.stop-region { margin: .875rem 0 0; max-width: 32rem; }
.stop-region-row {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 2.75rem; padding: .375rem .5rem .375rem 1rem; cursor: pointer;
  background: var(--rpm-bg); border: 1px solid var(--rpm-border-soft); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(10, 37, 64, .04);
}
.stop-region-row:hover { background: var(--rpm-bg-alt); }
.stop-region-row:focus-within { border-color: var(--rpm-blue); box-shadow: 0 0 0 3px rgba(0, 117, 191, .15); }
.stop-region-label { font-weight: 600; color: var(--rpm-navy); }
.stop-region-select {
  appearance: none; -webkit-appearance: none; flex: 0 1 auto; min-width: 0; max-width: 70%;
  margin: 0; padding: .375rem 1.75rem .375rem .625rem; border: 0; border-radius: 8px;
  background: transparent url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='16' viewBox='0 0 12 16' fill='none' stroke='%236b7280' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'><path d='M2.5 6 6 2.5 9.5 6'/><path d='M2.5 10 6 13.5 9.5 10'/></svg>") no-repeat right .5rem center;
  font: inherit; font-size: .9375rem; font-weight: 600; color: var(--rpm-text-soft);
  text-align: right; text-overflow: ellipsis; cursor: pointer;
}
.stop-region-select:hover { background-color: rgba(10, 37, 64, .05); }
.stop-region-select:focus { outline: none; }
.stop-region--unset .stop-region-select { color: #92400e; background-color: #fffbeb; }
.stop-region-help { margin: .375rem .25rem 0; font-size: .8125rem; line-height: 1.45; color: var(--rpm-text-muted); }
.stop-region-help strong { color: #92400e; font-weight: 600; }
.stop-region noscript .btn-mini { margin-top: .5rem; }
/* One visit: date | badge + title + meta, with the action buttons on a
   second row under the title. They used to share a row in a third `auto`
   column; once the equipment work added Read / Notify buttons that column
   grew to ~550px and the title wrapped one word per line. */
.stop-visit {
  display: grid; grid-template-columns: 7rem minmax(0, 1fr);
  grid-template-areas: "when what" "when actions";
  gap: .375rem 1rem; align-items: start;
  padding: .75rem 0; border-bottom: 1px solid var(--rpm-border-soft);
}
.stop-visit:last-child { border-bottom: 0; }
.stop-visit-when { grid-area: when; font-weight: 700; color: var(--rpm-navy); white-space: nowrap; }
.stop-visit-what { grid-area: what; min-width: 0; display: flex; flex-direction: column; gap: .25rem; }
.stop-visit-actions { grid-area: actions; display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.stop-visit-actions form { margin: 0; }
.stop-visit-actions .btn-mini-outline, .stop-visit-actions .btn-mini { min-width: 0; }
@media (max-width: 640px) {
  .stop-visit { grid-template-columns: 1fr; grid-template-areas: "when" "what" "actions"; }
}
.danger-zone { border-color: #fecaca; }
.danger-zone summary { cursor: pointer; color: var(--rpm-danger-dark); font-weight: 700; }
.danger-zone .form { margin-top: .75rem; }

/* ── stop page top band: the four numbers + the equipment list ─────────── */
/* Left: Bi-Monthly / Monthly / Quarterly filter counts and the coil count as
   2x2 tiles. Right: one line per unit, click to expand. Stacks on narrow
   screens. `align-items: start` keeps the tile grid from stretching to the
   height of a 35-unit equipment card. */
.stop-top { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem; align-items: start; margin-bottom: 1.25rem; }
.stop-top > * { min-width: 0; }
.stop-top .ws-tiles, .stop-top .card { margin-bottom: 0; }
.stop-top-note { margin: .625rem 0 0; }
.ws-tile--static { cursor: default; }
.ws-tile--static:hover { box-shadow: none; }
.ws-tile--empty .ws-tile-num { color: var(--rpm-text-muted); }
@media (min-width: 1024px) {
  .stop-top { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
  .stop-top .ws-tiles { grid-template-columns: repeat(2, 1fr); }
}
/* On a large monitor there is room for all four numbers in one row, so give
   the band a single column and let the tiles stretch the full width with the
   equipment card underneath -- better than a skinny 2x2 block beside it. */
@media (min-width: 1440px) {
  .stop-top { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .stop-top .ws-tiles { grid-template-columns: repeat(4, 1fr); }
}

/* Equipment list (admin stop page and customer stop page). A <details> per
   unit: the summary is the one-line row, the body is a label / value grid. */
.equip-count { margin: 0 0 .25rem; }
.equip-list { list-style: none; margin: .5rem 0 0; padding: 0; }
.equip-list > li { border-bottom: 1px solid var(--rpm-border-soft); }
.equip-list > li:last-child { border-bottom: 0; }
.equip-summary {
  cursor: pointer; list-style: none; display: flex; align-items: baseline; flex-wrap: wrap;
  gap: .25rem .5rem; padding: .5rem 0; font-size: .9375rem; min-width: 0;
}
.equip-summary::-webkit-details-marker { display: none; }
.equip-summary::before { content: "\25B8"; color: var(--rpm-text-muted); flex: 0 0 auto; }
.equip-unit[open] > .equip-summary::before { content: "\25BE"; }
.equip-summary:hover .equip-name { text-decoration: underline; }
.equip-name { font-weight: 700; color: var(--rpm-navy); overflow-wrap: anywhere; }
.equip-type { font-size: .6875rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--rpm-text-muted); }
.equip-flags { margin-left: auto; display: inline-flex; gap: .25rem; }
.equip-summary code, .equip-kv dd { overflow-wrap: anywhere; }
.equip-body { padding: 0 0 .75rem 1.125rem; }
.equip-kv { gap: .25rem .75rem; margin: .25rem 0 .5rem; }
@media (min-width: 641px) { .equip-kv { grid-template-columns: 7rem minmax(0, 1fr); } }
.equip-kv dt, .equip-kv dd { font-size: .875rem; }
.equip-edit { margin: 0; }
.equip-retired { margin-top: .75rem; }
.equip-retired > summary { cursor: pointer; }
/* In the admin top band a long list scrolls inside the card so the visits
   below stay within reach; the customer page shows the whole list. */
.stop-top .equip-list { max-height: 26rem; overflow-y: auto; padding-right: .25rem; }

/* "You are on the Manage page" strip at the top of the old account page. */
.ws-newhome {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  background: var(--rpm-blue-soft); border: 1px solid #bfdbfe; border-radius: var(--rpm-radius-lg);
  padding: .75rem 1rem; margin-bottom: 1.25rem; color: var(--rpm-navy);
}

/* Equipment registry: the filter sizes a unit takes, shown first when the
   unit is opened and inline on the closed summary. */
.eq-filters { margin: 0 0 .5rem; font-size: 1.02em; }
.eq-filter-line code { font-size: 1em; font-weight: 600; }
.equip-unit[open] .eq-filters-inline { display: none; }
.eq-filters-inline { color: var(--rpm-text-soft); }
.filter-check { margin: 0 0 .6rem; display: flex; flex-wrap: wrap; gap: .3rem .4rem; align-items: center; }
.equip-edit-inline { display: flex; flex-wrap: wrap; gap: .4rem .75rem; align-items: flex-end; margin: 0 0 .6rem; }
.equip-edit-inline label { display: flex; flex-direction: column; gap: .15rem; font-size: .82em; color: var(--rpm-text-muted); }
.equip-edit-inline input[type=text] { font-size: .95rem; padding: .3rem .4rem; }
.equip-edit-inline .equip-apply-family { flex-direction: row; align-items: center; gap: .3rem; }

/* ── "Run AI?" confirm dialog ──────────────────────────────────────────
 * The nameplate read spends real API money, so it gets a deliberate
 * centered dialog instead of the browser's confirm() strip, which is easy
 * to click through without reading. Shaped like an iOS alert: a small
 * floating panel of frosted glass, centered copy, and two flat buttons
 * split by hairlines. Where backdrop-filter is unsupported the rgba
 * background alone still reads as a solid light panel. */
.ai-confirm {
  border: 0; padding: 0;
  width: 17rem; max-width: calc(100% - 3rem);
  border-radius: 14px;
  overflow: hidden;
  background: rgba(250, 250, 252, .72);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  box-shadow: 0 12px 40px rgba(6, 22, 41, .22);
  color: var(--rpm-text);
  text-align: center;
  /* iOS alerts animate up to full size rather than appearing hard. */
  animation: ai-confirm-in .18s ease-out;
}
@keyframes ai-confirm-in {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: scale(1); }
}
.ai-confirm::backdrop {
  background: rgba(6, 22, 41, .28);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.ai-confirm-body { padding: 1.1rem 1.1rem .95rem; }
.ai-confirm h2 {
  margin: 0 0 .3rem;
  font-size: 1.0625rem; font-weight: 600; line-height: 1.3;
  letter-spacing: -.01em;
}
.ai-confirm p {
  margin: 0;
  font-size: .8125rem; line-height: 1.35;
  color: var(--rpm-text-soft);
}
/* Hairline dividers, as thin as the display allows. */
.ai-confirm-actions {
  display: flex;
  border-top: 1px solid rgba(60, 60, 67, .18);
}
.ai-confirm-actions button {
  flex: 1 1 0;
  font-family: inherit; font-size: 1.0625rem;
  padding: .7rem .5rem;
  background: transparent; border: 0; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.ai-confirm-actions button + button { border-left: 1px solid rgba(60, 60, 67, .18); }
.ai-confirm-actions button:active { background: rgba(60, 60, 67, .09); }
.ai-confirm-no { color: var(--rpm-text-muted); font-weight: 400; }
.ai-confirm-yes { color: #1c8c3c; font-weight: 600; }
@media (min-resolution: 2dppx) {
  .ai-confirm-actions { border-top-width: .5px; }
  .ai-confirm-actions button + button { border-left-width: .5px; }
}

/* Settings — collapsed "healthy customers" table under the sync card. */
.settings-details {
  margin-top: 1rem;
}
.settings-details > summary {
  cursor: pointer;
  padding: .5rem 0;
  font-size: .9375rem;
  font-weight: 500;
  color: var(--rpm-text-soft, #475569);
}
.settings-details > summary:hover {
  color: var(--rpm-blue, #2563eb);
}
.settings-all-clear {
  margin: 1rem 0 0;
}

/* Top customers — the handful of accounts RPM opens every day, pinned
   between the stat tiles and the full customer list so the common case
   is one click instead of a scroll. Wraps to as many rows as it needs. */
.top-customers { margin: 0 0 1.5rem; }
.top-customers-label {
  display: block; margin-bottom: .5rem;
  color: #6b7280; font-size: .75rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
}
.top-customers-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.top-customer {
  display: inline-block;
  padding: .5rem .875rem; border-radius: 999px;
  background: white; border: 1px solid #e5e7eb;
  color: var(--rpm-blue); font-size: .875rem; font-weight: 700;
  text-decoration: none; line-height: 1.25;
}
.top-customer:hover {
  background: var(--rpm-blue); border-color: var(--rpm-blue);
  color: white; text-decoration: none;
}


/* Print the whole settings page, not just the open tab. */
@media print {
  .settings-tablist { display: none; }
  .js-tabs .settings-panel[hidden] { display: block !important; }
  .js-tabs .settings-section-head {
    position: static;
    width: auto;
    height: auto;
    margin: 2rem 0 .25rem;
    clip: auto;
  }
}

/* ── account roster (admin/dashboard.html) ────────────────────────────
   The roster is ordered by newest visit, so the search box carries the
   weight the old alphabetical scan used to: it is how you reach an
   account that has not been serviced lately. */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
.roster-search {
  display: flex; align-items: center; gap: .5rem;
  margin: 0 0 .75rem; flex-wrap: wrap;
}
.roster-search input[type="search"] {
  appearance: none; -webkit-appearance: none;
  flex: 1 1 22rem; max-width: 30rem; min-width: 0;
  padding: .5rem .875rem;
  border: 1.5px solid var(--rpm-border); border-radius: 999px;
  font: inherit; font-size: .9375rem; color: var(--rpm-text);
  background: var(--rpm-bg-alt);
}
.roster-search input[type="search"]:focus {
  outline: none; background: white;
  border-color: var(--rpm-blue); box-shadow: 0 0 0 3px rgba(0, 117, 191, 0.15);
}
.roster-search .btn-mini { padding: .5rem 1rem; min-width: 0; }
/* Stop/job counts under the account name — the line that tells you which
   accounts are the business, which alphabetical order used to bury. */
.roster-sub { display: block; }
.roster-more { margin: .875rem 0 0; font-size: .9375rem; }
.roster-more a { font-weight: 600; }
/* Row action cluster: keeps the pair on one line and off the row's text. */
.row-actions { white-space: nowrap; text-align: right; }
.row-actions > * + * { margin-left: .375rem; }
@media (max-width: 640px) {
  .roster-search input[type="search"] { flex: 1 1 100%; max-width: none; min-height: 44px; }
  .roster-search .btn-mini { min-height: 44px; }
  .row-actions { text-align: left; white-space: normal; }
  .row-actions > * + * { margin-left: 0; }
}

/* Completed-visits row: the account sits under the stop name, and the three
   reference numbers share one cell. Nine columns stacked into nine labelled
   lines on a phone; six is readable. */
.stop-sub { display: block; font-weight: 400; }
.col-ticket .ticket-line { display: block; font-size: .8125rem; line-height: 1.5; }
.col-ticket { white-space: nowrap; }

/* ── workspace account switcher ───────────────────────────────────────
   Home opens the busiest account; this is how you get to another one
   without going back to the roster. A <details> menu so it needs no JS
   and stays keyboard-operable. */
.ws-title-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.ws-title-row h1 { margin: 0; }
.ws-switch { position: relative; }
.ws-switch-btn {
  display: inline-flex; align-items: center; gap: .375rem;
  min-height: 34px; padding: 0 .75rem;
  border: 1px solid var(--rpm-navy); border-radius: var(--rpm-radius);
  background: var(--rpm-bg-alt); color: var(--rpm-navy);
  font-size: .8125rem; font-weight: 700; cursor: pointer;
  list-style: none; white-space: nowrap;
}
.ws-switch-btn::-webkit-details-marker { display: none; }
.ws-switch-btn:hover { background: var(--rpm-blue-soft); }
.ws-switch-btn svg { width: 14px; height: 14px; transition: transform .15s ease; }
.ws-switch[open] .ws-switch-btn svg { transform: rotate(180deg); }
.ws-switch-menu {
  position: absolute; left: 0; top: calc(100% + .5rem); z-index: 60;
  width: 21rem; max-width: 80vw;
  background: white; border: 1px solid var(--rpm-border-soft);
  border-radius: var(--rpm-radius-lg);
  box-shadow: 0 12px 32px rgba(10, 37, 64, .16);
  padding: .75rem;
}
.ws-switch-search { margin: 0 0 .5rem; }
.ws-switch-search input[type="search"] {
  appearance: none; -webkit-appearance: none; width: 100%;
  padding: .5rem .75rem; border: 1.5px solid var(--rpm-border);
  border-radius: var(--rpm-radius); font: inherit; font-size: .875rem;
  background: var(--rpm-bg-alt);
}
.ws-switch-search input[type="search"]:focus {
  outline: none; background: white; border-color: var(--rpm-blue);
  box-shadow: 0 0 0 3px rgba(0, 117, 191, .15);
}
.ws-switch-label {
  display: block; font-size: .6875rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--rpm-text-muted); padding: .5rem .5rem .25rem;
}
.ws-switch-item {
  display: flex; align-items: center; justify-content: space-between; gap: .625rem;
  padding: .5rem; border-radius: var(--rpm-radius);
  color: var(--rpm-text); font-size: .9375rem; min-height: 38px;
}
.ws-switch-item:hover { background: var(--rpm-bg-alt); text-decoration: none; color: var(--rpm-navy); }
.ws-switch-item.is-current { background: var(--rpm-blue-soft); color: var(--rpm-navy); font-weight: 700; }
.ws-switch-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-switch-meta { flex: 0 0 auto; color: var(--rpm-text-muted); font-size: .8125rem; }
.ws-switch-meta.is-here { color: var(--rpm-blue); font-weight: 700; }
.ws-switch-all {
  display: block; margin-top: .5rem; padding: .625rem .5rem 0;
  border-top: 1px solid #f3f4f6; font-size: .875rem; font-weight: 700;
}
@media (max-width: 640px) {
  .ws-switch { position: static; }
  .ws-switch-menu { width: 100%; left: 0; }
  .ws-switch-btn { min-height: 44px; }
  .ws-switch-item { min-height: 44px; }
}

/* Empty states carry a sentence of context now, so they read as prose
   rather than a single centred fragment. */
.empty-state { line-height: 1.6; max-width: 62ch; margin-left: auto; margin-right: auto; }
.empty-state strong { color: var(--rpm-navy); }

/* Role tag legibility. 10px uppercase at weight 800 is below the size this
   face stays readable at; 12px is the floor used everywhere else. */
.topnav-user small { font-size: .75rem; }
.role-pill { font-size: .75rem; }

/* Reference numbers — PO, work order, invoice, filter sizes. These were the
   hardest text in the console to read: 11px, and proportional digits made
   the columns ragged. Same size as the rest of the row, tabular figures. */
.col-ticket .ticket-line,
.filter-line, .filter-size {
  font-variant-numeric: tabular-nums;
  font-size: .8125rem;
}

/* ─── Service change requests (customer "Manage service" + admin board) ── */
.manage-service { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; justify-content: flex-end; }
.manage-service .btn-sm { white-space: nowrap; }
.request-list { list-style: none; margin: 0; padding: 0; }
.request-list li { padding: .6rem 0; border-bottom: 1px solid #e5e7eb; }
.request-list li:last-child { border-bottom: 0; }
.change-field { display: grid; grid-template-columns: minmax(180px, 1fr) 2fr; gap: .5rem; align-items: center; margin: .35rem 0; }
@media (max-width: 640px) { .change-field { grid-template-columns: 1fr; } }
.change-board h2 { font-size: 1.05rem; margin: 1.5rem 0 .5rem; }
.change-board .checklist { margin: .5rem 0 .75rem 1.1rem; padding: 0; }
.change-board .checklist li { margin: .2rem 0; }
.change-board .stale { border-left: 3px solid #d97706; padding-left: .75rem; }
.change-board details.done-list > summary { cursor: pointer; color: var(--rpm-text-muted); }
.change-board .form-row--split > span { min-width: 0; }
.change-board .form-row--split input { width: 100%; }
