/* Vilobot ERP — Vilo Fence brand light theme
   Brand: burgundy #691d20, teal-green #253938, tan #f1e8df, rose #c18774
   Same DOM/class structure as original ViloBot so layout is identical. */
:root {
  --bg: #ffffff;           /* white page background */
  --bg2: #f1e8df;          /* tan cards / sidebar */
  --bg3: #e6dbcc;          /* slightly darker neutral for hovers/rows */
  --border: rgba(37,57,56,0.12);
  --text: #000000;         /* true black — high contrast on tan/white surfaces */
  --text-dim: rgba(0,0,0,0.65);
  --accent: #691d20;       /* burgundy primary action */
  --accent-hover: #500f12;
  --green: #2f7a3d;        /* status green */
  --yellow: #b88312;
  --red: #b91c1c;          /* error / destructive */
  --blue: #2563eb;
  --purple: #6d28d9;
  --radius: 10px;
  --sidebar-width: 260px;
}

/* Light theme tweaks for active nav item (was rgba on dark, needs adjustment on light) */
.nav-item.active { background: rgba(105,29,32,0.08) !important; color: var(--accent) !important; border-left-color: var(--accent) !important; }
.nav-item:hover { background: rgba(105,29,32,0.05) !important; color: var(--accent) !important; }
.sidebar-header h1 { color: var(--accent) !important; }
body { background: var(--bg); }
.sidebar { background: var(--bg2); }
.app-header { background: var(--bg2); border-bottom: 1px solid var(--border); }

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); font-size: 15px; line-height: 1.5; -webkit-font-smoothing: antialiased; min-height: 100vh; overflow-x: hidden; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── Scrollbar ─────────────────────────── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--bg3); border-radius: 3px; }

/* ── Layout ────────────────────────────── */
.app-shell { display: flex; min-height: 100vh; }

/* ── Sidebar ───────────────────────────── */
.sidebar { width: var(--sidebar-width); background: var(--bg2); border-right: 1px solid var(--border); display: flex; flex-direction: column; position: fixed; top: 0; left: 0; bottom: 0; height: 100dvh; overflow: hidden; z-index: 100; transition: transform 0.25s ease, width 0.2s ease; }
/* Collapsed sidebar — thin vertical bar */
.sidebar.collapsed { width: 44px; min-width: 44px; overflow: visible; }
.sidebar.collapsed .sidebar-header h1,
.sidebar.collapsed .sidebar-header .user-name,
.sidebar.collapsed .sidebar-section,
.sidebar.collapsed .nav-label,
.sidebar.collapsed .sidebar-footer { display: none; }
.sidebar.collapsed .sidebar-header { padding: 12px 8px; }
.sidebar.collapsed .sidebar-nav { overflow-y: hidden; overflow-x: hidden; width: 44px; }
.sidebar.collapsed .nav-item { padding: 10px 12px; justify-content: center; gap: 0; border-left: none; overflow: hidden; white-space: nowrap; width: 44px; }
.sidebar.collapsed .nav-item .nav-icon { width: auto; font-size: 18px; }
/* Toggle bar — sits on top edge of sidebar */
.sidebar-toggle { position: absolute; top: 8px; right: -20px; width: 20px; height: 40px; background: var(--bg2); border: 1px solid var(--border); border-left: none; border-radius: 0 6px 6px 0; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--text-dim); font-size: 11px; z-index: 200; }
.sidebar-toggle:hover { background: var(--accent); color: #fff; }
.sidebar-header { padding: 20px 16px; border-bottom: 1px solid var(--border); }
.sidebar-header h1 { font-size: 22px; font-weight: 700; color: var(--accent); }
.sidebar-header .user-name { font-size: 13px; color: var(--text-dim); margin-top: 2px; }
.sidebar-nav { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: 8px 0 16px; }
.sidebar-section { padding: 8px 16px 4px; font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
.nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 16px; color: var(--text-dim); font-size: 14px; cursor: pointer; transition: all 0.15s; border-left: 3px solid transparent; }
.nav-item:hover { background: var(--bg3); color: var(--text); }
.nav-item.active { background: rgba(232,115,90,0.1); color: var(--accent); border-left-color: var(--accent); font-weight: 600; }
.nav-item .nav-icon { width: 20px; text-align: center; font-size: 16px; }
.sidebar-footer { padding: 12px 16px; border-top: 1px solid var(--border); }
.logout-btn { width: 100%; padding: 10px; background: var(--bg3); border: 1px solid var(--border); color: var(--text-dim); border-radius: 8px; cursor: pointer; font-size: 13px; }
.logout-btn:hover { color: var(--red); border-color: var(--red); }

/* ── Main content ──────────────────────── */
.main-content { flex: 1; margin-left: var(--sidebar-width); min-height: 100vh; overflow-x: hidden; width: calc(100vw - var(--sidebar-width)); max-width: calc(100vw - var(--sidebar-width)); transition: margin-left 0.2s ease, max-width 0.2s ease; }
.sidebar.collapsed ~ .main-content { margin-left: 44px; width: calc(100vw - 44px); max-width: calc(100vw - 44px); }
.app-header { position: sticky; top: 0; background: var(--bg); border-bottom: 1px solid var(--border); padding: 14px 20px; display: flex; align-items: center; gap: 12px; z-index: 50; }
.app-header .menu-toggle { display: none; background: none; border: none; color: var(--text); font-size: 22px; cursor: pointer; padding: 4px; }
.app-header h2 { font-size: 18px; font-weight: 600; flex: 1; }
.app-body { padding: 16px 20px; width: 100%; max-width: 100%; overflow-x: auto; box-sizing: border-box; }

/* ── Mobile sidebar ────────────────────── */
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 99; }

@media (max-width: 768px) {
  .sidebar { transform: translateX(-100%); width: 280px; }
  .sidebar.open { transform: translateX(0); }
  .sidebar-overlay.open { display: block; }
  .sidebar-toggle { display: none; }
  .sidebar.open .sidebar-toggle { display: flex; }
  .main-content { margin-left: 0 !important; max-width: 100vw !important; }
  .app-header .menu-toggle { display: block; }
  .app-body { padding: 12px; }
}

/* ── Tablet (iPad) ────────────────────── */
@media (min-width: 769px) and (max-width: 1180px) {
  :root { --sidebar-width: 200px; }
  .sidebar .nav-label { font-size: 12px; }
  .app-body { padding: 12px 14px; }
}

/* ── Kiosk mode (hideSidebar roles) ───── */
body.app-kiosk .sidebar { display: none !important; }
body.app-kiosk .main-content { margin-left: 0 !important; width: 100vw !important; max-width: 100vw !important; }
body.app-kiosk .app-header .menu-toggle { display: none !important; }
.kiosk-logout { background: none; border: none; cursor: pointer; font-size: 18px; color: var(--text-dim); padding: 4px 8px; line-height: 1; }
.kiosk-logout:hover { color: var(--accent); }

/* ── Cards ─────────────────────────────── */
.card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; margin-bottom: 12px; }
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.card-title { font-size: 15px; font-weight: 600; }
.card-subtitle { font-size: 13px; color: var(--text-dim); }

/* ── Badges ────────────────────────────── */
.badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-size: 12px; font-weight: 600; }
.badge-green { background: rgba(74,222,128,0.15); color: var(--green); }
.badge-yellow { background: rgba(251,191,36,0.15); color: var(--yellow); }
.badge-red { background: rgba(248,113,113,0.15); color: var(--red); }
.badge-blue { background: rgba(96,165,250,0.15); color: var(--blue); }
.badge-purple { background: rgba(167,139,250,0.15); color: var(--purple); }
.badge-gray { background: rgba(255,255,255,0.08); color: var(--text-dim); }
.badge-accent { background: rgba(232,115,90,0.15); color: var(--accent); }

/* ── Buttons ───────────────────────────── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 20px; border-radius: 10px; font-size: 15px; font-weight: 600; border: none; cursor: pointer; transition: all 0.15s; min-height: 44px; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.btn:active { transform: scale(0.97); opacity: 0.9; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary { background: var(--bg3); color: var(--text); border: 1px solid var(--border); }
.btn-secondary:hover { border-color: var(--accent); }
.btn-sm { padding: 10px 14px; font-size: 13px; min-height: 40px; }
.btn-icon { padding: 10px; background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 18px; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }

/* ── Forms ─────────────────────────────── */
.form-input, .form-select { width: 100%; padding: 12px 14px; background: var(--bg3); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: 16px; outline: none; min-height: 44px; -webkit-appearance: none; }
.form-input:focus, .form-select:focus { border-color: var(--accent); }
.form-select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='rgba(255,255,255,0.5)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; }
.form-label { display: block; font-size: 13px; color: var(--text-dim); margin-bottom: 4px; }
.form-group { margin-bottom: 12px; }

/* ── Search bar ────────────────────────── */
.search-bar { position: relative; }
.search-bar input { padding-left: 36px; }
.search-bar::before { content: "\1F50D"; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: 14px; opacity: 0.5; }

/* ── Tabs ──────────────────────────────── */
.tabs { display: flex; gap: 4px; padding: 4px; background: var(--bg2); border-radius: 12px; margin-bottom: 16px; overflow-x: auto; flex-wrap: wrap; }
.tab { padding: 10px 18px; border-radius: 10px; font-size: 14px; font-weight: 600; color: var(--text-dim); cursor: pointer; white-space: nowrap; transition: all 0.15s; min-height: 44px; display: flex; align-items: center; -webkit-tap-highlight-color: transparent; }
.tab.active { background: var(--accent); color: #fff; }
.tab:active:not(.active) { background: var(--bg3); }
.tab:hover:not(.active) { color: var(--text); }

/* ── Table ─────────────────────────────── */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th { text-align: left; padding: 10px 12px; font-size: 12px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 1px solid var(--border); }
.data-table td { padding: 10px 12px; font-size: 14px; border-bottom: 1px solid var(--border); }
.data-table tr:hover td { background: rgba(255,255,255,0.02); }
.data-table .clickable { cursor: pointer; }
.data-table .clickable:hover td { background: rgba(232,115,90,0.05); }

/* ── Kanban ────────────────────────────── */
.kanban-board { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 12px; }
.kanban-col { min-width: 280px; flex: 1; }
.kanban-col-header { padding: 10px 12px; background: var(--bg2); border-radius: var(--radius) var(--radius) 0 0; border: 1px solid var(--border); border-bottom: 2px solid var(--accent); font-size: 13px; font-weight: 600; display: flex; align-items: center; justify-content: space-between; }
.kanban-col-header .count { background: var(--bg3); padding: 2px 8px; border-radius: 10px; font-size: 11px; color: var(--text-dim); }
.kanban-col-body { border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius) var(--radius); padding: 8px; min-height: 100px; background: var(--bg); }
.kanban-card { background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; padding: 12px; margin-bottom: 8px; cursor: pointer; transition: border-color 0.15s; }
.kanban-card:hover { border-color: var(--accent); }
.kanban-card .kc-name { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.kanban-card .kc-customer { font-size: 13px; color: var(--text-dim); }
.kanban-card .kc-amount { font-size: 14px; font-weight: 600; color: var(--green); margin-top: 6px; }
.kanban-card .kc-date { font-size: 12px; color: var(--text-dim); }
.kanban-card .kc-meta { display: flex; gap: 8px; margin-top: 6px; flex-wrap: wrap; }

/* ── Stats row ─────────────────────────── */
.stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin-bottom: 16px; width: 100%; box-sizing: border-box; }
.stat-card { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; text-align: center; }
.stat-value { font-size: 24px; font-weight: 700; }
.stat-label { font-size: 12px; color: var(--text-dim); margin-top: 2px; }

/* ── Filter bar ────────────────────────── */
.filter-bar { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; align-items: center; }
.filter-bar .form-input, .filter-bar .form-select { width: auto; min-width: 140px; padding: 8px 12px; font-size: 14px; }

/* ── Detail view ───────────────────────── */
.detail-back { display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); font-size: 14px; cursor: pointer; margin-bottom: 16px; }
.detail-back:hover { color: var(--accent); }
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 768px) { .detail-grid { grid-template-columns: 1fr; } }

