/* ══════════════════════════════════════════════════════════════════════════
   Visitor Management module styles — scoped under .vm-scope
   Colour comes from brand-tokens.css (var(--brand*) = theme, var(--ok/--warn/
   --danger) = fixed status). Never type a hex here — npm run brand-doctor.
   borders #d1d5db/#d4d4d8, radius 7-10px (mirrors .bc-scope conventions).
   ══════════════════════════════════════════════════════════════════════════ */
.vm-scope { color: #111; }
/* Visitor screens scroll WITH THE PAGE, like every other module.
   They used to be their own scroll box (`max-height: calc(100vh - 96px);
   overflow-y: auto`), which put a clipping edge partway down the screen —
   BELOW the fixed black header, not behind it. Anything scrolling up was cut
   in half out in the open white (the heading, and the + tile most visibly)
   instead of disappearing behind the opaque header, and the strip between the
   header and that edge read as an unexplained white band. The 96px was also a
   guess at the chrome height that no breakpoint actually matches.
   Nothing scrolls these sections programmatically, so normal page flow is a
   straight win. Sideways scroll is already prevented by `main`'s
   `overflow-x: hidden` on touch widths plus the per-table scrollers below. */

.vm-scope .vm-header { margin-bottom: 1rem; }
/* The heading row carries the screen's + action in its trailing corner
   (placeTitleAction() moves it here), so it is a flex row, not a plain heading. */
.vm-scope .vm-title {
  font-size: 1.35rem; font-weight: 700; margin: 0 0 .75rem; color: #111;
  display: flex; align-items: center; gap: .5rem;
}
/* Only the + moves to the right. space-between would spread the heading's icon
   and its text apart as well, leaving the title floating in the middle. */
.vm-scope .vm-title .vm-icon-add { margin-left: auto; }
.vm-scope .vm-loading { color: #666; padding: 2rem; text-align: center; }

/* ── Tab bar ── */
.vm-scope .vm-tabbar { display: flex; flex-wrap: wrap; gap: .375rem; border-bottom: 2px solid #e5e7eb; padding-bottom: 0; }
.vm-scope .vm-tab {
  background: transparent !important; color: #555 !important;
  font-size: .8125rem; font-weight: 600; border: none; border-bottom: 3px solid transparent;
  border-radius: 0; padding: .5rem .875rem; cursor: pointer; margin-bottom: -2px;
}
.vm-scope .vm-tab:hover { color: #111 !important; background: #f4f4f5 !important; }
.vm-scope .vm-tab.active { color: #111 !important; border-bottom-color: var(--brand); background: transparent !important; }

/* ── Toolbar / actions ── */
.vm-scope .vm-toolbar { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; margin: 1rem 0; }
.vm-scope .vm-add-btn {
  display: inline-flex; align-items: center; gap: .375rem;
  background: #111 !important; color: #fff !important;
  font-size: .8125rem; font-weight: 700; border: none; border-radius: 7px;
  padding: .5rem 1rem; cursor: pointer;
}
.vm-scope .vm-add-btn:hover { background: #333 !important; }
.vm-scope .vm-btn {
  display: inline-flex; align-items: center; gap: .375rem;
  background: #fff !important; color: #374151 !important;
  font-size: .8125rem; font-weight: 600; border: 1.5px solid #d1d5db; border-radius: 7px;
  padding: .5rem 1rem; cursor: pointer;
}
.vm-scope .vm-btn:hover { background: #111 !important; color: #fff !important; border-color: #111; }
/* The "add" action as a single + tile in the toolbar's trailing corner, rather
   than a wide labelled bar. The screen you are on already says what is being
   added, so the label was spending a whole row to repeat the heading — and on a
   phone that row pushed the first card below the fold. Same 36px / 12px-radius
   tile as every other action control in the module; title + aria-label carry the
   wording for tooltips and screen readers. */
.vm-scope .vm-icon-add {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 12px;
  /* Brand green, matching the per-visitor .vm-gp-fab — both are "+ new gate
     pass" actions, so they must not read as two different kinds of button. */
  background: var(--brand-dark) !important; color: var(--brand-contrast) !important; border: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  font-size: 1.25rem; font-weight: 400; line-height: 1;
  box-shadow: 0 2px 6px rgba(34,118,5,.28);
  transition: transform .12s ease, filter .12s ease;
}
.vm-scope .vm-icon-add:hover { transform: scale(1.05); filter: brightness(1.15); }
.vm-scope .vm-btn-green { background: var(--brand) !important; color: var(--brand-contrast) !important; border: none; }
.vm-scope .vm-btn-green:hover { background: var(--brand-dark) !important; }
.vm-scope .vm-btn-red { background: #ef4444 !important; color: #fff !important; border: none; }
.vm-scope .vm-btn-red:hover { background: #dc2626 !important; }
.vm-scope .vm-btn-blue { background: #1d4ed8 !important; color: #fff !important; border: none; }
.vm-scope .vm-btn-blue:hover { background: #1e40af !important; }
.vm-scope .vm-btn-amber { background: #f59e0b !important; color: #fff !important; border: none; }
.vm-scope .vm-btn-amber:hover { background: #d97706 !important; }
.vm-scope .vm-btn:disabled, .vm-scope .vm-add-btn:disabled { opacity: .5; cursor: not-allowed; }
.vm-scope .vm-btn-sm { padding: .3rem .625rem; font-size: .75rem; border-radius: 6px; }

/* ── Search ── */
.vm-scope .vm-search {
  flex: 1 1 260px; max-width: 420px; padding: .55rem .875rem; border-radius: 10px;
  font-size: .875rem; outline: none; box-sizing: border-box;
  background: #f4f4f5; border: 1.5px solid #d4d4d8; color: #111;
}
.vm-scope .vm-search:focus { border-color: var(--brand); }
.vm-scope select.vm-filter {
  padding: .5rem .625rem; border-radius: 7px; font-size: .8125rem;
  background: #fff; border: 1.5px solid #d4d4d8; color: #111; outline: none;
}

/* ── KPI cards (dashboard) ── */
.vm-scope .vm-kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: .75rem; margin: 1rem 0; }
.vm-scope .vm-kpi { background: #fff; border: 1.5px solid #e5e7eb; border-radius: 10px; padding: .875rem 1rem; }
.vm-scope .vm-kpi-num { font-size: 1.75rem; font-weight: 800; color: #111; line-height: 1.1; }
.vm-scope .vm-kpi-lbl { font-size: .75rem; font-weight: 600; color: #666; margin-top: .25rem; }
.vm-scope .vm-kpi--green .vm-kpi-num { color: var(--ok); }
.vm-scope .vm-kpi--amber .vm-kpi-num { color: #f59e0b; }
.vm-scope .vm-kpi--blue .vm-kpi-num { color: #1d4ed8; }
.vm-scope .vm-kpi--red .vm-kpi-num { color: #ef4444; }

/* ── Table ── */
.vm-scope .vm-table-wrap { overflow-x: auto; background: #fff; border: 1.5px solid #e5e7eb; border-radius: 10px; }
.vm-scope table.vm-table { width: 100%; border-collapse: collapse; }
.vm-scope .vm-table th {
  text-align: left; font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: #666; background: #f9fafb; padding: .625rem .75rem; border-bottom: 1.5px solid #e5e7eb; white-space: nowrap;
}
.vm-scope .vm-table td { font-size: .8125rem; color: #111; padding: .625rem .75rem; border-bottom: 1px solid #f1f3f4; vertical-align: middle; }
.vm-scope .vm-table tr:last-child td { border-bottom: none; }
.vm-scope .vm-table tr:hover td { background: #fafafa; }
.vm-scope .vm-empty { color: #999; text-align: center; padding: 3rem 1rem; font-size: .875rem; }

/* ── Status pills (MES-Lite palette) ── */
.vm-scope .vm-pill { display: inline-block; font-size: .6875rem; font-weight: 700; border-radius: 999px; padding: .2rem .625rem; white-space: nowrap; }
.vm-scope .vm-pill--PENDING     { background: #fef3c7; color: #92400e; }
.vm-scope .vm-pill--APPROVED    { background: #dcfce7; color: var(--ok-darker); }
.vm-scope .vm-pill--REJECTED    { background: #fee2e2; color: #991b1b; }
.vm-scope .vm-pill--CHECKED_IN  { background: #dbeafe; color: #1e40af; }
.vm-scope .vm-pill--CHECKED_OUT { background: #e5e7eb; color: #374151; }
.vm-scope .vm-pill--DENIED      { background: #111; color: #fff; }

/* ── Visitor avatar ── */
.vm-scope .vm-avatar { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; border: 2px solid #e5e7eb; vertical-align: middle; }
.vm-scope .vm-avatar-ph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%; background: var(--brand); color: var(--brand-contrast);
  font-size: .75rem; font-weight: 700; vertical-align: middle;
}

/* ── Forms ── */
.vm-scope .vm-form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .875rem; }
.vm-scope .vm-field label { display: block; font-size: .75rem; font-weight: 600; color: #444; margin-bottom: .25rem; }
.vm-scope .vm-field label .vm-req { color: #c0392b; }
.vm-scope .vm-field input, .vm-scope .vm-field select, .vm-scope .vm-field textarea {
  width: 100%; padding: .5rem .625rem; border-radius: 7px; font-size: .8125rem;
  border: 1.5px solid #d4d4d8; background: #fff; color: #111; outline: none; box-sizing: border-box; font-family: inherit;
}
.vm-scope .vm-field input:focus, .vm-scope .vm-field select:focus, .vm-scope .vm-field textarea:focus { border-color: var(--brand); }

/* Picker trigger (visit dates, time slot). Deliberately styled as a FIELD, not a
   button: it sits in the same column as the real inputs, so anything that looks
   like a button here reads as an action rather than a value. It replaces the
   native date/time inputs, whose rendering differs on every browser and looked
   out of place next to the app's own green calendar. */
.vm-scope .vm-field .vm-picker-btn {
  width: 100%; padding: .5rem .625rem; border-radius: 7px; font-size: .8125rem;
  border: 1.5px solid #d4d4d8; background: #fff !important; color: #111 !important;
  box-sizing: border-box; font-family: inherit; text-align: left; cursor: pointer;
  display: flex; align-items: center; gap: .5rem; min-height: 36px;
  transition: border-color .15s ease;
}
.vm-scope .vm-field .vm-picker-btn:hover  { border-color: var(--brand); }
.vm-scope .vm-field .vm-picker-btn:focus  { border-color: var(--brand); outline: none; }
.vm-scope .vm-field .vm-picker-ico { font-size: .9375rem; line-height: 1; flex: 0 0 auto; }
.vm-scope .vm-card { background: #fff; border: 1.5px solid #e5e7eb; border-radius: 10px; padding: 1rem 1.25rem; margin-bottom: 1rem; }
.vm-scope .vm-card h3 { margin: 0 0 .75rem; font-size: .9375rem; font-weight: 700; color: #111; }

/* ── Photo capture (also inside modals — backdrops carry vm-scope) ── */
.vm-scope .vm-photo-box {
  width: 160px; height: 160px; border-radius: 10px; border: 2px dashed #d4d4d8;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  background: #f9fafb; color: #999; font-size: .75rem; text-align: center; position: relative;
}
.vm-scope .vm-photo-box img { width: 100%; height: 100%; object-fit: cover; }
.vm-scope .vm-photo-box video { width: 100%; height: 100%; object-fit: cover; }
.vm-scope .vm-photo-actions { display: flex; gap: .5rem; margin-top: .5rem; flex-wrap: wrap; }

/* ── Duplicate-mobile warning card ── */
.vm-scope .vm-dup-warn {
  background: #fffbeb; border: 1.5px solid #f59e0b; border-radius: 10px;
  padding: .875rem 1rem; margin-top: .75rem; font-size: .8125rem; color: #92400e;
}

/* ── Modal ── */
.vm-modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 10050;
  display: flex; align-items: center; justify-content: center; padding: 1rem;
}
.vm-modal {
  background: #fff; border-radius: 12px; max-width: 720px; width: 100%;
  max-height: 92vh; overflow-y: auto; padding: 1.25rem 1.5rem; box-sizing: border-box; color: #111;
}
.vm-modal h3 { margin: 0 0 1rem; font-size: 1.05rem; font-weight: 700; color: #111; }
.vm-modal .vm-modal-actions { display: flex; justify-content: flex-end; gap: .625rem; margin-top: 1.25rem; }
.vm-modal .vm-field { margin-bottom: .75rem; }
.vm-modal .vm-field label { display: block; font-size: .75rem; font-weight: 600; color: #444; margin-bottom: .25rem; }
.vm-modal .vm-field input, .vm-modal .vm-field select, .vm-modal .vm-field textarea {
  width: 100%; padding: .5rem .625rem; border-radius: 7px; font-size: .8125rem;
  border: 1.5px solid #d4d4d8; background: #fff; color: #111; outline: none; box-sizing: border-box; font-family: inherit;
}
.vm-modal .vm-field input:focus, .vm-modal .vm-field select:focus, .vm-modal .vm-field textarea:focus { border-color: var(--brand); }

/* ── Approver / host picker chips ── */
.vm-scope .vm-chips, .vm-modal .vm-chips { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: .375rem; }
.vm-scope .vm-chip, .vm-modal .vm-chip {
  display: inline-flex; align-items: center; gap: .375rem;
  background: #f0fdf4; border: 1px solid var(--ok-soft); color: var(--ok-darker);
  font-size: .75rem; font-weight: 600; border-radius: 999px; padding: .2rem .625rem;
}
.vm-modal .vm-chip button { background: none !important; border: none; color: var(--ok-darker) !important; cursor: pointer; padding: 0; font-size: .875rem; line-height: 1; }

/* ── Typeahead dropdown ── */
/* The app's GLOBAL stylesheet gives every input position:relative; z-index:9999.
   The open dropdown must beat that: placeTaList() raises the ACTIVE wrapper to
   z-index 10002 (inline), so its fixed-position list paints above all fields. */
.vm-modal .vm-ta-wrap { position: relative; }
.vm-modal .vm-ta-list {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 10000;
  background: #fff; border: 1.5px solid #d4d4d8; border-radius: 7px; margin-top: 2px;
  max-height: 220px; overflow-y: auto; box-shadow: 0 8px 20px rgba(0,0,0,.18);
}
.vm-modal .vm-ta-item { padding: .5rem .75rem; font-size: .8125rem; cursor: pointer; border-bottom: 1px solid #f1f3f4; }
.vm-modal .vm-ta-item:hover { background: #f0fdf4; }
.vm-modal .vm-ta-item .vm-ta-sub { font-size: .6875rem; color: #999; }

/* ── Star rating ── */
.vm-modal .vm-stars { display: flex; gap: .25rem; font-size: 1.5rem; cursor: pointer; user-select: none; }
.vm-modal .vm-stars span { color: #d4d4d8; }
.vm-modal .vm-stars span.on { color: #f59e0b; }

/* ── MES-Lite style gate-pass cards (mobile-first) ── */
.vm-scope .vm-gp-grid { display: grid; grid-template-columns: 1fr; gap: .75rem; }
@media (min-width: 560px) { .vm-scope .vm-gp-grid { grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); } }
.vm-scope .vm-gp-card {
  background: #fff; border: 1.5px solid #e5e7eb; border-radius: 16px;
  overflow: hidden; display: flex; flex-direction: column;
}
.vm-scope .vm-gp-body { padding: 1rem; display: flex; flex-direction: column; gap: .75rem; flex: 1; }
/* The date-range calendar's week grid.
   This MUST stay a class and must never go back to an inline style. index.html
   carries a global mobile rule —
       @media (max-width: 768px) { div[style*="grid-template-columns"] { grid-template-columns: 1fr !important } }
   — which collapses ANY div with an inline grid template to one column. It was
   written for the overview page but matches app-wide, and it turned the seven
   weekday columns into a single vertical list of 31 dates on every phone. An
   inline style cannot win against it (!important beats inline), a class simply
   isn't matched by it. */
.vm-scope .vm-dr-grid, .vm-modal .vm-dr-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}

.vm-scope .vm-gp-top { display: flex; align-items: flex-start; gap: .75rem; }
/* Primary action in the card's free top-right corner, instead of a full-width
   row underneath that cost a card's worth of height on every visitor.
   Same rounded square as .vm-gp-aicon — every action button in this module is a
   36px / 12px-radius tile, and one circle among them reads as a foreign control. */
.vm-scope .vm-gp-fab {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 12px;
  background: var(--brand-dark) !important; border: none; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform .12s ease, filter .12s ease;
}
.vm-scope .vm-gp-fab:hover { transform: scale(1.05); filter: brightness(1.1); }
.vm-scope .vm-gp-fab svg { width: 16px; height: 16px; }
@media (max-width: 1024px), (pointer: coarse) and (max-width: 1400px) {
  /* Matches the 44px touch target the other action tiles get. */
  .vm-scope .vm-gp-fab { width: 44px; height: 44px; border-radius: 14px; }
  .vm-scope .vm-gp-fab svg { width: 20px; height: 20px; }
}
.vm-scope .vm-gp-avatar { width: 44px; height: 44px; border-radius: 50%; overflow: hidden; flex-shrink: 0; }
.vm-scope .vm-gp-avatar img { width: 100%; height: 100%; object-fit: cover; }
.vm-scope .vm-gp-avatar-ph {
  width: 44px; height: 44px; border-radius: 50%; flex-shrink: 0;
  background: var(--brand-a10); color: var(--brand); font-weight: 700; font-size: .875rem;
  display: flex; align-items: center; justify-content: center;
}
.vm-scope .vm-gp-name { font-size: .875rem; font-weight: 600; color: #111; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; }
.vm-scope .vm-gp-mobile { font-size: .75rem; color: #666; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; }
.vm-scope .vm-gp-passno { font-size: .75rem; font-weight: 500; color: var(--brand); margin: 0; }
.vm-scope .vm-gp-details { display: grid; grid-template-columns: 1fr 1fr; gap: .25rem 1rem; font-size: .75rem; }
.vm-scope .vm-gp-details .vm-gp-dl { color: #999; }
.vm-scope .vm-gp-details .vm-gp-dv { color: #111; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0 0 .25rem; }
.vm-scope .vm-gp-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.vm-scope .vm-gp-abtn { display: flex; flex-direction: column; align-items: center; gap: .125rem; background: none !important; border: none; padding: 0; cursor: pointer; }
.vm-scope .vm-gp-abtn .vm-gp-aicon {
  width: 36px; height: 36px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  transition: transform .12s ease, filter .12s ease;
}
.vm-scope .vm-gp-abtn:hover .vm-gp-aicon { transform: scale(1.05); filter: brightness(1.1); }
.vm-scope .vm-gp-abtn .vm-gp-aicon svg { width: 16px; height: 16px; }
.vm-scope .vm-gp-abtn .vm-gp-albl { font-size: 9px; color: #666; line-height: 1; }
.vm-scope .vm-gp-statusbar {
  padding: .375rem .75rem; text-align: center; font-size: .75rem; font-weight: 500;
  color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Status filter pills (MES-Lite) ── */
.vm-scope .vm-ftabs { display: flex; flex-wrap: wrap; gap: .375rem; }
.vm-scope .vm-ftab {
  padding: .375rem .75rem; border-radius: 8px; font-size: .75rem; font-weight: 500;
  background: #fff !important; color: #666 !important; border: 1.5px solid #e5e7eb; cursor: pointer;
}
.vm-scope .vm-ftab:hover { color: #111 !important; }
.vm-scope .vm-ftab.active { background: var(--brand) !important; color: var(--brand-contrast) !important; border-color: var(--brand); font-weight: 700; }

/* ── Status board cards ── */
.vm-scope .vm-board { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: .75rem; }
.vm-scope .vm-board-card { background: #fff; border: 1.5px solid #e5e7eb; border-left: 4px solid #1d4ed8; border-radius: 10px; padding: .875rem 1rem; }
.vm-scope .vm-board-card .vm-bc-name { font-weight: 700; font-size: .9375rem; color: #111; }
.vm-scope .vm-board-card .vm-bc-sub { font-size: .75rem; color: #666; margin-top: .2rem; }

/* ── Analytics ── */
.vm-scope .vm-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1rem; }
.vm-scope .vm-chart-card { background: #fff; border: 1.5px solid #e5e7eb; border-radius: 10px; padding: 1rem; }
.vm-scope .vm-chart-card h4 { margin: 0 0 .75rem; font-size: .8125rem; font-weight: 700; color: #444; }
.vm-scope .vm-chart-card canvas { max-height: 260px; }

/* ── RFID config ── */
.vm-scope .vm-rfid-status { display: inline-flex; align-items: center; gap: .5rem; font-size: .8125rem; font-weight: 600; }
.vm-scope .vm-rfid-dot { width: 10px; height: 10px; border-radius: 50%; background: #ef4444; }
.vm-scope .vm-rfid-dot.on { background: var(--ok); }
.vm-scope .vm-rfid-log {
  background: #111; color: var(--ok-soft); font-family: 'Courier New', monospace; font-size: .6875rem;
  border-radius: 8px; padding: .75rem; max-height: 220px; overflow-y: auto; white-space: pre-wrap;
}

/* Shared form grids. Two columns on a desktop, one on a touch device — kept as
   classes rather than inline styles so the touch layer below can collapse them. */
.vm-scope .vm-2col, .vm-modal .vm-2col { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
.vm-scope .vm-3col, .vm-modal .vm-3col { display: grid; grid-template-columns: repeat(3, 1fr); gap: .625rem; }

/* ════════════════════════════════════════════════════════════════════════════
   TOUCH LAYER — phone and tablet
   ════════════════════════════════════════════════════════════════════════════
   Everything above is the desktop design and is deliberately untouched. This
   block only ever ADDS overrides, so a mouse-driven browser renders exactly as
   it did before.

   The trigger is width OR pointer type, because width alone is wrong: an iPad
   Pro in landscape is 1194px — wider than plenty of laptops — and its user is
   still using a finger. `pointer: coarse` catches every tablet and phone
   regardless of size; the 1400px ceiling stops a touchscreen desktop monitor
   from being treated as a tablet.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1024px), (pointer: coarse) and (max-width: 1400px) {

  /* iOS Safari zooms the whole page whenever a focused field is under 16px.
     Every input in this module was 13px, so tapping any of them threw the
     layout around. 16px is the minimum that prevents it — not a style choice. */
  .vm-scope .vm-field input, .vm-scope .vm-field select, .vm-scope .vm-field textarea,
  .vm-modal .vm-field input, .vm-modal .vm-field select, .vm-modal .vm-field textarea,
  .vm-scope .vm-search, .vm-scope .vm-filter, .vm-scope input[type="date"],
  .vm-scope input[type="time"], .vm-modal input[type="date"], .vm-modal input[type="time"],
  .vm-scope .vm-field .vm-picker-btn, .vm-modal .vm-field .vm-picker-btn {
    font-size: 16px !important;
    padding: .625rem .75rem;
  }
  /* The picker triggers are buttons, so they miss the input sizing above and the
     44px rule further down (which targets .vm-btn). Size them here or they end up
     noticeably shorter than the fields they sit beside. */
  .vm-scope .vm-field .vm-picker-btn, .vm-modal .vm-field .vm-picker-btn { min-height: 44px; }
  .vm-scope .vm-field label, .vm-modal .vm-field label { font-size: .8125rem; }

  /* Forms stack. Two 150px-wide columns on a phone is unusable. */
  .vm-scope .vm-2col, .vm-modal .vm-2col { grid-template-columns: 1fr; }

  /* Toolbars stay a WRAPPING ROW, never a column. Stacking every control ate a
     whole screen of height before the first card — three action buttons became
     three full-width rows. Now each kind of control gets the width it deserves:
       - the things you type in or swipe (search, status tabs) take a full line;
       - the primary action (+ New …) takes a full line, because it is the point
         of the screen;
       - everything else — filters, Refresh / Print / CSV — shares one line,
         splitting it evenly.
     `flex: 1 1 0` with `min-width: 0` is what lets three buttons share a line
     without any of them forcing a horizontal scroll. */
  .vm-scope .vm-toolbar { flex-direction: row; flex-wrap: wrap; align-items: center; gap: .5rem; }
  .vm-scope .vm-toolbar > * { flex: 1 1 0 !important; min-width: 0; }
  .vm-scope .vm-toolbar > .vm-ftabs,
  .vm-scope .vm-toolbar > .vm-add-btn,
  .vm-scope .vm-toolbar > #vmHistRangeBtn { flex: 1 1 100% !important; width: 100%; max-width: none; }
  /* The search box keeps a whole line to itself; the + is not in this row at all
     — placeTitleAction() moves it up onto the heading. */
  .vm-scope .vm-toolbar > .vm-search { flex: 1 1 100% !important; width: 100%; max-width: none; }
  /* A thumb-sized target for the heading's + action. */
  .vm-scope .vm-icon-add { width: 44px; height: 44px; border-radius: 14px; font-size: 1.5rem; }
  .vm-scope .vm-toolbar > .vm-btn, .vm-scope .vm-toolbar > .vm-add-btn { justify-content: center; }
  /* Labels stay on one line and never widen their button past its share. */
  .vm-scope .vm-toolbar > .vm-btn {
    padding-left: .5rem; padding-right: .5rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  /* Status tabs scroll sideways in their own strip instead of wrapping into
     four ragged rows and pushing the cards off screen. */
  .vm-scope .vm-ftabs {
    display: flex; flex-wrap: nowrap; overflow-x: auto; gap: .375rem;
    -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: .25rem;
  }
  .vm-scope .vm-ftabs::-webkit-scrollbar { display: none; }
  .vm-scope .vm-ftab { flex: 0 0 auto; font-size: .8125rem; padding: .5rem .875rem; }

  /* NOTE: the card grid is deliberately NOT overridden here. The base rule
     (auto-fill, minmax(270px, 1fr) above 560px) already does the right thing on
     every device — one card per row on a phone, two on a tablet, three on a big
     landscape tablet. Forcing `1fr` was wrong: it left an iPad showing one
     enormous card per row with half the screen empty.
     44px is the smallest action target reliably hittable with a thumb. */
  .vm-scope .vm-gp-abtn .vm-gp-aicon { width: 44px; height: 44px; }
  .vm-scope .vm-gp-abtn .vm-gp-aicon svg { width: 20px; height: 20px; }
  .vm-scope .vm-gp-albl { font-size: .625rem; }
  .vm-scope .vm-gp-actions { gap: 1rem; }
  .vm-scope .vm-btn, .vm-scope .vm-add-btn { min-height: 44px; }

  /* Modals become sheets: full width, anchored to the bottom, so the content is
     near the thumb and long forms scroll inside instead of the page. */
  .vm-modal-backdrop { padding: 0; align-items: flex-end; }

  /* …except dialogs marked .vm-center — currently the date-range picker. It is
     a compact, fixed-height calendar, not a long form, so the sheet treatment
     bought nothing and cost a lot: anchored to the bottom edge, its own inline
     `overflow:hidden` combined with the sheet's 92vh cap clipped the
     Cancel/Apply row clean off the bottom of the screen. Centred, it fits with
     room to spare. overflow-y needs !important to beat that inline rule. */
  .vm-modal-backdrop.vm-center { align-items: center; padding: 1rem; }
  .vm-modal-backdrop.vm-center .vm-modal {
    width: 100%; max-width: 340px;
    border-radius: 16px;
    max-height: 88vh; overflow-y: auto !important;
  }
  /* The sheet pulls its action bar full-bleed with negative side margins; in a
     centred dialog that would hang off both edges. */
  .vm-modal-backdrop.vm-center .vm-modal .vm-modal-actions {
    margin-left: 0; margin-right: 0;
  }
  .vm-modal {
    max-width: none; width: 100%; max-height: 92vh;
    border-radius: 16px 16px 0 0;
    /* the safe-area inset keeps the last button clear of the iPhone home bar */
    padding: 1rem 1rem calc(1.25rem + env(safe-area-inset-bottom, 0px));
  }
  /* Save/Cancel sit at the END of the form, in normal flow — deliberately NOT
     sticky. A sticky footer inside a scrolling sheet kept failing: it is pinned
     against the modal's own padding box, so it drifted off the bottom edge and
     the form scrolled through the gap beneath it, and the typeahead dropdown on
     the last field rendered over it. Buttons that stay where you left them are
     worth more here than buttons that follow the scroll — a phone form is
     scrolled to the end anyway, which is where the button now is.
     The negative side margins make it a full-width bar with a rule above it, so
     it reads as the end of the form rather than a stray pair of buttons. */
  .vm-modal .vm-modal-actions {
    margin: 1.25rem -1rem 0;
    padding: .75rem 1rem 0;
    border-top: 1px solid #e5e7eb;
  }
  /* Both button classes are inline-flex with no justification — stretched to
     flex:1 they would left-align their own label. */
  .vm-modal .vm-modal-actions .vm-btn, .vm-modal .vm-modal-actions .vm-add-btn {
    flex: 1; min-height: 46px; justify-content: center; text-align: center;
  }

  /* Wide content must scroll inside its own box — the page itself must never
     scroll sideways. */
  .vm-scope table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* The date-range button had a fixed 220px minimum and overflowed narrow phones. */
  .vm-scope #vmHistRangeBtn { min-width: 0 !important; width: 100%; }

  /* Photo capture box: fill the width rather than sit as a small square. */
  .vm-scope .vm-photo-box { width: 100%; max-width: 240px; height: 240px; margin: 0 auto; }

  /* Room for the bottom bar so the last card is never trapped under it.
     The :not() is essential — every modal backdrop also carries .vm-scope, so
     without it the backdrop got 72px of bottom padding: the sheet was shoved
     72px UP the screen, its Save/Cancel bar floated in mid-air over the form,
     and a dead grey strip sat underneath. */
  .vm-scope:not(.vm-modal-backdrop) { padding-bottom: 4.5rem; }
}

/* ── Bottom tab bar: the four screens gate staff actually use ────────────────
   Hidden entirely on desktop, where the sidebar already does this job. Shown
   only while a visitor screen is open (body.vm-touch-nav), so it never appears
   over another module. */
#vmTabBar { display: none; }

@media (max-width: 1024px), (pointer: coarse) and (max-width: 1400px) {
  body.vm-touch-nav #vmTabBar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 9000;
    background: #fff; border-top: 1.5px solid #e5e7eb;
    padding: .25rem .25rem calc(.25rem + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -2px 12px rgba(0,0,0,.08);
  }
  /* !important on colour/background is required here: index.html carries global
     button styling (white text on a coloured fill) that otherwise wins and
     renders these labels white-on-white — invisible but still taking up space. */
  #vmTabBar button {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: .125rem;
    background: none !important; border: 0; padding: .5rem .25rem; min-height: 52px;
    font-family: inherit; font-size: .6875rem; font-weight: 600;
    color: #6b7280 !important; cursor: pointer; box-shadow: none !important;
  }
  #vmTabBar button span { color: inherit !important; }
  #vmTabBar button .vm-tb-ico { font-size: 1.125rem; line-height: 1; }
  #vmTabBar button.active, #vmTabBar button.active span { color: var(--brand-dark) !important; }
  #vmTabBar button:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

  /* The QC Assistant pill is fixed bottom-right at a higher z-index, so it would
     sit on top of the last two tabs. Lift it clear of the bar rather than
     fighting over stacking order — both stay reachable. */
  body.vm-touch-nav #qcai-toggle {
    bottom: calc(4.25rem + env(safe-area-inset-bottom, 0px)) !important;
  }

  /* While a sheet is open it owns the screen: the bar and the assistant would
     otherwise cover the last field and the Save button. */
  body.vm-touch-nav:has(.vm-modal-backdrop) #vmTabBar,
  body.vm-touch-nav:has(.vm-modal-backdrop) #qcai-toggle { display: none !important; }
}

/* ── Print: only the badge prints (window.print path) ── */
@media print {
  #vmTabBar { display: none !important; }
  body.vm-printing * { visibility: hidden !important; }
  body.vm-printing #vm-print-area, body.vm-printing #vm-print-area * { visibility: visible !important; }
  body.vm-printing #vm-print-area { position: absolute; left: 0; top: 0; }
}