/* Payment & Signature card — 2-col inner grid (desktop), stacks on mobile */
.pay-card-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px 24px; }
@media (max-width: 900px) { .pay-card-grid { grid-template-columns: 1fr; } }
.pay-card-grid .pay-col { min-width: 0; }
.pay-card-grid .pay-rows { display: grid; grid-template-columns: 130px 1fr; gap: 10px 14px; align-items: center; font-size: 14px; }
.pay-card-grid .pay-rows > .label { color: var(--text-dim); font-weight: 600; }
.pay-card-grid .pay-rows > .value { font-size: 14px; }
.pay-card-grid .pay-subhead { font-size: 12px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; }
.pay-card-grid .pay-divider { border-top: 1px solid var(--border); margin: 14px 0; }
.pay-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.pay-actions .btn { font-size: 14px; min-height: 40px; padding: 8px 16px; }
.pay-actions .btn-primary-action { background: var(--green); color: #fff; font-weight: 700; }
.detail-section { margin-bottom: 20px; }
.detail-section h3 { font-size: 14px; font-weight: 600; color: var(--text-dim); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.detail-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.detail-row .label { color: var(--text-dim); }
.detail-row .value { font-weight: 500; text-align: right; }

/* ── Line items table ──────────────────── */
.line-items { width: 100%; }
.line-items th { text-align: left; padding: 8px; font-size: 12px; color: var(--text-dim); border-bottom: 1px solid var(--border); }
.line-items td { padding: 8px; font-size: 13px; border-bottom: 1px solid var(--border); }
.line-items .section-row td { font-weight: 600; color: var(--accent); padding-top: 14px; }
.line-items .note-row td { color: var(--text-dim); font-style: italic; }

/* ── Loading ───────────────────────────── */
.loading { display: flex; align-items: center; justify-content: center; padding: 40px; color: var(--text-dim); }
.loading::before { content: ""; width: 24px; height: 24px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.6s linear infinite; margin-right: 10px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Empty state ───────────────────────── */
.empty-state { text-align: center; padding: 60px 20px; color: var(--text-dim); }
.empty-state .icon { font-size: 48px; margin-bottom: 12px; opacity: 0.3; }
.empty-state h3 { font-size: 16px; margin-bottom: 4px; color: var(--text); }
.empty-state p { font-size: 14px; }

/* ── Toast ─────────────────────────────── */
.toast { position: fixed; bottom: 20px; right: 20px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 20px; font-size: 14px; z-index: 1000; transform: translateY(100px); opacity: 0; transition: all 0.3s; }
.toast.show { transform: translateY(0); opacity: 1; }
.toast.error { border-color: var(--red); }

/* ── Calendar grid ─────────────────────── */
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-header { font-size: 11px; font-weight: 600; color: var(--text-dim); text-align: center; padding: 8px 4px; }
.cal-day { background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; min-height: 80px; padding: 4px; }
.cal-day-num { font-size: 11px; color: var(--text-dim); padding: 2px 4px; }
.cal-event { background: rgba(232,115,90,0.15); border-left: 2px solid var(--accent); border-radius: 3px; padding: 2px 4px; font-size: 11px; margin-top: 2px; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 768px) {
  .kanban-board { flex-direction: column; }
  .kanban-col { min-width: unset; }
  .stats-row { grid-template-columns: repeat(2, 1fr); }
  .filter-bar { flex-direction: column; }
  .filter-bar .form-input, .filter-bar .form-select { width: 100%; }

  /* ── Mobile SO Detail ────────────── */
  .so-detail-grid { grid-template-columns: 1fr !important; }

  /* Line items table → card layout on mobile */
  .line-items { display: block; }
  .line-items thead { display: none; }
  .line-items tbody { display: flex; flex-direction: column; gap: 8px; }
  .line-items tr {
    display: flex; flex-wrap: wrap; gap: 4px 12px;
    padding: 12px 12px 10px; padding-right: 40px; /* room for action buttons */
    background: var(--bg2); border-radius: 8px; border: 1px solid var(--border);
    align-items: flex-start; position: relative;
  }
  .line-items tr.section-row, .line-items tr.note-row { background: none; border: none; padding: 8px 0; }
  .line-items td { border: none; padding: 2px 0; font-size: 13px; }
  .line-items td:first-child { flex: 1 1 100%; min-width: 0; padding-right: 30px; }
  .line-items td:nth-child(2), .line-items td:nth-child(3), .line-items td:nth-child(4) { font-size: 13px; font-weight: 600; }
  .line-items td:nth-child(2)::before { content: "Qty: "; color: var(--text-dim); font-weight: 400; font-size: 11px; }
  .line-items td:nth-child(3)::before { content: "Price: "; color: var(--text-dim); font-weight: 400; font-size: 11px; }
  .line-items td:nth-child(4)::before { content: "Total: "; color: var(--text-dim); font-weight: 400; font-size: 11px; }
  .line-items td:last-child { position: absolute; right: 6px; top: 6px; display: flex; flex-direction: column; gap: 2px; }

  /* ── Mobile Site Plan Editor ──────── */
  .spe-top { padding: 4px 6px !important; gap: 3px !important; }
  .spe-top .spe-tb { min-width: 40px !important; min-height: 40px !important; padding: 2px !important; font-size: 13px !important; }
  .spe-bot { padding: 4px 8px !important; gap: 3px !important; }
  .spe-float-btn { padding: 10px 16px !important; font-size: 13px !important; bottom: 8px !important; }

  /* Footer totals row — clean layout */
  .line-items tfoot { display: block; margin-top: 8px; }
  .line-items tfoot tr {
    background: none; border: none; padding: 4px 0;
    display: flex; justify-content: flex-end; gap: 8px; align-items: center;
  }
  .line-items tfoot td { border: none; padding: 0; }
  .line-items tfoot td::before { content: none !important; }
}

/* ── iPad / Tablet optimizations (768px+, touch device) ── */
@media (min-width: 768px) and (pointer: coarse) {
  /* Bigger touch targets for field workers */
  .btn { padding: 14px 22px; font-size: 15px; border-radius: 12px; min-height: 48px; }
  .btn-sm { padding: 11px 16px; font-size: 14px; min-height: 44px; }
  .btn-icon { min-width: 48px; min-height: 48px; font-size: 20px; }

  /* Navigation */
  .nav-item { padding: 14px 18px; font-size: 15px; min-height: 48px; }
  .nav-item .nav-icon { font-size: 18px; }
  .sidebar-section { font-size: 12px; padding: 10px 18px 6px; }

  /* Cards — more breathing room */
  .card { padding: 18px; margin-bottom: 14px; border-radius: 12px; }
  .card-title { font-size: 16px; }

  /* Forms */
  .form-input, .form-select { padding: 14px 16px; font-size: 16px; border-radius: 10px; min-height: 48px; }

  /* Tabs */
  .tab { padding: 12px 20px; font-size: 15px; min-height: 48px; }

  /* Table cells */
  .data-table td { padding: 14px; font-size: 15px; }
  .data-table th { padding: 12px 14px; font-size: 13px; }
  .line-items td { padding: 12px 10px; font-size: 14px; }

  /* Detail rows */
  .detail-row { padding: 10px 0; font-size: 15px; }
  .detail-back { font-size: 15px; padding: 10px 0; min-height: 44px; display: inline-flex; align-items: center; }

  /* Badges */
  .badge { padding: 5px 12px; font-size: 13px; }

  /* Kanban cards */
  .kanban-card { padding: 16px; }
  .kanban-card .kc-name { font-size: 15px; }

  /* Toast */
  .toast { padding: 16px 24px; font-size: 15px; border-radius: 12px; }

  /* Stats */
  .stat-value { font-size: 28px; }
  .stat-label { font-size: 13px; }
  .stat-card { padding: 18px; }
}

/* ── iPhone / Small phone (max 480px) ── */
@media (max-width: 480px) {
  /* Global font bump */
  body { font-size: 16px; }
  .app-body { padding: 8px !important; }
  .app-header { padding: 8px 12px; }
  .app-header .app-title { font-size: 18px; }

  /* Tabs — horizontal scroll, bigger touch */
  .tabs { flex-wrap: nowrap; overflow-x: auto; gap: 2px; padding: 3px; -webkit-overflow-scrolling: touch; }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { font-size: 14px; padding: 10px 14px; min-height: 44px; white-space: nowrap; flex-shrink: 0; }

  /* Buttons — bigger touch targets */
  .btn { font-size: 15px; padding: 12px 16px; min-height: 48px; }
  .btn-sm { font-size: 14px; padding: 10px 14px; min-height: 44px; }

  /* Inputs — bigger */
  .form-input, .form-select, input, select, textarea { font-size: 16px !important; padding: 12px !important; min-height: 48px !important; border-radius: 8px; }

  /* Cards — tighter padding */
  .card { padding: 12px; margin-bottom: 8px; border-radius: 10px; }
  .card-title { font-size: 17px; }
  .card-subtitle { font-size: 14px; }

  /* Stats — 2 cols on small phones, compact */
  .stats-row { grid-template-columns: 1fr 1fr !important; gap: 6px; }
  .cs-stats-grid { grid-template-columns: repeat(5, 1fr) !important; gap: 4px !important; }
  .cs-stats-grid > div { padding: 8px 4px !important; }
  .cs-stats-grid > div > div:first-child { font-size: 18px !important; }
  .cs-stats-grid > div > div:last-child { font-size: 9px !important; }
  .stat-card { padding: 10px; }
  .stat-value { font-size: 22px; }
  .stat-label { font-size: 11px; }

  /* Badges */
  .badge { font-size: 11px; padding: 3px 8px; }

  /* Tables */
  .data-table { font-size: 13px; }
  .data-table td { padding: 8px 6px; }
  .data-table th { padding: 6px; font-size: 11px; }

  /* Navigation sidebar items */
  .nav-item { padding: 14px 16px; font-size: 16px; min-height: 48px; }

  /* Button groups — stack vertically */
  .filter-bar { flex-direction: column; gap: 6px; }
  .filter-bar > * { width: 100%; }

  /* Modals/Overlays — near full width with padding */
  [style*="max-width:400px"], [style*="max-width:420px"], [style*="max-width:450px"], [style*="max-width:460px"] {
    max-width: calc(100vw - 16px) !important; width: calc(100vw - 16px) !important; border-radius: 12px !important;
  }

  /* SO detail action buttons — wrap nicely */
  [style*="display:flex"][style*="gap:8px"][style*="flex-wrap:wrap"] > .btn,
  [style*="display:flex"][style*="gap:6px"] > button {
    flex: 1 1 45%; min-width: 120px;
  }

  /* Estimator quote cards */
  .est-quote-card { padding: 12px !important; }

  /* Site plan editor — compact toolbar for phone */
  .spe-top { padding: 3px 4px !important; gap: 1px !important; flex-wrap: wrap; }
  .spe-top .spe-tb { min-width: 36px !important; height: 36px !important; padding: 2px !important; font-size: 14px !important; }
  .spe-top .spe-tbl { font-size: 8px !important; }
  .spe-bot { padding: 3px 6px !important; gap: 2px !important; flex-wrap: wrap; }
  .spe-bot .spe-tb { min-width: 34px !important; height: 34px !important; font-size: 12px !important; }
  .spe-bot .spe-tbl { font-size: 9px !important; }

  /* Grid layouts — single column */
  [style*="grid-template-columns: 1fr 1fr"] { grid-template-columns: 1fr !important; }
  [style*="grid-template-columns:1fr 1fr"] { grid-template-columns: 1fr !important; }

  /* ── Estimator + Sales: mobile-first compaction (2026-05-29) ──────── */
  /* Estimator's horizontal-scroll tab strip lives inline-styled — keep
     the scroll behaviour, just enlarge tap targets and tighten spacing. */
  .est-tabs.est-tabs--primary {
    padding: 6px 8px 8px !important;
    gap: 5px !important;
  }
  .est-tabs.est-tabs--primary .est-tab {
    padding: 6px 11px !important;
    font-size: 12px !important;
    min-height: 34px !important;
  }
  .est-tabs.est-tabs--primary .est-tab span:first-child {
    font-size: 13px !important;
  }
  /* iOS renders <input type=date> as a giant tan block by default. Lock
     down the visual to the inline-style cap so it never grows past it. */
  input[type="date"].form-input {
    font-size: 13px !important;
    padding: 6px 8px !important;
    min-height: 36px !important;
    max-width: 170px;
  }
  /* Sales / Quotes data-table — too wide on phones; restyle as stacked
     cards so each quote row reads as a card. Tap row to open. */
  .data-table tbody tr.clickable {
    display: block;
    margin-bottom: 8px;
    padding: 10px 12px;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: 10px;
    background: var(--bg-card, #fff);
  }
  .data-table tbody tr.clickable td {
    display: inline-block;
    border: none;
    padding: 2px 6px 2px 0;
  }
  .data-table tbody tr.clickable td:first-child {
    font-weight: 700;
    font-size: 15px;
    width: 100%;
  }
  .data-table thead {
    display: none;
  }
}

/* Skeleton loading animation */
.skeleton{background:linear-gradient(90deg,var(--bg3,#21262d) 25%,var(--bg2,#161b22) 50%,var(--bg3,#21262d) 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:4px}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.skeleton-text{height:14px;margin-bottom:8px}
.skeleton-title{height:22px;width:60%;margin-bottom:12px}
.skeleton-block{height:80px;margin-bottom:12px}
.skeleton-line{height:12px;margin-bottom:6px}
#sales-content,#ea-content,[id^="est-section-"]{transition:opacity .15s ease}
#sales-content.fading,#ea-content.fading{opacity:0}
/* Estimator app — larger fonts for readability */
[data-app="estimator"]{font-size:15px}
[data-app="estimator"] .card{font-size:15px}
[data-app="estimator"] .form-input,[data-app="estimator"] .form-select,[data-app="estimator"] .btn{font-size:15px;padding:10px 14px}
[data-app="estimator"] .btn-sm{font-size:14px;padding:8px 12px}
[data-app="estimator"] .tab,[data-app="estimator"] .tabs .tab{font-size:14px;padding:10px 16px}
[data-app="estimator-admin"]{font-size:15px}
[data-app="estimator-admin"] .card{font-size:15px}
[data-app="estimator-admin"] .form-input,[data-app="estimator-admin"] .form-select,[data-app="estimator-admin"] .btn{font-size:15px;padding:10px 14px}
[data-app="estimator-admin"] .btn-sm{font-size:14px;padding:8px 12px}

/* ── Locked photo apps — no scroll (mobile only) ── */
@media (max-width: 768px) {
[data-app="powder-coating"]:not(.nav-item),[data-app="driver-scanner"]:not(.nav-item),[data-app="welding-photos"]:not(.nav-item),[data-app="aluminum-photos"]:not(.nav-item){position:relative;overflow:hidden !important;height:calc(100dvh - 52px);padding:0 !important}
body.photo-app-lock{overflow:hidden !important;height:100dvh;position:fixed;width:100%}
body.photo-app-lock .main-content{overflow:hidden !important;height:100dvh;min-height:0}
}

/* ────── Drafting Workflow ──────
   Per-day calendar of installs/deliveries with drafting work, plus a
   templates manager. Re-uses ops-* calendar styles where possible. */
.dw-shell { display: flex; flex-direction: column; gap: 12px; }
.dw-tabs {
  display: flex; gap: 4px; padding: 4px;
  background: var(--bg2); border-radius: 12px;
  overflow-x: auto; flex-wrap: wrap;
}
.dw-tab {
  padding: 10px 16px; border-radius: 10px; font-size: 14px; font-weight: 600;
  color: var(--text-dim); cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px;
  transition: all 0.15s;
}
.dw-tab:hover { background: rgba(105,29,32,0.06); color: var(--accent); }
.dw-tab.active { background: var(--accent); color: #fff; }
.dw-tab-icon { font-size: 16px; }

.dw-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.dw-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  background: var(--bg2); border: 1px solid var(--border);
  color: var(--text); cursor: pointer; transition: all 0.15s;
}
.dw-chip:hover { background: rgba(105,29,32,0.06); border-color: var(--accent); }
.dw-chip.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.dw-chip-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  font-size: 11px; font-weight: 700;
  background: rgba(0,0,0,0.08); color: inherit;
}
.dw-chip.active .dw-chip-count { background: rgba(255,255,255,0.25); color: #fff; }

.dw-card {
  background: var(--bg); border: 1px solid var(--border); border-left: 4px solid var(--border);
  border-radius: 8px; padding: 8px 10px; margin-bottom: 6px; cursor: pointer;
  transition: opacity 0.15s, transform 0.1s, box-shadow 0.15s;
}
.dw-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.08); transform: translateY(-1px); }
.dw-card.dw-stage-needs    { border-left-color: #9ca3af; }
.dw-card.dw-stage-progress { border-left-color: #2563eb; }
.dw-card.dw-stage-awaiting { border-left-color: #eab308; }
.dw-card.dw-stage-done     { border-left-color: var(--green); background: #f0fdf4; }
.dw-card-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-bottom: 2px; }
.dw-card-so { font-weight: 700; color: var(--accent); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-card-cust { font-size: 12px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-card-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 4px; }
.dw-card-assignee { font-size: 11px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-day-count { background: var(--accent); color: #fff; padding: 1px 6px; border-radius: 8px; font-weight: 700; font-size: 10px; }
.dw-undated-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 6px; margin-top: 8px; }

.dw-tpl-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; gap: 12px; flex-wrap: wrap; }
.dw-tpl-title { font-size: 18px; color: var(--accent); margin: 0; }
.dw-tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.dw-tpl-tile {
  position: relative; background: var(--bg2); border: 1px solid var(--border);
  border-radius: 12px; padding: 14px; cursor: pointer;
  display: flex; flex-direction: column; gap: 8px;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}
.dw-tpl-tile:hover { border-color: var(--accent); box-shadow: 0 2px 10px rgba(0,0,0,0.08); transform: translateY(-1px); }
.dw-tpl-thumb { font-size: 36px; text-align: center; padding: 10px 0; background: var(--bg); border-radius: 8px; }
.dw-tpl-info { flex: 1; }
.dw-tpl-name { font-weight: 700; font-size: 14px; color: var(--text); margin-bottom: 4px; }
.dw-tpl-meta { font-size: 11px; color: var(--text-dim); }
.dw-tpl-desc { font-size: 12px; color: var(--text-dim); margin-top: 6px; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.dw-tpl-del {
  position: absolute; top: 8px; right: 8px;
  width: 22px; height: 22px; border-radius: 50%;
  border: none; background: rgba(0,0,0,0.06); color: var(--text-dim);
  font-size: 12px; cursor: pointer; line-height: 1;
}
.dw-tpl-del:hover { background: var(--red); color: #fff; }

.dw-tpl-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.dw-tpl-detail-desc { color: var(--text-dim); font-size: 13px; padding: 8px 0; line-height: 1.5; }

.dw-modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45);
  display: flex; align-items: center; justify-content: center;
  z-index: 9999; padding: 16px;
}
.dw-modal {
  background: var(--bg); border-radius: 12px; max-width: 480px; width: 100%;
  box-shadow: 0 12px 40px rgba(0,0,0,0.25); display: flex; flex-direction: column;
}
.dw-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.dw-modal-head h3 { margin: 0; font-size: 16px; color: var(--accent); }
.dw-modal-x { background: none; border: none; font-size: 18px; cursor: pointer; color: var(--text-dim); line-height: 1; }
.dw-modal-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.dw-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--text-dim); }
.dw-field input, .dw-field textarea { font-size: 14px; }
.dw-modal-hint { font-size: 12px; color: var(--text-dim); font-style: italic; }
.dw-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); }

/* ────── Ops Manager v2 ────── */
/* Dark teal mode bar (matches reference screenshots). White text on dark
   teal background per the brand. */
.opsv2-modebar {
  display: flex; align-items: stretch; gap: 8px;
  background: var(--text); /* dark teal #253938 */
  border-radius: 12px; padding: 6px 8px;
  margin-bottom: 12px; color: #fff;
  position: relative;
}
.opsv2-modebar-inner {
  display: flex; gap: 2px; flex: 1; overflow-x: auto; align-items: center;
  scrollbar-width: thin;
}
.opsv2-modebar-inner::-webkit-scrollbar { height: 4px; }
.opsv2-modebar-inner::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); }

.opsv2-mode {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: 8px;
  background: transparent; color: rgba(255,255,255,0.78);
  border: none; cursor: pointer; font-size: 13px; font-weight: 600;
  white-space: nowrap; transition: background 0.12s, color 0.12s;
  position: relative;
}
.opsv2-mode:hover { background: rgba(255,255,255,0.08); color: #fff; }
.opsv2-mode.active {
  background: var(--accent); color: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.opsv2-mode-anchor {
  background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);
  padding: 8px 10px;
}
.opsv2-mode-anchor.active { background: var(--accent); }
.opsv2-mode-icon { font-size: 15px; line-height: 1; }
.opsv2-mode-label { font-size: 13px; font-weight: 600; }
.opsv2-mode-dot {
  width: 8px; height: 8px; border-radius: 50%; display: inline-block;
  margin-left: 2px;
}
.opsv2-dot-purple { background: var(--purple); }
.opsv2-dot-green  { background: var(--green); }
.opsv2-dot-red    { background: var(--red); }
.opsv2-dot-yellow { background: var(--yellow); }
.opsv2-mode-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; border-radius: 9px; padding: 0 5px;
  font-size: 10px; font-weight: 800; color: #fff; margin-left: 2px;
}
.opsv2-count-red    { background: #dc2626; }
.opsv2-count-yellow { background: #d97706; }

.opsv2-search-wrap {
  display: flex; align-items: center; flex-shrink: 0;
  padding-left: 6px; border-left: 1px solid rgba(255,255,255,0.12);
}
.opsv2-search {
  width: 220px; padding: 8px 12px; border-radius: 8px;
  background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.18);
  color: #fff; font-size: 13px; outline: none;
}
.opsv2-search::placeholder { color: rgba(255,255,255,0.55); }
.opsv2-search:focus { background: rgba(255,255,255,0.18); border-color: rgba(255,255,255,0.4); }

/* ── Sub-bar (date nav + view toggle) ─────────────────────────────────── */
.opsv2-subbar {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  padding: 8px 10px; margin-bottom: 10px; flex-wrap: wrap;
}
.opsv2-date-nav { display: flex; align-items: center; gap: 6px; }
.opsv2-subbar-right {
  display: flex; align-items: center; gap: 4px; margin-left: auto;
}
.opsv2-date-range {
  font-size: 13px; font-weight: 700; color: var(--text); margin-left: 8px;
}
.opsv2-weld-alerts {
  font-size: 12px; color: var(--yellow); font-weight: 600;
}
.opsv2-icon-btn {
  background: var(--bg3); border: 1px solid var(--border); color: var(--text);
  width: 30px; height: 30px; border-radius: 6px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; padding: 0;
}
.opsv2-icon-btn:hover { border-color: var(--accent); color: var(--accent); }
.opsv2-icon-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.opsv2-pill {
  background: var(--bg3); border: 1px solid var(--border); color: var(--text);
  padding: 6px 12px; border-radius: 6px; cursor: pointer;
  font-size: 12px; font-weight: 700; min-height: 30px;
}
.opsv2-pill:hover { border-color: var(--accent); color: var(--accent); }
.opsv2-pill.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* ── Calendar mode 7-day grid ─────────────────────────────────────────── */
.opsv2-cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px; margin-top: 4px;
}
.opsv2-day-col {
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: 8px; min-height: 200px; display: flex; flex-direction: column;
  overflow: hidden;
}
.opsv2-day-col.is-today { outline: 2px solid var(--accent); outline-offset: -1px; }
.opsv2-day-head {
  padding: 6px 8px; border-bottom: 1px solid var(--border);
  background: var(--bg3);
}
.opsv2-day-wd { font-size: 11px; font-weight: 800; color: var(--accent); letter-spacing: 0.5px; }
.opsv2-day-md { font-weight: 600; color: var(--text); margin-left: 4px; }
.opsv2-day-count {
  font-size: 10px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.4px; margin-top: 2px;
}
.opsv2-day-body { padding: 4px; flex: 1; display: flex; flex-direction: column; gap: 4px; }
.opsv2-day-empty {
  color: var(--text-dim); font-size: 12px; text-align: center; padding: 12px 0;
}

/* Install card sizing — mirrors legacy Business-Hub compact card
   (artifacts/vilobot/portal/js/ops-manager.js L3164-3168, L2807-2816):
     • flex:1, height:100%, min-height:130px → fills slot top-to-bottom
     • compact padding 7px 9px, font-size 11px, SO 15px, partner 13px
   Slot container .op-cal-installs is already display:flex column with
   flex:1 1 auto, so flex:1 on each card stretches them to share remaining
   space — no whitespace gap below the bottom card. */
.opsv2-install-card {
  /* Card sizes to content (legacy parity: padding 7px 9px, min-height 130px).
     Slot container .op-cal-installs no longer forces extra min-height, so
     card + slot heights match — no whitespace at the bottom. */
  min-height: 130px;
  padding: 7px 9px;
  border-radius: 6px; cursor: pointer; font-size: 11px;
  line-height: 1.25;
  box-sizing: border-box; overflow: hidden;
  transition: transform 0.1s, box-shadow 0.1s;
}
.opsv2-install-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(0,0,0,0.18);
}
.opsv2-install-card.compact { font-size: 11px; }
.opsv2-card-row1 {
  display: flex; align-items: center; gap: 4px; min-width: 0;
}
/* Compact top strip holding the order # + original-date + status chips on ONE
   short line, so the title below owns the full width and remaining height. */
.opsv2-card-top {
  display: flex; align-items: center; gap: 3px; flex-wrap: wrap;
  min-width: 0; margin-bottom: 2px; line-height: 1.1;
}
.opsv2-card-sosuffix { font-weight: 700; font-size: 12px; }
.opsv2-card-so {
  font-weight: 800; display: block; width: 100%; min-width: 0; font-size: 15px;
  /* Title can wrap to multiple lines so the field-crew tag
     ("YENSI--PVC--M-27-Coddington Community Association") stays
     legible. Cards grow to fit instead of clipping the prefix the
     install team relies on to identify the job at a glance. */
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
  line-height: 1.2;
}
.opsv2-card-local { font-size: 10px; font-weight: 600; flex-shrink: 0; }
.opsv2-badge-811 {
  font-size: 9px; font-weight: 800; padding: 1px 4px; border-radius: 3px;
  flex-shrink: 0;
}
.opsv2-card-vendor {
  font-size: 10px; font-weight: 500; line-height: 1.2; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opsv2-card-partner {
  font-weight: 600; line-height: 1.2; margin-top: 1px; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opsv2-card-footer {
  display: flex; justify-content: space-between; align-items: center; margin-top: 1px;
}
.opsv2-card-crew { font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 3px; background: rgba(0,0,0,0.25); color: #fff; }
.opsv2-card-assign {
  font-size: 11px; font-weight: 700; color: #fff;
  text-decoration: underline; padding: 0;
  background: transparent;
}
.opsv2-repair-tag {
  display: inline-block; font-size: 9px; font-weight: 800;
  background: #fde68a; color: #92400e; padding: 1px 5px;
  border-radius: 3px; margin-bottom: 3px;
}

.opsv2-unscheduled { margin-top: 16px; }
.opsv2-unscheduled summary {
  cursor: pointer; font-weight: 700; padding: 8px 0;
  color: var(--text); list-style: none;
}
.opsv2-unscheduled summary::before { content: "▸ "; color: var(--accent); }
.opsv2-unscheduled[open] summary::before { content: "▾ "; }

/* ── Welding sub-tabs + filter chips ─────────────────────────────────── */
.opsv2-weld-subtabs {
  display: flex; gap: 4px; padding: 6px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  margin-bottom: 8px; align-items: center; overflow-x: auto;
}
.opsv2-weld-subtab {
  background: transparent; border: none; color: var(--text-dim);
  padding: 6px 12px; border-radius: 6px; cursor: pointer;
  font-size: 13px; font-weight: 600; white-space: nowrap;
  position: relative;
}
.opsv2-weld-subtab:hover { background: var(--bg3); color: var(--text); }
.opsv2-weld-subtab.active { background: var(--accent); color: #fff; }
.opsv2-subtab-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; border-radius: 8px; padding: 0 4px;
  font-size: 10px; font-weight: 800; color: #fff;
  background: var(--red); margin-left: 4px;
}
.opsv2-add-block-btn {
  margin-left: auto; background: var(--accent); color: #fff; border: none;
  padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 700;
  cursor: pointer; white-space: nowrap;
}
.opsv2-add-block-btn:hover { background: var(--accent-hover); }

.opsv2-weld-chips {
  display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px;
}
.opsv2-chip {
  background: var(--bg2); border: 1px solid var(--border); color: var(--text-dim);
  padding: 5px 10px; border-radius: 16px; cursor: pointer;
  font-size: 12px; font-weight: 600; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 4px;
}
.opsv2-chip:hover { border-color: var(--accent); color: var(--accent); }
.opsv2-chip.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.opsv2-chip-count { font-size: 11px; font-weight: 800; opacity: 0.85; }

/* Welder legend */
.opsv2-weld-legend {
  display: flex; flex-wrap: wrap; gap: 8px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  padding: 8px 12px; margin-bottom: 8px;
}
.opsv2-legend-item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--text);
}
.opsv2-legend-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; }
.opsv2-legend-count { color: var(--text-dim); font-size: 12px; }

/* Welder week grid */
.opsv2-grid-wrap {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  overflow-x: auto;
}
.opsv2-grid-header {
  display: grid; grid-template-columns: 140px repeat(7, minmax(120px, 1fr));
  background: var(--bg3); border-bottom: 1px solid var(--border);
}
.opsv2-grid-corner {
  padding: 8px 10px; font-size: 11px; font-weight: 800; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.5px; border-right: 1px solid var(--border);
}
.opsv2-grid-dayhdr {
  padding: 8px 10px; font-size: 12px; color: var(--text);
  border-right: 1px solid var(--border); display: flex; align-items: center; gap: 6px;
}
.opsv2-grid-dayhdr:last-child { border-right: none; }
.opsv2-grid-dayname { font-weight: 800; }
.opsv2-grid-dayhours { color: var(--text-dim); font-weight: 600; font-size: 11px; }

.opsv2-grid-body { display: flex; flex-direction: column; }
.opsv2-grid-row {
  display: grid; grid-template-columns: 140px repeat(7, minmax(120px, 1fr));
  border-bottom: 1px solid var(--border); min-height: 80px;
}
.opsv2-grid-row:last-child { border-bottom: none; }
.opsv2-grid-welder {
  padding: 8px 10px; background: var(--bg3); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; justify-content: center;
}
.opsv2-grid-welder-name { font-size: 13px; font-weight: 700; color: var(--text); }
.opsv2-grid-welder-hours { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.opsv2-grid-cell {
  padding: 4px; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 4px;
}
.opsv2-grid-cell:last-child { border-right: none; }

/* Task card (used inside grid cells & kanban) */
.opsv2-task-card {
  background: #fff; border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 8px; font-size: 11px; cursor: pointer;
  transition: transform 0.1s, box-shadow 0.1s;
}
.opsv2-task-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
.opsv2-task-row1 {
  display: flex; align-items: center; justify-content: space-between; gap: 4px;
}
.opsv2-task-so { font-weight: 800; color: var(--accent); font-size: 12px; }
.opsv2-task-dept {
  font-size: 9px; font-weight: 800; color: #fff; padding: 1px 4px;
  border-radius: 3px; letter-spacing: 0.3px;
}
.opsv2-dept-wld { background: #2563eb; }
.opsv2-dept-al  { background: #b88312; }
.opsv2-dept-pnt { background: #6d28d9; }
.opsv2-dept-hr  { background: #ec4899; }
.opsv2-dept-bd  { background: #0f766e; }

.opsv2-task-partner {
  font-weight: 600; color: var(--text); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opsv2-task-meta { font-size: 10px; color: var(--text-dim); }
.opsv2-task-stage {
  display: inline-block; margin-top: 3px; font-size: 9px; font-weight: 700;
  padding: 1px 5px; border-radius: 3px; background: var(--bg3); color: var(--text-dim);
  text-transform: capitalize;
}
.opsv2-stage-in-progress { background: rgba(37,99,235,0.15); color: var(--blue); }
.opsv2-stage-done        { background: rgba(47,122,61,0.15); color: var(--green); }
.opsv2-stage-on-hold     { background: rgba(184,131,18,0.15); color: var(--yellow); }
.opsv2-stage-queued      { background: rgba(105,29,32,0.10); color: var(--accent); }

.opsv2-task-actions {
  display: flex; gap: 2px; margin-top: 4px; padding-top: 4px;
  border-top: 1px solid var(--border);
}
.opsv2-task-action {
  flex: 1; background: transparent; border: none; cursor: pointer;
  font-size: 11px; padding: 2px 4px; border-radius: 3px; color: var(--text-dim);
}
.opsv2-task-action:hover { background: var(--bg3); color: var(--accent); }

/* Kanban view (welding) */
.opsv2-kanban {
  display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px;
}
.opsv2-kanban-col {
  flex: 1 1 0; min-width: 200px; background: var(--bg2);
  border: 1px solid var(--border); border-radius: 8px; padding: 8px;
  display: flex; flex-direction: column; gap: 6px;
}
.opsv2-kanban-head {
  font-size: 12px; font-weight: 800; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.4px;
  display: flex; align-items: center; justify-content: space-between;
}
.opsv2-kanban-count { font-size: 11px; font-weight: 700; color: var(--text-dim); }
.opsv2-kanban-body { display: flex; flex-direction: column; gap: 4px; }
.opsv2-kanban-empty { color: var(--text-dim); font-size: 12px; text-align: center; padding: 12px 0; }

/* Empty state */
.opsv2-empty {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  padding: 36px 20px; text-align: center;
}
.opsv2-empty-title { font-size: 16px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.opsv2-empty-sub { font-size: 13px; color: var(--text-dim); }

/* Generic list row (handrails, sub-work, stopped, blockers) */
.opsv2-list-head {
  font-size: 13px; font-weight: 600; color: var(--text-dim); margin-bottom: 8px;
}
.opsv2-row-list { display: flex; flex-direction: column; gap: 6px; }
.opsv2-row {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 8px;
  padding: 10px 14px; cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  transition: background 0.1s;
}
.opsv2-row:hover { background: var(--bg3); }
.opsv2-row-main { flex: 1; min-width: 0; }
.opsv2-row-so { font-weight: 700; color: var(--accent); font-size: 14px; }
.opsv2-row-cust { font-size: 13px; color: var(--text); margin-top: 2px; }
.opsv2-row-tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 6px; }
.opsv2-row-side { flex-shrink: 0; text-align: right; display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }
.opsv2-row-date { font-size: 11px; color: var(--text-dim); }

/* Reports stats */
.opsv2-stats-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px; margin-bottom: 16px;
}
.opsv2-stat-card {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  padding: 14px; text-align: center;
}
.opsv2-stat-value { font-size: 28px; font-weight: 800; color: var(--accent); line-height: 1.1; }
.opsv2-stat-label { font-size: 11px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.4px; margin-top: 4px; }
.opsv2-bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 13px; }
.opsv2-bar-label { width: 140px; color: var(--text); font-weight: 600; text-transform: capitalize; }
.opsv2-bar-track { flex: 1; height: 16px; background: var(--bg3); border-radius: 4px; overflow: hidden; }
.opsv2-bar-fill { height: 100%; background: var(--accent); transition: width 0.3s; }
.opsv2-bar-count { width: 100px; text-align: right; color: var(--text-dim); font-weight: 600; }

/* Bot mode card */
.opsv2-bot-card {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
  padding: 48px 24px; text-align: center; margin-top: 12px;
}
.opsv2-bot-icon { font-size: 56px; margin-bottom: 12px; }
.opsv2-bot-title { font-size: 22px; font-weight: 800; color: var(--accent); margin-bottom: 4px; }
.opsv2-bot-sub { font-size: 14px; color: var(--text-dim); }

/* ── ViloBot AI assistant chat ─────────────────────────────────────────── */
.opsv2-bot-chat {
  display: flex; flex-direction: column;
  height: calc(100vh - 220px); min-height: 360px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: 12px;
  margin-top: 12px; overflow: hidden;
}
.opsv2-bot-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--bg2);
}
.opsv2-bot-head-icon { font-size: 26px; }
.opsv2-bot-head-title { font-weight: 800; color: var(--accent); }
.opsv2-bot-head-sub { font-size: 12px; color: var(--text-dim); }
.opsv2-bot-clear {
  margin-left: auto; font-size: 12px; padding: 5px 10px; cursor: pointer;
  background: transparent; color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 6px;
}
.opsv2-bot-clear:hover { color: var(--accent); border-color: var(--accent); }

.opsv2-bot-log { flex: 1; overflow-y: auto; padding: 16px; }
.opsv2-bot-empty { text-align: center; color: var(--text-dim); padding: 32px 12px; }
.opsv2-bot-empty-icon { font-size: 40px; margin-bottom: 8px; }
.opsv2-bot-suggest { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 16px; }
.opsv2-bot-chip {
  font-size: 13px; padding: 7px 12px; cursor: pointer;
  background: var(--bg); color: var(--text); text-align: left;
  border: 1px solid var(--border); border-radius: 16px;
}
.opsv2-bot-chip:hover { border-color: var(--accent); color: var(--accent); }

.opsv2-bot-msg { display: flex; margin-bottom: 12px; }
.opsv2-bot-msg.user { justify-content: flex-end; }
.opsv2-bot-bubble {
  max-width: 80%; padding: 10px 14px; border-radius: 14px;
  font-size: 14px; line-height: 1.5; white-space: pre-wrap; word-wrap: break-word;
}
.opsv2-bot-msg.user .opsv2-bot-bubble { background: var(--accent); color: #fff; border-bottom-right-radius: 4px; }
.opsv2-bot-msg.bot .opsv2-bot-bubble { background: var(--bg); color: var(--text); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
.opsv2-bot-bubble code { font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; background: rgba(0,0,0,0.06); padding: 1px 4px; border-radius: 4px; }
.opsv2-bot-tools { margin-top: 8px; font-size: 11px; color: var(--text-dim); font-style: italic; }
.opsv2-bot-typing { color: var(--text-dim); font-style: italic; }

.opsv2-bot-inputrow {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 12px 16px; border-top: 1px solid var(--border);
}
.opsv2-bot-input {
  flex: 1; resize: none; font: inherit; font-size: 14px; line-height: 1.4;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg); color: var(--text); max-height: 140px;
}
.opsv2-bot-input:focus { outline: none; border-color: var(--accent); }
.opsv2-bot-input:disabled { opacity: 0.6; cursor: not-allowed; }
.opsv2-bot-send {
  padding: 10px 18px; font-weight: 700; cursor: pointer;
  background: var(--accent); color: #fff; border: none; border-radius: 10px;
}
.opsv2-bot-send:hover { filter: brightness(1.08); }
.opsv2-bot-send:disabled { opacity: 0.5; cursor: not-allowed; }

/* Schedule mode (month) */
.opsv2-sched-weekdays {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 4px;
}
.opsv2-sched-wd {
  text-align: center; font-size: 11px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.5px; padding: 4px;
}
.opsv2-sched-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.opsv2-sched-cell {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 6px;
  min-height: 100px; padding: 4px; display: flex; flex-direction: column; gap: 2px;
}
.opsv2-sched-cell.is-today { outline: 2px solid var(--accent); outline-offset: -2px; }
.opsv2-sched-cell.empty { visibility: hidden; }
.opsv2-sched-daynum { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 2px; }
.opsv2-sched-pills { display: flex; flex-direction: column; gap: 2px; }
.opsv2-sched-pill {
  font-size: 10px; font-weight: 700; color: #fff;
  padding: 2px 6px; border-radius: 4px; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.opsv2-sched-pill:hover { opacity: 0.85; }
.opsv2-sched-pill-more { font-size: 10px; color: var(--text-dim); padding: 1px 4px; }

.opsv2-blockers-sum { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.opsv2-blockers-sum .badge { font-size: 13px; padding: 6px 12px; }

/* ── Operations Calendar — pre-determined block grid (Vilobot parity) ────
   Rows = 30-min time slots from 07:00 to 20:00, plus an "unscheduled" lane.
   Cols = TIME header + 7 days of the displayed week.
   Cells hold zero-or-more install blocks rendered side-by-side via flex.
   Block position is read from `calendarDay`/`calendarSlot`/`sideBySideCol`
   with a `scheduledDate` fallback; drag-drop persists those columns.
*/
/* ────── Operations Calendar — day-column block grid (Vilobot parity) ──────
   Layout per day column, top-to-bottom:
     • day header (DOW, date, INSTALLS count)
     • install block stack (drag between days to reschedule)
     • "+ new slot" dashed tile
     • MATERIAL PICKUP header + stacked pickup cards
     • MATERIAL DELIVERY header + stacked delivery cards
   ----------------------------------------------------------------------- */
.op-cal-wrap {
  background: var(--bg2); border: 1px solid var(--border); border-radius: 10px;
  overflow: auto; max-height: calc(100vh - 200px); padding: 6px;
}
.op-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(150px, 1fr));
  gap: 6px;
  align-items: start;
}
.op-cal-daycol {
  background: var(--bg3); border: 1px solid var(--border); border-radius: 8px;
  display: flex; flex-direction: column;
  min-height: 480px;
  overflow: hidden;
}
.op-cal-daycol.is-today {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.op-cal-dayhdr {
  padding: 8px 6px; text-align: center;
  background: var(--bg2);
  border-bottom: 1px solid var(--border);
}
.op-cal-daycol.is-today .op-cal-dayhdr {
  background: rgba(232, 115, 90, 0.08);
}
.op-cal-wd { font-size: 11px; font-weight: 800; color: var(--accent); letter-spacing: 0.6px; }
.op-cal-md { font-size: 14px; font-weight: 800; color: var(--text); margin-top: 2px; }
.op-cal-daycount {
  font-size: 10px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.4px; margin-top: 4px;
}
.op-cal-daycount-num {
  display: inline-block; padding: 1px 6px; min-width: 18px;
  background: var(--border); color: var(--text);
  border-radius: 4px; font-weight: 800; margin-left: 2px;
}

.op-cal-installs {
  padding: 6px;
  display: flex; flex-direction: column; gap: 4px;
  min-height: 80px;
  flex: 0 0 auto;
  /* Calendar's day column never lets content escape — install cards live
     in fixed-height row slots so wide titles can't push the whole column
     wider than its grid column. */
  overflow: hidden;
}
/* Fixed row height — every row across all 7 days renders at the same Y so
   the calendar reads like a true grid. Slim by default, can be raised
   per-row when content needs more room. */
:root { --op-cal-slot-h: 140px; }
/* One horizontal row in a day. Children are .op-cal-slot cells flexed
   side-by-side. */
.op-cal-installs-row {
  display: flex;
  gap: 4px;
  align-items: stretch;
  /* Fixed height = uniform blocks, exactly like legacy. The full title
     is made to fit by SHRINKING the title font (JS auto-fit in
     ops-manager.js fitInstallTitles), not by growing the card. */
  height: var(--op-cal-slot-h);
  width: 100%;
  min-width: 0;
}
/* Multiple installs in ONE day-row (same installer, same day): the cards EVENLY
   SHARE the day column width (flex:1 1 0 from the base .op-cal-slot rule, no
   fixed width / no horizontal scroll — those made the 2nd/3rd card overflow and
   get clipped). The title is a full-width block (.opsv2-card-so) so it wraps
   readably instead of collapsing per-character, and fitInstallTitles shrinks
   the font to whatever fits the narrower share. Per Dariel 2026-06-25. */
.op-cal-installs-row.is-multi { overflow: visible; }
/* Order badge (1,2,3 left→right) shown on side-by-side cards so the crew sees
   the install sequence for the day. White pill reads on any card color. */
.opsv2-card-num {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  min-width: 15px; height: 15px; padding: 0 4px; margin-right: 4px;
  background: rgba(255,255,255,0.92); color: #111;
  font-size: 10px; font-weight: 800; line-height: 1;
  border-radius: 8px; box-shadow: 0 0 0 1px rgba(0,0,0,0.15);
}
/* Thin drop-target sliver below the last row in a day — drag a card on
   it and a brand-new horizontal row materialises across all 7 columns.
   18 px tall (was 10 px) so the cursor can release inside it reliably;
   browsers fire `drop` on whatever element is under the cursor at
   mouseup, and a 10 px target proved too small in practice. */
.op-cal-installs-row.is-newrow-sliver {
  height: 18px;
  min-height: 18px;
}
.op-cal-installs-row.is-newrow-sliver.is-droptarget,
.op-cal-installs-row.is-newrow-sliver.is-droptarget .op-cal-slot {
  background: rgba(232, 115, 90, 0.15);
  border-color: var(--accent);
}
.op-cal-slot {
  border-radius: 6px;
  overflow: hidden;
  flex: 1 1 0;
  /* Critical: lets flex children shrink to share a parent's width evenly
     instead of forcing their content's intrinsic width. Without min-width:0
     cards with long titles claim more than their fair share and squeeze
     siblings to a sliver. */
  min-width: 0;
  display: flex; flex-direction: column;
  height: 100%;
}
.op-cal-slot.is-empty {
  border: 2px dashed var(--border);
  align-items: center; justify-content: center;
  color: var(--text-dim);
  font-size: 18px; font-weight: 700;
  user-select: none;
  background: rgba(0,0,0,0.01);
  transition: border-color 0.1s, background 0.1s, color 0.1s;
}
.op-cal-slot.is-empty.is-newrow-sliver-slot {
  /* The sliver slot is the bottom drop-only zone — no hover affordance,
     no padding. Fills the 18 px sliver row entirely so anywhere inside
     the row catches drag/drop events. */
  border: 1px dashed var(--border);
  border-radius: 4px;
  height: 100%;
  background: rgba(0,0,0,0.02);
}
.op-cal-installs-row.is-newrow-sliver .op-cal-slot.is-empty.is-newrow-sliver-slot.is-droptarget {
  background: rgba(232, 115, 90, 0.22);
  border-color: var(--accent);
  border-style: solid;
}
.op-cal-slot.is-droptarget {
  border-color: var(--accent);
  background: rgba(232, 115, 90, 0.18);
  outline: 2px dashed var(--accent); outline-offset: -2px;
}
.op-cal-slot.is-filled {
  /* Filled slots have the install card stretched edge-to-edge. */
  background: transparent;
}
.op-cal-slot.is-filled .op-cal-block {
  height: 100%;
  min-height: 0;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  /* Hard clip everything past the card edges so no title or address
     escapes into a neighbouring slot. The title is shrunk to fit by the
     JS auto-fit pass (fitInstallTitles) so nothing is actually clipped. */
  overflow: hidden;
}
.op-cal-slot-plus { line-height: 1; }
/* Every text row inside an install card truncates with ellipsis instead
   of wrapping past the card width. Cascade min-width:0 so flex children
   can shrink below their content size — the real fix for "text overflows
   the block" reported on the ops calendar. */
.op-cal-slot.is-filled .op-cal-block,
.op-cal-slot.is-filled .op-cal-block * {
  min-width: 0;
  max-width: 100%;
}
.op-cal-slot.is-filled .opsv2-card-row1,
.op-cal-slot.is-filled .opsv2-card-partner,
.op-cal-slot.is-filled .opsv2-card-vendor,
.op-cal-slot.is-filled .opsv2-card-footer {
  min-width: 0;
}
.op-cal-slot.is-filled .opsv2-card-so {
  /* Same wrap-don't-clip policy as the base .opsv2-card-so rule:
     install crews need the full "YENSI--PVC--M-27-…" title at a
     glance even inside a filled calendar slot. */
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
}
.op-cal-slot.is-filled .opsv2-card-partner,
.op-cal-slot.is-filled .opsv2-card-vendor {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.op-cal-installs.is-droptarget {
  background: rgba(232, 115, 90, 0.12);
  outline: 2px dashed var(--accent); outline-offset: -2px;
}
.op-cal-day-empty {
  text-align: center; color: var(--text-dim); font-size: 12px;
  padding: 12px 0;
}

.op-cal-newslot {
  margin: 4px 6px;
  padding: 6px;
  border: 2px dashed var(--border);
  border-radius: 6px;
  text-align: center;
  color: var(--text-dim);
  font-size: 11px; font-weight: 600;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.1s, background 0.1s, color 0.1s;
}
.op-cal-newslot:hover {
  border-color: var(--accent); color: var(--accent);
  background: rgba(232, 115, 90, 0.06);
}
.op-cal-newslot.is-droptarget {
  border-color: var(--accent);
  background: rgba(232, 115, 90, 0.12);
  color: var(--accent);
}

.op-cal-matsection {
  border-top: 1px solid var(--border);
  padding: 4px 6px 6px 6px;
  background: var(--bg2);
}
.op-cal-mat-hdr {
  font-size: 10px; font-weight: 800; letter-spacing: 0.4px;
  text-transform: uppercase; margin: 2px 0 4px 0;
  display: flex; align-items: center; gap: 5px;
}
.op-cal-mat-hdr-pickup { color: #d97706; }
.op-cal-mat-hdr-delivery { color: #7c3aed; }
.op-cal-mat-hdr-manufacture { color: #0d9488; }
/* Collapsible "PICKUP" (normal, no shop op) — closed by default. */
.op-cal-mat-normalpickup > summary { color: #6b7280; }
.op-cal-mat-normalpickup > summary::-webkit-details-marker { display: none; }
.op-cal-mat-normalpickup[open] > summary .op-cal-mat-caret { transform: rotate(90deg); }
.op-cal-mat-caret { transition: transform .12s ease; display: inline-block; }
/* Collapsible "PICKUP" (normal, no shop op) — closed by default. */
.op-cal-mat-normalpickup > summary { color: #6b7280; }
.op-cal-mat-normalpickup > summary::-webkit-details-marker { display: none; }
.op-cal-mat-normalpickup[open] > summary .op-cal-mat-caret { transform: rotate(90deg); }
.op-cal-mat-caret { transition: transform .12s ease; display: inline-block; }
.op-cal-mat-count {
  display: inline-block; padding: 1px 5px; min-width: 16px;
  background: var(--border); color: var(--text);
  border-radius: 4px; font-weight: 800; font-size: 10px;
}
.op-cal-mat-count-blue { background: #ede9fe; color: #6d28d9; }
.op-cal-mat-body {
  display: flex; flex-direction: column; gap: 3px;
  min-height: 18px;
}
.op-cal-mat-empty {
  text-align: center; color: var(--text-dim);
  font-size: 11px; padding: 2px 0;
}

.op-cal-matblock {
  background: #fff; border: 1px solid var(--border); border-radius: 5px;
  padding: 4px 6px; font-size: 11px;
  display: flex; flex-direction: column; gap: 2px;
}
.op-cal-matblock.is-delivery { background: #ede9fe; border-color: #c4b5fd; }
.op-cal-matblock-row1 {
  display: flex; justify-content: space-between; align-items: center;
  gap: 4px;
}
.op-cal-matblock-label {
  font-weight: 800; color: #1a1a2e; font-size: 11px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.op-cal-matblock-state {
  font-size: 8px; font-weight: 800; padding: 1px 5px;
  border-radius: 3px; color: #fff; text-transform: uppercase;
  letter-spacing: 0.3px; flex-shrink: 0;
}
.op-cal-matblock-ref {
  font-size: 9px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.op-cal-matblock-cust {
  font-size: 11px; font-weight: 600; color: #333;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.op-cal-block {
  cursor: grab; border-radius: 6px;
  font-size: 11px;
  display: flex; flex-direction: column;
  padding: 7px 9px;
  line-height: 1.25;
  overflow: hidden; box-sizing: border-box;
  user-select: none;
  transition: box-shadow 0.1s, transform 0.1s;
  min-height: 130px;
}
.op-cal-block:active { cursor: grabbing; }
.op-cal-block:hover {
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.18);
  transform: translateY(-1px);
}
.op-cal-block.is-dragging { opacity: 0.45; }

/* Responsive */
@media (max-width: 1100px) {
  .op-cal-grid { grid-template-columns: repeat(7, minmax(120px, 1fr)); }
  .opsv2-grid-header, .opsv2-grid-row { grid-template-columns: 120px repeat(7, minmax(100px, 1fr)); }
}
@media (max-width: 900px) {
  /* Stack the calendar 2 days per row on narrow viewports — keep the
     day-column-with-blocks shape, just wrap. */
  .op-cal-grid { grid-template-columns: repeat(2, 1fr); }
  .opsv2-mode-label { display: none; }
  .opsv2-search { width: 140px; }
  .opsv2-mode { padding: 8px 10px; }
  .opsv2-grid-header, .opsv2-grid-row { grid-template-columns: 110px repeat(7, minmax(90px, 1fr)); }
}
/* Phones — the calendar renders ONE day per page (see ops-manager.js day-paging
   with ◀ ▶ nav). This makes that single day full-width and shows only its real
   installs (the 140px empty alignment rows that keep the desktop side-by-side
   grid lined up are dropped). Tap an install to open the (full-screen) popup.
   Per Dariel 2026-06-29. */
@media (max-width: 700px) {
  .op-cal-wrap { max-height: none; overflow: visible; padding: 0; border: 0; background: transparent; }
  .op-cal-grid { grid-template-columns: 1fr; gap: 12px; }
  .op-cal-daycol { min-height: auto; }
  /* Drop the empty alignment rows + the add-slot sliver; let real rows size to content. */
  .op-cal-installs-row:has(> .op-cal-emptycell) { display: none; }
  .op-cal-emptycell { display: none; }
  .op-cal-installs-row.is-newrow-sliver { display: none; }
  .op-cal-installs-row, .op-cal-slot { height: auto; min-height: 0; }
  .op-cal-dayhdr { position: sticky; top: 0; z-index: 2; }
}

/* ────── Inventory ────── */
.inv-shell { display: flex; flex-direction: column; gap: 12px; }
.inv-tabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border); padding-bottom: 0;
}
.inv-tab {
  background: transparent; border: none; cursor: pointer;
  padding: 10px 16px; font-size: 14px; font-weight: 600;
  color: var(--text-dim); border-bottom: 2px solid transparent;
  transition: color 0.12s, border-color 0.12s;
}
.inv-tab:hover { color: var(--text); }
.inv-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.inv-toolbar {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-bottom: 12px;
}
.inv-toolbar-spacer { flex: 1; }
.inv-toolbar-info { color: var(--text-dim); font-size: 12px; }

.inv-table-wrap {
  background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow-x: auto;
}
.inv-stock-table, .inv-moves-table, .inv-low-table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: 13px;
}
.inv-stock-table thead th, .inv-moves-table thead th, .inv-low-table thead th {
  background: var(--bg3); color: var(--text); font-weight: 700;
  text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 1; white-space: nowrap;
}
.inv-stock-table tbody td, .inv-moves-table tbody td, .inv-low-table tbody td {
  padding: 8px 12px; border-bottom: 1px solid var(--border); vertical-align: middle;
}
.inv-stock-table tbody tr:hover, .inv-moves-table tbody tr:hover { background: rgba(105,29,32,0.04); }
.inv-row-low { background: rgba(185,28,28,0.06); }
.inv-row-low:hover { background: rgba(185,28,28,0.12) !important; }

.inv-col-img { width: 48px; padding: 6px !important; }
.inv-thumb { width: 36px; height: 36px; object-fit: cover; border-radius: 6px; background: var(--bg3); display: block; }
.inv-thumb-empty { display: flex; align-items: center; justify-content: center; font-size: 18px; color: var(--text-dim); }
.inv-col-sku { font-family: monospace; font-size: 12px; white-space: nowrap; }
.inv-col-sku code { background: var(--bg3); padding: 2px 6px; border-radius: 4px; font-size: 11px; }
.inv-col-name { min-width: 180px; font-weight: 600; }
.inv-col-name .inv-uom { font-size: 11px; color: var(--text-dim); font-weight: 400; margin-top: 2px; }
.inv-col-cat { font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.inv-col-loc { text-align: center; min-width: 70px; font-variant-numeric: tabular-nums; }
.inv-col-total { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; min-width: 60px; }
.inv-col-min { text-align: right; color: var(--text-dim); font-variant-numeric: tabular-nums; min-width: 50px; }
.inv-col-actions { text-align: right; white-space: nowrap; min-width: 160px; }
.inv-col-actions .btn { padding: 4px 10px; font-size: 12px; }
.inv-col-num { text-align: right; font-variant-numeric: tabular-nums; }
.inv-col-note { font-size: 12px; color: var(--text-dim); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-qty-pos { color: var(--green); font-weight: 600; }
.inv-qty-zero { color: var(--text-dim); }
.inv-qty-low { color: var(--red); font-weight: 700; }
.inv-loc-head { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 11px; }
.inv-loc-head > span:first-child { font-size: 16px; }
.inv-empty-cell { text-align: center; padding: 24px !important; color: var(--text-dim); }

/* Locations tab */
.inv-loc-grid { display: flex; flex-direction: column; gap: 18px; }
.inv-loc-group-title { font-size: 14px; color: var(--text-dim); margin-bottom: 8px; font-weight: 700; }
.inv-loc-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.inv-loc-card {
  background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px;
}
.inv-loc-name { font-size: 15px; font-weight: 700; color: var(--text); }
.inv-loc-meta { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.inv-loc-addr { font-size: 12px; color: var(--text); margin-top: 6px; }

/* Modals */
.inv-modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45);
  display: flex; align-items: center; justify-content: center;
  z-index: 2000; padding: 20px;
}
.inv-modal {
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
  width: 100%; max-width: 460px; box-shadow: 0 20px 60px rgba(0,0,0,0.25);
  display: flex; flex-direction: column; max-height: 90vh;
}
.inv-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border); }
.inv-modal-head h3 { font-size: 16px; color: var(--text); }
.inv-modal-close { background: transparent; border: none; font-size: 24px; color: var(--text-dim); cursor: pointer; padding: 0 4px; }
.inv-modal-close:hover { color: var(--text); }
.inv-modal-body { padding: 16px 18px; overflow-y: auto; }
.inv-modal-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 18px; border-top: 1px solid var(--border); }
.inv-modal-sku { font-family: monospace; font-size: 12px; color: var(--text-dim); margin-bottom: 12px; }

@media (max-width: 700px) {
  .inv-col-cat, .inv-col-min { display: none; }
  .inv-col-actions { min-width: 130px; }
  .inv-col-actions .btn { padding: 3px 8px; font-size: 11px; }
}

/* ────── Inventory Extended (BOMs, Reorder Points, Requisitions) ────── */

/* Yellow / warn row (below target but above min) — companion to .inv-row-low. */
.inv-row-warn { background: rgba(184,131,18,0.07); }
.inv-row-warn:hover { background: rgba(184,131,18,0.14) !important; }

/* BOM / Requisition detail header — shown above lines table. */
.inv-bom-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  padding: 12px 14px; margin: 8px 0 4px;
  background: var(--bg2); border: 1px solid var(--border); border-radius: 8px;
}
.inv-bom-title { font-size: 18px; font-weight: 700; color: var(--text); }
.inv-bom-sub { font-size: 13px; color: var(--text-dim); margin-top: 4px; }
.inv-bom-sub code { background: transparent; padding: 0; }
.inv-bom-section-title {
  margin: 18px 0 8px;
  font-size: 13px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.inv-bom-empty {
  text-align: center; padding: 20px; color: var(--text-dim);
  border: 1px dashed var(--border); border-radius: 6px;
  font-size: 13px; background: var(--bg2);
}

/* Inline add-component row (used by BOM detail + new-requisition modal). */
.inv-bom-addline { margin-top: 10px; }
.inv-bom-addrow {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 10px; background: var(--bg2); border-radius: 6px;
  border: 1px solid var(--border);
}
.inv-bom-addrow .form-select,
.inv-bom-addrow .form-input { min-width: 0; }

/* Wide variant of the inventory modal — used by New Requisition. */
.inv-modal-wide { max-width: 720px; }

/* ── Quick Cost: a scratch calculator, not a saved record ────────────────── */
/* The button is in the tab strip but is NOT a tab — pushed right and given a
   border so it reads as a tool. Per Dariel 2026-08-14. */
.inv-quickcost-btn {
  margin-left: auto; align-self: center; color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 6px 12px; margin-bottom: 4px;
}
.inv-quickcost-btn:hover { border-color: var(--accent); color: var(--accent); }

.inv-qc .inv-modal-body { padding-top: 12px; }
.inv-qc-note { font-size: 12px; color: var(--text-dim); margin-bottom: 10px; }
.inv-qc-search { position: relative; margin-bottom: 12px; }
.inv-qc-results {
  margin-top: 6px; max-height: 240px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg2);
}
.inv-qc-hit {
  display: flex; gap: 10px; align-items: baseline; width: 100%; text-align: left;
  background: transparent; border: none; border-bottom: 1px solid var(--border);
  padding: 7px 10px; cursor: pointer; font-size: 13px; color: var(--text);
}
.inv-qc-hit:last-child { border-bottom: none; }
.inv-qc-hit:hover { background: var(--bg); }
.inv-qc-hit-sku { font-family: monospace; font-size: 11px; color: var(--text-dim); flex: 0 0 auto; }
.inv-qc-hit-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-qc-hit-cost { flex: 0 0 auto; font-weight: 700; }
.inv-qc-empty, .inv-qc-blank {
  padding: 14px 10px; font-size: 13px; color: var(--text-dim); text-align: center;
}
.inv-qc-nocost { color: #d97706; }

/* On a phone the qty/cost columns don't fit — scroll the table inside its own
   box rather than letting it push the modal sideways. */
.inv-qc-lines { overflow-x: auto; }
.inv-qc-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.inv-qc-table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-dim); font-weight: 700; padding: 4px 6px; border-bottom: 1px solid var(--border);
}
.inv-qc-table td { padding: 6px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.inv-qc-num { text-align: right; }
.inv-qc-table th.inv-qc-num { text-align: right; }
.inv-qc-name { font-weight: 600; color: var(--text); }
.inv-qc-sku { font-family: monospace; font-size: 11px; color: var(--text-dim); margin-top: 1px; }
.inv-qc-qty, .inv-qc-cost { width: 88px; text-align: right; padding: 4px 6px; }
.inv-qc-linetotal { font-weight: 700; white-space: nowrap; }
.inv-qc-del {
  background: transparent; border: none; color: var(--text-dim);
  cursor: pointer; font-size: 14px; padding: 2px 4px;
}
.inv-qc-del:hover { color: #b91c1c; }

/* Grid, not flex: build | total | buttons must stay one row at any width, and
   flex kept wrapping the buttons underneath once the total grew. */
.inv-qc-foot {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 8px 12px;
}
.inv-qc-build { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-dim); }
.inv-qc-build .form-input { width: 70px; text-align: right; padding: 4px 6px; }
.inv-qc-total { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
/* Keep Copy / Clear / Done together — they wrapped apart once the total grew. */
.inv-qc-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.inv-qc-sub { font-size: 12px; color: var(--text-dim); }
.inv-qc-grand { font-size: 20px; font-weight: 800; color: var(--text); }
/* Its own line inside the total block, so the number and the action buttons
   still share one row instead of the buttons wrapping underneath. */
.inv-qc-warn { flex: 1 0 100%; text-align: right; font-size: 11px; font-weight: 700; color: #d97706; }

@media (max-width: 480px) {
  .inv-qc-qty, .inv-qc-cost { width: 66px; }
  .inv-qc-grand { font-size: 17px; }
  .inv-qc-build .form-input { width: 56px; }
}

/* Side-by-side from / to location selects in new-requisition modal. */
.inv-newreq-locs { display: flex; gap: 10px; }
.inv-newreq-locs > div { display: flex; flex-direction: column; }

/* Clickable BOM rows in the list. */
.inv-bom-row { cursor: pointer; }
.inv-bom-row:hover { background: var(--bg2); }

/* Generic badge fallbacks if not already styled. */
.inv-stock-table .badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; font-weight: 700; }

@media (max-width: 700px) {
  .inv-bom-head { flex-direction: column; gap: 8px; }
  .inv-newreq-locs { flex-direction: column; }
  .inv-bom-addrow { flex-direction: column; align-items: stretch; }
  .inv-bom-addrow .form-input,
  .inv-bom-addrow .form-select { width: 100%; max-width: none; }
}

/* ────── Purchasing ────── */
.pur-shell { display: flex; flex-direction: column; gap: 12px; }
.pur-tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border); padding-bottom: 6px; flex-wrap: wrap; align-items: center; }
.pur-tab { background: transparent; border: none; padding: 8px 14px; font-size: 14px; font-weight: 600; color: var(--text-dim); cursor: pointer; border-radius: 6px 6px 0 0; }
.pur-tab:hover { color: var(--text); }
.pur-tab.active { background: var(--bg2); color: var(--accent); border-bottom: 2px solid var(--accent); }
.pur-tab-action { margin-left: auto; color: #fff !important; background: var(--accent) !important; border-radius: 6px; padding: 8px 16px; }
.pur-tab-action:hover { background: var(--accent-hover) !important; }
.pur-detail-head { display: flex; flex-direction: column; gap: 12px; }
.pur-action-row { display: flex; gap: 8px; flex-wrap: wrap; padding-top: 8px; border-top: 1px solid var(--border); }
.pur-totals { display: flex; justify-content: flex-end; gap: 16px; padding: 10px 14px; margin-top: 8px; border-top: 1px solid var(--border); background: var(--bg2); border-radius: 6px; }
.pur-totals-label { font-weight: 700; color: var(--text-dim); }
.pur-totals-value { font-weight: 800; color: var(--green); font-size: 18px; }

/* Modals (used by new PO, vendor edit, receive line, link SO) */
.pur-modal-bg { position: fixed; inset: 0; background: rgba(37,57,56,0.45); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; }
.pur-modal { background: var(--bg); border-radius: 12px; max-width: 520px; width: 100%; max-height: 90vh; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.2); }
.pur-modal-wide { max-width: 720px; }
.pur-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border); }
.pur-modal-head h3 { font-size: 16px; color: var(--text); margin: 0; }
.pur-modal-x { background: transparent; border: none; font-size: 24px; color: var(--text-dim); cursor: pointer; padding: 0 4px; }
.pur-modal-x:hover { color: var(--text); }
.pur-modal-body { padding: 16px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
.pur-modal-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 18px; border-top: 1px solid var(--border); }
.pur-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.pur-field > span { font-size: 11px; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; }

@media (max-width: 700px) {
  .pur-tab-action { margin-left: 0; }
  .pur-action-row .btn { font-size: 12px; padding: 6px 10px; }
}

/* ────── Purchasing Extended (Vendor Pricing / Bills / RFQs / Reports) ────── */

/* Filter chip row (Vendor Bills + RFQs state filter) */
.pur-chip-row { display: flex; gap: 4px; flex-wrap: wrap; }
.pur-chip {
  background: var(--bg2); border: 1px solid var(--border); padding: 5px 12px;
  font-size: 12px; font-weight: 600; color: var(--text-dim); cursor: pointer;
  border-radius: 999px; text-transform: capitalize;
}
.pur-chip:hover { background: var(--bg); color: var(--text); }
.pur-chip.active {
  background: var(--accent); color: #fff; border-color: var(--accent);
}

/* Vendor multi-select chips (used in New RFQ modal) */
.pur-vendor-chip-row { display: flex; gap: 6px; flex-wrap: wrap; }
.pur-vendor-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px; border: 1px solid var(--border); border-radius: 999px;
  font-size: 12px; cursor: pointer; background: var(--bg2);
}
.pur-vendor-chip input { margin: 0; }
.pur-vendor-chip.active { background: rgba(105,29,32,0.08); border-color: var(--accent); color: var(--accent); }

/* Reports KPI grid */
.pur-kpi-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
}
.pur-kpi-card {
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  padding: 14px;
}
.pur-kpi-label {
  font-size: 11px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.pur-kpi-value { font-size: 24px; font-weight: 800; color: var(--accent); margin-top: 4px; }
.pur-kpi-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.pur-top-vendor {
  display: flex; justify-content: space-between; padding: 3px 0;
  font-size: 13px; border-bottom: 1px dashed var(--border);
}
.pur-top-vendor:last-child { border-bottom: none; }
.pur-top-vendor-name { color: var(--text); font-weight: 500; }
.pur-top-vendor-amt { color: var(--green); font-weight: 700; }

/* Monthly spend HTML/CSS bar chart */
.pur-bar-chart { display: flex; flex-direction: column; gap: 6px; }
.pur-bar-row {
  display: grid; grid-template-columns: 80px 1fr 110px; gap: 8px;
  align-items: center;
}
.pur-bar-label { font-size: 12px; color: var(--text-dim); font-weight: 600; }
.pur-bar-track {
  height: 18px; background: var(--bg2); border-radius: 4px; overflow: hidden;
  border: 1px solid var(--border);
}
.pur-bar-fill {
  height: 100%; background: linear-gradient(90deg, var(--accent), var(--green));
  transition: width 200ms ease-out;
}
.pur-bar-value { font-size: 12px; font-weight: 700; color: var(--text); text-align: right; }

/* Small responsive nudges */
@media (max-width: 700px) {
  .pur-bar-row { grid-template-columns: 60px 1fr 80px; }
  .pur-kpi-value { font-size: 20px; }
}

/* ────── Operations & Status v2 ────── */
.ops-status-card { display: grid; gap: 14px; }
.ops-status-header {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  flex-wrap: wrap;
}
.ops-status-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.btn-ops-mini {
  font-size: 11px; padding: 5px 10px; border-radius: 6px; border: 0;
  font-weight: 600; cursor: pointer; line-height: 1; color: #fff;
  display: inline-flex; align-items: center; gap: 4px;
}
.btn-ops-mini:hover { filter: brightness(0.92); }
.btn-ops-orange { background: #e07a3c; }
.btn-ops-purple { background: var(--purple); }
.btn-ops-dark   { background: var(--text); }
.btn-ops-danger {
  background: var(--red); color: #fff; border: 0; border-radius: 6px;
  font-weight: 600; cursor: pointer; padding: 5px 12px;
}
.btn-ops-danger:hover { filter: brightness(0.92); }

.ops-section { display: grid; gap: 6px; }
.ops-section-label {
  font-size: 12px; font-weight: 700; color: var(--text);
  text-transform: none; letter-spacing: 0;
}
.ops-section-label-row {
  display: flex; justify-content: space-between; align-items: center;
}

/* Chip row for operation types */
.ops-chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.ops-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; padding: 5px 10px; border-radius: 999px;
  background: var(--bg3); color: var(--text-dim);
  border: 1px solid var(--border); cursor: pointer;
  transition: background 0.12s, color 0.12s, transform 0.06s;
}
.ops-chip[data-state="ready"] {
  background: var(--bg); color: var(--text);
  border-color: rgba(105, 29, 32, 0.2);
}
.ops-chip[data-state="ready"]:hover {
  background: rgba(105, 29, 32, 0.08); color: var(--accent);
  border-color: var(--accent);
}
.ops-chip:active { transform: scale(0.97); }
.ops-chip-icon { font-size: 12px; }
.ops-chip-label { font-weight: 600; }

.ops-chip-detail {
  margin-top: 6px; padding: 8px 10px; border: 1px dashed var(--border);
  border-radius: 8px; background: var(--bg);
}

.ops-create-row {
  display: flex; align-items: center; gap: 10px; margin-top: 2px;
}
.ops-create-hint { font-size: 11px; color: var(--text-dim); }

/* Generic list rows used by shop orders & installations */
.ops-list { display: grid; gap: 6px; }
.ops-list-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg);
}
.ops-list-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ops-list-actions { display: flex; gap: 4px; }
.btn-icon-soft {
  background: transparent; border: 0; cursor: pointer; padding: 4px 6px;
  border-radius: 6px; font-size: 13px;
}
.btn-icon-soft:hover { background: var(--bg3); }
.btn-icon-danger:hover { background: rgba(185, 28, 28, 0.1); color: var(--red); }

.ops-kind-chip {
  font-size: 10px; font-weight: 700; padding: 2px 8px;
  border-radius: 999px; text-transform: uppercase;
  background: var(--bg3); color: var(--text-dim); letter-spacing: 0.4px;
}
.ops-kind-welding        { background: rgba(232,115,90,0.15); color: #b04830; }
.ops-kind-aluminum       { background: rgba(37,99,235,0.15);  color: #1d4ed8; }
.ops-kind-breakdown      { background: rgba(184,131,18,0.18); color: #8a6210; }
.ops-kind-permit         { background: rgba(109,40,217,0.15); color: #5b21b6; }
.ops-kind-powder_coating { background: rgba(139,92,246,0.15); color: #6d28d9; }
.ops-kind-handrail       { background: rgba(168,85,247,0.15); color: #7e22ce; }
.ops-kind-drafting       { background: rgba(6,182,212,0.18);  color: #0e7490; }

.ops-stage-chip {
  font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 999px;
  background: var(--bg3); color: var(--text-dim);
}
.ops-stage-not-started { background: var(--bg3); color: var(--text-dim); }
.ops-stage-queued      { background: rgba(37,99,235,0.15); color: #1d4ed8; }
.ops-stage-in-progress { background: rgba(47,122,61,0.15); color: var(--green); }
.ops-stage-qc          { background: rgba(184,131,18,0.18); color: #8a6210; }
.ops-stage-done        { background: rgba(47,122,61,0.22); color: var(--green); }
.ops-stage-on-hold     { background: rgba(184,131,18,0.18); color: #8a6210; }
.ops-stage-cancelled   { background: rgba(185,28,28,0.15); color: var(--red); }
.ops-due { font-size: 11px; color: var(--text-dim); }

/* Operation-as-parent card on the SO detail OPERATIONS & STATUS section.
   Burgundy left rail keeps it visually tied to the install-popup OP cards.
   Shop rows nest inside .ops-op-card-shops; "+ Add" buttons sit on a
   dashed-border row at the bottom. */
.ops-ops-cards { display: grid; gap: 10px; }
.ops-op-card {
  border: 1px solid var(--border);
  border-left: 4px solid #691d20;
  border-radius: 10px;
  padding: 10px 12px;
  background: rgba(241, 232, 223, 0.35);
  display: grid;
  gap: 8px;
}
.ops-op-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.ops-op-card-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 22px;
  padding: 0 6px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  background: #691d20;
  letter-spacing: 0.5px;
}
.ops-op-card-title { font-size: 13px; font-weight: 700; }
.ops-op-card-meta { font-size: 11px; color: var(--text-dim); margin-left: auto; }
.ops-op-card-shops { display: grid; gap: 6px; }
.ops-op-addrow {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--border);
}
.ops-op-add-btn { min-height: 44px; }
.ops-op-deco-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.ops-chip-deco {
  font-size: 11px;
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: 999px;
  padding: 4px 10px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  color: var(--text-dim);
}
.ops-chip-deco:hover {
  background: rgba(105, 29, 32, 0.06);
  border-color: #691d20;
  color: var(--text);
}

.ops-install-lead { font-weight: 600; color: var(--text); }
.ops-install-meta { font-size: 11px; color: var(--text-dim); }

/* Promised date row */
.ops-promised-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px;
  align-items: center; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg);
}
.ops-promised-label { font-weight: 600; color: var(--text); font-size: 13px; }
.ops-promised-value { font-size: 13px; color: var(--text); }

/* Blockers row */
.ops-blockers-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px;
  align-items: center; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg);
}
.ops-blockers-label { font-weight: 600; color: var(--text); font-size: 13px; }
.ops-blockers-list { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.ops-blockers-empty { font-size: 12px; color: var(--text-dim); font-style: italic; }
.ops-blocker-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; padding: 3px 8px; border-radius: 999px;
  background: rgba(185, 28, 28, 0.10); color: var(--red);
  border: 1px solid rgba(185, 28, 28, 0.25);
}
.ops-blocker-pill[data-severity="critical"] {
  background: rgba(185, 28, 28, 0.18);
  border-color: rgba(185, 28, 28, 0.45);
}
.ops-blocker-sev {
  text-transform: uppercase; font-size: 9px; font-weight: 800; letter-spacing: 0.4px;
}
.ops-blocker-desc { font-weight: 500; }

.ops-followups-card { display: grid; gap: 6px; }

/* ────── Install Popup v3 ──────
   Pixel-match the legacy Vilobot universal SO popup: two-column layout,
   tinted operation cards on the right, sticky action bar at the bottom.
   Re-uses the existing overlay backdrop (.vb-popup-overlay). */

.vb-ipv3-overlay {
  position: fixed; inset: 0; background: rgba(37,57,56,0.55);
  z-index: 9999; display: flex; align-items: flex-start; justify-content: center;
  padding: 24px 16px; overflow-y: auto;
}
.vb-ipv3-card {
  width: 100%; max-width: 1520px; background: var(--bg2); color: var(--text);
  border-radius: 14px; box-shadow: 0 14px 56px rgba(37,57,56,0.32);
  position: relative; padding: 0; max-height: calc(100vh - 48px);
  overflow: hidden; display: flex; flex-direction: column;
}
.vb-ipv3-close {
  position: absolute; top: 10px; right: 12px; width: 32px; height: 32px;
  border-radius: 50%; background: rgba(255,255,255,0.6); border: none;
  color: var(--text); font-size: 20px; cursor: pointer; line-height: 1; z-index: 3;
}
.vb-ipv3-close:hover { background: var(--accent); color: #fff; }
.vb-ipv3-body { padding: 16px 18px 0; overflow-y: auto; overscroll-behavior: contain; }

/* Header ----------------------------------------------------------------- */
.vb-ipv3-header {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; padding: 4px 32px 12px 4px; border-bottom: 1px solid var(--border);
  margin-bottom: 12px; flex-wrap: wrap;
}
.vb-ipv3-header-l { flex: 1; min-width: 0; }
.vb-ipv3-num-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vb-ipv3-custom-label {
  flex: 1 1 240px; min-width: 180px; max-width: 360px;
  padding: 10px 12px; font-size: 14px; font-weight: 600;
  background: #fffefa; border: 2px solid var(--accent, #691d20);
  border-radius: 8px; color: var(--text); min-height: 40px;
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.04);
}
.vb-ipv3-custom-label::placeholder { color: var(--text-dim); font-weight: 400; font-style: italic; }
.vb-ipv3-custom-label:focus {
  outline: none; border-color: var(--accent, #691d20);
  box-shadow: 0 0 0 3px rgba(105,29,32,0.18);
}
.vb-ipv3-custom-label:disabled { opacity: 0.4; cursor: not-allowed; border-style: dashed; }
.vb-ipv3-num { font-size: 24px; font-weight: 700; color: var(--accent); }
.vb-ipv3-num-sub { font-size: 12px; color: var(--text-dim); }
.vb-ipv3-cust-name { font-size: 18px; color: var(--text); margin-top: 4px; }
.vb-ipv3-header-r { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.vb-ipv3-est-row { display: flex; align-items: center; gap: 8px; }
.vb-ipv3-est { font-size: 12px; font-weight: 700; color: var(--text-dim); letter-spacing: 0.6px; }

/* Grid — left info, right operation cards, far-right the Invoices/Client
   Communications vertical-tab column (added wide to fit it; per Dariel
   2026-08-05 — "make the popup wider to the right, invoices its own
   vertical tab"). */
.vb-ipv3-grid {
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(280px, 360px);
  gap: 14px; align-items: start;
}
.vb-ipv3-left, .vb-ipv3-right { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

@media (max-width: 1180px) {
  .vb-ipv3-grid { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .vb-ipv3-grid { grid-template-columns: 1fr; }
}
/* Phones — the install popup fills the width and sizes to its content (no forced
   full-height = no big empty gap), clears the iOS status bar, and its footer
   wraps inside the screen. The two columns already stack at 900px. Per Dariel
   2026-06-29. */
@media (max-width: 640px) {
  .vb-ipv3-overlay { padding: 0; align-items: flex-start; }
  .vb-ipv3-card {
    max-width: 100%; width: 100%;
    height: auto; max-height: 100dvh;            /* fit content, cap at screen */
    border-radius: 0;
    padding-top: env(safe-area-inset-top, 0px);  /* clear notch / status bar */
  }
  .vb-ipv3-body { padding: 12px 12px 0; }
  .vb-ipv3-header { padding: 4px 44px 10px 4px; }
  .vb-ipv3-num { font-size: 20px; }
  .vb-ipv3-close { top: calc(8px + env(safe-area-inset-top, 0px)); right: 10px; width: 36px; height: 36px; }
  /* Not a footer — flows at the bottom of the content. Two buttons per row so
     none clip off the right; comfortably tappable. Per Dariel 2026-06-29. */
  .vb-ipv3-actionbar { gap: 8px; }
  .vb-ipv3-actionbar .vb-ipv3-btn {
    flex: 1 1 calc(50% - 4px); min-width: 0; justify-content: center;
    padding: 12px 8px; font-size: 14px;
  }
  /* Confirmation send/log buttons carry inline sizing → override so they're
     comfortably tappable on a phone. */
  [data-action^="cq-"] { font-size: 13px !important; padding: 10px 12px !important; }
}

/* Sections (left column) ------------------------------------------------- */
.vb-ipv3-section {
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px;
}
.vb-ipv3-section-title {
  font-size: 11px; font-weight: 700; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 8px;
}
.vb-ipv3-empty { font-size: 12px; color: var(--text-dim); font-style: italic; padding: 4px 0; }
.vb-ipv3-muted { color: var(--text-dim); }

.vb-ipv3-info-row {
  font-size: 13px; color: var(--text); padding: 6px 12px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.vb-ipv3-info-row strong { color: var(--text); }

.vb-ipv3-addr { font-size: 14px; color: var(--text); padding: 4px 0; }
.vb-ipv3-addr a { color: var(--accent); }

.vb-ipv3-contact-row { display: flex; gap: 14px; flex-wrap: wrap; padding: 2px 0 4px; }
.vb-ipv3-contact-link {
  font-size: 15px; color: var(--accent); font-weight: 600; text-decoration: none;
}
.vb-ipv3-contact-link:hover { text-decoration: underline; }

/* Install row 3-col grid ------------------------------------------------- */
.vb-ipv3-install-grid {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px;
}
.vb-ipv3-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.vb-ipv3-field label {
  font-size: 10px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.vb-ipv3-input, .vb-ipv3-textarea {
  background: var(--bg); color: var(--text); border: 1px solid var(--border);
  border-radius: 6px; padding: 6px 8px; font-size: 13px; font-family: inherit;
  width: 100%; box-sizing: border-box;
}
.vb-ipv3-input-sm { padding: 4px 6px; font-size: 12px; }
.vb-ipv3-textarea { min-height: 60px; resize: vertical; }
.vb-ipv3-row-actions { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.vb-ipv3-row-inline { display: flex; gap: 6px; align-items: center; }
.vb-ipv3-row-inline .vb-ipv3-input { flex: 1; }

/* Ops manager note pinned banner ----------------------------------------- */
.vb-ipv3-opsnote {
  background: rgba(37,99,235,0.06); border: 1px solid rgba(37,99,235,0.18);
  border-left: 4px solid var(--blue); border-radius: 8px; padding: 8px 12px;
}
.vb-ipv3-opsnote-label {
  font-size: 10px; font-weight: 700; color: var(--blue);
  text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 4px;
}
.vb-ipv3-opsnote-body { font-size: 13px; color: var(--text); white-space: pre-wrap; }

/* 811 line --------------------------------------------------------------- */
.vb-ipv3-811-line {
  font-size: 13px; color: var(--text); padding: 6px 12px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.vb-ipv3-ok { color: var(--green); font-weight: 700; }
.vb-ipv3-warn { color: var(--yellow); font-weight: 700; }
.vb-ipv3-bad { color: var(--red); font-weight: 700; }
.vb-ipv3-link { color: var(--blue); text-decoration: underline; cursor: pointer; }

/* SO items --------------------------------------------------------------- */
.vb-ipv3-items { display: flex; flex-direction: column; }
.vb-ipv3-item {
  display: flex; justify-content: space-between; gap: 8px;
  padding: 3px 0; font-size: 13px;
}
.vb-ipv3-item-name { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; word-break: break-word; line-height: 1.35; }
.vb-ipv3-item-qty { color: var(--text-dim); font-weight: 600; flex-shrink: 0; }
.vb-ipv3-item-canvas .vb-ipv3-item-name { color: var(--text-dim); font-style: italic; }

/* Blockers --------------------------------------------------------------- */
.vb-ipv3-blockers { background: rgba(185,28,28,0.04); border-color: rgba(185,28,28,0.22); }
.vb-ipv3-blockers .vb-ipv3-section-title { color: var(--red); }

/* Other visits ----------------------------------------------------------- */
.vb-ipv3-visit-row {
  display: flex; align-items: center; gap: 8px; padding: 4px 0;
  font-size: 13px; border-bottom: 1px dashed rgba(37,57,56,0.08);
}
.vb-ipv3-visit-row:last-child { border-bottom: none; }
.vb-ipv3-visit-date { font-weight: 600; }
.vb-ipv3-visit-state { color: var(--text-dim); text-transform: capitalize; }
/* The visit/install the popup was actually opened on — so it doesn't get lost
   in its own schedule list. Per Dariel 2026-07-27. */
.vb-ipv3-visit-row-current {
  background: rgba(105,29,32,0.07); border: 1px solid var(--accent);
  border-radius: 8px; padding: 6px 8px; margin: 2px 0;
}
.vb-ipv3-visit-current-tag { font-weight: 800; color: var(--accent); white-space: nowrap; }
.vb-ipv3-visit-clickarea[data-action="focus-install"]:hover { background: rgba(37,57,56,0.06); border-radius: 6px; }
.vb-ipv3-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.vb-ipv3-dot-green { background: var(--green); }
.vb-ipv3-dot-blue { background: var(--blue); }
.vb-ipv3-dot-yellow { background: var(--yellow); }
.vb-ipv3-dot-red { background: var(--red); }
.vb-ipv3-dot-purple { background: var(--purple); }

/* Right column — Operation cards ----------------------------------------- */
.vb-ipv3-card-op {
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; display: flex; flex-direction: column; gap: 6px;
}
.vb-ipv3-card-empty { background: rgba(255,255,255,0.55); }
.vb-ipv3-card-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vb-ipv3-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 22px; padding: 0 6px; border-radius: 4px;
  font-size: 11px; font-weight: 700; color: #fff; letter-spacing: 0.5px;
  flex-shrink: 0;
}
.vb-ipv3-badge-orange { background: #e07a3a; }
.vb-ipv3-badge-teal { background: #1f8a87; }
.vb-ipv3-badge-purple { background: #6d28d9; }
.vb-ipv3-badge-blue { background: #2563eb; }
.vb-ipv3-badge-cyan { background: #0891b2; }
.vb-ipv3-badge-green { background: #2f7a3d; }
.vb-ipv3-badge-red { background: #b91c1c; }
.vb-ipv3-card-state { font-size: 13px; font-weight: 600; }
.vb-ipv3-files-count {
  font-size: 11px; color: var(--text-dim); background: var(--bg3);
  padding: 1px 8px; border-radius: 10px;
}
.vb-ipv3-card-headbtn { margin-left: auto; }
.vb-ipv3-card-meta { font-size: 12px; color: var(--text); }
.vb-ipv3-card-targ {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  color: var(--text); font-weight: 600;
}
.vb-ipv3-card-targ input { margin-left: 4px; }
.vb-ipv3-card-empty-body {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 0;
}
.vb-ipv3-card-empty-msg { font-size: 12px; color: var(--text-dim); font-style: italic; }
.vb-ipv3-card-assign { font-size: 12px; color: var(--text-dim); }
.vb-ipv3-card-notes { font-size: 12px; color: var(--text); }
.vb-ipv3-card-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }

/* Invoices + Client Communications — its own vertical-tab column at the far
   right of the (widened) popup, sticky within the shared body scroll so it
   reads as full-height/independent instead of one more stacked card. */
.vb-ipv3-vtabcol {
  position: sticky; top: 0; align-self: start;
  min-height: 480px; max-height: calc(100vh - 140px); overflow: hidden;
  display: flex; background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
}
.vb-ipv3-vtabbar { display: flex; flex-direction: column; gap: 2px; padding: 8px 6px; border-right: 1px solid var(--border); flex-shrink: 0; }
.vb-ipv3-vtabbtn {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 68px; padding: 10px 4px; border: none; background: transparent; cursor: pointer;
  font-size: 10px; font-weight: 700; letter-spacing: .2px; color: var(--text-dim);
  border-radius: 8px; line-height: 1.25; text-align: center;
}
.vb-ipv3-vtabbtn span { white-space: normal; }
.vb-ipv3-vtabbtn.active { color: #fff; background: var(--accent); }
.vb-ipv3-vtabbody { flex: 1; min-width: 0; overflow-y: auto; padding: 12px 14px; }
.vb-ipv3-tabpane { display: none; }
.vb-ipv3-tabpane.active { display: block; }

/* Client Communications — plain sent → opened audit rows, not a chat thread. */
.vb-ipv3-sendlog { display: flex; flex-direction: column; gap: 10px; }
.vb-ipv3-sendlog-row { background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; }
.vb-ipv3-sendlog-head { display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
.vb-ipv3-sendlog-when { margin-left: auto; color: var(--text-dim); font-size: 11px; }
.vb-ipv3-sendlog-to { font-size: 12px; color: var(--text); margin-top: 3px; }
.vb-ipv3-sendlog-opened { font-size: 11px; color: #16a34a; font-weight: 700; margin-top: 5px; }
.vb-ipv3-sendlog-notopened { font-size: 11px; color: var(--text-dim); margin-top: 5px; }

@media (max-width: 1180px) {
  .vb-ipv3-vtabcol { position: static; max-height: none; }
  .vb-ipv3-vtabbody { max-height: 480px; }
}

.vb-ipv3-tag-yellow {
  display: inline-block; padding: 2px 8px; background: #fde68a; color: #78350f;
  border-radius: 4px; font-size: 12px; font-weight: 600;
}
.vb-ipv3-files { display: flex; flex-direction: column; gap: 4px; }
.vb-ipv3-file-row {
  display: block; padding: 4px 8px; background: #fef3c7; color: var(--text);
  border-radius: 4px; font-size: 12px; text-decoration: none;
}
.vb-ipv3-file-row:hover { background: #fde68a; }

/* Operations-as-parent card. Wraps nested shop rows so they read as
   "Operation #N → its shop orders". Brand burgundy left rail. */
.vb-ipv3-op-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-left: 4px solid #691d20;
  border-radius: 10px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.vb-ipv3-op-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.vb-ipv3-op-card-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
.vb-ipv3-op-card-meta {
  font-size: 11px;
  color: var(--text-dim);
  margin-left: auto;
}
.vb-ipv3-op-card-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.vb-ipv3-op-card-body .vb-ipv3-card-op {
  /* Nested shop cards inside an op card — softer border + tan tint. */
  background: rgba(241, 232, 223, 0.55);
}
.vb-ipv3-op-empty { padding: 4px 0; }
.vb-ipv3-op-addrow {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--border);
}
.vb-ipv3-op-addrow .vb-ipv3-btn {
  /* Tap targets ≥ 44px on touch (iPad). */
  min-height: 44px;
  font-size: 12px;
  font-weight: 600;
}

.vb-ipv3-photos-row { display: flex; gap: 4px; flex-wrap: wrap; }
.vb-ipv3-photo-group { margin-top: 6px; }
.vb-ipv3-photo-group-label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .4px; color: var(--muted, #888); margin-bottom: 3px;
}
.vb-ipv3-photo-group { margin-top: 6px; }
.vb-ipv3-photo-group-label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .4px; color: var(--muted, #888); margin-bottom: 3px;
}
.vb-ipv3-thumb {
  display: inline-block; width: 48px; height: 48px; border-radius: 4px;
  overflow: hidden; background: var(--bg3); border: 1px solid var(--border);
  position: relative;
}
.vb-ipv3-thumb img { width: 100%; height: 100%; object-fit: cover; }
.vb-ipv3-thumb-play {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; font-size: 16px; color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,.8); pointer-events: none;
}
.vb-ipv3-thumb-tag {
  position: absolute; left: 2px; bottom: 2px; font-size: 8px; line-height: 1;
  padding: 1px 3px; border-radius: 3px; background: rgba(0,0,0,.65);
  color: #fff; text-transform: uppercase; letter-spacing: .3px;
  pointer-events: none;
}
.vb-ipv3-upload-row { display: flex; gap: 4px; align-items: center; }
.vb-ipv3-invs { display: flex; flex-direction: column; gap: 4px; }
.vb-ipv3-inv-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 6px;
  font-size: 12px; padding: 2px 0; align-items: center;
}

/* Buttons ---------------------------------------------------------------- */
.vb-ipv3-btn {
  padding: 6px 12px; font-size: 13px; background: var(--bg);
  color: var(--text); border: 1px solid var(--border); border-radius: 6px;
  cursor: pointer; font-weight: 500; display: inline-flex; align-items: center;
  gap: 4px; text-decoration: none;
}
.vb-ipv3-btn:hover { background: var(--bg3); }
.vb-ipv3-btn:disabled { opacity: 0.55; cursor: not-allowed; }
.vb-ipv3-btn-sm { padding: 3px 8px; font-size: 12px; }
.vb-ipv3-btn-green { background: #2f7a3d; color: #fff; border-color: #2f7a3d; }
.vb-ipv3-btn-green:hover { background: #246030; }
.vb-ipv3-btn-blue { background: #2563eb; color: #fff; border-color: #2563eb; }
.vb-ipv3-btn-blue:hover { background: #1d4ed8; }
.vb-ipv3-btn-orange { background: #e07a3a; color: #fff; border-color: #e07a3a; }
.vb-ipv3-btn-orange:hover { background: #c5642a; }
.vb-ipv3-btn-red { background: #b91c1c; color: #fff; border-color: #b91c1c; }
.vb-ipv3-btn-red:hover { background: #991414; }
.vb-ipv3-btn-purple { background: #6d28d9; color: #fff; border-color: #6d28d9; }
.vb-ipv3-btn-purple:hover { background: #5b21b6; }
.vb-ipv3-btn-cyan { background: #0891b2; color: #fff; border-color: #0891b2; }
.vb-ipv3-btn-cyan:hover { background: #0e7490; }
.vb-ipv3-btn-teal { background: #1f8a87; color: #fff; border-color: #1f8a87; }

/* Sticky action bar at the bottom of the popup --------------------------- */
/* Action buttons flow at the END of the popup content — NOT a sticky footer.
   Sticky + the -18px bleed margins overflowed the mobile card and clipped the
   right-hand buttons off-screen. Per Dariel 2026-06-29. */
.vb-ipv3-actionbar {
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
  padding: 14px 0 calc(6px + env(safe-area-inset-bottom, 0px));
  margin: 16px 0 0; border-top: 1px solid var(--border);
}


/* ══════════════════════════════════════════════════════════════════════════
   Site Plan Editor (sp-*) — full-replacement port of Vilobot's site-plan-editor.
   Brand: burgundy #691d20, teal-green #253938, tan #f1e8df.
   ────────────────────────────────────────────────────────────────────────── */

/* Shell */
/* touch-action: manipulation — the canvas editor is an app-like tool, not
   scrollable content, so a double-tap ANYWHERE in it (a toolbar button, a
   field, empty chrome) should never trigger iOS Safari's native double-tap-
   zoom gesture. The viewport meta tag's user-scalable=no doesn't reliably
   stop this on modern iOS. Per Dariel 2026-07-20 ("i doube clicked th etool
   and it zoomed in... i should not be able to zoom in no matter how many
   times i click anywhere on the ipad"). */
.sp-shell { display: flex; flex-direction: column; height: calc(100dvh - 56px); margin: -16px -20px; background: var(--bg); overflow: hidden; position: relative; touch-action: manipulation; }

/* ── sp-fullscreen mode (added 2026-05-28) ───────────────────────────────
   When the site-plan editor is mounted, drafting.js adds `sp-fullscreen`
   to <body>. We:
     1. Hide the outer dashboard `.app-header` ("? canvas" white bar) —
        the sp-header already carries "Edit Site Plan — Sxxxxx".
     2. Re-flow `.app-body` so the editor lives edge-to-edge with no
        16/20px page padding eating screen height.
     3. Let `.sp-shell` claim the full viewport instead of `100dvh - 56px`.
   Result: ~50-60px of extra vertical canvas room on both iPad + desktop,
   without rewriting the dashboard layout. */
body.sp-fullscreen .app-header { display: none !important; }
body.sp-fullscreen .app-body { padding: 0 !important; overflow: hidden; }
body.sp-fullscreen .sp-shell { height: 100dvh; margin: 0; }

/* Header */
.sp-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: var(--bg2); border-bottom: 1px solid var(--border); flex-shrink: 0; gap: 12px; }
.sp-header-l { display: flex; align-items: center; gap: 12px; min-width: 0; }
.sp-back { cursor: pointer; font-size: 22px; color: var(--text-dim); padding: 4px 8px; border-radius: 6px; }
.sp-back:hover { color: var(--accent); background: rgba(105,29,32,0.06); }
.sp-title { font-size: 15px; font-weight: 700; color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sp-subtitle { font-size: 12px; color: var(--text-dim); }
.sp-header-r { display: flex; gap: 8px; flex-shrink: 0; align-items: center; }

.sp-hbtn { padding: 8px 14px; font-size: 13px; font-weight: 600; border-radius: 8px; border: 1px solid var(--border); cursor: pointer; background: var(--bg); color: var(--text); }
.sp-hbtn-secondary { background: var(--bg); }
.sp-hbtn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.sp-hbtn-primary:hover { background: var(--accent-hover); }
.sp-hbtn[disabled] { opacity: 0.5; cursor: not-allowed; }

/* Autosave indicator */
.sp-autosave { font-size: 11px; color: var(--text-dim); padding: 4px 10px; border-radius: 8px; background: transparent; transition: background 0.15s, color 0.15s; min-height: 22px; display: inline-flex; align-items: center; }
.sp-autosave.pending { color: var(--yellow); background: rgba(184,131,18,0.10); }
.sp-autosave.saving { color: var(--blue); background: rgba(37,99,235,0.10); }
.sp-autosave.saved { color: var(--green); background: rgba(47,122,61,0.10); }
.sp-autosave.error { color: var(--red); background: rgba(185,28,28,0.10); }

/* Dark top toolbar (matches Vilobot's icon-above-label style) */
.sp-toolbar { display: flex; align-items: center; gap: 4px; padding: 6px 10px; background: #1a1f26; color: #e6edf3; border-bottom: 1px solid #2d343d; flex-shrink: 0; flex-wrap: wrap; overflow-x: auto; }
.sp-toolbar::-webkit-scrollbar { height: 0; }
.sp-tb { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; min-width: 56px; padding: 5px 7px; border: none; background: transparent; color: #c9d1d9; cursor: pointer; font-size: 18px; border-radius: 6px; transition: background 0.12s, color 0.12s; -webkit-tap-highlight-color: transparent; flex-shrink: 0; touch-action: manipulation; }
.sp-tb:hover { background: #21262d; color: #fff; }
.sp-tb.on { background: var(--accent); color: #fff; }
.sp-tb .sp-tb-ic { font-size: 18px; line-height: 1; }
.sp-tb .sp-tb-lb { font-size: 10px; font-weight: 700; letter-spacing: 0.3px; color: inherit; }
.sp-tb-armed { font-size: 10px; padding: 1px 5px; background: rgba(255,255,255,0.18); border-radius: 4px; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-tb-util { color: #c9d1d9; }
.sp-sep { width: 1px; height: 32px; background: #2d343d; margin: 0 4px; flex-shrink: 0; }
.sp-cpick { width: 38px; height: 38px; padding: 0; border: 2px solid #2d343d; border-radius: 6px; background: none; cursor: pointer; flex-shrink: 0; }
.sp-cpick::-webkit-color-swatch-wrapper { padding: 0; }
.sp-cpick::-webkit-color-swatch { border: none; border-radius: 4px; }
.sp-slider { width: 100px; accent-color: var(--accent); flex-shrink: 0; }
.sp-lw-val { font-size: 12px; color: #c9d1d9; min-width: 22px; text-align: center; }

/* ── Big-button "cave-man on an iPad" rail (Dariel 2026-06-16) ──────────────
   Four fat primary buttons (House / Fence / Gate / Material) + Undo / Redo /
   Erase / More. Huge touch targets, plain words, no duplicate clutter. */
.sp-rail { display: flex; align-items: stretch; gap: 8px; flex: 1; flex-wrap: wrap; }
.sp-rail-gap { flex: 1 1 12px; min-width: 8px; }
.sp-big {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; min-width: 88px; min-height: 62px; padding: 8px 14px;
  border: 2px solid transparent; background: #2d333b; color: #e6edf3; cursor: pointer;
  border-radius: 12px; transition: background .12s, border-color .12s, transform .05s;
  -webkit-tap-highlight-color: transparent; flex-shrink: 0; touch-action: manipulation;
}
.sp-big:hover { background: #383f47; }
.sp-big:active { transform: translateY(1px); }
.sp-big.on { background: var(--accent); border-color: #fff3; color: #fff; box-shadow: 0 0 0 2px rgba(255,255,255,.08) inset; }
.sp-big .sp-big-ic { font-size: 26px; line-height: 1; }
.sp-big .sp-big-lb { font-size: 13px; font-weight: 800; letter-spacing: .3px; }
.sp-big-util { background: #21262d; color: #c9d1d9; min-width: 74px; }
.sp-big-util:hover { background: #2d333b; color: #fff; }
.sp-big-armed { margin-top: 2px; font-size: 10px; font-weight: 700; padding: 1px 6px; background: rgba(255,255,255,.18); border-radius: 5px; max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* "More" popover — dark sheet of every legacy tool + color/width/image/books */
.sp-more-pop { position: fixed; right: 12px; z-index: 1200; width: min(360px, 92vw);
  background: #1a1f26; border: 1px solid #2d343d; border-radius: 14px; padding: 12px;
  box-shadow: 0 18px 48px rgba(0,0,0,.5); color: #e6edf3; }
.sp-more-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.sp-more-b { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 58px; padding: 6px 4px; border: 1px solid #2d343d; background: #21262d; color: #c9d1d9;
  border-radius: 10px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.sp-more-b:hover { background: #2d333b; color: #fff; }
.sp-more-b.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.sp-more-b .sp-more-ic { font-size: 20px; line-height: 1; }
.sp-more-b .sp-more-lb { font-size: 10px; font-weight: 700; text-align: center; }
.sp-more-row { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
.sp-more-ctl { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 600; color: #c9d1d9; }
.sp-more-ctl input[type=color] { width: 34px; height: 34px; padding: 0; border: 2px solid #2d343d; border-radius: 6px; background: none; cursor: pointer; }
.sp-more-ctl input[type=range] { width: 110px; accent-color: var(--accent); }
.sp-more-wide { flex: 1 1 140px; min-height: 44px; border: 1px solid #2d343d; background: #21262d; color: #e6edf3; border-radius: 10px; font-size: 13px; font-weight: 700; cursor: pointer; }
.sp-more-wide:hover { background: #2d333b; }

/* ── Guided Fence + Gate flow popups (big, one-step) ───────────────────────── */
.sp-flow { width: min(420px, 94vw); }
.sp-flow-h { margin: 0 0 14px; font-size: 20px; font-weight: 800; }
.sp-flow-lbl { font-size: 13px; font-weight: 700; color: var(--text-dim); margin: 14px 0 6px; }
.sp-flow-lbl small { font-weight: 500; opacity: .8; }
/* Numbered checklist sections — a thin rule between each top-level section
   of the Gate/Fence Run popups, with the section's own label prefixed by a
   step number, so a rep can visually track "what stops here, what's next"
   working straight down the sheet instead of it reading as one undivided
   block. Per Dariel 2026-07-28 ("separate the sections with a simple
   horizontal line... number from 1... so they kinda go through it as a
   checklist, don't forget"). */
/* The divider is a border on the step label ITSELF, not a separate <hr> —
   that absorbs into the label's own existing top margin instead of adding
   a whole new element's worth of height on top of it, which matters on
   the Gate popup specifically (its 900px/819px height budget was already
   fully spent fitting every option without scrolling). */
.sp-flow-step { display: flex; align-items: center; gap: 8px; }
.sp-flow-step.sp-flow-step-div { border-top: 1px solid var(--border); padding-top: 8px; }
.sp-flow-step-num {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 18px; height: 18px; border-radius: 50%; background: var(--accent);
  color: #fff; font-size: 10.5px; font-weight: 800; line-height: 1;
}
#sp-sheet[data-kind="gate"] .sp-flow-step-num,
#sp-sheet[data-kind="fence"] .sp-flow-step-num { width: 16px; height: 16px; font-size: 9.5px; }
#sp-sheet[data-kind="gate"] .sp-flow-step.sp-flow-step-div,
#sp-sheet[data-kind="fence"] .sp-flow-step.sp-flow-step-div { padding-top: 2px; }
/* Gate is already at its 900px/819px height budget (every option shown at
   once) — claw back the ~20-30px the new dividers add across ~5 sections
   by trimming the toolgrid gap and post-toggle top margins slightly
   further. Per Dariel 2026-07-28. */
#sp-sheet[data-kind="gate"] .sp-typegrid { gap: 4px; }
#sp-sheet[data-kind="gate"] .sp-gate-colors { margin-top: 2px; }
.sp-flow-fld { display: block; }
.sp-flow-fld .sp-flow-lbl { margin-top: 0; }
.sp-flow-num { width: 100%; box-sizing: border-box; font-size: 30px; font-weight: 800; text-align: center;
  padding: 14px; border: 2px solid var(--border); border-radius: 12px; background: var(--bg); color: var(--text);
  min-height: 44px; }
.sp-flow-num:focus { outline: none; border-color: var(--accent); }
.sp-flow-pick { width: 100%; box-sizing: border-box; margin-top: 14px; display: flex; align-items: center; gap: 12px;
  min-height: 60px; padding: 12px 16px; border: 2px dashed var(--border); border-radius: 12px;
  background: var(--bg-subtle, rgba(0,0,0,.03)); color: var(--text); font-size: 16px; font-weight: 700; cursor: pointer; text-align: left; }
.sp-flow-pick:hover { border-color: var(--accent); }
.sp-flow-pick.on { border-style: solid; border-color: var(--green); background: rgba(47,122,61,.08); }
.sp-flow-pick-ic { font-size: 26px; }
.sp-flow-pick-lb { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Sticky footer — pinned to the bottom of the sheet's own scroll area so
   Save/Cancel stay reachable on a long form (Gate has ~15 fields) without
   scrolling to hunt for them, and the button row never drifts as the
   on-screen keyboard/focus-scroll nudge the sheet's scroll position. Per
   Dariel 2026-07-21 ("the gate... so long i have to scroll... need to
   scroll to see the save... it be so high up it doesnt move around"). */
.sp-flow-actions {
  display: flex; gap: 10px; margin-top: 20px;
  position: sticky; bottom: 0; z-index: 5;
  background: var(--bg, #fff);
  padding: 10px 0 2px;
  box-shadow: 0 -8px 16px -8px rgba(0,0,0,.18);
}
.sp-flow-skip { flex: 0 0 auto; min-height: 54px; padding: 0 20px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg); color: var(--text-dim); font-size: 15px; font-weight: 700; cursor: pointer; }
.sp-flow-done { flex: 1; min-height: 54px; border: none; border-radius: 12px; background: var(--accent); color: #fff; font-size: 18px; font-weight: 800; cursor: pointer; }
.sp-flow-done:hover { background: var(--accent-hover); }

/* Segmented toggles (Side/End, In/Out) — big, obvious */
.sp-seg { display: flex; gap: 8px; }
.sp-seg-b { flex: 1; min-height: 54px; border: 2px solid var(--border); border-radius: 12px; background: var(--bg);
  color: var(--text); font-size: 16px; font-weight: 800; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.sp-seg-b:hover { border-color: var(--accent); }
.sp-seg-b.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.sp-gate-colors { display: flex; flex-wrap: wrap; gap: 8px; }
.sp-gate-color { min-width: 64px; min-height: 44px; padding: 0 12px; border: 2px solid rgba(0,0,0,.15); border-radius: 10px;
  font-size: 13px; font-weight: 800; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.sp-gate-color.on { outline: 3px solid var(--accent); outline-offset: 1px; }
.sp-dock-div { width: 2px; align-self: stretch; background: #2d343d; margin: 2px 6px; border-radius: 2px; }

/* ── Bottom-sheet system (iPad-first slide-up) ─────────────────────────────── */
/* Flexbox-centered (not left:50%+translate(-50%,-50%)) — that combo lands the
   sheet at a fractional sub-pixel offset on an odd-width viewport, which
   Chromium doesn't always snap to the pixel grid, so every popup rendered
   visibly blurry (text + borders soft, worst on the Gate popup). Flexbox
   centering needs no percentage/translate math at all, so there's nothing to
   land off-grid. Per Dariel 2026-07-14 ("why are the popups blurry, the gate
   also — fix this"). */
.sp-sheet-scrim {
  /* fixed, not absolute — its top/height get overridden inline by JS to
     visualViewport bounds so the sheet stays reachable in the VISIBLE area
     when the on-screen keyboard is up (iPadOS doesn't shrink the layout
     viewport for the keyboard, so a plain 100dvh-anchored popup landed half
     behind it). See openSheet()/closeSheet() in drafting.js. Per Dariel
     2026-07-15. Anchored near the TOP (not vertically centered) — --vb-
     toolbar-h is the live toolbar height (set in drafting.js), so the sheet
     opens right under the tools instead of floating over the middle of the
     canvas. Per Dariel 2026-07-20 ("add the pop up... at the top of the
     screen right under the tools instead of the bottom of the canvas").
  */
  position: fixed; inset: 0; background: rgba(0,0,0,.38); z-index: 1400;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 16px; padding-top: var(--vb-toolbar-h, 140px); box-sizing: border-box; overflow-y: auto;
}
/* NO keyboard-reactive repositioning — there USED to be a rule here that
   dropped the scrim's padding-top to 12px whenever the keyboard opened
   (:root.vb-kbd-open), reclaiming the toolbar clearance for tall sheets.
   REMOVED: any rule that moves/resizes a popup on keyboard state can land
   its reflow between a tap's pointerdown and click, misrouting the tap to
   whatever shifted underneath ("click Width, it changes Color; click
   Description, the popup closes"). Popups must be COMPLETELY inert to the
   keyboard: top-anchored at a height that never changes at runtime, with
   internal overflow-y scrolling + the focusin auto-scroll (see
   initKeyboardAwarePopups) covering fields the keyboard hides. Same
   reasoning as the Gate/Fence pins below, applied to every sheet. Per
   Dariel 2026-07-21 ("make sure no button in any scenario triggers this"). */
.sp-sheet {
  /* Horizontal-only auto margin — a vertical `auto` here is a flexbox
     centering trick that WINS over the scrim's `align-items: flex-start`
     regardless of that setting, which is why the top-anchor fix above never
     actually took effect (the sheet still centered). Per Dariel 2026-07-20. */
  position: relative; z-index: 1401; margin: 0 auto;
  background: var(--bg, #fff); color: var(--text, #1a1a1a);
  border-radius: 20px;
  box-shadow: 0 16px 48px rgba(0,0,0,.30); padding: 16px 20px;
  /* Wide, not the old 560px cap — a wide sheet lets long content (Gate's
     type/opens/hinge grids and color swatches, a run's Description
     textarea) use horizontal room instead of stacking, so the sheet reads
     shorter top-to-bottom. Anchored near the top already; the goal is that
     the keyboard opening never has to cover the Save button because the
     sheet was too tall in the first place. The short "new run, no material
     picked yet" sheet doesn't have enough content to fill this width and
     stays exactly as short as before. Per Dariel 2026-07-20 ("make the gate
     pop up full width... nothing needs to move... make the other ones just
     wider, there's plenty of space").
  */
  width: min(900px, 96vw);
  /* FIXED height cap — deliberately NOT --vb-vvh (the live visualViewport
     height). Sizing off the keyboard-tracking var meant every keyboard
     open/close/layout-switch resized the sheet mid-tap and misrouted the
     tap (see the scrim comment above). Plain vh is inert: iPadOS never
     shrinks the LAYOUT viewport for the keyboard. Fields the keyboard
     covers are reached via the sheet's own scrolling + the focusin
     auto-scroll. Per Dariel 2026-07-21. 720px left a large unused gap
     above the keyboard on a 12.9" iPad Pro portrait (~1850px tall) —
     raised to 860px; the 88vh half of the min() still governs on
     shorter screens, so this only grows the sheet on tall ones. Per
     Dariel 2026-07-27 ("make the fence panel a little taller... you can
     see there's a little space left before keyboard"). */
  max-height: min(88vh, 860px);
  overflow-y: auto;
  transform: scale(.96); opacity: 0;
  transition: transform .15s ease-out, opacity .15s ease-out;
}
.sp-sheet.sp-sheet-up { transform: scale(1); opacity: 1; }
/* While any bottom sheet is open, the Legend slide-up + its FAB are force-hidden
   so they can never show behind/under the sheet (Dariel: "why the legend open"). */
.sp-shell.sp-sheet-open .sp-legend-bar,
.sp-shell.sp-sheet-open .sp-legend-fab { display: none !important; }
.sp-sheet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.sp-sheet-title { font-size: 20px; font-weight: 800; }
.sp-sheet-x { width: 44px; height: 44px; border-radius: 50%; border: none; background: var(--bg-subtle, rgba(0,0,0,.06)); color: var(--text); font-size: 20px; cursor: pointer; }
.sp-sheet-x:active { background: rgba(0,0,0,.12); }

/* Options sheet — fat full-width rows */
.sp-opt-list { display: flex; flex-direction: column; gap: 10px; }
.sp-opt-row { display: flex; align-items: center; gap: 14px; width: 100%; min-height: 60px; padding: 0 18px;
  border: 1.5px solid var(--border); border-radius: 14px; background: var(--bg); color: var(--text);
  font-size: 16px; font-weight: 600; cursor: pointer; -webkit-tap-highlight-color: transparent; text-align: left; }
.sp-opt-row:active { background: var(--bg-subtle, rgba(0,0,0,.04)); }
.sp-opt-ic { font-size: 22px; width: 28px; text-align: center; }
.sp-opt-lb { flex: 1; }
.sp-opt-val { font-size: 15px; font-weight: 800; color: var(--text-dim); }
.sp-opt-row.is-green .sp-opt-val { color: var(--green); }
.sp-opt-row.is-yellow .sp-opt-val { color: var(--yellow); }
.sp-opt-row.is-danger { border-color: rgba(185,28,28,.4); color: var(--red); }
.sp-opt-row.is-danger .sp-opt-ic { color: var(--red); }

/* ── In-app number pad (no iOS keyboard) ───────────────────────────────────── */
.sp-pad-wrap { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
.sp-pad-left { flex: 1 1 240px; min-width: 220px; }
.sp-feet { display: flex; align-items: baseline; justify-content: center; gap: 8px;
  border: 2px solid var(--border); border-radius: 14px; padding: 14px; background: var(--bg-subtle, rgba(0,0,0,.03)); }
.sp-feet-num { font-size: 40px; font-weight: 800; color: var(--text); min-width: 1ch; }
.sp-feet-unit { font-size: 18px; font-weight: 700; color: var(--text-dim); }
.sp-keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 12px; flex: 0 0 264px; }
.sp-key { min-height: 62px; border: none; border-radius: 14px; background: var(--bg-subtle, rgba(0,0,0,.05)); color: var(--text);
  font-size: 26px; font-weight: 700; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.sp-key:active { background: rgba(0,0,0,.12); }
.sp-key-del { background: rgba(185,28,28,.10); color: var(--red); font-size: 22px; }
.sp-chk-big { display: flex; align-items: center; gap: 12px; margin-top: 12px; padding: 12px 14px; border: 1.5px solid var(--border); border-radius: 12px; font-size: 15px; font-weight: 600; color: var(--text); cursor: pointer; }
.sp-chk-big input { width: 22px; height: 22px; accent-color: var(--accent); }
.sp-flow-desc { width: 100%; box-sizing: border-box; margin-top: 4px; border: 1.5px solid var(--border); border-radius: 12px; padding: 10px 12px; font: inherit; font-size: 15px; resize: vertical; background: var(--bg); color: var(--text); }
.sp-flow-desc:focus { outline: none; border-color: var(--accent); }
.sp-typegrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.sp-flow-text { width: 100%; box-sizing: border-box; font-size: 18px; padding: 12px 14px; border: 2px solid var(--border); border-radius: 12px; background: var(--bg); color: var(--text); }
.sp-flow-text:focus { outline: none; border-color: var(--accent); }
.sp-flow-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.sp-flow-row2 .sp-chk-big { margin-top: 0; }
.sp-gate-w-row { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 8px; align-items: end; }
.sp-gate-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

/* ── Gate popup — compact layout ─────────────────────────────────────────────
   Scoped to #sp-sheet[data-kind="gate"] only, so Edit run/Repair wizard/
   Catalog etc. (which share these same base classes) stay exactly as they
   are — only Gate gets denser labels/buttons/fields. Combined with the
   sp-gate-w-row/sp-gate-2col rows above (Width+Units+Measured, Title+Price
   each on one row instead of stacked) and Gate type now a single row of 4
   (grid-template-columns override below) instead of a 2x2 grid, this cuts
   the popup to roughly half its previous height. The scrim override further
   down pins Gate to a small fixed top offset regardless of keyboard state —
   a short-enough sheet never needs the keyboard-open padding shrink other
   (taller) sheets still rely on, so it can just stay put instead of jumping
   when the keyboard opens. Per Dariel 2026-07-21 ("half the vertical size...
   pinned to the top... the keyboard doesn't move it at all").
*/
#sp-sheet[data-kind="gate"] .sp-typegrid { grid-template-columns: repeat(4, 1fr); gap: 6px; }
/* min-height 40->36 and the label/num trims below claw back ~35px total —
   the difference between fitting and NOT fitting under the 900px cap in
   iPad landscape (1024pt tall, 80vh=819px) with every optional row shown
   at once (teardown+slab+full track+driveway+exempt, ~844px unshrunk).
   Per Dariel 2026-07-27 ("no scrolling at all even if all options are
   selected"). */
#sp-sheet[data-kind="gate"] .sp-seg-b { min-height: 36px; font-size: 13px; border-radius: 10px; }
#sp-sheet[data-kind="gate"] .sp-flow-lbl { margin: 3px 0 2px; font-size: 11.5px; }
#sp-sheet[data-kind="gate"] .sp-flow-fld .sp-flow-lbl { margin-top: 0; }
#sp-sheet[data-kind="gate"] .sp-flow-num { font-size: 20px; padding: 6px; }
#sp-sheet[data-kind="gate"] .sp-flow-pick { min-height: 44px; padding: 8px 14px; margin-top: 6px; }
#sp-sheet[data-kind="gate"] .sp-flow-pick-ic { font-size: 20px; }
#sp-sheet[data-kind="gate"] .sp-gate-colors { gap: 6px; }
#sp-sheet[data-kind="gate"] .sp-gate-color { min-width: 52px; min-height: 34px; padding: 0 8px; font-size: 12px; }
#sp-sheet[data-kind="gate"] .sp-chk-big { padding: 8px 12px; margin-top: 6px; font-size: 13px; gap: 8px; }
#sp-sheet[data-kind="gate"] .sp-chk-big input { width: 18px; height: 18px; }
#sp-sheet[data-kind="gate"] .sp-flow-desc { padding: 6px 10px; font-size: 13px; margin-top: 2px; }
#sp-sheet[data-kind="gate"] .sp-flow-text { font-size: 15px; padding: 8px 12px; }
/* Fixed near-top offset, keyboard state notwithstanding — see comment block
   above. Both selectors needed: base rule for keyboard-closed, and this one
   again to also beat the .vb-kbd-open variant once the keyboard opens. */
.sp-sheet-scrim:has(#sp-sheet[data-kind="gate"]) { padding-top: 16px; overflow: hidden; }
:root.vb-kbd-open .sp-sheet-scrim:has(#sp-sheet[data-kind="gate"]) { padding-top: 16px; overflow: hidden; }
/* Fixed max-height too, NOT the --vb-vvh (live visualViewport height) the
   base .sp-sheet rule uses — that var shrinks by a LOT more when the Width
   field switches from the numeric keypad (Feet) to the full QWERTY keyboard
   (In, since fractions need "/" and a space), so switching units resized
   the sheet out from under an in-flight tap and misrouted it to whatever
   was newly underneath — "click Width, it changes Color; click Description,
   the whole popup closes." Plain vh is immune: iPadOS never shrinks the
   LAYOUT viewport for the keyboard, only the visual one --vb-vvh tracks.
   Per Dariel 2026-07-21 ("the moment I changed from feet to inches... I
   click anywhere and it just clicks randomly... why do buttons shift").
*/
/* 640px was sized for the gate popup as it stood in 2026-07-21 — the slab
   and driveway sections added since then push the fully-expanded content
   (item + slab + full-track + driveway + exempt) to ~820px, well past that
   ceiling, forcing a scroll no amount of row-consolidation alone could fix.
   Raised to clear that on a real device (iPad Pro's 80vh is far above
   820px); short screens still fall back to the 80vh side of the min().
   Per Dariel 2026-07-27 ("no scrolling at all even if all options are
   selected"). */
#sp-sheet[data-kind="gate"] { max-height: min(80vh, 900px) !important; }

/* ── Fence run popup — same compact/pinned treatment as Gate ────────────────
   Line type now a single row of 4 (was a 2x2 grid) and everything else
   denser, same idea as the Gate rules above. The 2x2 grid mattered for a
   second reason beyond height: "Edit run" auto-focuses the Length field
   30ms after opening (see openFenceFlow in drafting.js), which pops the
   keyboard immediately — on a TALL, un-pinned sheet that shift happens
   WHILE the rep's first tap on a Line-type button is still in flight, so
   "New fence + teardown" (top row) can resolve as "Teardown" (the button
   directly below it) once the keyboard-open reflow lands mid-tap. Pinning
   the top offset (like Gate) removes that reflow entirely, and the 4-across
   row removes the "row below" a tap could even land on. Per Dariel
   2026-07-21 ("do the same for the normal fence popup as you did for the
   gate so it doesn't have to be pushed up every time I click to type
   something... it's clicking the one right under it").
*/
#sp-sheet[data-kind="fence"] .sp-typegrid { grid-template-columns: repeat(4, 1fr); gap: 6px; }
#sp-sheet[data-kind="fence"] .sp-seg-b { min-height: 40px; font-size: 12px; border-radius: 10px; padding: 2px 4px; }
/* Existing-fence material picker: 5 options, all on one row (not 4+1
   wrapping to a lonely "Steel" row below). Per Dariel 2026-07-27. */
#sp-sheet[data-kind="fence"] #sp-ff-existmat.sp-typegrid { grid-template-columns: repeat(5, 1fr); gap: 4px; }
#sp-sheet[data-kind="fence"] #sp-ff-existmat .sp-seg-b { font-size: 10.5px; padding: 2px 2px; min-height: 36px; }
#sp-sheet[data-kind="fence"] .sp-flow-lbl { margin: 6px 0 3px; font-size: 11.5px; }
#sp-sheet[data-kind="fence"] .sp-flow-fld .sp-flow-lbl { margin-top: 0; }
#sp-sheet[data-kind="fence"] .sp-flow-num { font-size: 20px; padding: 8px; }
#sp-sheet[data-kind="fence"] .sp-flow-pick { min-height: 44px; padding: 8px 14px; margin-top: 0; }
#sp-sheet[data-kind="fence"] .sp-flow-pick-ic { font-size: 20px; }
#sp-sheet[data-kind="fence"] .sp-flow-desc { padding: 6px 10px; font-size: 13px; margin-top: 2px; }
#sp-sheet[data-kind="fence"] .sp-flow-text { font-size: 15px; padding: 8px 12px; }
.sp-sheet-scrim:has(#sp-sheet[data-kind="fence"]) { padding-top: 16px; overflow: hidden; }
:root.vb-kbd-open .sp-sheet-scrim:has(#sp-sheet[data-kind="fence"]) { padding-top: 16px; overflow: hidden; }
/* Same fixed max-height decouple as Gate above — see its comment. Raised to
   match Gate's 900px once the numbered steps + Photo thumbnail pushed this
   popup's content past the old 640px cap. Per Dariel 2026-07-28 ("still
   have to scroll"). */
#sp-sheet[data-kind="fence"] { max-height: min(80vh, 900px) !important; }

/* Bottom chip bar (Survey/Clearing/Undo/Redo/Clear/Comm/Labor + zoom) */
.sp-bottombar { display: flex; align-items: center; gap: 6px; padding: 6px 10px; background: #22272e; color: #c9d1d9; border-bottom: 1px solid #2d343d; flex-shrink: 0; flex-wrap: wrap; }
.sp-chip { padding: 6px 12px; border-radius: 16px; border: 1px solid #2d343d; background: #2d333b; color: #c9d1d9; font-size: 12px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; -webkit-tap-highlight-color: transparent; flex-shrink: 0; }
.sp-chip:hover { background: #383f47; color: #fff; }
.sp-chip .sp-chip-g { font-weight: 700; }
.sp-chip.set-green { background: #1a3a1f; color: #3fb950; border-color: #3fb95055; }
.sp-chip.set-yellow { background: #2a2210; color: #d29922; border-color: #d2992288; }
.sp-chip.set-cyan   { background: #08313a; color: #1f9ece; border-color: #1f9ece88; }
.sp-chip.set-red    { background: #3a1518; color: #f85149; border-color: #f8514988; }
.sp-chip.set-neutral { background: #2d333b; color: #8b949e; }
.sp-chip.sp-chip-danger:hover { background: #b91c1c; color: #fff; border-color: #b91c1c; }
.sp-bb-sep { flex: 1; }
.sp-zoom-slider { width: 110px; accent-color: var(--accent); }
.sp-zm-val { font-size: 12px; min-width: 38px; text-align: center; color: #c9d1d9; }

/* Warning banner */
.sp-banner { padding: 10px 18px; background: rgba(240,131,62,0.14); border-bottom: 1px solid rgba(240,131,62,0.45); color: #c0651b; font-size: 13px; font-weight: 600; text-align: center; flex-shrink: 0; }
.sp-banner strong { color: #b85a10; }

/* Body */
.sp-body { flex: 1; display: flex; min-height: 0; overflow: hidden; }
/* Legend-below variant — main+legend stack vertically.
   PREVIOUS layout pinned the legend at the bottom of the canvas viewport
   with `position: sticky` + `max-height: 30vh`, which always ate 30% of
   the screen even when the user didn't need the legend. Per user request
   (2026-05-28), the legend now sits BELOW the canvas in the document
   flow: the canvas fills the viewport, and scrolling the body reveals
   the legend underneath. More canvas room on iPad + desktop, legend is
   still one swipe away. */
.sp-body-vstack { flex-direction: column; overflow: hidden; }
/* sp-main claims the full visible body height. The legend is a tap-open
   slide-up sheet anchored to the editor shell (see .sp-legend-bar.
   sp-legend-open below) — earlier "scroll to reveal" UX was unreachable
   on iPad because .sp-cw uses touch-action:none for drawing and the
   swipe never bubbled up. */
.sp-body-vstack .sp-main { flex: 1 1 auto; min-height: 0; }
/* Legend sheet — pinned to bottom of the editor shell. Hidden by default
   (translated down out of view), slides up when .sp-legend-open is on.
   Capped at 60% of viewport so the canvas behind stays visible. */
.sp-legend-bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  max-height: 60vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--bg);
  border-top: 1px solid var(--border);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom)) 14px;
  box-shadow: 0 -8px 24px rgba(0,0,0,0.12);
  transform: translateY(100%);
  transition: transform 220ms ease-out;
  z-index: 5;
}
.sp-legend-bar.sp-legend-open { transform: translateY(0); }
/* FAB to open the legend sheet — bottom-right of the editor body. Stays
   visible at all times when the sheet is CLOSED; hides while open to
   avoid covering the close affordance. */
.sp-legend-fab {
  position: absolute;
  right: 14px;
  bottom: calc(14px + env(safe-area-inset-bottom));
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  background: var(--red, #691d20);
  color: #fff;
  border: none;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  box-shadow: 0 6px 18px rgba(0,0,0,0.22);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.sp-legend-fab:active { transform: translateY(1px); }
.sp-legend-fab .sp-legend-fab-count {
  display: inline-block;
  min-width: 22px;
  padding: 2px 6px;
  border-radius: 11px;
  background: rgba(255,255,255,0.18);
  font-size: 12px;
  font-weight: 800;
  text-align: center;
}
.sp-legend-fab[hidden] { display: none; }
/* Close (X) button on the legend sheet header. */
.sp-legend-close {
  position: absolute;
  top: 8px;
  right: 10px;
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
  font-size: 22px;
  line-height: 1;
  color: var(--text);
  cursor: pointer;
}
.sp-legend-close:active { opacity: 0.6; }
.sp-legend-bar .sp-panel-title { margin-bottom: 6px; padding-right: 36px; /* leave room for the close × */ }
.sp-legend-bar .sp-legend {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 6px;
}
/* Grand-totals header — spans the full row in grid view so the user sees
   the one-line summary above the per-letter cards. */
.sp-legend-bar .sp-legend .sp-legend-totals { grid-column: 1 / -1; }

/* Grand-totals row */
.sp-legend-totals {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 6px;
  position: sticky;
  top: 0;
  z-index: 1;
}
.sp-legend-totals-label { font-size: 11px; font-weight: 800; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.6px; }
.sp-legend-totals-chips { display: flex; flex-wrap: wrap; gap: 4px; flex: 1; }
.sp-legend-totals-price { font-size: 14px; font-weight: 800; color: var(--green); }

/* Per-letter state chips — colored to match marker visuals: blue = normal,
   red = teardown, orange = optional. Sized for thumb-tap on iPad. */
.sp-legend-chips { display: flex; flex-wrap: wrap; gap: 3px; align-items: center; flex-shrink: 0; max-width: 140px; justify-content: flex-end; }
.sp-legend-chip {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 10px;
  white-space: nowrap;
  line-height: 1.3;
}
.sp-chip-count  { background: #eef2f7; color: var(--text); border: 1px solid #d6dde5; }
.sp-chip-normal { background: #dbeafe; color: #1e4ea0; border: 1px solid #aaccff; }
.sp-chip-td     { background: #fde2e2; color: var(--red); border: 1px solid #f5b5b5; }
.sp-chip-opt    { background: #ffe5cc; color: #b15c00; border: 1px solid #ffc890; }

/* iPad portrait: legend cards stack single-column so the chip stack fits
   without wrapping into a second line. Bigger tap targets too. */
@media (max-width: 900px) {
  .sp-legend-bar .sp-legend { grid-template-columns: 1fr; }
  .sp-legend-item { padding: 10px; }
  .sp-legend-chip { font-size: 11px; padding: 3px 8px; }
}

/* Main */
.sp-main { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--bg); position: relative; }
.sp-tabs { display: flex; gap: 4px; padding: 6px 12px 0; background: var(--bg2); border-bottom: 1px solid var(--border); overflow-x: auto; flex-shrink: 0; align-items: flex-end; }
.sp-tab { padding: 8px 16px; font-size: 13px; font-weight: 600; color: var(--text-dim); cursor: pointer; border-bottom: 3px solid transparent; white-space: nowrap; transition: all 0.15s; }
.sp-tab:hover { color: var(--text); }
.sp-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.sp-tab-new { padding: 6px 12px; font-size: 12px; color: var(--text-dim); background: none; border: 1px dashed var(--border); border-radius: 6px; cursor: pointer; margin-left: 8px; margin-bottom: 4px; }
.sp-tab-new:hover { color: var(--accent); border-color: var(--accent); }

/* Inline plan-tabs variant — lives at the right edge of the dark toolbar
   instead of its own row above the canvas. Saves ~36px of vertical space
   and reuses the empty horizontal area to the right of Pen/Line/Books/etc. */
.sp-tabs.sp-tabs-inline {
  margin-left: auto;
  background: transparent;
  border-bottom: none;
  padding: 0;
  align-items: center;
  gap: 4px;
}
.sp-tabs.sp-tabs-inline .sp-tab {
  padding: 4px 10px;
  font-size: 12px;
  color: #c9d1d9;
  border-bottom: none;
  border-radius: 6px;
  background: rgba(255,255,255,0.04);
  border: 1px solid transparent;
}
.sp-tabs.sp-tabs-inline .sp-tab:hover { background: rgba(255,255,255,0.08); color: #fff; }
.sp-tabs.sp-tabs-inline .sp-tab.active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.sp-tabs.sp-tabs-inline .sp-tab-close { color: rgba(255,255,255,0.7) !important; }
.sp-tabs.sp-tabs-inline .sp-tab.active .sp-tab-close { color: rgba(255,255,255,0.9) !important; }
.sp-tabs.sp-tabs-inline .sp-tab-new {
  margin: 0 0 0 4px;
  padding: 4px 10px;
  font-size: 12px;
  color: #c9d1d9;
  border: 1px dashed rgba(255,255,255,0.35);
  border-radius: 6px;
}
.sp-tabs.sp-tabs-inline .sp-tab-new:hover { color: #fff; border-color: #fff; }

/* Canvas wrapper — touch-action: none across the board.
   Per Dariel 2026-06-01 (multiple reports): tapping titles wouldn't
   register, dragging titles stopped after ~5px, tapping the canvas to
   place an armed product did nothing, Alejandro was "completely locked"
   trying to add. All same root cause: iOS Safari was treating the canvas
   as a pan-y scroll surface and hijacking every touch at the browser
   layer BEFORE our pointer handlers could fire. The previous
   `.sp-cw.sp-dragging` class-flip ran AFTER pointerdown set the drag
   state, but touch-action is evaluated at gesture START — too late to
   un-hijack.
   The canvas isn't a scroll surface (parent shell scrolls); blocking
   touch-action entirely keeps every touch on our pointermove handler. */
.sp-cw { flex: 1; position: relative; overflow: hidden; background: #f0e7d8; touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; min-height: 400px; }
.sp-cw.tool-active { touch-action: none; }
.sp-cw.sp-dragging { touch-action: none; }
.sp-cw canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: block; }

/* Floating tool-hint pill (top center) */
.sp-hint { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); background: var(--accent); color: #fff; padding: 8px 18px; border-radius: 24px; font-size: 13px; font-weight: 600; z-index: 30; box-shadow: 0 4px 12px rgba(105,29,32,0.35); white-space: nowrap; pointer-events: auto; }
.sp-hint-x { display: inline-block; margin-left: 10px; padding: 2px 10px; background: rgba(255,255,255,0.22); border-radius: 12px; font-size: 11px; cursor: pointer; }

/* Floating deselect button */
/* top was a fixed 110px guess at "just below the toolbar" — the site-plan
   tabs (Site Plan 1 | + New Plan) live INSIDE that same toolbar row,
   right-aligned, so on a narrower viewport or with enough plans open, the
   tabs could shift under this fixed spot and eat its taps. Anchored to the
   toolbar's real live height instead so it always sits clear below the
   whole row. Per Dariel 2026-07-20 ("when the x button is... over the new
   site plan button... it ignores it and clicks the add site plan"). */
.sp-deselect { position: fixed; top: calc(var(--vb-toolbar-h, 140px) + 4px); right: 16px; z-index: 99999; background: #ef4444; color: #fff; border: 3px solid #fff; border-radius: 50%; width: 50px; height: 50px; font-size: 22px; font-weight: 800; cursor: pointer; box-shadow: 0 4px 18px rgba(239,68,68,0.6); display: flex; align-items: center; justify-content: center; -webkit-tap-highlight-color: transparent; }

/* Inline drawing-delete popover */
.sp-popover { position: absolute; background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 8px; min-width: 160px; box-shadow: 0 6px 20px rgba(37,57,56,0.18); z-index: 30; display: flex; flex-direction: column; gap: 6px; }
.sp-pop-row { font-size: 13px; padding: 2px 4px; }
.sp-pop-btn { padding: 6px 12px; font-size: 12px; font-weight: 600; border-radius: 6px; border: 1px solid var(--border); background: var(--bg); cursor: pointer; }
.sp-pop-btn:hover { background: var(--bg2); }
.sp-pop-btn-del { background: #fdecec; color: var(--red); border-color: #f5c2c2; }
.sp-pop-btn-del:hover { background: var(--red); color: #fff; border-color: var(--red); }

/* Sidebar Legend */
.sp-sidebar { width: 280px; flex-shrink: 0; background: var(--bg2); border-left: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; padding: 10px; gap: 10px; }
.sp-panel { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.sp-panel-title { font-size: 11px; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; }
.sp-panel-hint { font-size: 11px; color: var(--text-dim); font-weight: 400; text-transform: none; letter-spacing: 0; }
.sp-legend { display: flex; flex-direction: column; gap: 4px; flex: 1; overflow-y: auto; }
.sp-legend-empty { font-size: 12px; color: var(--text-dim); padding: 12px 4px; line-height: 1.5; }
.sp-legend-item { display: flex; align-items: center; gap: 8px; padding: 8px; background: var(--bg2); border-radius: 8px; cursor: pointer; transition: background 0.12s; }
.sp-legend-item:hover { background: var(--bg3); }
.sp-legend-sw { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; font-size: 14px; flex-shrink: 0; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.18); }
.sp-legend-info { flex: 1; min-width: 0; }
.sp-legend-name { font-size: 13px; font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 4px; }
.sp-legend-meta { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.sp-legend-tag { display: inline-block; font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 3px; margin-left: 4px; }
.sp-tag-td { background: #fde2e2; color: var(--red); border: 1px solid #f5b5b5; }
.sp-tag-opt { background: #dbeafe; color: var(--blue); border: 1px solid #aaccff; }
.sp-legend-price { font-size: 13px; font-weight: 700; color: var(--green); flex-shrink: 0; }

/* Modal backdrop + dialogs */
/* Already top-anchored — padding-top now uses the live toolbar height
   (--vb-toolbar-h, set in drafting.js) so it opens right under the tools
   instead of overlapping them. Per Dariel 2026-07-20. */
.sp-modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.55); display: flex; align-items: flex-start; justify-content: center; z-index: 100000; padding: 24px 0; padding-top: var(--vb-toolbar-h, 140px); overflow-y: auto; }
/* margin: 0, not `auto 0` — a vertical auto margin here overrides the
   backdrop's `align-items: flex-start` and re-centers the modal regardless.
   Per Dariel 2026-07-20. */
/* max-height: fixed vh, NOT --vb-vvh — same keyboard-inertness rule as
   .sp-sheet above; a keyboard-reactive height moved the modal's bottom
   buttons mid-tap. Per Dariel 2026-07-21. */
.sp-modal { background: #fff; border-radius: 14px; padding: 20px; min-width: 360px; max-width: 580px; width: 92vw; max-height: min(84vh, 860px); overflow-y: auto; box-shadow: 0 16px 50px rgba(0,0,0,0.4); margin: 0; }
.sp-modal-narrow { max-width: 420px; }
.sp-modal-edit { max-width: 540px; }
/* Near-full-width on a large tablet (iPad Pro 12.9" portrait = 1024 CSS px)
   for field-dense sheets (Gate/Fence/Transition) — spreads what used to
   stack into one tall column across 2-4 columns instead, so the sheet stays
   SHORT: on an iPad's on-screen keyboard (which covers roughly the bottom
   half), a short-and-wide sheet keeps its Save button above the keyboard
   line instead of getting pushed under it. Per Dariel 2026-07-25. */
.sp-modal-wide { max-width: 980px; }
.sp-modal-title { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; font-size: 16px; }
.sp-modal-name { font-weight: 700; min-width: 0; overflow-wrap: anywhere; color: var(--text); flex: 1; }
.sp-modal-count { color: var(--text-dim); font-size: 12px; font-weight: 500; flex-shrink: 0; }
.sp-edit-chip { width: 36px; height: 36px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-weight: 800; font-size: 16px; flex-shrink: 0; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.2); }

.sp-fld { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.sp-fld-row { display: flex; gap: 12px; margin-bottom: 12px; }
.sp-fld-row > .sp-fld { flex: 1; margin-bottom: 0; }
.sp-fld-lbl { font-size: 12px; color: var(--text-dim); font-weight: 600; }
.sp-input { padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: #fff; color: var(--text); font-size: 14px; font-family: inherit; width: 100%; box-sizing: border-box; outline: none; transition: border-color 0.12s; }
.sp-input:focus { border-color: var(--accent); }
textarea.sp-input { resize: vertical; min-height: 70px; line-height: 1.5; }
.sp-fld-chks { display: flex; gap: 18px; margin: 6px 0 4px; }
.sp-chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); cursor: pointer; }
.sp-chk input[type=checkbox] { width: 18px; height: 18px; }

.sp-sku-row { font-size: 12px; color: var(--text-dim); margin: -6px 0 12px; }
.sp-sku-row code { background: var(--bg2); padding: 1px 6px; border-radius: 4px; font-family: ui-monospace, monospace; font-size: 11px; }

.sp-swap-link { display: inline-block; color: #f0883e; font-size: 13px; font-weight: 600; cursor: pointer; text-decoration: none; padding: 4px 0; }
.sp-swap-link:hover { text-decoration: underline; }
.sp-swap-section { margin: -6px 0 12px; }
.sp-swap-list { max-height: 200px; overflow-y: auto; border: 1px solid var(--border); border-radius: 6px; margin-top: 6px; background: var(--bg2); }
.sp-swap-empty { padding: 10px; color: var(--text-dim); font-size: 13px; }
.sp-swap-row { padding: 8px 10px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; justify-content: space-between; gap: 6px; align-items: center; }
.sp-swap-row:hover { background: var(--bg3); }
.sp-swap-row:last-child { border-bottom: none; }
.sp-swap-text { flex: 1; font-size: 13px; color: var(--text); min-width: 0; }
.sp-swap-chip { background: var(--accent); color: #fff; padding: 1px 6px; border-radius: 3px; font-size: 10px; margin-right: 4px; font-weight: 700; }
.sp-swap-sku { color: var(--text-dim); font-size: 11px; margin-left: 4px; }
.sp-swap-price { color: var(--green); font-size: 12px; font-weight: 600; }
.sp-swap-picked { padding: 8px 10px; color: var(--green); font-size: 13px; font-weight: 600; }

.sp-total-box { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; background: #f3f4f6; border-radius: 8px; margin: 10px 0 0; }
.sp-total-lbl { color: var(--text-dim); font-size: 13px; font-weight: 600; }
.sp-total-val { font-size: 16px; font-weight: 800; color: var(--green); }

.sp-modal-actions { display: flex; gap: 8px; margin-top: 14px; align-items: center; flex-wrap: wrap; }
.sp-mbtn { padding: 9px 16px; font-size: 13px; font-weight: 700; border-radius: 8px; border: 1px solid var(--border); cursor: pointer; background: var(--bg); color: var(--text); touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.sp-mbtn-secondary { background: #fff; color: var(--text); }
.sp-mbtn-primary { background: var(--blue); color: #fff; border-color: var(--blue); }
.sp-mbtn-primary:hover { background: #1d4ed8; }
.sp-mbtn-warn { background: #f0883e; color: #fff; border-color: #f0883e; }
.sp-mbtn-warn:hover { background: #d97520; }
.sp-mbtn-danger { background: var(--red); color: #fff; border-color: var(--red); }
.sp-mbtn-danger:hover { background: #991b1b; }

/* ────── Survey + Site-Clearing popups (Vilobot verbatim port — 0031) ─────
   z-index above install/section popups (100100 ≥ 100100 spec). Buttons are
   ≥44px tall for iPad tap-targets and use the brand-status palette. */
.sp-modal-backdrop.spe-popup-z { z-index: 100200; }
.spe-popup { background: #161b22 !important; color: #c9d1d9 !important; padding: 22px 22px 18px; max-width: 440px; }
.spe-popup h3 { color: #f0f6fc; font-size: 17px; }
.spe-sc-btn {
  display: block; width: 100%;
  min-height: 48px; padding: 12px 14px;
  margin: 0 0 8px;
  border: 1px solid transparent; border-radius: 10px;
  font-size: 14px; font-weight: 700;
  text-align: left; cursor: pointer;
  color: #fff;
  -webkit-tap-highlight-color: transparent;
}
.spe-sc-btn:hover { filter: brightness(1.1); }
.spe-sc-btn.green  { background: #2ea043; }
.spe-sc-btn.cyan   { background: #1f9ece; }
.spe-sc-btn.yellow { background: #d29922; color: #1f1305; }
.spe-sc-btn.red    { background: #da3633; }
.spe-sc-btn.blue   { background: #1f6feb; }
.spe-sc-step-title { font-size: 13px; color: #8b949e; margin: 0 0 10px; }
.spe-sc-input {
  display: block; width: 100%; box-sizing: border-box;
  padding: 12px 14px; min-height: 44px;
  background: #0d1117; color: #f0f6fc;
  border: 1px solid #30363d; border-radius: 8px;
  font-size: 16px; font-weight: 600; outline: none;
}
.spe-sc-input:focus { border-color: #1f6feb; }
.spe-btns { display: flex; gap: 8px; margin-top: 14px; align-items: center; flex-wrap: wrap; }
.spe-bs, .spe-bp {
  padding: 10px 16px; min-height: 44px;
  border-radius: 8px; border: 1px solid #30363d;
  font-size: 13px; font-weight: 700; cursor: pointer;
}
.spe-bs { background: #21262d; color: #c9d1d9; }
.spe-bs:hover { background: #30363d; }
.spe-bp { background: #1f6feb; color: #fff; border-color: #1f6feb; margin-left: auto; }
.spe-bp:hover { background: #1d4ed8; }

/* Color picker (first-marker prompt) */
.sp-color-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(82px, 1fr)); gap: 6px; margin-top: 8px; }
.sp-color-opt { padding: 12px 10px; border: 2px solid transparent; border-radius: 8px; font-weight: 700; font-size: 13px; cursor: pointer; transition: transform 0.1s; }
.sp-color-opt:hover { transform: scale(1.03); }
.sp-color-opt.on { border-color: var(--text); box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--text); }

/* Code Books modal list */
.sp-cb-list { display: flex; flex-direction: column; gap: 6px; }
.sp-cb-link { padding: 12px 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; font-size: 14px; font-weight: 600; color: var(--text); text-decoration: none; transition: background 0.1s; }
.sp-cb-link:hover { background: var(--bg3); color: var(--accent); text-decoration: none; }

/* ────── Product Catalog full-page overlay ────── */
.sp-cat-ov { position: fixed; inset: 0; background: var(--bg); z-index: 1000000; display: flex; flex-direction: column; overflow: hidden; font-family: inherit; color: var(--text); }
.sp-cat-topbar { display: flex; align-items: center; gap: 14px; padding: 14px 22px; background: var(--bg2); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.sp-cat-title { margin: 0; font-size: 18px; font-weight: 700; color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 30vw; }
.sp-cat-srch { flex: 1; max-width: 720px; }
.sp-cat-srch input { width: 100%; padding: 11px 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg); color: var(--text); font-size: 15px; box-sizing: border-box; outline: none; transition: border-color 0.12s; }
.sp-cat-srch input:focus { border-color: var(--accent); }
.sp-cat-xbtn { width: 44px; height: 44px; border: none; border-radius: 10px; background: var(--bg); color: var(--text); font-size: 22px; cursor: pointer; flex-shrink: 0; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.sp-cat-xbtn:hover { background: var(--accent); color: #fff; }
/* Prominent, always-visible alternative to the searchable grid — browse by
   code sheet instead. Sized to match the search bar so it reads as a real
   second way in, not a buried footer link. Per Dariel 2026-07-14 ("big book
   button on the catalog if I want to see it that way instead of the regular
   list and search"). */
.sp-cat-book-btn { flex-shrink: 0; padding: 11px 20px; border: 1px solid var(--accent); border-radius: 10px; background: var(--accent); color: #fff; font-size: 15px; font-weight: 700; cursor: pointer; white-space: nowrap; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.sp-cat-book-btn:hover { opacity: 0.9; }

.sp-cat-body { flex: 1; overflow-y: auto; padding: 16px 22px; }
.sp-cat-sectn { font-size: 11px; font-weight: 800; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.7px; padding: 12px 4px 8px; }
.sp-cat-count { color: var(--text-dim); font-weight: 500; font-size: 11px; }
.sp-cat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; margin-bottom: 16px; }
/* touch-action: manipulation — critical. Per Dariel 2026-06-01:
   Alejandro tapped catalog cards on tablet, nothing happened. iOS Safari
   treats taps inside scrollable containers (.sp-cat-body has overflow-y:
   auto) as potential scroll gestures; >5px finger drift converts the
   touch into a pan and suppresses the click. `manipulation` strips the
   double-tap-zoom + scroll heuristics so the tap fires immediately. */
.sp-cat-card { background: #fff; border: 1px solid var(--border); border-radius: 12px; padding: 12px 10px; cursor: pointer; display: flex; flex-direction: column; align-items: center; text-align: center; transition: border-color 0.1s, background 0.1s, transform 0.1s; min-height: 170px; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.sp-cat-card:hover { border-color: var(--accent); background: var(--bg2); transform: translateY(-2px); }
.sp-cat-cimg, .sp-cat-cimg-ph { width: 72px; height: 72px; border-radius: 10px; object-fit: contain; background: var(--bg2); margin-bottom: 8px; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; color: var(--text-dim); font-size: 28px; }
.sp-cat-ccd { font-size: 11px; color: var(--text-dim); font-family: ui-monospace, monospace; margin-bottom: 3px; }
.sp-cat-cnm { font-size: 12px; font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin-bottom: 4px; color: var(--text); }
.sp-cat-ccat { font-size: 10px; color: var(--text-dim); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.3px; }
.sp-cat-cpr { font-size: 13px; font-weight: 700; color: var(--green); margin-top: auto; }
.sp-cat-empty { padding: 60px 20px; text-align: center; color: var(--text-dim); font-size: 14px; }
.sp-cat-empty-glyph { font-size: 40px; margin-bottom: 12px; opacity: 0.5; }
.sp-cat-loading { padding: 40px; text-align: center; color: var(--text-dim); font-size: 14px; }

.sp-cat-footer { display: flex; align-items: center; gap: 14px; padding: 12px 22px; background: var(--bg2); border-top: 1px solid var(--border); flex-shrink: 0; }
.sp-cat-hint { flex: 1; font-size: 12px; color: var(--text-dim); text-align: right; line-height: 1.4; }
.sp-cat-hint kbd { display: inline-block; padding: 1px 6px; margin: 0 2px; background: var(--bg); border: 1px solid var(--border); border-radius: 4px; font-family: ui-monospace, "SF Mono", monospace; font-size: 11px; color: var(--text); box-shadow: 0 1px 0 var(--border); }

/* Mobile / tablet tweaks for site-plan editor */
@media (max-width: 900px) {
  .sp-toolbar { padding: 4px 8px; gap: 3px; }
  .sp-tb { min-width: 48px; padding: 4px 5px; }
  .sp-tb .sp-tb-lb { font-size: 9px; }
  .sp-bottombar { padding: 4px 8px; gap: 4px; }
  .sp-chip { padding: 5px 9px; font-size: 11px; }
  .sp-sidebar { width: 240px; }
  .sp-cat-topbar { padding: 10px 14px; gap: 8px; }
  .sp-cat-title { font-size: 15px; max-width: 30vw; }
  .sp-cat-srch input { padding: 10px 12px; font-size: 14px; }
  .sp-cat-xbtn { width: 40px; height: 40px; font-size: 20px; }
  .sp-cat-book-btn { padding: 9px 12px; font-size: 13px; }
  .sp-cat-body { padding: 12px 14px 20px; }
}
/* iPad / tablet — 3-wide grid, larger tiles. The desktop default packs
   ~6 columns at 160px each which is too cramped on touch screens.
   Force 3 columns with big tiles so a rep can read SKU + price at a
   glance and tap accurately. Sits between the desktop default and the
   phone-narrow override below. */
@media (min-width: 641px) and (max-width: 1100px) {
  .sp-cat-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; }
  .sp-cat-card { min-height: 230px; padding: 16px 12px; border-radius: 14px; }
  .sp-cat-cimg, .sp-cat-cimg-ph { width: 132px; height: 132px; font-size: 42px; margin-bottom: 12px; border-radius: 12px; }
  .sp-cat-ccd { font-size: 12px; margin-bottom: 4px; }
  .sp-cat-cnm { font-size: 15px; line-height: 1.35; -webkit-line-clamp: 3; }
  .sp-cat-ccat { font-size: 11px; }
  .sp-cat-cpr { font-size: 18px; margin-top: 10px; }
}
@media (max-width: 640px) {
  .sp-sidebar { display: none; }
  .sp-cat-title { display: none; }
  .sp-cat-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .sp-cat-card { padding: 12px 10px; min-height: 180px; }
  .sp-cat-cimg, .sp-cat-cimg-ph { width: 90px; height: 90px; }
  .sp-cat-cnm { font-size: 13px; }
  .sp-cat-cpr { font-size: 15px; }
  .sp-cat-hint { display: none; }
}

/* =========================================================================
   New Quote Wizard (VBNewQuote)
   --------------------------------------------------------------------------
   5-step modal: Customer → Service → Address → Sales Rep → Details.
   Was missing — restored to brand light theme after the canvas-rewrite agent
   inadvertently dropped them.
   ========================================================================= */
.vbnq-overlay {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(0,0,0,0.55);
  /* Anchor card at top so tall content stays reachable even on short screens. */
  display: flex; align-items: flex-start; justify-content: center;
  padding: 24px 24px 48px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.vbnq-card {
  background: var(--bg2, #ffffff);
  border: 1px solid var(--border, #e6dbcc);
  border-radius: 14px;
  width: 100%; max-width: 720px;
  /* Let card grow with content; the overlay scrolls. No max-height. */
  display: flex; flex-direction: column;
  box-shadow: 0 24px 64px rgba(0,0,0,.35);
  margin: auto 0;
}
/* Ensure the body section can grow without an internal scroll hiding results. */
.vbnq-body { padding: 22px; min-height: 320px; }
.vbnq-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 16px 22px;
  border-bottom: 1px solid var(--border, #e6dbcc);
  background: var(--bg, #f1e8df);
}
.vbnq-header h3 {
  margin: 0; color: var(--accent, #691d20);
  font-size: 18px; font-weight: 800; letter-spacing: 0.3px;
}
.vbnq-x {
  background: transparent; border: none; cursor: pointer;
  font-size: 28px; line-height: 1; color: var(--text-dim, #888);
  padding: 0 6px;
}
.vbnq-x:hover { color: var(--accent, #691d20); }
.vbnq-stepper {
  display: flex; align-items: center; gap: 6px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--border, #e6dbcc);
  background: var(--bg2, #ffffff);
}
.vbnq-step {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700;
  color: var(--text-dim, #888);
}
.vbnq-step.active { color: var(--accent, #691d20); }
.vbnq-step.done { color: #16a34a; }
.vbnq-step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 11px;
  background: var(--bg, #f1e8df); color: var(--text-dim, #888);
  font-size: 11px; font-weight: 800;
}
.vbnq-step.active .vbnq-step-n {
  background: var(--accent, #691d20); color: #ffffff;
}
.vbnq-step.done .vbnq-step-n { background: #16a34a; color: #ffffff; }
.vbnq-step-l { letter-spacing: 0.3px; }
.vbnq-step-sep {
  flex: 1; min-width: 12px; height: 1px;
  background: var(--border, #e6dbcc);
}
/* Body grows with content; OUTER overlay scrolls. Inner scroll hid the
   customer-search results on small viewports. */
.vbnq-body { padding: 22px; min-height: 280px; }
.vbnq-footer {
  display: flex; gap: 8px; align-items: center;
  padding: 14px 22px;
  border-top: 1px solid var(--border, #e6dbcc);
  background: var(--bg, #f1e8df);
}

/* =========================================================================
   New Quote Wizard — Step content
   Inner element styles (the shell rules are above; these style every step's
   form fields, cards, lists). Without these, results render as raw text.
   ========================================================================= */
.vbnq-body { padding: 22px; min-height: 320px; font-size: 14px; color: var(--text); }
.vbnq-label { display:block; font-size:11px; font-weight:700; color:var(--text-dim); letter-spacing:.5px; margin-bottom:4px; }
.vbnq-sub-label { font-size:11px; color:var(--text-dim); }
.vbnq-help { font-size:11px; color:var(--text-dim); margin-top:6px; }
.vbnq-section { margin-bottom:18px; }
.vbnq-row { display:flex; gap:10px; align-items:center; margin-bottom:8px; flex-wrap:wrap; }
.vbnq-check { display:flex; gap:6px; align-items:center; cursor:pointer; font-size:13px; }

/* ── Step 1: Customer ──────────────────────────────────────────────────── */
.vbnq-cust-search {
  width:100%; padding:10px 12px; font-size:14px;
  border:1px solid var(--border); border-radius:8px;
  background:var(--bg); color:var(--text); box-sizing:border-box;
  margin-bottom:10px;
}
.vbnq-results { display:flex; flex-direction:column; gap:6px; max-height:380px; overflow-y:auto; padding:2px; }
.vbnq-results-loading, .vbnq-results-empty {
  padding:14px; text-align:center; color:var(--text-dim); font-size:13px;
}
.vbnq-result-item {
  display:block; width:100%; text-align:left;
  padding:10px 14px; background:var(--bg2); border:1px solid var(--border);
  border-radius:8px; cursor:pointer; transition:all .12s;
  color:var(--text);
}
.vbnq-result-item:hover { background:#fff5f0; border-color:var(--accent); }
.vbnq-result-name { font-weight:700; color:var(--text); font-size:14px; display:flex; align-items:center; gap:6px; }
.vbnq-result-sub { font-size:12px; color:var(--text-dim); margin-top:2px; }
.vbnq-new-cust-btn {
  margin-top:10px; padding:8px 14px;
  background:transparent; color:var(--accent);
  border:1px dashed var(--accent); border-radius:8px;
  font-weight:700; font-size:12px; cursor:pointer;
}
.vbnq-new-cust-btn:hover { background:var(--accent); color:#fff; }
.vbnq-new-cust-form, .vbnq-new-addr-form, .vbnq-subform {
  margin-top:14px; padding:14px;
  background:var(--bg); border:1px solid var(--border); border-radius:10px;
  display:flex; flex-direction:column; gap:8px;
}
.vbnq-nc-name, .vbnq-nc-email, .vbnq-nc-phone, .vbnq-nc-company,
.vbnq-na-street, .vbnq-na-city, .vbnq-na-state, .vbnq-na-zip, .vbnq-na-country, .vbnq-na-label,
.vbnq-validity, .vbnq-notes, .vbnq-terms {
  width:100%; padding:8px 10px; font-size:13px;
  border:1px solid var(--border); border-radius:6px;
  background:var(--bg2); color:var(--text); box-sizing:border-box;
}
.vbnq-nc-save, .vbnq-na-save {
  padding:8px 14px; background:var(--accent); color:#fff;
  border:none; border-radius:6px; font-weight:700; font-size:12px; cursor:pointer;
}
.vbnq-nc-cancel, .vbnq-na-cancel {
  padding:8px 14px; background:var(--bg2); color:var(--text-dim);
  border:1px solid var(--border); border-radius:6px; font-size:12px; cursor:pointer;
}

/* "Selected customer" pill at top of subsequent steps */
.vbnq-selected {
  display:flex; justify-content:space-between; align-items:center;
  padding:10px 14px; background:#fff5f0;
  border:1px solid #e6c8c0; border-radius:8px; margin-bottom:14px;
}
.vbnq-selected-name { font-weight:700; color:var(--accent); }
.vbnq-selected-sub { font-size:12px; color:var(--text-dim); }
.vbnq-change-cust {
  background:transparent; border:1px solid var(--accent); color:var(--accent);
  padding:4px 10px; border-radius:6px; font-size:11px; cursor:pointer; font-weight:700;
}

/* ── Step 2: Service ───────────────────────────────────────────────────── */
.vbnq-services {
  display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));
  gap:10px;
}
.vbnq-service-card {
  padding:18px 14px; background:var(--bg2);
  border:2px solid var(--border); border-radius:12px;
  cursor:pointer; transition:all .15s;
  text-align:center; color:var(--text);
  display:flex; flex-direction:column; gap:6px; align-items:center;
}
.vbnq-service-card:hover {
  border-color:var(--accent);
  background:#fff5f0;
  transform:translateY(-1px);
}
.vbnq-service-card.selected {
  border-color:var(--accent);
  background:#fff5f0;
  box-shadow:0 4px 12px rgba(105,29,32,.18);
}
.vbnq-service-emoji { font-size:32px; line-height:1; }
.vbnq-service-label { font-size:14px; font-weight:800; color:var(--accent); }
.vbnq-service-note { font-size:11px; color:var(--text-dim); line-height:1.3; }

/* ── Step 3: Address ───────────────────────────────────────────────────── */
.vbnq-addr-list { display:flex; flex-direction:column; gap:6px; max-height:340px; overflow-y:auto; }
.vbnq-addr-row {
  display:flex; gap:10px; align-items:flex-start; padding:10px 12px;
  background:var(--bg2); border:1px solid var(--border); border-radius:8px;
  cursor:pointer;
}
.vbnq-addr-row:hover { border-color:var(--accent); }
.vbnq-addr-radio { margin-top:3px; }
.vbnq-addr-label { font-weight:700; color:var(--text); font-size:13px; }
.vbnq-addr-info { font-size:12px; color:var(--text); }
.vbnq-addr-sub { font-size:11px; color:var(--text-dim); }
.vbnq-new-addr-btn {
  margin-top:10px; padding:8px 14px;
  background:transparent; color:var(--accent);
  border:1px dashed var(--accent); border-radius:8px;
  font-weight:700; font-size:12px; cursor:pointer;
}

/* ── Step 4: Sales Rep ─────────────────────────────────────────────────── */
.vbnq-rep-card {
  display:flex; gap:12px; align-items:center;
  padding:12px 14px; background:var(--bg2);
  border:2px solid var(--border); border-radius:10px;
  cursor:pointer; margin-bottom:6px;
}
.vbnq-rep-card:hover { border-color:var(--accent); background:#fff5f0; }
.vbnq-rep-card.selected { border-color:var(--accent); background:#fff5f0; }
.vbnq-rep-avatar {
  width:38px; height:38px; border-radius:50%;
  background:var(--accent); color:#fff;
  display:flex; align-items:center; justify-content:center;
  font-weight:800; font-size:14px; flex-shrink:0;
}
.vbnq-rep-name { font-weight:700; color:var(--text); }
.vbnq-rep-sub { font-size:11px; color:var(--text-dim); }

/* ── Step 5: Details ───────────────────────────────────────────────────── */
.vbnq-summary {
  background:#fff5f0; border:1px solid #e6c8c0; border-radius:10px;
  padding:12px 14px; margin-top:14px;
}
.vbnq-summary div { padding:4px 0; font-size:13px; color:var(--text); }

/* Footer button — disabled state */
.vbnq-footer button[disabled] { opacity:.5; cursor:not-allowed; }

/* ── Address row selected state + radio glyph ──────────────────────────── */
.vbnq-addr-row.selected {
  border-color: var(--accent);
  background: #fff5f0;
  box-shadow: 0 2px 8px rgba(105,29,32,.12);
}
.vbnq-addr-radio {
  width: 16px; height: 16px; display: inline-flex;
  align-items: center; justify-content: center;
  color: var(--accent);
  font-size: 18px; line-height: 1; margin-top: 0;
}

/* ── Inline submit-error banner on Step 5 ──────────────────────────────── */
.vbnq-submit-error {
  margin-top: 12px;
  padding: 10px 12px;
  background: #fdecea;
  border: 1px solid #f5c6c2;
  border-radius: 8px;
  color: #8a1f24;
  font-size: 13px;
  font-weight: 600;
}

/* Force two-column rows in subforms to stretch properly */
.vbnq-row.two { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.vbnq-row.two .form-input { width: 100%; }

/* ── CHATTER (activity timeline) ────────────────────────────────────────── */
.vb-chatter-card { padding: 14px; }
.vb-chatter-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; gap: 8px;
}
.vb-chatter-count { color: var(--text-dim); font-weight: 500; }
.vb-chatter-input {
  display: flex; flex-direction: column; gap: 8px;
  margin-bottom: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.vb-chatter-textarea { min-height: 44px; font-size: 14px; resize: vertical; }
.vb-chatter-input-actions {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
}
.vb-chatter-pending { font-size: 12px; color: var(--text-dim); flex: 1; }
.vb-chatter-list { display: flex; flex-direction: column; gap: 12px; max-height: 520px; overflow-y: auto; }
.vb-chatter-empty { padding: 12px; }
.vb-chatter-entry {
  display: flex; gap: 10px; padding: 8px 0;
  border-bottom: 1px dashed var(--border);
}
.vb-chatter-entry:last-child { border-bottom: none; }
.vb-chatter-avatar {
  flex: 0 0 36px; width: 36px; height: 36px; border-radius: 50%;
  background: #691d20; color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; letter-spacing: 0.5px;
}
.vb-chatter-entry-main { flex: 1; min-width: 0; }
.vb-chatter-entry-meta {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13px; color: var(--text-dim); margin-bottom: 4px;
}
.vb-chatter-entry-meta strong { color: var(--text); }
.vb-chatter-time { font-size: 12px; color: var(--text-dim); }
.vb-chatter-entry-actions { margin-left: auto; display: inline-flex; gap: 4px; }
.vb-chatter-entry-actions .btn-icon { min-width: 32px; min-height: 32px; font-size: 14px; padding: 4px; }
.vb-chatter-entry-body { font-size: 14px; word-break: break-word; line-height: 1.45; }
.vb-chatter-entry-body a { color: #253938; text-decoration: underline; }
.vb-chatter-atts {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px;
}
.vb-chatter-thumb {
  display: inline-block; width: 96px; height: 96px;
  border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); background: var(--bg3);
}
.vb-chatter-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vb-chatter-audio { max-width: 100%; }
.vb-chatter-file {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border-radius: 8px;
  background: var(--bg3); color: var(--text);
  font-size: 13px; text-decoration: none;
  border: 1px solid var(--border);
  min-height: 32px;
}
.vb-chatter-file:hover { background: var(--bg2); }

/* ============================================================
   VBKeypad — full-viewport dark navy numeric entry for field-ops
   ============================================================ */
.vb-kp-root {
  display: flex; flex-direction: column;
  width: 100%;
  min-height: calc(100vh - 64px); /* fills section area below app header */
  /* iOS Safari: 100vh ignores the dynamic toolbar, so the bottom keypad row
     (NEXT) was pushed off-screen on small iPhones. dvh = the actual visible
     height, so the whole keypad fits. (Falls back to the vh line above.) */
  min-height: calc(100dvh - 64px);
  background: #1a1f2e;
  color: #fff;
  font-family: inherit;
  user-select: none; -webkit-user-select: none;
  /* Block iOS Safari's double-tap-to-zoom across the entire keypad
     shell. Without this, rapid taps on the same digit (e.g. typing
     "9966") fire iOS's double-tap gesture and zoom the page. */
  touch-action: manipulation;
}

/* Slotted display: a row of underscores; filled slots show the typed digit */
.vb-kp-display {
  display: flex; align-items: center; justify-content: center;
  gap: 16px;
  padding: 28px 16px 20px;
  font-size: 56px; font-weight: 700; letter-spacing: 6px;
  color: rgba(255,255,255,0.45);
  font-variant-numeric: tabular-nums;
  background: #1a1f2e;
}
.vb-kp-slot {
  display: inline-block;
  min-width: 34px; text-align: center;
  line-height: 1;
  font-size: inherit; font-weight: 700;
}
.vb-kp-slot-filled { color: #fff; }
/* Prefix glyph in front of the digit slots. Static when there's only one
   prefix; pulses + underlines when more than one is configured to hint the
   rep can tap it to cycle (e.g. V → S → Z for native/legacy disambig).
   flex-shrink:0 — on a narrow phone with a long maxLen (7-8 digit slots),
   the row can outgrow the screen width; without this the PREFIX itself (V/S)
   was what got squeezed/clipped off since it's flex-positioned first. The
   digit slots have their own min-width instead, so shrink math never eats
   into the prefix. Per Dariel 2026-07-23 ("the s and v is being cut of"). */
.vb-kp-prefix {
  display: inline-block;
  flex-shrink: 0;
  color: #fff;
  font-weight: 700;
  margin-right: 8px;
}
.vb-kp-prefix-cycle {
  cursor: pointer;
  border-bottom: 3px dashed rgba(255,255,255,0.45);
  padding-bottom: 2px;
}
.vb-kp-prefix-cycle:active { color: #f97171; }
/* Live preview row between the slot display and the keypad grid. Shows
   "S10051 · John Smith" as the rep types so they can verify before NEXT.
   Reserve line-height to keep the layout stable when the hint is empty. */
.vb-kp-hint {
  min-height: 22px;
  padding: 0 12px 10px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: rgba(255,255,255,0.85);
  background: #1a1f2e;
  letter-spacing: 0.2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vb-kp-hint-ok   { color: #5be584; }
.vb-kp-hint-warn { color: #f59e0b; }

/* 4-row x 3-col grid that fills the rest of the viewport. Buttons are
   edge-to-edge with subtle 1px separators (lighter than the bg). */
.vb-kp-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 1fr;
  gap: 1px;
  background: rgba(255,255,255,0.06);
  flex: 1;
  min-height: 0;
  /* Same double-tap-zoom guard as the buttons — covers the 1px gap
     between keys so a stray tap there doesn't trigger zoom either. */
  touch-action: manipulation;
}
.vb-kp-key {
  background: #1a1f2e;
  color: #fff;
  border: none;
  font-size: 44px;
  font-weight: 700;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: rgba(255,255,255,0.08);
  transition: background 0.08s ease;
  font-variant-numeric: tabular-nums;
  /* iOS Safari: prevent double-tap zoom + text selection on rapid taps.
     Per Dariel 2026-06-01: typing "9966" was zooming the page because
     iOS interprets two quick taps on the same number as a double-tap
     gesture. `touch-action: manipulation` opts out of double-tap-zoom
     while keeping pan and pinch. `user-select: none` + the webkit
     prefix stop iOS from selecting the digit text on a long-press. */
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.vb-kp-key:active:not(:disabled) { background: #2a2f3e; }
.vb-kp-key:disabled { opacity: 0.45; cursor: not-allowed; }

/* Red backspace, gray NEXT — matches Vilobot's look */
.vb-kp-key-bksp {
  background: #ef4444; color: #fff;
}
.vb-kp-key-bksp:active:not(:disabled) { background: #dc2626; }
.vb-kp-key-bksp span { font-size: 38px; }

.vb-kp-key-next {
  background: #3a3f4e; color: #cbd1d8;
  font-size: 32px; font-weight: 700; letter-spacing: 2px;
}
.vb-kp-key-next:not(:disabled) { background: #ef4444; color: #fff; }
.vb-kp-key-next:not(:disabled):active { background: #dc2626; }

.vb-kp-status {
  text-align: center;
  font-size: 14px; color: rgba(255,255,255,0.7);
  padding: 8px 12px;
  background: #1a1f2e;
  min-height: 20px;
}
.vb-kp-status-error { color: #ff6b6b; font-weight: 700; }
.vb-kp-status-warn { color: #ffd86b; font-weight: 600; }

/* Collapsed pill after a WO is loaded — sits at top of section content */
.vb-kp-pill {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 18px;
  background: #1a1f2e; color: #fff;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.vb-kp-pill-value {
  font-weight: 800; font-size: 18px; letter-spacing: 2px;
  font-variant-numeric: tabular-nums;
}
.vb-kp-pill-edit {
  margin-left: auto;
  background: rgba(255,255,255,0.14); color: #fff;
  border: none; border-radius: 8px; padding: 8px 16px;
  font-weight: 700; font-size: 12px; cursor: pointer;
  letter-spacing: 1px; text-transform: uppercase;
}
.vb-kp-pill-edit:hover { background: rgba(255,255,255,0.24); }

@media (min-width: 720px) {
  .vb-kp-display { font-size: 64px; gap: 20px; padding: 36px 16px 24px; }
  .vb-kp-slot { min-width: 40px; }
  .vb-kp-key { font-size: 52px; }
  .vb-kp-key-next { font-size: 36px; }
}

/* Short OR narrow viewports — small iPhones (SE/mini/regular width) and
   landscape. Shrink the display + keys so the full keypad, including the
   NEXT row, fits without being cut off (Dariel 2026-06-03: keypad cut off on
   a small iPhone). The width leg is separate from the height leg: a 7-8
   character maxLen (prefix + digits) at the default 56px font/16px gap needs
   ~430px of width — MORE than a 375-393px iPhone has in PORTRAIT even though
   its viewport is comfortably taller than 600px, which is why the prefix
   (V/S) was getting clipped off the edge on regular (not just short) phones.
   Per Dariel 2026-07-23 ("the s and v is being cut of"). */
@media (max-height: 600px), (max-width: 430px) {
  .vb-kp-display { font-size: 30px; gap: 8px; padding: 8px 12px 4px; letter-spacing: 3px; }
  .vb-kp-slot { min-width: 20px; }
  .vb-kp-hint { min-height: 14px; padding: 0 8px 4px; font-size: 11px; }
  .vb-kp-key { font-size: 24px; }
  .vb-kp-key-bksp span { font-size: 22px; }
  .vb-kp-key-next { font-size: 16px; letter-spacing: 1px; }
  .vb-kp-status { padding: 3px 8px; min-height: 12px; font-size: 11px; }
}

/* WO Scanner keypad overlays (Ship Photos PKG/DLV + Receive Order PO#) stack a
   header — and Ship Photos also a PACKAGE/DELIVERY toggle — ABOVE the keypad.
   The keypad's default min-height (100dvh - 64px) assumes ONLY the app header
   sits above it, so that extra chrome pushed the bottom row (⌫ / 0 / NEXT) off
   the screen. Inside these flex-column overlays the host is already flex:1, so
   let the keypad fill its HOST (dynamic) instead of the whole viewport — it
   then adapts to however much chrome is above and the NEXT row always shows.
   Per Dariel 2026-06-10. */
#wo-ship-kp-overlay .vb-kp-root,
#wo-recv-kp-overlay .vb-kp-root {
  min-height: 0;
  height: 100%;
  /* Clear the iOS home indicator so the NEXT row sits above it. With the
     global box-sizing:border-box, this stays inside height:100% (no overflow);
     the strip below is the keypad's own dark bg. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* WO Scanner keypad overlays (Ship Photos PKG/DLV + Receive Order PO#) stack a
   header — and Ship Photos also a PACKAGE/DELIVERY toggle — ABOVE the keypad.
   The keypad's default min-height (100dvh - 64px) assumes ONLY the app header
   sits above it, so that extra chrome pushed the bottom row (⌫ / 0 / NEXT) off
   the screen. Inside these flex-column overlays the host is already flex:1, so
   let the keypad fill its HOST (dynamic) instead of the whole viewport — it
   then adapts to however much chrome is above and the NEXT row always shows.
   Per Dariel 2026-06-10. */
#wo-ship-kp-overlay .vb-kp-root,
#wo-recv-kp-overlay .vb-kp-root {
  min-height: 0;
  height: 100%;
  /* Clear the iOS home indicator so the NEXT row sits above it. With the
     global box-sizing:border-box, this stays inside height:100% (no overflow);
     the strip below is the keypad's own dark bg. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* ════════════════════════════════════════════════════════════════════════
 * Ops calendar — install block STATUS overlays (2026-05-28)
 * ----------------------------------------------------------------------
 * Server-computed paymentState + permit811State map to one of four classes:
 *   .opsv2-card--status-red       SO signed, deposit not paid, NOT pay-at-end
 *   .opsv2-card--status-black     no 811 ticket OR PDF not uploaded (not_needed)
 *   .opsv2-card--status-striped   both of the above apply
 *   .opsv2-card--status-none      neither — default border (no override)
 *
 * The 4px LEFT BORDER overrides the inline crew-color border (set by
 * buildInstallCard for back-compat). Background gets a faint 6% tinted
 * overlay on top of the existing crew tint via a ::before layer so the
 * crew identity stays readable underneath. !important needed because
 * the renderer still emits an inline `border-left` for crew color.
 * ════════════════════════════════════════════════════════════════════════ */
.opsv2-card--status-red {
  border-left: 4px solid #dc2626 !important;
  position: relative;
}
.opsv2-card--status-red::before {
  content: ""; position: absolute; inset: 0;
  background: rgba(220, 38, 38, 0.06);
  pointer-events: none; border-radius: inherit;
}
.opsv2-card--status-black {
  border-left: 4px solid #1a1a1a !important;
  position: relative;
}
.opsv2-card--status-black::before {
  content: ""; position: absolute; inset: 0;
  background: rgba(26, 26, 26, 0.06);
  pointer-events: none; border-radius: inherit;
}
.opsv2-card--status-striped {
  /* Diagonal red/black stripes painted directly into the 4px left border
   * via a CSS gradient. border-image keeps it tight to the 4px strip; the
   * fallback border color preserves layout if border-image isn't supported. */
  border-left: 4px solid #1a1a1a !important;
  border-image: repeating-linear-gradient(
      45deg,
      #dc2626 0 8px,
      #1a1a1a 8px 16px
    ) 1 !important;
  position: relative;
}
.opsv2-card--status-striped::before {
  content: ""; position: absolute; inset: 0;
  background: rgba(120, 20, 20, 0.06);
  pointer-events: none; border-radius: inherit;
}
.opsv2-card--status-none {
  /* No-op anchor class kept for completeness so JS can switch through all
   * four states without ever leaving an empty className branch. */
}
/* Keep card contents above the tinted ::before layer. */
.opsv2-install-card.opsv2-card--status-red > *,
.opsv2-install-card.opsv2-card--status-black > *,
.opsv2-install-card.opsv2-card--status-striped > * {
  position: relative;
  z-index: 1;
}

/* ════════════════════════════════════════════════════════════════════════
 * Field Workflow Shell — Welding Photos / Aluminum Photos / Driver /
 * Powder Coating, post-keypad. Forces a fullscreen dark-navy takeover
 * regardless of the app's theme so the shop-floor tech sees the same
 * giant-button UI in every section.
 *
 * Mounted into #app-body via JS as `<div class="vb-field-shell">…`. The
 * shell pulls itself out of normal flow and paints over the whole viewport
 * below the app header.
 * ════════════════════════════════════════════════════════════════════════ */
.vb-field-shell {
  position: fixed;
  /* The app-header is 56px tall (see .app-header). Sit below it so the
     hamburger + section title still work. */
  top: 56px;
  left: 0; right: 0; bottom: 0;
  background: #0c121f;
  color: #fff;
  z-index: 30;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  font-family: inherit;
}
@media (min-width: 769px) {
  /* On desktop the sidebar is 240px wide and pinned left. Keep the shell
     visible only over the main content. */
  .vb-field-shell { left: 240px; }
  /* …but kiosk users (drivers etc.) have NO sidebar, so the 240px gap left a
     white strip exposing the page behind the shell. Cover the full width. */
  body.app-kiosk .vb-field-shell { left: 0; }
}
.vb-field-shell * { box-sizing: border-box; }

.vb-fw {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px;
  /* Full width — per Dariel 2026-06-03 the 640px cap made the capture
     UI a narrow strip on a landscape tablet with empty space around it.
     Use the whole shell width (which already sits right of the 240px
     sidebar on desktop). */
  max-width: 100%;
  margin: 0 auto;
  min-height: 100%;
}

/* Top SO-header row: small kind label (left) + giant SO short name (right). */
.vb-fw-hdr {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px;
  padding: 4px 4px 0;
}
.vb-fw-hdr-l {
  font-size: 14px; font-weight: 700; color: rgba(255,255,255,0.55);
  text-transform: uppercase; letter-spacing: 1.5px;
  padding-top: 8px;
}
.vb-fw-hdr-r { text-align: right; min-width: 0; flex: 1; }
.vb-fw-hdr-so {
  font-size: 36px; font-weight: 900; color: #fff;
  letter-spacing: 1px; line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.vb-fw-hdr-cust {
  font-size: 14px; font-weight: 600;
  color: rgba(255,255,255,0.55);
  margin-top: 4px; word-break: break-word;
}

/* Big red VOLVER button. Tap → expand keypad. */
.vb-fw-back {
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  min-height: 64px;
  background: #dc2626;
  color: #fff;
  border: none;
  border-radius: 14px;
  font-size: 22px; font-weight: 900; letter-spacing: 2px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.vb-fw-back:active { background: #b91c1c; }

/* Question prompt + category picker buttons (Step 2). */
.vb-fw-prompt {
  text-align: center;
  font-size: 20px; font-weight: 800;
  color: rgba(255,255,255,0.6);
  margin: 12px 0 4px;
  letter-spacing: 0.5px;
}
.vb-fw-cats { display: flex; flex-direction: column; gap: 12px; }
.vb-fw-cat {
  width: 100%;
  min-height: 160px;
  display: flex; align-items: center; justify-content: center;
  gap: 18px;
  background: rgba(255,255,255,0.04);
  border-radius: 20px;
  border: 4px solid currentColor;
  font-size: 38px; font-weight: 900; letter-spacing: 2px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  padding: 18px;
  text-align: center;
}
.vb-fw-cat:active { background: rgba(255,255,255,0.08); }
.vb-fw-cat-emoji { font-size: 48px; line-height: 1; }
.vb-fw-cat-label { line-height: 1.1; }

/* Selected category chip (Step 3). */
.vb-fw-chip {
  display: inline-flex; align-items: center; gap: 8px;
  align-self: flex-start;
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 16px; font-weight: 900; letter-spacing: 1px;
  background: rgba(255,255,255,0.06);
  border: 2px solid currentColor;
}

/* Reference photos collapsible panel. */
.vb-fw-ref {
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  background: rgba(255,255,255,0.03);
  overflow: hidden;
}
.vb-fw-ref-head {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%;
  padding: 12px 14px;
  background: transparent;
  border: none;
  color: #fff;
  font-size: 14px; font-weight: 800;
  cursor: pointer;
  text-align: left;
  letter-spacing: 0.5px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.vb-fw-ref-head:active { background: rgba(255,255,255,0.06); }
.vb-fw-ref-body {
  padding: 8px 12px 12px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  max-height: 50vh;
  overflow-y: auto;
}
.vb-fw-ref-empty {
  padding: 14px;
  color: rgba(255,255,255,0.55);
  font-size: 13px;
  text-align: center;
}
.vb-fw-ref-thumb {
  /* Square tile via the padding-bottom percentage trick (height:0 +
     padding-bottom:100%) instead of relying solely on `aspect-ratio` —
     that property gives the tile its ENTIRE height, and if it's ever
     ignored (older/embedded WebView engines) the tile collapses to zero
     height with nothing to stop it, so every photo's `img` (which fills
     "100% of the tile") also collapses and photos end up stacked on top
     of each other instead of each getting its own block in the grid.
     padding-bottom:100% is supported everywhere CSS grid itself is, so
     it can't silently fail the same way. Per Dariel 2026-07-20 ("every
     single image has their own full block" — not overlapping). */
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 100%;
  box-sizing: border-box;
  background: #1a2030;
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
}
.vb-fw-ref-thumb img {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* Per-shop group header inside the reference-photo grid. */
.vb-fw-ref-group {
  font-size: 12px; font-weight: 800; letter-spacing: .4px;
  color: rgba(255,255,255,0.85);
  text-transform: uppercase;
  padding: 10px 2px 2px;
}
/* Trash button on a deletable (paint) reference thumb. */
.vb-fw-ref-del {
  position: absolute; top: 4px; right: 4px;
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(220,38,38,0.92); color: #fff;
  border: none; font-size: 13px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  z-index: 2;
}
.vb-fw-ref-del:active { background: #b91c1c; }
/* Non-image attachment (PDF / doc) tile in the reference grid — tap to open. */
.vb-fw-ref-doc {
  width: 100%; aspect-ratio: 1 / 1;
  background: #1a2030;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 8px;
  cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 8px; text-align: center;
  -webkit-tap-highlight-color: transparent;
}
.vb-fw-ref-doc:active { background: #222c44; }
.vb-fw-ref-doc-ic { font-size: 34px; line-height: 1; }
.vb-fw-ref-doc-nm {
  font-size: 10px; color: rgba(255,255,255,0.82); line-height: 1.2; word-break: break-word;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.vb-fw-ref-doc-open { font-size: 10px; font-weight: 800; color: #58a6ff; }

/* Big dashed camera-tile. */
.vb-fw-cam {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  width: 100%;
  /* Grow to fill the leftover vertical space so the camera target is a
     big, easy tap on a tablet instead of a short tile with a dead band
     of empty screen below it. min-height keeps a floor on tiny phones. */
  flex: 1 1 auto;
  min-height: 240px;
  border: 3px dashed rgba(255,255,255,0.28);
  border-radius: 18px;
  background: rgba(255,255,255,0.03);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  padding: 16px;
}
.vb-fw-cam:active { background: rgba(255,255,255,0.06); }
/* Secondary "pick from gallery" link under the camera tile. */
.vb-fw-gallery {
  display: block;
  text-align: center;
  font-size: 14px;
  font-weight: 700;
  color: rgba(255,255,255,0.62);
  padding: 10px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.vb-fw-gallery:active { color: #fff; }
.vb-fw-gallery .vb-fw-cam-input { display: none; }
.vb-fw-cam-emoji { font-size: 56px; line-height: 1; margin-bottom: 8px; }
.vb-fw-cam-label {
  font-size: 24px; font-weight: 900; color: #fff;
  letter-spacing: 1px; text-align: center;
}
.vb-fw-cam-input { display: none; }
.vb-fw-cam-help {
  text-align: center;
  font-size: 13px;
  color: rgba(255,255,255,0.45);
  margin-top: -4px;
}

/* Queued photos grid + counter chip. */
.vb-fw-queue { display: flex; flex-direction: column; gap: 8px; }
.vb-fw-queue-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.vb-fw-queue-item {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
  background: #1a2030;
  border: 1px solid rgba(255,255,255,0.10);
}
.vb-fw-queue-item img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.vb-fw-queue-rm {
  position: absolute; top: -6px; right: -6px;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: #dc2626; color: #fff;
  border: 2px solid #0c121f;
  font-size: 14px; font-weight: 900;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.vb-fw-queue-count {
  align-self: center;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(255,255,255,0.10);
  color: #fff;
  font-size: 14px; font-weight: 900; letter-spacing: 1px;
}

/* Sticky LISTO submit. */
.vb-fw-listo {
  position: sticky; bottom: 8px;
  width: 100%;
  min-height: 80px;
  background: #10b981;
  color: #fff;
  border: none;
  border-radius: 16px;
  font-size: 28px; font-weight: 900; letter-spacing: 2px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  box-shadow: 0 8px 24px rgba(16,185,129,0.35);
}
.vb-fw-listo:disabled { opacity: 0.6; cursor: not-allowed; box-shadow: none; }
.vb-fw-listo:active:not(:disabled) { background: #059669; }

/* Step 5 — success screen. */
.vb-fw-done {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  padding: 32px 16px;
  gap: 14px;
  min-height: calc(100vh - 56px - 28px);
}
.vb-fw-done-emoji { font-size: 120px; line-height: 1; }
.vb-fw-done-title {
  font-size: 36px; font-weight: 900;
  color: #10b981; letter-spacing: 2px;
}
.vb-fw-done-so {
  font-size: 28px; font-weight: 800;
  color: #fff; letter-spacing: 1px;
}
.vb-fw-done-next {
  width: 100%;
  min-height: 72px;
  margin-top: 14px;
  background: #3b82f6;
  color: #fff;
  border: none;
  border-radius: 14px;
  font-size: 22px; font-weight: 900; letter-spacing: 1.5px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.vb-fw-done-next:active { background: #2563eb; }

/* Inline error/info banner inside a shell. */
.vb-fw-banner {
  background: rgba(220,38,38,0.15);
  border: 1px solid rgba(220,38,38,0.45);
  color: #fecaca;
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 700;
}

/* Small top-right toggle to expose Manager View from inside the shell
 * (powder coating only). */
.vb-fw-mgr-row {
  display: flex; justify-content: flex-end;
  padding: 0 4px;
}
.vb-fw-mgr-toggle {
  background: rgba(255,255,255,0.10);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.20);
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.5px;
  cursor: pointer;
}
.vb-fw-mgr-toggle:active { background: rgba(255,255,255,0.18); }

/* Lightbox shared by reference-photo viewer. */
.vb-fw-lb {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.94);
  z-index: 99999;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 12px;
}
.vb-fw-lb-img {
  max-width: 100%; max-height: calc(100vh - 80px);
  object-fit: contain; border-radius: 8px;
}
.vb-fw-lb-close {
  /* Pushed BELOW the iPhone status bar (battery/signal) via the top safe-area
     inset — at a flat top:12px the ✕ landed right under the battery icon and
     couldn't be tapped. viewport-fit=cover is set on the page so env() is
     live. Now a real 46px circular tap target (was just the bare glyph) with
     a translucent backing so it's easy to see + hit over any photo. Shared by
     every shop photo viewer (powder/welding/aluminum/makina/driver). Per
     Dariel 2026-07-23 ("the x is too close to battery on iphone and i can't
     click the x"). */
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 10px);
  right: calc(env(safe-area-inset-right, 0px) + 12px);
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.5); border: none; color: #fff;
  font-size: 26px; font-weight: 900; line-height: 1;
  cursor: pointer; z-index: 3;
  -webkit-tap-highlight-color: transparent;
}
.vb-fw-lb-close:active { background: rgba(0,0,0,0.75); }

/* ─────────────────────────────────────────────────────────────────────────
   Kill native number-input spinners on every <input type="number">.
   Per Dariel 2026-05-29: "remove the circle and the arrows to go up and
   down i cant use that all is o nthe canvas and it cullters the area."
   The corresponding inputs are tagged inputmode="decimal" so iPad gets the
   proper numeric keypad even without the spinner control.
   ───────────────────────────────────────────────────────────────────────── */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* Inline one-tap voice note (OPS MANAGER NOTE thread) — compact mic in the
   composer. STEADY red while recording (no flashing). Per Dariel 2026-06-25. */
.vb-ops-voice-btn {
  flex: 0 0 auto;
  padding: 6px 10px;
  font-size: 15px;
  line-height: 1;
}
.vb-ops-voice-btn.vb-recording {
  background: #ef4444 !important;
  border-color: #ef4444 !important;
  color: #fff !important;
}

/* ── Visit checklist ─────────────────────────────────────────────────────── */
/* The estimator's post-visit form. Chips instead of dropdowns because this is
   filled on a phone in a driveway. Per Dariel 2026-08-18. */
.vc-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 3000;
  display: flex; align-items: center; justify-content: center; padding: 16px; }
.vc-modal { background: var(--bg, #fff); border-radius: 16px; width: 100%; max-width: 520px;
  max-height: 92vh; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,.3); }
.vc-head { display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; border-bottom: 1px solid var(--border, #eee); }
.vc-head h3 { font-size: 16px; color: #691d20; margin: 0; }
.vc-x { background: none; border: 0; font-size: 26px; line-height: 1; color: var(--text-dim, #999); cursor: pointer; }
.vc-body { padding: 14px 18px; overflow-y: auto; }
.vc-q { font-size: 13.5px; font-weight: 800; color: var(--text, #1a1a1a); margin: 14px 0 7px; }
.vc-q:first-child { margin-top: 0; }
.vc-hint { font-weight: 600; font-size: 11.5px; color: var(--text-dim, #888); }
.vc-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.vc-chip { border: 1px solid var(--border, #d9cab8); background: var(--bg, #fff); color: var(--text, #444);
  border-radius: 999px; padding: 8px 14px; font-size: 13.5px; font-weight: 700; cursor: pointer; }
.vc-chip:hover { border-color: #691d20; }
.vc-chip.on { background: #691d20; border-color: #691d20; color: #fff; }
.vc-notes { width: 100%; border: 1px solid var(--border, #d9cab8); border-radius: 10px;
  padding: 9px 11px; font-size: 14px; font-family: inherit; resize: vertical; background: var(--bg, #fff); color: var(--text, #222); }
.vc-note { font-size: 11.5px; color: var(--text-dim, #888); margin-top: 6px; }
.vc-check { display: flex; gap: 10px; align-items: flex-start; margin-top: 14px;
  padding: 11px 13px; border: 1px solid var(--border, #e5e7eb); border-radius: 10px; font-size: 13.5px; cursor: pointer; }
.vc-check input { margin-top: 2px; width: 17px; height: 17px; }
.vc-foot { display: flex; gap: 8px; justify-content: flex-end; align-items: center;
  padding: 12px 18px; border-top: 1px solid var(--border, #eee); flex-wrap: wrap; }
.vc-err { flex: 1; color: #b91c1c; font-size: 12.5px; font-weight: 700; }
.vc-done { max-width: 420px; }
.vc-next { background: #dcfce7; border: 1px solid #bbf7d0; border-radius: 12px; padding: 12px 14px; text-align: left; }
.vc-next-off { background: #f3f4f6; border-color: #e5e7eb; }
.vc-next-lbl { font-size: 10.5px; text-transform: uppercase; letter-spacing: .5px; font-weight: 800; color: #166534; }
.vc-next-off .vc-next-lbl { color: #6b7280; }
.vc-next-when { font-size: 15px; font-weight: 800; color: #1a1a2e; margin-top: 2px; }
.vc-next-body { font-size: 12px; color: #4b5563; margin-top: 6px; font-style: italic; }

/* ── Estimator prompt — the blocking follow-up nag ───────────────────────── */
/* It covers everything on purpose. No backdrop-click escape: the only ways out
   are 20 minutes, going to the order, or logging it. Per Dariel 2026-08-18. */
.ep-overlay { position: fixed; inset: 0; background: rgba(26,26,46,.82); z-index: 9000;
  display: flex; align-items: center; justify-content: center; padding: 18px;
  backdrop-filter: blur(3px); }
.ep-modal { background: #fff; border-radius: 18px; width: 100%; max-width: 440px;
  overflow: hidden; box-shadow: 0 30px 80px rgba(0,0,0,.45); }
.ep-top { background: #691d20; color: #fff; padding: 13px 20px; font-size: 14px;
  font-weight: 800; letter-spacing: .3px; }
.ep-body { padding: 18px 20px 6px; }
.ep-order { font-size: 20px; font-weight: 800; color: #253938; }
.ep-amt { font-size: 14px; font-weight: 700; color: #691d20; }
.ep-who { font-size: 14px; color: #6f6258; margin-top: 2px; }
.ep-msg { font-size: 14.5px; line-height: 1.5; color: #1f1a17; margin-top: 12px; }
.ep-late { margin-top: 10px; background: #fee2e2; border: 1px solid #fecaca; color: #b91c1c;
  border-radius: 8px; padding: 7px 10px; font-size: 12.5px; font-weight: 800; }
.ep-foot { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px 18px; }
.ep-btn { border-radius: 10px; padding: 11px 14px; font-size: 13.5px; font-weight: 800;
  cursor: pointer; border: 1px solid #d9cab8; background: #fff; color: #691d20; flex: 1 1 auto; }
.ep-btn:disabled { opacity: .6; cursor: wait; }
.ep-go { background: #166534; border-color: #166534; color: #fff; flex: 1 1 100%; order: 3; }
.ep-ghost:hover { border-color: #691d20; }
.ep-ctx { margin-top: 12px; background: #f1e8df; border-radius: 10px; padding: 10px 12px;
  font-size: 12.5px; line-height: 1.5; color: #1f1a17; }
.ep-ctx > div + div { margin-top: 5px; }
.ep-k { display: inline-block; font-size: 10px; text-transform: uppercase; letter-spacing: .4px;
  font-weight: 800; color: #6f6258; margin-right: 5px; }

/* ── Estimator Admin: follow-up runway ───────────────────────────────────── */
.fu-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;
  flex-wrap: wrap; margin-bottom: 14px; }
.fu-sub { font-size: 12.5px; color: var(--text-dim, #6f6258); margin-top: 3px; }
.fu-sec { margin-bottom: 18px; }
.fu-sec-h { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .5px; color: #691d20;
  border-bottom: 2px solid #691d20; padding-bottom: 4px; margin-bottom: 6px; }
.fu-count { background: #691d20; color: #fff; border-radius: 999px; padding: 1px 8px; font-size: 11px; }
.fu-note { font-size: 11.5px; color: var(--text-dim, #888); margin-bottom: 6px; }
.fu-row { display: flex; gap: 12px; padding: 9px 8px; border-bottom: 1px solid var(--border, #eee); }
.fu-row:hover { background: var(--bg2, #faf7f3); }
.fu-when { flex: 0 0 78px; font-size: 12px; font-weight: 800; color: #253938; text-align: right; line-height: 1.35; }
.fu-main { min-width: 0; flex: 1; }
.fu-top { font-size: 13.5px; }
.fu-cust { color: var(--text-dim, #6f6258); }
.fu-amt { color: #691d20; font-weight: 700; font-size: 12.5px; }
.fu-kind { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .3px;
  padding: 1px 6px; border-radius: 4px; background: #e6dbcc; color: #6f6258; }
.fu-checklist { background: #fee2e2; color: #b91c1c; }
.fu-skip { font-size: 10.5px; font-weight: 800; color: #b45309; }
.fu-ctx { font-size: 11.5px; color: var(--text-dim, #6f6258); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fu-none { font-style: italic; opacity: .7; }
.fu-msg { font-size: 11.5px; color: var(--text-dim, #6f6258); margin-top: 2px; font-style: italic; }
