/* ============================================================
   JahFeel Client Portal — styles
   Brand tokens mirror projects/jahfeelautomation-deploy/css/tokens.css
   (warm cream + teal, Plus Jakarta Sans). Self-contained so the portal
   has no cross-site CSS dependency. Mobile-first.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap');

:root {
  --color-bg:            #FAF9F7;
  --color-bg-subtle:     #F5F2EE;
  --color-card:          #FFFFFF;
  --color-card-hover:    #FDFCFA;
  --color-border:        #ECE9E4;
  --color-border-strong: #D6D1C9;

  --color-text:          #1F2937;
  --color-text-strong:   #0F172A;
  --color-text-muted:    #6B7280;
  --color-text-faint:    #9CA3AF;
  --color-text-on-accent:#FFFFFF;

  --color-accent:        #0D9488;
  --color-accent-hover:  #0F766E;
  --color-accent-soft:   #CCFBF1;
  --color-accent-faint:  #F0FDFA;

  --color-success:       #15803D;
  --color-success-soft:  #D1FAE5;
  --color-warning:       #B45309;
  --color-warning-soft:  #FEF3C7;
  --color-danger:        #B91C1C;
  --color-danger-soft:   #FEE2E2;
  --color-info:          #1D4ED8;
  --color-info-soft:     #DBEAFE;

  --shadow-card:       0 1px 2px rgba(20,14,8,.04), 0 1px 6px rgba(20,14,8,.05);
  --shadow-card-hover: 0 1px 2px rgba(20,14,8,.05), 0 4px 12px rgba(20,14,8,.08);
  --shadow-overlay:    0 8px 32px rgba(20,14,8,.16);
  --shadow-pill:       0 1px 3px rgba(13,148,136,.18);

  --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --radius-sm: 6px;  --radius-md: 10px;  --radius-lg: 14px;
  --radius-xl: 20px; --radius-full: 9999px;

  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px;
  --space-5:20px; --space-6:24px; --space-8:32px; --space-10:40px; --space-12:48px;

  --motion-fast: 140ms;
  --motion-easing: cubic-bezier(0.16, 1, 0.3, 1);

  --tabbar-h: 64px;
  --header-h: 56px;

  /* Content-column cap. 640px is the phone-first column — UNCHANGED on mobile.
     Widened on desktop (see the min-width:700px block below) so the portal stops
     rendering a narrow strip with huge empty sides on a laptop/monitor (Bryan C,
     2026-06-18: "desktop renders inbox + everything mobile-width with huge wasted
     side space — use the viewport"). One variable keeps .app-main + .operator-bar
     in lockstep; the bottom tab bar keeps its own narrower cap (touch targets
     shouldn't sprawl across a wide screen). Locked by styles-css.test.ts. */
  --app-max: 640px;
}

.paperwork-engine-status { margin-top:8px; padding:10px 12px; border-radius:12px; border:1px solid #f59e0b55; background:#fffbeb; color:#78350f; font-size:13px; }
.paperwork-engine-status.is-ready { border-color:#16a34a55; background:#f0fdf4; color:#14532d; }
.paperwork-engine-status.is-running { border-color:#2563eb55; background:#eff6ff; color:#1e3a8a; }
.paperwork-engine-status.is-review { border-color:#d9770655; background:#fffbeb; color:#78350f; }
.paperwork-engine-status ul { margin:6px 0 0; padding-left:18px; }
.paperwork-engine-status li + li { margin-top:4px; }
.paperwork-engine-findings { display:grid; gap:8px; margin-top:9px; }
.paperwork-engine-finding { padding:9px 10px; border-radius:10px; border:1px solid #f59e0b66; background:#fff; }
.paperwork-engine-finding.is-action_required { border-color:#dc262666; background:#fef2f2; color:#7f1d1d; }
.paperwork-engine-finding-severity { display:inline-block; margin-bottom:5px; padding:2px 7px; border-radius:999px; background:#fef3c7; color:#92400e; font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.03em; }
.paperwork-engine-finding.is-action_required .paperwork-engine-finding-severity { background:#fee2e2; color:#991b1b; }
.paperwork-engine-finding strong { display:block; }
.paperwork-engine-finding p { margin:4px 0 0; }
.submission-instruction { margin-top:8px; padding:12px; border:2px solid #dc2626; border-radius:12px; background:#fef2f2; color:#7f1d1d; font-size:13px; }
.submission-instruction strong { display:block; color:#991b1b; font-size:13px; font-weight:800; letter-spacing:.03em; text-transform:uppercase; }
.submission-instruction p { margin:5px 0 0; color:#7f1d1d; font-weight:600; }

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1 { font-size: 28px; font-weight: 700; line-height: 1.2; letter-spacing: -0.02em; color: var(--color-text-strong); margin: 0 0 var(--space-3); }
h2 { font-size: 22px; font-weight: 700; line-height: 1.3; letter-spacing: -0.02em; color: var(--color-text-strong); margin: 0 0 var(--space-2); }
h3 { font-size: 17px; font-weight: 600; line-height: 1.35; color: var(--color-text-strong); margin: 0 0 var(--space-2); }
p  { margin: 0 0 var(--space-3); }
.muted { color: var(--color-text-muted); font-size: 14px; }
.faint { color: var(--color-text-faint); font-size: 12px; font-weight: 300; }
strong { font-weight: 600; color: var(--color-text-strong); }

/* ---- Cards ---- */
.card {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-card);
}

/* ---- Buttons ---- */
.btn {
  font-family: var(--font-sans); font-size: 15px; font-weight: 500;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  min-height: 44px;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  text-decoration: none;
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease, transform 80ms ease;
}
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent-faint), 0 0 0 4px var(--color-accent); }
.btn[disabled] { opacity: .6; cursor: progress; }
.btn-primary { background: var(--color-accent); color: var(--color-text-on-accent); font-weight: 700; }
.btn-primary:hover { background: var(--color-accent-hover); }
.btn-secondary { background: transparent; color: var(--color-text); border-color: var(--color-border-strong); }
.btn-secondary:hover { background: var(--color-bg-subtle); }
.btn-ghost { background: transparent; color: var(--color-accent); }
.btn-ghost:hover { background: var(--color-accent-faint); }
.btn-danger-ghost { background: transparent; color: var(--color-danger); border-color: transparent; }
.btn-danger-ghost:hover { background: var(--color-danger-soft); }
.btn-block { width: 100%; }
.btn-sm { min-height: 36px; padding: var(--space-2) var(--space-3); font-size: 14px; }

/* Google sign-in: white surface, neutral border, brand-coloured G mark. */
.btn-google {
  background: var(--color-card); color: var(--color-text-strong);
  border-color: var(--color-border-strong); font-weight: 600;
  gap: var(--space-3);
}
.btn-google:hover { background: var(--color-bg-subtle); }
.btn-google .g-icon { display: inline-flex; align-items: center; }
.btn-google .g-icon svg { display: block; }

/* ---- Inputs ---- */
label {
  display: block; font-size: 13px; font-weight: 500;
  color: var(--color-text); margin: var(--space-4) 0 var(--space-1);
}
input[type="text"], input[type="email"], input[type="password"] {
  font-family: var(--font-sans); font-size: 16px; /* >=16px stops iOS zoom-on-focus */
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  min-height: 48px; width: 100%;
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }

.pw-wrap { position: relative; }
.pw-wrap input { padding-right: 64px; }
.pw-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: transparent; border: 0; cursor: pointer;
  color: var(--color-accent); font-weight: 600; font-size: 13px;
  padding: var(--space-2) var(--space-3); min-height: 36px; border-radius: var(--radius-sm);
}
.pw-toggle:hover { background: var(--color-accent-faint); }

.form-error {
  margin: var(--space-3) 0 0;
  background: var(--color-danger-soft); color: var(--color-danger);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
  font-size: 14px; font-weight: 500;
}

/* ---- Pills ---- */
.pill {
  font-size: 12px; font-weight: 500; padding: 2px 10px; border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
  display: inline-flex; align-items: center; gap: 4px;
}
.pill-success { background: var(--color-success-soft); color: var(--color-success); }
.pill-warning { background: var(--color-warning-soft); color: var(--color-warning); }
.pill-accent  { background: var(--color-accent-soft);  color: var(--color-accent); }
.pill-info    { background: var(--color-info-soft);    color: var(--color-info); }
.pill-danger  { background: var(--color-danger-soft);  color: var(--color-danger); }

/* ---- Brand ---- */
.brand { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-5); }
.brand-mark { border-radius: var(--radius-md); display: block; }
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-name { font-weight: 800; font-size: 18px; color: var(--color-text-strong); letter-spacing: -0.01em; }
.brand-sub { font-size: 12px; color: var(--color-text-muted); }
.brand-sm { margin-bottom: 0; gap: var(--space-2); }
.brand-sm .brand-name { font-size: 16px; }

/* ---- Views / layout ---- */
/* The [hidden] attribute must beat the display rules below (.view-center,
   .view-app, etc. all set display:flex, which would otherwise override the
   UA [hidden]{display:none} and stack every view on top of each other). */
[hidden] { display: none !important; }
.view { min-height: 100vh; min-height: 100dvh; }
.view-center {
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  padding-top: max(var(--space-6), env(safe-area-inset-top));
  padding-bottom: max(var(--space-6), env(safe-area-inset-bottom));
}

/* ---- Spinner ---- */
.spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid var(--color-border); border-top-color: var(--color-accent);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Auth cards ---- */
.auth-card { width: 100%; max-width: 400px; }
.auth-intro { margin-bottom: var(--space-5); }
.auth-foot { margin: var(--space-5) 0 0; text-align: center; }
#google-signin { margin-top: var(--space-6); }

/* ============================================================
   App shell
   ============================================================ */
.view-app { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }

.app-header {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between;
  height: calc(var(--header-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) var(--space-4) 0;
  background: rgba(250,249,247,.9);
  backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--color-border);
}
.app-header-right { display: flex; align-items: center; gap: var(--space-2); }
.app-greeting { max-width: 38vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
/* JFA-663 — Light / Dark / Match device picker (theme.js). A 36px round icon so the
   phone header keeps its room; the native <select> sits on top of it, invisible. */
.theme-picker {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-full);
  color: var(--color-text-muted); border: 1px solid var(--color-border);
}
.theme-picker:hover { color: var(--color-accent); border-color: var(--color-border-strong); }
.theme-picker:focus-within { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.theme-select {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; font-size: 16px; /* >=16px: no iOS zoom on tap */
}
/* v158 — hide the global "Hi, name" + Sign out bar when inside a message thread.
   JS adds inbox-thread-open to <body> on openThread and removes it on back. */
body.inbox-thread-open .app-header { display: none; }
/* Bryan 2026-08-19 "there's waisted space at the top... when typing a message you cant see
   any of the conversation": inside a thread the View-as bar and the bottom tab bar are dead
   chrome — together ~150px that belongs to messages (the keyboard already covers half the
   screen). "‹ Inbox" (.dm-back) is always visible in a thread so there is still a way back;
   closing the thread restores both bars. The tab bar is position:fixed, so the .app-main
   reserve that keeps the composer above it goes with it (safe-area inset stays for the
   gesture bar). */
body.inbox-thread-open .operator-bar { display: none; }
body.inbox-thread-open .tabbar { display: none; }
body.inbox-thread-open .app-main { padding-bottom: calc(env(safe-area-inset-bottom) + var(--space-2)); }
/* Bryan 2026-08-20 "I still can't see any of the conversation when typing a reply":
   while the homeowner reply box has focus (inbox.js toggles body.inbox-typing), the
   address/status head and the AI-draft/Actions toolbar hand their rows to the message
   scroller — the body.inbox-typing rules live next to each base rule below. */

.app-main {
  flex: 1;
  width: 100%; max-width: var(--app-max); margin: 0 auto;
  padding: var(--space-5) var(--space-4);
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--space-6));
}

/* ---- Operator cockpit bar (operators only) ---- */
.operator-bar {
  position: sticky; top: calc(var(--header-h) + env(safe-area-inset-top)); z-index: 9;
  display: flex; align-items: center; gap: var(--space-2);
  max-width: var(--app-max); margin: 0 auto; width: 100%;
  padding: var(--space-2) var(--space-4);
  background: var(--color-accent-faint);
  border-bottom: 1px solid var(--color-border);
}
.operator-bar-eye { display: inline-flex; color: var(--color-accent); flex: none; }
.operator-bar-label { color: var(--color-text-muted); font-size: 13px; font-weight: 600; flex: none; }
.operator-switcher {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--font-sans); font-size: 14px; font-weight: 600;
  color: var(--color-text-strong); background: var(--color-card);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3); min-height: 36px;
}
.operator-switcher:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent-faint), 0 0 0 4px var(--color-accent); }
.operator-exit { flex: none; background: var(--color-card); border-color: var(--color-border-strong); color: var(--color-text); }
.operator-exit:hover { background: var(--color-bg-subtle); }
/* Actively viewing another client — make it unmistakable. */
.operator-bar.is-acting { background: var(--color-warning-soft); border-bottom-color: var(--color-warning); }
.operator-bar.is-acting .operator-bar-eye,
.operator-bar.is-acting .operator-bar-label { color: var(--color-warning); }

.panel-head { margin-bottom: var(--space-5); }
.panel-head h2 { margin-bottom: var(--space-1); }

/* Header row that pairs a section title with an action (e.g. Download .xlsx). */
.section-head-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
}
.section-head-row > h2,
.section-head-row > h3 { margin: 0; }

.tab-panel { animation: fade var(--motion-fast) var(--motion-easing); }
@keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---- Credential cards ---- */
.cred-list { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-6); }
.cred-item {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-4);
  box-shadow: var(--shadow-card);
  display: flex; align-items: flex-start; gap: var(--space-3);
}
.cred-item-icon {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--radius-md);
  background: var(--color-accent-faint); color: var(--color-accent);
  display: flex; align-items: center; justify-content: center;
}
.cred-item-body { flex: 1; min-width: 0; }
.cred-item-label { font-weight: 600; color: var(--color-text-strong); }
.cred-item-user { font-size: 14px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cred-item-meta { margin-top: var(--space-2); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.cred-item-actions { flex: 0 0 auto; }

.cred-form-card { margin-top: var(--space-2); }
/* Connect MillInfo (2026-10-13) */
.millinfo-connect { margin-bottom: var(--space-4); }
.millinfo-connect-note { font-size: 13px; }
.millinfo-account { margin-top: var(--space-3); }
.millinfo-row { margin-top: var(--space-2); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; overflow-wrap: anywhere; }
.millinfo-user, .millinfo-pw { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--color-text-strong); }
.millinfo-eye { position: static; transform: none; display: inline-flex; align-items: center; gap: var(--space-1); }
.millinfo-eye svg { flex: none; }
.millinfo-eye-off { font-size: 13px; }
.millinfo-form { margin-top: var(--space-3); }
.millinfo-form .cred-form-actions { flex-wrap: wrap; margin-top: var(--space-3); }
.cred-form-help { margin-bottom: var(--space-2); }
.cred-form-actions { display: flex; gap: var(--space-3); margin-top: var(--space-6); }
.cred-form-actions .btn-primary { flex: 1; }

/* ---- Empty / work states ---- */
.empty-state { text-align: center; padding: var(--space-10) var(--space-6); }
.empty-emoji { font-size: 40px; margin-bottom: var(--space-3); }
.work-list { display: flex; flex-direction: column; gap: var(--space-3); }
.work-item {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); padding: var(--space-4); box-shadow: var(--shadow-card);
}
.work-item-addr { font-weight: 600; color: var(--color-text-strong); }

/* ---- Bottom tab bar ---- */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex; justify-content: space-around; align-items: stretch;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: rgba(255,255,255,.92);
  backdrop-filter: saturate(1.4) blur(10px);
  border-top: 1px solid var(--color-border);
}
.tabbar-btn {
  position: relative;
  flex: 1; background: transparent; border: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  color: var(--color-text-muted); font-family: var(--font-sans);
  padding: var(--space-2) 0; min-height: 44px;
  transition: color var(--motion-fast) ease;
}
.tabbar-icon { display: flex; }
.tabbar-label { font-size: 11px; font-weight: 600; letter-spacing: 0.01em; }
.tabbar-btn.is-active { color: var(--color-accent); }
.tabbar-btn:active { transform: scale(0.96); }
/* Unread count bubble on a tab (board questions). Painted by app.js's
   startBoardUnreadPoll; sits at the top-right of the tab button. */
.tab-bubble {
  position: absolute; top: 4px; left: 50%; margin-left: 6px;
  min-width: 16px; height: 16px; line-height: 16px; text-align: center;
  font-size: 10px; font-weight: 700; padding: 0 4px;
  color: var(--color-text-on-accent); background: var(--color-danger);
  border-radius: var(--radius-full);
}

/* ---- More overflow menu (#tab declutter) ---- */
/* Pull the secondary tabs off the bar; they live in the ⋯ More sheet instead. */
.tabbar > [data-tab="credentials"],
.tabbar > [data-tab="messages"],
.tabbar > [data-tab="dm"],
.tabbar > [data-tab="live"],
.tabbar > [data-tab="settings"],
.tabbar > [data-tab="connections"],
.tabbar > [data-tab="routes"],
.tabbar > [data-tab="workdone"] { display: none; }
/* #43 — Assistant center button: floating circle above the tabbar baseline. */
.tabbar-btn--center {
  flex: none;
  width: 52px; height: 52px;
  margin-bottom: 6px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  align-self: center;
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
  padding: 0; gap: 0;
}
.tabbar-btn--center .tabbar-label { display: none; }
.tabbar-btn--center.is-active { background: var(--color-accent); }
.tabbar-btn--center:active { transform: scale(0.93); }
.more-sheet {
  position: fixed; z-index: 40;
  right: max(var(--space-3), env(safe-area-inset-right));
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--space-2));
  min-width: 208px; max-width: calc(100vw - 24px);
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-overlay);
  padding: var(--space-2); display: flex; flex-direction: column; gap: 2px;
}
.more-sheet[hidden] { display: none; }
.more-item {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; background: transparent; border: 0; cursor: pointer;
  padding: var(--space-3); border-radius: var(--radius-md);
  color: var(--color-text-strong); font-family: var(--font-sans);
  font-size: 15px; font-weight: 600; text-align: left;
}
.more-item:hover { background: var(--color-bg-subtle); }
.more-item.is-active { color: var(--color-accent); }
.more-item .tabbar-icon { color: var(--color-text-muted); flex: none; }
.more-item.is-active .tabbar-icon { color: var(--color-accent); }
/* Keep the sheet anchored to the centered column's right edge on wide screens. */
@media (min-width: 720px) {
  .more-sheet { right: calc(50% - 320px + var(--space-3)); }
}

/* ---- Toast ---- */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--space-4));
  z-index: 50;
  max-width: calc(100vw - 32px);
  background: var(--color-text-strong); color: #fff;
  padding: var(--space-3) var(--space-5); border-radius: var(--radius-full);
  box-shadow: var(--shadow-overlay); font-size: 14px; font-weight: 500;
  opacity: 0; transition: opacity var(--motion-fast) ease, transform var(--motion-fast) ease;
}
.toast.show { opacity: 1; }
.toast.toast-error { background: var(--color-danger); }
.toast.toast-success { background: var(--color-success); }

/* ---- Larger screens: center the bottom tab bar (it keeps a comfortable 640px cap so
   the touch targets don't sprawl, even when the content column is wider). ---- */
@media (min-width: 720px) {
  .tabbar { max-width: 640px; left: 50%; transform: translateX(-50%); border-radius: var(--radius-xl) var(--radius-xl) 0 0; }
}

/* ---- Desktop: stop wasting the sides — let the CONTENT column use the viewport ----
   Bryan (C, 2026-06-18): "desktop portal renders inbox + everything narrow/mobile-width
   with huge wasted side space." The shell is a phone-first centered column (--app-max:640px).
   From 700px up we grow that column with the viewport so the two-pane Inbox/DM (a fixed
   240px thread list + a flex conversation pane) and the card lists fill the screen instead
   of a 640px strip. min(1240px,94vw) ramps smoothly on tablets/laptops, then caps at 1240px
   on a big monitor so text lines stay readable rather than stretching edge to edge. Mobile
   (<700px) is untouched — --app-max stays 640px. Locked by styles-css.test.ts. */
@media (min-width: 700px) {
  :root { --app-max: min(1240px, 94vw); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 1ms !important; transition-duration: 60ms !important; }
}

/* ============================================================
   Land Tools tab
   ============================================================ */
#tab-land .land-card { margin-bottom: var(--space-5); }
#tab-land .land-card h3 { margin-bottom: var(--space-1); }
#tab-land .land-form-actions { margin-top: var(--space-5); }
#tab-land .land-form-actions .btn { width: 100%; }

/* Browser-helper setup card — accent-tinted so it reads as the prerequisite. */
#tab-land .land-helper {
  background: var(--color-accent-faint);
  border: 1px solid var(--color-accent-soft);
}
#tab-land .land-helper .land-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
#tab-land .land-helper .land-form-actions .btn { width: auto; flex: 1 1 auto; }
#tab-land .land-helper-steps {
  margin: var(--space-4) 0 0;
  padding-left: 1.25rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  font-size: .9rem;
}
#tab-land .land-helper-steps code {
  background: var(--color-bg-subtle);
  border-radius: var(--radius-sm);
  padding: 0 .35em;
}

#tab-land .land-mode {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-4) 0 0;
  display: flex; flex-wrap: wrap; gap: var(--space-3);
}
#tab-land .land-mode legend {
  font-size: 12px; font-weight: 600; color: var(--color-text-muted);
  padding: 0 var(--space-2);
}
#tab-land .land-radio {
  margin: 0; font-size: 14px; font-weight: 500;
  color: var(--color-text); display: inline-flex; align-items: center; gap: 4px;
}
#tab-land .land-radio input { margin: 0; }

#tab-land .land-status {
  margin-top: var(--space-4); padding: var(--space-3) var(--space-4);
  background: var(--color-bg-subtle); border-radius: var(--radius-md);
  font-style: italic; font-size: 14px; min-height: 1.5em;
}
#tab-land .land-status:empty { display: none; }
#tab-land .land-status a { color: var(--color-accent); font-weight: 600; }

#tab-land .land-history-title {
  margin: var(--space-5) 0 var(--space-2);
  font-size: 13px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--color-text-muted);
}
#tab-land .land-history {
  margin: 0; padding-left: var(--space-5);
  font-size: 14px; color: var(--color-text);
  display: flex; flex-direction: column; gap: var(--space-1);
}
#tab-land .land-history li { line-height: 1.4; }
#tab-land .land-history a { color: var(--color-accent); font-weight: 600; }

/* ============================================================
   Pending Texts tab
   ============================================================ */
.messages-list { display: flex; flex-direction: column; gap: var(--space-4); margin-bottom: var(--space-6); }

.message-card {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: opacity var(--motion-fast) ease;
}

.message-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.message-name {
  font-weight: 700;
  color: var(--color-text-strong);
  font-size: 16px;
}
.message-phone {
  font-size: 14px;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.message-pill { margin-left: auto; }

.message-body {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--color-bg-subtle, #FAF8F4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--color-text);
  white-space: pre-wrap;
  word-wrap: break-word;
}

.message-actions {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.message-actions .btn { flex: 1 1 auto; min-width: 0; }

.mark-sent {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
  font-size: 14px;
  color: var(--color-text-muted);
  cursor: pointer;
  user-select: none;
}
.mark-sent input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--color-accent);
  cursor: pointer;
}
.mark-sent input[type="checkbox"]:disabled { cursor: progress; }

/* Approved state — subtle green accent on the left edge so a glance
   tells you the card is actionable (Copy + Mark sent). */
.message-card.msg-approved {
  border-left: 3px solid var(--color-success, #16A34A);
}

/* Sent state — terminal. Greyed out + no actions. */
.message-card.msg-sent {
  opacity: 0.55;
}
.message-card.msg-sent .message-body { background: transparent; }

/* Scheduling picker on pending cards. */
.message-schedule {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.schedule-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1 1 140px;
  min-width: 0;
}
.schedule-label {
  font-size: 12px;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.schedule-field input[type="date"],
.schedule-field select {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-card);
  color: var(--color-text);
  font-size: 14px;
  font-family: inherit;
}
.schedule-when {
  font-size: 14px;
  color: var(--color-text-strong);
  font-weight: 500;
}
.schedule-when.faint { color: var(--color-text-muted); font-weight: normal; }
.schedule-hint {
  margin: 0;
  font-size: 12px;
  color: var(--color-text-muted);
  line-height: 1.4;
}
.schedule-error {
  font-size: 13px;
  color: var(--color-warning, #C2410C);
  margin-top: var(--space-1);
}

/* ============================================================
   Routes tab — same card pattern as messages, but each card is
   one optimized weekly route (Mon/Tue/Wed/...). Distributed
   plans get a green left-edge accent + greyed body, matching
   how the messages "sent" terminal state reads.
   ============================================================ */
.routes-list { display: flex; flex-direction: column; gap: var(--space-4); margin-bottom: var(--space-6); }

.route-card {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: opacity var(--motion-fast) ease;
}

.route-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.route-name {
  font-weight: 700;
  color: var(--color-text-strong);
  font-size: 16px;
}
.route-pill { margin-left: auto; }

.route-meta {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: 14px;
  color: var(--color-text-muted);
}
.route-meta-item { display: inline-flex; align-items: center; }
.route-link { color: var(--color-accent); text-decoration: none; }
.route-link:hover { text-decoration: underline; }

.route-actions {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  align-items: center;
}
.route-actions .btn { flex: 1 1 auto; min-width: 0; }
.route-actions .faint { color: var(--color-text-muted); font-size: 14px; }

.route-card.route-distributed {
  border-left: 3px solid var(--color-success, #16A34A);
  opacity: 0.75;
}

.field-route-group {
  margin-top: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface, #fff);
  overflow: clip;
}

.field-route-group > .field-route-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-3);
  cursor: pointer;
  font-weight: 700;
  color: var(--color-text-strong);
  list-style: none;
  user-select: none;
}

.field-route-group > .field-route-head::-webkit-details-marker { display: none; }
.field-route-group > .field-route-head::before { content: '▶'; font-size: 11px; color: var(--color-accent); transition: transform var(--motion-fast) ease; }
.field-route-group[open] > .field-route-head::before { transform: rotate(90deg); }
.field-route-group > .field-route-head > :first-child { flex: 1 1 auto; }
.field-route-progress { white-space: nowrap; font-weight: 600; color: var(--color-text-muted); }
.field-route-cases { padding: 0 var(--space-3) var(--space-3); }

/* Route disclosure — collapsible stops + map panel. */
.route-disclosure {
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-2);
  padding-top: var(--space-3);
}
.route-toggle {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--color-accent);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 14px;
}
.route-toggle:hover { text-decoration: underline; }
.route-toggle-caret { font-size: 10px; opacity: 0.75; transition: transform var(--motion-fast) ease; }
.route-detail {
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.route-map-wrap {
  width: 100%;
  display: flex;
  justify-content: center;
}
.route-map-img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-bg-subtle, #FAF8F4);
}
.route-map-placeholder {
  width: 100%;
  padding: var(--space-5) var(--space-3);
  text-align: center;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-subtle, #FAF8F4);
  font-size: 14px;
}
.route-stops {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: 14px;
}
.route-stops-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  border-bottom: 1px dashed var(--color-border);
}
.route-stops-item:last-child { border-bottom: none; }
.route-stops-num {
  flex: 0 0 auto;
  min-width: 24px;
  text-align: right;
  font-weight: 600;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.route-stops-body { flex: 1 1 auto; min-width: 0; }
.route-stop-addr { color: var(--color-text); }
.route-stop-name { color: var(--color-text-muted); }
.route-stop-depot {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0 6px;
  background: var(--color-success-soft, #DCFCE7);
  color: var(--color-success, #16A34A);
  border-radius: var(--radius-sm, 4px);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.route-stops-empty {
  padding: var(--space-3) 0;
  text-align: center;
}

/* Case photo on a George queue card — the picture he draws the sketch from.
   A bounded preview that opens full-size in a new tab on tap. */
.queue-card-photo {
  display: block;
  margin: var(--space-2) 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--color-border, #E5E7EB);
  background: var(--color-bg-subtle, #F9FAFB);
  line-height: 0;
}
.queue-card-photo img {
  display: block;
  width: 100%;
  max-height: 240px;
  object-fit: cover;
}

/* ============================================================
   Unified stop board (board.js) — JF operator board
   5-stage progress per stop, questions inbox, driver-completion panel.
   ============================================================ */
#board-root { display: flex; flex-direction: column; gap: var(--space-6); }
.board-stops, .board-questions, .board-drivers { display: flex; flex-direction: column; gap: var(--space-3); }
.board-stops h3, .board-questions h3, .board-drivers h3 { margin-bottom: var(--space-1); }

/* Stop cards */
.board-stop {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  box-shadow: var(--shadow-card);
  display: flex; flex-direction: column; gap: var(--space-3);
}
/* Change 1 (JF) — submitted stops no longer dim or sink under a "Submitted"
   divider. They stay in route order, looking the same; the only marker is the
   in-place .done-pill, which switches to "✓ Submitted" (accent) once George
   signs off. The pill variant lives next to .done-pill below. */
.board-stop-head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
/* Bryan 2026-09-24: high value, mid value and commercial cases get their own background on the case list,
   and every card shows its tier and photo count beside the address (visible even when the stop is folded). */
.board-stop.tier-high { background: #F4F0FF; border-color: #CFC2F2; border-left: 5px solid #7B5CD6; }
.board-stop.tier-mid { background: #EEF6FF; border-color: #C4DAF2; border-left: 5px solid #3B82C4; }
.board-stop.tier-commercial { background: #FFF6E8; border-color: #EED7AE; border-left: 5px solid #C98A1B; }
.board-case-facts { font-size: 12px; font-weight: 600; color: var(--color-text-muted); white-space: nowrap; }
.board-stop.tier-high .board-case-facts { color: #5B3FB0; }
.board-stop.tier-mid .board-case-facts { color: #245F96; }
.board-stop.tier-commercial .board-case-facts { color: #8A5C0C; }
.board-pos { font-weight: 700; color: var(--color-accent); font-size: 14px; }
.board-addr { font-weight: 600; color: var(--color-text-strong); }
/* #23 — the insurance carrier/account line under the address (board + cases cards) */
.board-carrier { font-size: 13px; font-weight: 600; color: var(--color-text-muted); letter-spacing: 0.02em; }
.board-stop-carrier-slot:empty { display: none; }
/* S2 (JF 2026-06-13) — the confirmed 4hr arrival window. Success-toned so Bryan can SEE at
   a glance that the block landed on the stop (the "block was not added" blind spot). The
   slot self-hides until Lane A projects entry.timing (dormant-safe). */
.board-arrival {
  display: inline-flex; align-items: center; align-self: flex-start;
  font-size: 12px; font-weight: 700; letter-spacing: 0.01em;
  color: var(--color-success); background: var(--color-success-soft);
  padding: 2px var(--space-2); border-radius: var(--radius-sm); margin-top: 2px;
}
.board-stop-arrival-slot:empty { display: none; }
/* JFA-60 — an appointment is required before arrival. Warning-toned and distinct from the green arrival window,
   which is the only confirmed-time display on the card. */
.board-appointment {
  display: inline-flex; align-items: center; align-self: flex-start;
  font-size: 12px; font-weight: 700; letter-spacing: 0.01em;
  color: var(--color-warning); background: var(--color-warning-soft);
  padding: 2px var(--space-2); border-radius: var(--radius-sm); margin-top: 2px;
}
.board-appointment.is-interior, .board-appointment.is-unclear { color: var(--color-danger); background: var(--color-danger-soft); }
.board-stop-appointment-slot:empty { display: none; }
.board-stop-closeout-slot:empty { display: none; }
.board-stop-closeout-slot { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.board-closeout-btn {
  min-height: 44px;
  border: 1px solid var(--color-warning);
  color: var(--color-warning);
  background: var(--color-card);
}
.board-closeout-btn.is-closed { background: var(--color-warning-soft); font-weight: 700; }
.closeout-state {
  font-size: 12px; font-weight: 700; color: var(--color-warning);
  background: var(--color-warning-soft); border-radius: var(--radius-full);
  padding: 3px var(--space-2);
}
/* A closeout still needs George's attention, so it is conspicuous without
   pretending the stop was inspected or submitted. */
.done-pill.is-closeout { color: #7a2e00; background: #ffdfb5; border: 2px solid #e07a00; font-weight: 900; letter-spacing: .035em; }
/* Bryan 2026-10-12: supplements show as short words ("Wood stove", "EHP"), not icons.
   The whole slot self-hides when the stop has no known supplement. */
.board-stop-supplement-slot:empty { display: none; }
.board-stop-supplements { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.board-supplement {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--color-border); background: var(--color-bg-subtle);
  color: var(--color-text); font-size: 12px; font-weight: 600; line-height: 1.4;
  white-space: nowrap; cursor: default;
}
.board-stop-foot { display: flex; gap: var(--space-2); }
.board-stop-foot:empty { display: none; }

/* JF's per-stop note + uploaded photo, shown with the stage bar */
.board-stop-note {
  font-size: 14px; color: var(--color-text); white-space: pre-wrap;
  background: var(--color-bg-subtle); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}
.board-stop-photo img {
  display: block; max-width: 220px; max-height: 160px; width: auto; height: auto;
  border-radius: var(--radius-sm); border: 1px solid var(--color-border);
}

/* #4 (JF 2026-06-11) — route-adjustment preview banner: a before/after diff of a
   driver's plan with Approve / Revert, shown above the board for the operator and
   George. The mount self-hides until route-preview.js fills it, so the whole feature
   stays invisible until Lane A's engine proposes a re-order (dormant-safe). */
.route-preview-mount:empty { display: none; }
.route-preview {
  background: var(--color-card);
  border: 1px solid var(--color-border-strong);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.route-preview-head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.route-preview-title { margin: 0; font-size: 15px; font-weight: 700; color: var(--color-text-strong); }
.route-preview-summary { font-size: 13px; font-weight: 600; color: var(--color-accent); }
.route-preview-reason { margin: var(--space-2) 0 0; font-size: 14px; color: var(--color-text-muted); }
.route-preview-cols {
  display: flex; align-items: stretch; gap: var(--space-3);
  margin-top: var(--space-3); flex-wrap: wrap;
}
.route-preview-col {
  flex: 1 1 240px; min-width: 0;
  background: var(--color-bg-subtle); border-radius: var(--radius-sm); padding: var(--space-3);
}
.route-preview-col-title {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-text-faint); margin-bottom: var(--space-2);
}
.route-preview-stops { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.route-preview-stop { display: flex; align-items: center; gap: var(--space-2); font-size: 13px; color: var(--color-text); padding: var(--space-1) 0; }
.route-preview-pos {
  flex: none; width: 20px; height: 20px; border-radius: var(--radius-full);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-card); border: 1px solid var(--color-border);
  font-size: 11px; font-weight: 700; color: var(--color-text-muted);
}
.route-preview-stop-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.route-preview-stop .muted { color: var(--color-text-faint); }
.route-preview-tag { flex: none; font-size: 11px; font-weight: 700; border-radius: var(--radius-full); padding: 1px var(--space-2); }
.route-preview-tag.is-moved { background: var(--color-warning-soft); color: var(--color-warning); }
.route-preview-tag.is-new { background: var(--color-success-soft); color: var(--color-success); }
.route-preview-stop.is-moved .route-preview-pos { border-color: var(--color-warning); color: var(--color-warning); }
.route-preview-stop.is-new .route-preview-pos { border-color: var(--color-success); color: var(--color-success); }
.route-preview-arrow { flex: none; align-self: center; font-size: 20px; color: var(--color-text-faint); }
.route-preview-removed { margin-top: var(--space-3); font-size: 13px; color: var(--color-danger); }
.route-preview-removed-title { font-weight: 700; }
.route-preview-actions { display: flex; gap: var(--space-2); justify-content: flex-end; margin-top: var(--space-4); }
.route-preview-approve, .route-preview-revert {
  font-family: inherit; font-size: 14px; font-weight: 600;
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-4);
  cursor: pointer; border: 1px solid transparent;
}
.route-preview-approve { background: var(--color-accent); color: var(--color-text-on-accent); }
.route-preview-approve:hover { background: var(--color-accent-hover); }
.route-preview-revert { background: var(--color-card); color: var(--color-text-muted); border-color: var(--color-border-strong); }
.route-preview-revert:hover { color: var(--color-text); border-color: var(--color-text-muted); }
.route-preview-approve:disabled, .route-preview-revert:disabled { opacity: .6; cursor: default; }
/* On a phone the before/after columns stack and the arrow rotates to point down. */
@media (max-width: 560px) {
  .route-preview-cols { flex-direction: column; }
  .route-preview-arrow { transform: rotate(90deg); }
}

/* Progress bar */
.stage-bar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1); }
.stage-seg {
  font-size: 12px; font-weight: 600; padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-faint);
  border: 1px solid var(--color-border); white-space: nowrap;
}
.stage-seg.stage-on { background: var(--color-accent-soft); color: var(--color-accent-hover); border-color: var(--color-accent-soft); }
.stage-seg.stage-dim { opacity: .45; font-style: italic; }              /* Paperwork — not wired in v1 */
.stage-seg.stage-failed { background: var(--color-danger-soft); color: var(--color-danger); border-color: var(--color-danger-soft); }
.stage-arrow { color: var(--color-text-faint); font-size: 11px; }
.stage-failed-badge {
  margin-left: var(--space-2); font-size: 11px; font-weight: 700;
  color: var(--color-text-on-accent); background: var(--color-danger);
  padding: 2px var(--space-2); border-radius: var(--radius-full);
}

/* Buttons (Ask George / inbox rows / sends) + unread badge */
.board-ask, .thread-row, .thread-send {
  font-family: var(--font-sans); font-size: 13px; font-weight: 500;
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-sm);
  cursor: pointer; border: 1px solid var(--color-border-strong);
  background: var(--color-card); color: var(--color-text);
}
.board-ask:hover, .thread-row:hover, .thread-send:hover { background: var(--color-card-hover); }
.badge {
  display: inline-block; min-width: 16px; text-align: center;
  font-size: 11px; font-weight: 700; line-height: 16px;
  color: var(--color-text-on-accent); background: var(--color-danger);
  border-radius: var(--radius-full); padding: 0 5px; margin-left: var(--space-1);
}

/* Questions inbox + thread view */
.board-questions .thread-row { display: block; width: 100%; text-align: left; margin-bottom: var(--space-1); }
.thread-view { margin-top: var(--space-3); border-top: 1px solid var(--color-border); padding-top: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.thread-msgs { display: flex; flex-direction: column; gap: var(--space-2); }
.thread-msg { font-size: 14px; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); background: var(--color-bg-subtle); }
.thread-msg.thread-george { background: var(--color-info-soft); }
.thread-compose { display: flex; gap: var(--space-2); align-items: flex-start; }
.thread-input, .driver-body {
  flex: 1; width: 100%; font-family: var(--font-sans); font-size: 14px;
  padding: var(--space-2); border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm); resize: vertical; min-height: 60px;
}

/* Driver completions panel */
.board-drivers h4 { margin: var(--space-2) 0 var(--space-1); font-size: 14px; color: var(--color-text-strong); }
.driver-notify {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); background: var(--color-bg-subtle);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}
.driver-notify label { font-size: 13px; color: var(--color-text-muted); }
.driver-to {
  font-family: var(--font-sans); font-size: 14px; padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); margin-left: var(--space-1);
}
.driver-send {
  align-self: flex-start; font-family: var(--font-sans); font-size: 13px; font-weight: 600;
  padding: var(--space-1) var(--space-4); border-radius: var(--radius-sm); cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent); border: 1px solid var(--color-accent);
}
.driver-send:hover { background: var(--color-accent-hover); }
.driver-send:disabled { opacity: .6; cursor: default; }

/* ============================================================
   Board v2 — unified card (stepper + editable note/photo + question
   thread + review actions), chip driver switcher, clean driver bar.
   Reuses .board-stop / .board-pos / .board-addr above.
   ============================================================ */
.board-controls { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-bottom: var(--space-3); }
.board-controls:empty { display: none; }
.board-driver-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.driver-chip {
  font: 600 13px var(--font-sans); padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full); border: 1px solid var(--color-border-strong);
  background: var(--color-card); color: var(--color-text); cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--space-2);
}
.driver-chip:hover { background: var(--color-card-hover); }
.driver-chip.is-active { background: var(--color-accent); color: var(--color-text-on-accent); border-color: var(--color-accent); }
/* #2 — per-driver "cases left for George" notification bubble inside each chip.
   Count = gateway readyCountsBySubmitter (george_review-stage cases for that driver),
   joined onto the roster by GET /api/board/drivers; board.js renders it only when > 0.
   On the active (accent) chip the colors invert so it stays legible. */
.driver-chip-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  border-radius: var(--radius-full); background: var(--color-accent);
  color: var(--color-text-on-accent); font: 700 11px var(--font-sans); line-height: 1;
}
.driver-chip.is-active .driver-chip-count { background: var(--color-card); color: var(--color-accent); }
/* Sharpierre explicitly marks a batch ready with “All cases uploaded and
   labeled”, so use a green completion dot instead of a competing quantity. */
.driver-chip-ready {
  width: 12px; height: 12px; flex: 0 0 12px; border-radius: 50%;
  background: var(--color-success); box-shadow: 0 0 0 2px var(--color-success-soft);
}
.driver-chip.is-active .driver-chip-ready { background: var(--color-card); box-shadow: 0 0 0 2px rgba(255,255,255,.35); }
.board-search {
  flex: 1; min-width: 140px; font: 14px var(--font-sans);
  padding: var(--space-1) var(--space-2); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm);
}
/* #126 — "Browse <driver>'s MillInfo" sits on its own full-width row under the driver
   chips (operator + George only) so it reads as a deliberate action, not a chip. */
.board-browse-row { flex-basis: 100%; display: flex; }
.board-browse-btn { width: 100%; justify-content: center; }
/* #126 — slot picker (Shane → Shane/Roz). A light centered modal appended to <body>. */
.slot-picker {
  position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .45); padding: var(--space-4);
}
.slot-picker-card {
  background: var(--color-card); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-4); width: 100%; max-width: 320px; box-shadow: var(--shadow-lg, 0 10px 30px rgba(0, 0, 0, .25));
  display: flex; flex-direction: column; gap: var(--space-3); text-align: center;
}
.slot-picker-title { font: 700 16px var(--font-sans); color: var(--color-text); }
.slot-picker-btns { display: flex; flex-direction: column; gap: var(--space-2); }
.slot-pick-btn { width: 100%; justify-content: center; }
.slot-picker-cancel { align-self: center; }
/* Spoke day picker (operator only) — sits above the board; cases.js reveals it. */
.board-day-select {
  font: 14px var(--font-sans); min-width: 200px; margin-bottom: var(--space-3);
  padding: var(--space-1) var(--space-2); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm);
  background: var(--color-card); color: var(--color-text);
}

/* Change 2 (JF) — cross-day search results. Drops in just under the controls (above the
   day's cards) and lists matching cases from OTHER route days; each row jumps George to
   that day. Hidden until a 2+ char query returns; collapses again when cleared. */
.board-search-results {
  margin-bottom: var(--space-3); overflow: hidden;
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); background: var(--color-card);
}
.board-search-results[hidden] { display: none; }
.board-search-head {
  font: 600 12px var(--font-sans); color: var(--color-text-muted);
  padding: var(--space-2); border-bottom: 1px solid var(--color-border);
}
.board-search-head:only-child { border-bottom: 0; }   /* "no matches" message stands alone */
.board-search-hit {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: var(--space-2); border: 0; border-top: 1px solid var(--color-border);
  background: var(--color-card); color: var(--color-text); cursor: pointer; font-family: var(--font-sans);
}
.board-search-hit:first-of-type { border-top: 0; }    /* head's border-bottom already divides */
.board-search-hit:hover { background: var(--color-card-hover); }
.hit-addr { font: 600 14px var(--font-sans); color: var(--color-text); }
.hit-city { font-weight: 400; color: var(--color-text-muted); }
.hit-when { font: 12px var(--font-sans); color: var(--color-text-muted); }

/* #1 (JF 2026-06-11) — George's Queue is ONE continuous per-driver list across days (the
   #42 carryover panel + #163 day picker are gone). A small day header rides on the FIRST
   card of each day-block (board.js renderDayGroup, gated on entry._queueDayFirst). It breaks
   the card's horizontal padding to span full width and sits under a hairline rule, reading
   as a section divider while staying a reconcile-safe per-card slot. A prior block is
   warn-toned ("still open" — Tuesday/Wednesday work George still owes); the active block is
   muted ("current route"). Empty slot collapses, so a single caught-up day stays dividerless. */
.board-stop-daygroup-slot:empty { display: none; }
.board-daygroup {
  display: flex; align-items: baseline; gap: var(--space-2);
  margin: 0 calc(-1 * var(--space-4)) var(--space-2);
  padding: 0 var(--space-4) var(--space-2);
  border-bottom: 1px solid var(--color-border);
}
.board-daygroup-label { font: 700 13px var(--font-sans); color: var(--color-text-strong); }
.board-daygroup-tag {
  font: 600 11px var(--font-sans); color: var(--color-text-muted);
  background: var(--color-bg-subtle); border-radius: var(--radius-full); padding: 1px var(--space-2);
}
.board-daygroup.is-prior { border-bottom-color: var(--color-warning); }
.board-daygroup.is-prior .board-daygroup-label { color: var(--color-warning); }
.board-daygroup.is-prior .board-daygroup-tag { color: var(--color-warning); background: var(--color-warning-soft); }
/* #30 — driver-section header in the "All" queue view */
.board-drivergroup {
  display: flex; align-items: center;
  margin: var(--space-3) calc(-1 * var(--space-4)) var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--color-bg-subtle); border-top: 1px solid var(--color-border);
}
.board-drivergroup-name { font: 700 14px var(--font-sans); color: var(--color-text-strong); }
/* Bryan 2026-09-25 ("Collapsible and consolidated"): on the queue each driver header is a
   button that folds that driver's cases away. A folded driver keeps only its header row. */
.board-drivergroup-toggle {
  width: calc(100% + 2 * var(--space-4)); gap: var(--space-2);
  border: 0; border-top: 1px solid var(--color-border); border-radius: 0;
  font-family: var(--font-sans); text-align: left; cursor: pointer; min-height: 44px;
}
.board-drivergroup-caret { width: 14px; color: var(--color-text-muted); font-size: 12px; }
.board-drivergroup-count { margin-left: auto; font: 600 12px var(--font-sans); color: var(--color-text-muted); }
.board-stop.is-group-hidden { display: none; }
.board-stop.is-group-head-only { background: transparent; border: 0; box-shadow: none; padding: 0 var(--space-4); gap: 0; }
.board-stop.is-group-head-only > :not(.board-stop-daygroup-slot) { display: none; }
.board-stop.is-group-head-only .board-drivergroup-toggle { margin-top: 0; margin-bottom: 0; }
.board-queue-row { display: flex; justify-content: flex-end; margin: 0 0 var(--space-2); }
/* Bryan's Queue tab: All / George's queue / My queue (board.js renderOperatorLaneBar). George's queue
   hides Bryan's own lists; My queue and All keep them. */
.board-queue-tabs { display: flex; flex-wrap: wrap; gap: var(--space-1); margin: 0 0 var(--space-3); }
.board-lane-empty { margin: 0 0 var(--space-2); }
#tab-queue[data-operator-lane="george"] #not-submitted-lane,
#tab-queue[data-operator-lane="george"] #millennium-corrections { display: none !important; }

/* Stepper */
.stepper { display: flex; align-items: flex-start; gap: 0; margin: var(--space-1) 0; }
/* Six nodes since "Portal sent" (Bryan 2026-09-22): 52px keeps all six plus their connectors inside a phone card. */
.stepper-node { display: flex; flex-direction: column; align-items: center; flex: 0 0 auto; width: 52px; text-align: center; }
.stepper-dot {
  width: 18px; height: 18px; border-radius: var(--radius-full);
  border: 2px solid var(--color-border-strong); background: var(--color-card);
  display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; line-height: 1;
}
/* Bryan 2026-09-22: one color for finished, another for in progress. Green = done, blue = in progress. */
.stepper-node.is-done .stepper-dot { background: var(--color-success); border-color: var(--color-success); color: #fff; }
.stepper-node.is-current .stepper-dot { background: var(--color-info-soft); border-color: var(--color-info); color: var(--color-info); box-shadow: 0 0 0 4px var(--color-info-soft); }
.stepper-node.is-dim { opacity: .45; }
.stepper-node.is-dim .stepper-dot { border-style: dashed; }
.stepper-node.is-failed .stepper-dot { background: var(--color-danger); border-color: var(--color-danger); color: var(--color-text-on-accent); }
/* New B follow-up (JF 2026-06-15) — the TERMINAL "Not submitted" outcome FILLS (so the final
   circle reads as lit, like the steps before it) but in the warning tone, matching
   .done-pill.is-not-submitted — never the green accent that would imply a successful submit. */
.stepper-node.is-not-submitted .stepper-dot { background: var(--color-warning); border-color: var(--color-warning); color: var(--color-text-on-accent); }
.stepper-label { font-size: 10px; color: var(--color-text-muted); margin-top: 3px; line-height: 1.15; }
.stepper-node.is-dim .stepper-label { font-style: italic; }
.stepper-conn { flex: 1 1 auto; height: 2px; background: var(--color-border); min-width: 6px; margin-top: 8px; }
.stepper-conn.is-done { background: var(--color-success); }
/* Item K (JF 2026-06-18) — the Inbox case-thread schedule bar reuses the board stepper but adds
   two tones the board never emits: is-warning = "pushed to next week" (amber, mirrors
   .is-not-submitted) and is-danger (red, mirrors .is-failed) for a future failed-reach branch.
   Additive — the board's stepperNode can't emit either, so existing board steppers are untouched. */
.stepper-node.is-warning .stepper-dot { background: var(--color-warning); border-color: var(--color-warning); color: var(--color-text-on-accent); }
.stepper-node.is-danger .stepper-dot { background: var(--color-danger); border-color: var(--color-danger); color: var(--color-text-on-accent); }

/* #61 follow-up — compact progress bar (operator/driver cases tab): the stepper's fill with
   NO node labels. A thin track under the address line that fills 0→100% across STAGES, so
   Bryan gets progress-at-a-glance without the "bunch of info" the labelled stepper carried. */
.progressbar { height: 6px; margin: var(--space-1) 0 var(--space-2); background: var(--color-border); border-radius: var(--radius-full); overflow: hidden; }
.progressbar-fill { display: block; height: 100%; width: 0; background: var(--color-accent); border-radius: var(--radius-full); transition: width .3s ease; }
.progressbar.is-failed .progressbar-fill { background: var(--color-danger); }

/* Item K (JF 2026-06-18) — compact case progress bar: the dots+connectors strip on the
   WrapDesk-style "contact card" (the manual worklist case card, renderCaseProgress). NO
   per-dot labels — the status pill names the current stage, and JF pulled the labelled
   stepper off this tab once (#2, "a bunch of info under the address"). Colors reuse the board
   stepper tokens so the two surfaces read as one product. A filled dot = a stage reached; the
   hollow ring = "you are here"; danger/warning tones flag an error / a paused case. */
.case-progress { display: flex; align-items: center; gap: 0; margin: var(--space-2) 0; }
.case-progress-dot {
  flex: 0 0 auto; width: 11px; height: 11px; border-radius: var(--radius-full); box-sizing: border-box;
  border: 2px solid var(--color-border-strong); background: var(--color-card);
}
.case-progress-conn { flex: 1 1 auto; height: 2px; min-width: 8px; background: var(--color-border); }
.case-progress-dot.is-done { background: var(--color-accent); border-color: var(--color-accent); }
.case-progress-conn.is-done { background: var(--color-accent); }
.case-progress-dot.is-current { border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.case-progress-dot.is-danger { background: var(--color-danger); border-color: var(--color-danger); }
.case-progress-dot.is-warning { background: var(--color-warning); border-color: var(--color-warning); }

.done-pill { font: 700 11px var(--font-sans); color: var(--color-success); background: var(--color-success-soft); border-radius: var(--radius-full); padding: 2px var(--space-2); }
/* Change 1 — "✓ Submitted" (George signed off, terminal) reads distinct from the
   green "✓ Done" (engine finished, awaiting review) via the teal accent. */
.done-pill.is-submitted { color: var(--color-accent); background: var(--color-accent-soft); }
/* #17 — "Not submitted" (George's other recorded outcome): terminal too, but
   warn-tinted so it never reads as a success at a glance. */
.done-pill.is-not-submitted { color: var(--color-warning); background: var(--color-warning-soft); }

/* #32 — the ONE stop the driver is currently headed to lights up: accent edge on the
   card, an "On route" head pill, and a filled first stepper dot. board.js applyOnRoute
   toggles .is-on-route / the pill's hidden attr from BoardLogic.onRouteStopId — at end
   of day (everything done/failed) nothing stays lit. */
.onroute-pill { font: 700 11px var(--font-sans); color: var(--color-text-on-accent); background: var(--color-accent); border-radius: var(--radius-full); padding: 2px var(--space-2); }
.board-stop.is-on-route { border-left: 3px solid var(--color-accent); box-shadow: 0 0 0 1px var(--color-accent-faint); }
.board-stop.is-on-route .stepper-node.is-current .stepper-dot { background: var(--color-accent); border-color: var(--color-accent); }

/* #3 (JF 2026-06-11) — picking a cross-day search hit scrolls to that stop and flashes it
   for ~2s, so JF lands straight on the chosen card (with its note + county sketch) instead of
   re-searching to surface it. board.js revealStop adds .is-search-reveal, then self-clears. */
.board-stop.is-search-reveal { animation: board-search-reveal 2.2s ease-out; }
@keyframes board-search-reveal {
  0%   { background: var(--color-accent-soft); box-shadow: 0 0 0 2px var(--color-accent); }
  65%  { background: var(--color-accent-soft); box-shadow: 0 0 0 2px var(--color-accent); }
  100% { background: transparent; box-shadow: 0 0 0 0 transparent; }
}

/* #162 — per-case MillInfo account tag (Shane / Roz). The slot sits just under the head.
   Operators/reviewers get a click-to-edit chip + Shane/Roz/Clear menu; read-only viewers
   see the static tag only when set. Empty slot collapses so untagged cards keep their height. */
.board-stop-account-slot:empty { display: none; }
.board-stop-account-slot { margin: 2px 0 var(--space-1); }

/* #41 (JF 2026-06-10) — once George marks a case submitted / not submitted the card
   collapses to just its address and grays out, so his queue reads as "done with this
   one". The detail slots hide while .is-collapsed is set; the head (with its status
   pill), the Expand/Collapse toggle, and the actions slot (its Undo) stay visible. The
   card stays grayed (.is-decided) until he taps Undo — which clears the outcome and
   restores the full card. createCard sets both classes by default; toggleDecided flips
   only .is-collapsed (the gray persists). */
.board-stop.is-decided { opacity: 0.6; }
.board-stop.is-decided .board-addr { color: var(--color-text-muted); }
.board-stop.is-collapsed .board-stop-carrier-slot,
.board-stop.is-collapsed .board-stop-account-slot,
.board-stop.is-collapsed .stepper,
.board-stop.is-collapsed .board-stop-note-slot,
.board-stop.is-collapsed .board-stop-assessor-slot,
.board-stop.is-collapsed .board-stop-thread-slot { display: none; }
.board-stop-collapse:empty { display: none; }

/* Bryan (2026-09-22) — his own board folds each stop to number, address and one work badge (like Nico's
   route list). Tapping the row opens the whole card; nothing inside is removed, only hidden while folded. */
.board-stop.is-foldable .board-stop-head { cursor: pointer; }
.board-stop.is-folded > :not(.board-stop-daygroup-slot):not(.board-stop-head):not(.board-stop-work-slot):not(.board-stop-supplement-slot):not(.stepper):not(.progressbar):not(dialog) { display: none; }
/* A folded stop keeps its supplement icons, so a Brush or EHP case never reads like any other stop. */
.board-stop.is-folded > .board-stop-supplement-slot { margin-top: 4px; }
/* A folded stop still shows how far it got: the same stepper, dots only. */
.board-stop.is-folded > .stepper { margin: 6px 0 0; }
.board-stop.is-folded > .stepper .stepper-label { display: none; }
/* Notes, preliminary comments and the interview live on the stop's Photos page (moved there while it is open). */
/* Bryan 2026-09-23: Add file stays on each case. The card keeps only the case's files and "+ Add file" from that box. */
.board-stop.notes-on-photo-page > .board-stop-note-slot .note-block > :not(.case-files):not(.photo-strip):not(.note-row-files):not(.note-row-add-photos):not(.photo-pending) { display: none; }
.board-stop.notes-on-photo-page > .board-stop-note-slot .note-block > .note-row.note-row-files { display: flex; }
/* Bryan 2026-09-23: a prominent "+ Add photos" right above "+ Add file" picks photos and opens the Photos page with them
   uploading. Only a card whose stop has a Photos page shows it; the Photos page itself has its own uploader. */
.note-block .note-row.note-row-add-photos { display: none; }
.board-stop.notes-on-photo-page > .board-stop-note-slot .note-block > .note-row.note-row-add-photos { display: flex; margin-top: 8px; }
.field-card-add-photos { min-height: 44px; padding-left: 20px; padding-right: 20px; font-size: 1rem; cursor: pointer; }
.field-board-notes .note-block { margin: 12px 0; }
.field-board-notes > .board-stop-note-slot ~ .board-stop-note-slot { display: none !important; }
.board-stop.is-folded { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.board-stop.is-folded .board-delivery-why { display: none; }
/* George's read-only case photos (2026-10-07): a button under the delivery chip opens a grid of small copies. */
.board-stop.is-folded .board-review-photos-btn, .board-stop.is-folded .board-review-photos,
.board-stop.is-folded .board-review-photos-msg, .board-stop.is-folded .board-review-note { display: none; }
.board-review-photos-btn { display: inline-flex; align-items: center; min-height: 40px; margin-top: 6px; padding: 0 14px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-card); color: var(--color-text); font: 600 14px var(--font-sans); cursor: pointer; }
.board-review-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; margin-top: 8px; }
.board-review-cell { margin: 0; min-width: 0; }
.board-review-thumb { display: block; width: 100%; padding: 0; border: 0; background: var(--color-bg); border-radius: var(--radius-sm); overflow: hidden; cursor: zoom-in; aspect-ratio: 4 / 3; }
.board-review-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.board-review-thumb-wait { display: flex; align-items: center; justify-content: center; aspect-ratio: 4 / 3; border-radius: var(--radius-sm); background: var(--color-bg); color: var(--color-text-muted); font-size: 12px; text-align: center; padding: 4px; }
.board-review-cell figcaption { margin-top: 2px; font-size: 12px; line-height: 1.3; color: var(--color-text-muted); overflow-wrap: anywhere; }
.board-review-photos-msg { margin-top: 6px; font-size: 13px; }
.board-review-note { margin-top: 8px; font-size: 13px; line-height: 1.4; white-space: pre-wrap; overflow-wrap: anywhere; }
.board-stop-head .done-pill, .board-stop-head .onroute-pill { white-space: nowrap; flex-shrink: 0; }
.board-fold-toggle { border: 0; background: none; padding: 0 4px; min-width: 28px; min-height: 28px; font-size: 16px; line-height: 1; color: var(--color-text-muted); cursor: pointer; align-self: center; }
.board-stop-work-slot:empty { display: none; }
.board-stop-work-slot { margin-top: 4px; }
.board-work-chip { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 13px; font-weight: 600; }
.board-work-chip.is-photos { background: var(--color-danger-soft); color: var(--color-danger); }
.board-work-chip.is-labels { background: var(--color-warning-soft); color: var(--color-warning); }
.board-work-chip.is-ready { background: var(--color-info-soft); color: var(--color-info); }
.board-work-chip.is-sending { background: var(--color-accent-soft); color: var(--color-accent-hover); }
.board-work-chip.is-sent { background: var(--color-success-soft); color: var(--color-success); }
.board-work-chip.is-check { background: var(--color-danger-soft); color: var(--color-danger); }
.board-work-dismiss { border: 0; background: none; padding: 0 6px; min-width: 32px; min-height: 32px; font-size: 18px; line-height: 1; color: var(--color-text-muted); cursor: pointer; vertical-align: middle; }
.board-work-restore { border: 0; background: none; padding: 4px 0; font: inherit; font-size: 13px; color: var(--color-text-muted); text-decoration: underline; cursor: pointer; }
.board-stop.is-folded .board-work-restore { display: none; }
.board-stop.is-folded .board-stop-work-slot:has(> .board-work-restore:only-child) { display: none; }
.board-route-send { margin: 0 0 var(--space-3); display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.board-route-send[hidden] { display: none; }
.board-route-send-status:empty { display: none; }
.board-route-send-status { font-size: 14px; }
.board-route-send-status .btn { margin-top: 6px; }
.board-route-send-blocked { margin: 4px 0 0; padding-left: 18px; font-size: 13px; color: var(--color-text-muted); }
.board-stop-collapse { margin: 2px 0; }
.decided-toggle { font: 600 12px var(--font-sans); color: var(--color-accent); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; }

.acct-wrap { position: relative; display: inline-block; }
.acct-chip {
  font: 600 11px var(--font-sans); padding: 2px var(--space-2); cursor: pointer;
  border-radius: var(--radius-full); border: 1px dashed var(--color-border-strong);
  background: var(--color-card); color: var(--color-text-muted);
  display: inline-flex; align-items: center; gap: 4px; line-height: 1.4;
}
.acct-chip:hover { background: var(--color-card-hover); }
.acct-chip.is-tagged {
  border-style: solid; border-color: var(--color-accent);
  background: var(--color-accent-faint); color: var(--color-accent);
}
span.acct-chip { cursor: default; }
.acct-menu {
  position: absolute; z-index: 20; top: calc(100% + 2px); left: 0; min-width: 120px;
  background: var(--color-card); border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm); box-shadow: 0 4px 12px rgba(0,0,0,.12);
  padding: 4px; display: flex; flex-direction: column; gap: 2px;
}
.acct-menu[hidden] { display: none; }
.acct-opt {
  font: 500 13px var(--font-sans); text-align: left; padding: var(--space-1) var(--space-2);
  border: 0; background: transparent; color: var(--color-text); cursor: pointer;
  border-radius: var(--radius-sm);
}
.acct-opt:hover { background: var(--color-bg-subtle); }
.acct-opt.is-selected { background: var(--color-accent-faint); color: var(--color-accent); font-weight: 700; }
.acct-opt.acct-clear { color: var(--color-text-muted); border-top: 1px solid var(--color-border); margin-top: 2px; }

/* Editable note + photo */
.note-block { background: var(--color-bg-subtle); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.note-tag { font: 700 11px var(--font-sans); color: var(--color-text-faint); text-transform: uppercase; letter-spacing: .04em; }
/* Portal notes and the eligible inline interview share one compact card. */
.note-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }

/* Preliminary form input is always visible and intentionally separate from the
   free-form Portal note so the paperwork engine can consume it deterministically. */
.paperwork-input { margin-top: 5px; padding-top: 7px; border-top: 1px solid var(--color-border); }
.paperwork-input-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.paperwork-input-head strong { color: var(--color-accent); font-size: 13px; }
.paperwork-input-head span { color: var(--color-text-faint); font-size: 10px; }
.paperwork-input-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px; align-items: start; }
.paperwork-input-row textarea { min-height: 40px; max-height: 96px; resize: vertical; box-sizing: border-box; padding: 8px 9px; border: 1px solid var(--color-border-strong); border-radius: 9px; font: 16px var(--font-sans); }
.paperwork-comments-readonly {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 9px;
  border: 1px solid var(--color-border);
  border-radius: 9px;
  background: var(--color-surface, #fff);
  color: var(--color-text);
  font-size: 14px;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.paperwork-input-row .btn { min-height: 40px; }
.paperwork-input-status { display: block; min-height: 14px; margin-top: 2px; color: var(--color-text-muted); font-size: 11px; }
.paperwork-input-status.is-pending { color: #7a5a13; font-weight: 700; }
.paperwork-input-status.is-applied { color: var(--color-success, #16745a); font-weight: 700; }
.paperwork-input-status.is-blocked { color: var(--color-danger, #a33a32); font-weight: 700; }

/* Compact inline inspection form: one calm summary row until Bryan opens it. */
.inline-interview { margin-top: 4px; padding-top: 7px; border-top: 1px solid var(--color-border); }
.inline-interview.is-loading { min-height: 54px; display: grid; align-items: center; }
/* JFA-33: Travelers HV MillInfo forms (hv-forms.js). */
.hv-form-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
.hv-form-open.is-active { background: var(--color-primary, #1f6feb); color: #fff; }
.hv-choice { display: flex; align-items: center; gap: 6px; font-size: 14px; }
.hv-required { color: #c62828; }
.inline-interview-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 5px; }
.inline-interview-title > div { display: flex; align-items: baseline; gap: 7px; }
.inline-interview-title strong { color: var(--color-accent); font-size: 14px; }
.inline-interview-title span { color: var(--color-text-faint); font-size: 11px; }
.inline-interview-readonly { padding: 3px 7px; border-radius: 999px; background: var(--color-bg-subtle); font-weight: 700; }
.inline-interview-more { margin: 0; }
.inline-interview-summary { min-height: 38px; display: flex; align-items: center; justify-content: space-between; gap: 8px; color: var(--color-text); cursor: pointer; list-style: none; }
.inline-interview-summary::-webkit-details-marker { display: none; }
.inline-interview-summary > span:first-child { display: flex; align-items: baseline; gap: 8px; }
.inline-interview-summary strong { color: var(--color-accent); font-size: 13px; }
.inline-interview-summary small { color: var(--color-text-faint); font-size: 10px; font-weight: 600; }
.inline-interview-chevron { color: var(--color-accent); font-size: 19px; line-height: 1; transition: transform .16s ease; }
.inline-interview-more[open] .inline-interview-chevron { transform: rotate(180deg); }
.inline-interview-panel { padding: 6px 0 2px; border-top: 1px solid var(--color-border); }
.inline-interview-save { width: auto; min-height: 38px; padding-inline: 14px; }
.inline-interview-error { margin: 0; color: var(--color-danger, #b42318); font-size: 13px; }
.inline-interview-late { margin: 0 0 8px; padding: 6px 8px; border-radius: 6px; background: #fff7e6; color: #7a4a00; font-size: 12px; line-height: 1.35; }
.field-interview-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px 8px; }
.field-interview-section {
  grid-column: 1 / -1;
  margin: 10px 0 1px;
  padding-top: 10px;
  border-top: 1px solid var(--color-border);
  color: var(--color-text);
  font-size: 13px;
  font-weight: 800;
}
.field-interview-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.field-interview-field { display: flex; flex-direction: column; gap: 3px; }
.field-interview-field.is-wide { grid-column: 1 / -1; }
.flooring-field { gap: 6px; }
.flooring-rows { display: grid; gap: 6px; }
.flooring-row { display: grid; grid-template-columns: minmax(0, 1fr) 92px 32px; gap: 7px; align-items: end; }
.flooring-row label { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.flooring-row label > span:first-child { color: var(--color-text-faint); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.flooring-percent-control { display: flex; align-items: center; min-height: 36px; border: 1px solid var(--color-border-strong); border-radius: 8px; background: var(--color-bg, #fff); overflow: hidden; }
.flooring-percent-control input { min-height: 34px; border: 0; border-radius: 0; text-align: right; }
.flooring-percent-control b { padding-right: 8px; color: var(--color-text-muted); }
.flooring-remove { width: 32px; height: 36px; border: 0; border-radius: 8px; background: var(--color-bg-subtle); color: var(--color-text-muted); font-size: 19px; }
.flooring-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 30px; }
.flooring-footer span { color: var(--color-text-muted); font-size: 11px; font-weight: 700; }
.flooring-footer span.is-complete { color: var(--color-success, #16794a); }
.flooring-footer span.is-error { color: var(--color-danger, #b42318); }
.flooring-footer button { min-height: 32px; border: 0; background: transparent; color: var(--color-accent); font-weight: 750; }
.field-interview-label { color: var(--color-text-muted); font-size: 12px; font-weight: 700; }
.field-interview-field input, .field-interview-field select {
  width: 100%; min-width: 0; min-height: 36px; box-sizing: border-box; border: 1px solid var(--color-border-strong);
  border-radius: 8px; padding: 6px 8px; background: var(--color-bg, #fff); color: var(--color-text);
  font: 16px var(--font-sans);
}
.field-interview-field input[readonly], .field-interview-field select:disabled { background: var(--color-bg-subtle); color: var(--color-text-muted); }
.field-interview-other, .flooring-other { margin-top: 4px; }
.occupancy-field { gap: 6px; }
.occupancy-choices { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 10px; }
.occupancy-choice { display: flex; align-items: center; gap: 8px; min-height: 36px; font-size: 14px; color: var(--color-text); }
.field-interview-field .occupancy-choice input[type="checkbox"] { width: 20px; min-width: 20px; min-height: 20px; height: 20px; margin: 0; padding: 0; flex: 0 0 auto; }
.fireplace-field { gap: 6px; }
.fireplace-rows { display: grid; gap: 6px; }
.fireplace-row { display: grid; grid-template-columns: minmax(0, 1fr) 72px 32px; gap: 7px; align-items: end; }
.fireplace-row label { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.fireplace-row label > span { color: var(--color-text-faint); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.fireplace-remove { width: 32px; height: 36px; border: 0; border-radius: 8px; background: var(--color-bg-subtle); color: var(--color-text-muted); font-size: 19px; }
.fireplace-footer { display: flex; justify-content: flex-end; min-height: 30px; }
.fireplace-footer button { min-height: 32px; border: 0; background: transparent; color: var(--color-accent); font-weight: 750; }
.field-interview-field.is-sourced { padding: 10px 12px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-bg-subtle); }
.field-interview-source { color: var(--color-text-faint); font-size: 11px; }
.field-interview-help { color: var(--color-text-faint); font-size: 11px; line-height: 1.3; }
.inline-interview-actions { min-height: 40px; margin-top: 8px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.field-interview-status { min-height: 18px; color: var(--color-danger, #b42318); font-size: 12px; font-weight: 600; }
@media (max-width: 360px) { .field-interview-fields { grid-template-columns: 1fr; } .field-interview-field.is-wide { grid-column: auto; } }
.note-edit {
  width: 100%; font: 14px var(--font-sans); line-height: 1.45; border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm); padding: var(--space-2); box-sizing: border-box;
  /* #139: JS (BoardLogic.autoGrowNote) owns the height — grows with content up to the cap,
     then scrolls. resize:none so a manual drag can't fight the auto-grow on the next keystroke. */
  resize: none; min-height: 52px; max-height: 360px; overflow-y: hidden; transition: height .08s ease;
}
.note-ro { font-size: 14px; white-space: pre-wrap; color: var(--color-text); }
.note-empty { font-size: 13px; color: var(--color-text-faint); font-style: italic; }
.note-hint { margin: 0; font-size: 11px; color: var(--color-text-faint); }
.note-row { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.photo-thumb { width: 108px; height: 82px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--color-border); }
.photo-add { font-size: 13px; color: var(--color-accent-hover); cursor: pointer; }

.assessor-candidate-help {
  margin: var(--space-1) 0 var(--space-2);
  color: var(--color-text-muted);
  font-size: 12px;
}
.assessor-candidate-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(84%, 1fr);
  gap: var(--space-3);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  padding: var(--space-1) var(--space-1) var(--space-3);
  touch-action: pan-x;
}
.assessor-candidate-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
  scroll-snap-align: start;
}
.assessor-candidate-count,
.assessor-candidate-type,
.assessor-candidate-apn {
  color: var(--color-text-muted);
  font-size: 12px;
}
.assessor-candidate-address { line-height: 1.3; }
.assessor-use-parcel { min-height: 44px; margin-top: auto; }
@media (min-width: 720px) {
  .assessor-candidate-track { grid-auto-columns: minmax(300px, 46%); }
}

/* #165 — collapse a long/multi-line note to ~2 lines so it stops eating the screen;
   the Expand/Collapse toggle (data-role="note-toggle", label from BoardLogic.noteToggleLabel)
   flips it. View-only — the textarea value / saved note is never modified. The editable box
   clamps to its 2-row floor (52px = min-height); the read-only note line-clamps with a soft
   fade hint that there's more below. */
.note-edit.is-collapsed { max-height: 52px; overflow-y: hidden; }
.note-ro.is-collapsed {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, #000 62%, transparent);
          mask-image: linear-gradient(180deg, #000 62%, transparent);
}
.note-toggle {
  align-self: flex-start; margin: 0; padding: 2px 0;
  background: transparent; border: 0; cursor: pointer;
  font: 600 12px var(--font-sans); color: var(--color-accent);
}
.note-toggle:hover { text-decoration: underline; }

/* #145 — a stop now holds MANY photos, shown as a wrapping strip of thumbnails (was a
   single thumb). On JF's editable cards each cell carries a small "×" to delete that one
   photo; George's read-only cards show the same strip without the ×. */
.photo-strip { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-1) 0; }
.photo-cell { position: relative; display: inline-flex; }
.photo-cell .photo-thumb { display: block; }
.photo-del {
  position: absolute; top: -6px; right: -6px;
  width: 20px; height: 20px; padding: 0; line-height: 18px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-border); border-radius: 50%;
  background: var(--color-surface, #fff); color: var(--color-text);
  font: 700 14px var(--font-sans); cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .18);
}
.photo-del:hover { background: var(--color-danger, #DC2626); color: #fff; border-color: var(--color-danger, #DC2626); }

/* County-assessor sketch (#122) — sits just below the note/photo block */
.assessor-block { display: flex; flex-direction: column; gap: var(--space-2); background: var(--color-bg-subtle); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); }
.assessor-address-control { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-2); padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); }
.assessor-address-value { font-size: 14px; color: var(--color-text-strong); }
.assessor-address-suggested { width: fit-content; padding: 2px 7px; border-radius: var(--radius-full); background: var(--color-warning-soft); color: var(--color-warning); font-size: 11px; font-weight: 700; }
.assessor-address-help { margin: 0; color: var(--color-text-muted); font-size: 12px; line-height: 1.4; }
.assessor-address-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.assessor-address-panel { display: flex; flex-direction: column; gap: var(--space-3); padding-top: var(--space-2); border-top: 1px solid var(--color-border); }
.assessor-address-panel[hidden] { display: none; }
.assessor-address-panel label { display: flex; flex-direction: column; gap: var(--space-1); color: var(--color-text-muted); font-size: 12px; font-weight: 600; }
.assessor-address-panel input { width: 100%; box-sizing: border-box; min-height: 44px; padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); background: var(--color-card); color: var(--color-text); font: 16px var(--font-sans); }
.assessor-tag { font: 700 11px var(--font-sans); color: var(--color-text-faint); text-transform: uppercase; letter-spacing: .04em; }
.assessor-figure { margin: 0; display: flex; flex-direction: column; gap: var(--space-1); }
/* Sketches are black line-art on white — force a white mat so a dark theme can't invert them */
.assessor-sketch { display: block; width: 100%; max-width: 360px; height: auto; background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.assessor-cap { font-size: 12px; color: var(--color-text-faint); }
.assessor-warn { margin: 0; font-size: 13px; color: var(--color-warning); }
.assessor-links { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.assessor-link { font: 600 13px var(--font-sans); color: var(--color-accent-hover); text-decoration: none; padding: var(--space-1) var(--space-3); border: 1px solid var(--color-accent-soft); border-radius: var(--radius-sm); }
.assessor-link:hover { background: var(--color-accent-faint); }

/* Question bubble (folded into the card). Neutral by default; turns yellow ONLY
   once George has actually asked a question (.has-question, set from
   BoardLogic.hasQuestion → thread.count > 0). JF: "a normal background color"
   until then. */
.qbubble { background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); display: flex; flex-direction: column; gap: var(--space-1); }
.qbubble.has-question { background: var(--color-warning-soft); border-color: #FDE68A; }
.qbubble-head { display: flex; align-items: center; gap: var(--space-2); font: 600 13px var(--font-sans); color: var(--color-text-muted); }
.qbubble.has-question .qbubble-head { color: var(--color-warning); }
.qbubble-dot { width: 8px; height: 8px; border-radius: var(--radius-full); background: var(--color-danger); }
.qbubble-msg { font-size: 14px; color: var(--color-text); }
/* #166 — an attached screenshot renders inline under the message text; click opens it full-size. */
.qbubble-img-link { display: block; margin-top: var(--space-1); width: max-content; max-width: 100%; }
.qbubble-img { max-width: 200px; max-height: 160px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); display: block; }
.qbubble-thread { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-1); }
/* #166 — compose stacks the textarea over an actions row (attach · filename · send). */
.q-compose { display: flex; flex-direction: column; gap: var(--space-2); align-items: stretch; }
.q-input { flex: 1; font: 14px var(--font-sans); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); padding: var(--space-2); resize: vertical; min-height: 40px; }
.q-compose-row { display: flex; align-items: center; gap: var(--space-2); }
.q-attach { font: 13px var(--font-sans); color: var(--color-text-muted); cursor: pointer; padding: var(--space-1) var(--space-2); border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); white-space: nowrap; }
.q-attach:hover { background: var(--color-bg-subtle); }
.q-attach-name { flex: 1; font-size: 12px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-compose-row .btn { margin-left: auto; }

/* Review actions (George) */
.review-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* Clean driver bar */
.board-driverbar:empty { display: none; }
.driverbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); background: var(--color-accent-faint); border: 1px solid var(--color-accent-soft); border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); margin-top: var(--space-3); }
.driverbar-text { font-size: 14px; color: var(--color-text); }
.driverbar-text b { color: var(--color-text-strong); }
.btn-text-driver { font: 600 13px var(--font-sans); padding: var(--space-1) var(--space-4); border-radius: var(--radius-sm); cursor: pointer; background: var(--color-accent); color: var(--color-text-on-accent); border: 1px solid var(--color-accent); }
.btn-text-driver:hover { background: var(--color-accent-hover); }
.btn-text-driver:disabled { opacity: .55; cursor: default; }
.driver-compose { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); padding: var(--space-3); background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.driver-compose label { font-size: 13px; color: var(--color-text-muted); }

/* ---- Chat (human↔human DM) tab #137b — adapted to the portal's light-theme tokens ---- */
/* The chat region flex-fills the capped chat column (.view-app.chat-app, below) at EVERY
   width, so the composer pins above the fixed bottom tab bar on the unfolded Z Fold inner
   screen (>640px) too — not only the <=640 cover screen. The old height:calc(100vh-180px)
   magic box ignored the operator bar / real chrome and dropped the composer ~65px behind
   the tabs whenever the viewport was wider than the 640 phone breakpoint (JF 2026-06-12). */
.dm-wrap { display: flex; gap: var(--space-3); position: relative; flex: 1 1 auto; min-height: 0; height: auto; }
.dm-list { flex: 0 0 240px; overflow-y: auto; border-right: 1px solid var(--color-border); }
.dm-thread-wrap { position: relative; border-bottom: 1px solid var(--color-border); }
.dm-thread { display: block; width: 100%; text-align: left; padding: var(--space-3) 42px var(--space-3) var(--space-3); background: none; border: 0; color: inherit; cursor: pointer; position: relative; font: inherit; }
.dm-thread.is-active { background: var(--color-bg-subtle); }
.dm-thread-name { display: block; font-weight: 600; color: var(--color-text-strong); }
.dm-thread-preview { display: block; font-size: 12px; color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dm-thread-badge { position: absolute; right: 40px; top: 12px; background: var(--color-accent); color: var(--color-text-on-accent); border-radius: var(--radius-full); font-size: 11px; padding: 1px 6px; }
.inbox-pin-toggle { position: absolute; right: 7px; bottom: 9px; width: 32px; height: 32px; border: 0; border-radius: var(--radius-full); background: transparent; color: var(--color-text-muted); font-size: 22px; cursor: pointer; }
.inbox-pin-toggle:hover { background: var(--color-accent-faint); color: var(--color-accent); }
.inbox-pinned { padding: var(--space-2) var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-border); }
.inbox-pinned-label { padding: 0 var(--space-1) var(--space-2); font: 700 11px var(--font-sans); letter-spacing: .05em; text-transform: uppercase; color: var(--color-text-muted); }
.inbox-pinned-strip { display: flex; gap: var(--space-2); overflow-x: auto; padding: 2px 2px 4px; }
.inbox-pinned-item { position: relative; flex: 0 0 68px; border-radius: var(--radius-md); }
.inbox-pinned-item.is-active { background: var(--color-accent-faint); }
.inbox-pinned-open { width: 68px; padding: 4px 2px; border: 0; background: transparent; color: inherit; cursor: pointer; }
.inbox-pinned-avatar { display: flex; align-items: center; justify-content: center; width: 48px; height: 48px; margin: 0 auto 4px; border-radius: 50%; background: var(--color-accent); color: var(--color-text-on-accent); font: 700 15px var(--font-sans); box-shadow: 0 0 0 2px var(--color-card), 0 0 0 3px var(--color-accent-soft); }
.inbox-pinned-unread { position: absolute; top: 0; right: 5px; min-width: 20px; height: 20px; padding: 0 5px; border: 2px solid var(--color-card); border-radius: var(--radius-full); background: var(--color-danger, #b42318); color: #fff; font: 700 11px/16px var(--font-sans); text-align: center; }
.inbox-pinned-name { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font: 600 11px var(--font-sans); text-align: center; }
.inbox-pin-remove { position: absolute; top: 0; right: 0; width: 20px; height: 20px; padding: 0; border: 1px solid var(--color-border); border-radius: 50%; background: var(--color-card); color: var(--color-text-muted); font: 700 14px/18px var(--font-sans); cursor: pointer; }
.inbox-pin-remove:hover { color: var(--color-danger, #b42318); }
.dm-pane { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.dm-pane-title { color: var(--color-text-strong); font-size: 16px; line-height: 1.2; min-height: 24px; padding: 4px var(--space-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Bryan 2026-08-20 — the back control and the thread name share ONE row instead of
   stacking two; every row saved above the messages is a row of conversation. */
.dm-pane-topbar { display: flex; align-items: center; gap: var(--space-1); min-height: 34px; flex: none; }
.dm-pane-topbar .dm-back { align-self: center; flex: none; }
.dm-pane-topbar .dm-pane-title { flex: 1 1 auto; min-width: 0; padding: 4px var(--space-1); }
.dm-sender-name { color: var(--color-text-muted); font-size: 11px; font-weight: 700; margin: 0 8px 2px; }
.dm-run.is-mine .dm-sender-name { text-align: right; }
/* overscroll-behavior:contain (#14, J) — the message list is the ONLY scroller in the
   chat pane; trapping its scroll here stops drag-to-scroll from chaining out to the page
   on a real phone, which is what let content/the composer slide behind the fixed bottom
   tab bar (the v73 flex bound is correct in-layout, but iOS scroll-chaining defeated it). */
.dm-messages { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: var(--space-2) var(--space-1); display: flex; flex-direction: column; gap: var(--space-2); }
.dm-show-earlier { align-self: center; border: 1px solid var(--color-border); border-radius: 999px; background: var(--color-card); color: var(--color-text); padding: .45rem .9rem; cursor: pointer; }
.dm-run { display: flex; flex-direction: column; gap: 2px; max-width: 78%; }
.dm-run.is-mine { align-self: flex-end; align-items: flex-end; }
.dm-run.is-theirs { align-self: flex-start; align-items: flex-start; }
/* white-space:pre-wrap (2026-06-12) — message bodies carry \n / \n\n (the appointment
   opener and every chat text); without this the CSS default `normal` collapses them into
   one run-on line. overflow-wrap guards a long unbroken token from overflowing the 78%
   bubble on a 390px phone. Fixes Bryan's "every message is one nonstop sentence". */
/* font-size:13px (Bryan 2026-06-24: "be smaller writing. no reason to be so large.") —
   message bodies were inheriting the 15px base; 13px reads lighter on a 390px phone. */
.dm-bubble { padding: var(--space-2) var(--space-3); border-radius: var(--radius-lg); background: var(--color-card); border: 1px solid var(--color-border); color: var(--color-text); white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; }
.dm-run.is-mine .dm-bubble { background: var(--color-accent); color: var(--color-text-on-accent); border-color: var(--color-accent); }
/* Internal recall (Bryan ↔ George ↔ drivers) — the tombstone that replaces a recalled
   body for every participant. Muted + italic + dashed, identical on both sides (override
   the is-mine accent fill so a recalled message never reads as a live one). */
.dm-recalled, .dm-run.is-mine .dm-bubble.dm-recalled { background: transparent; color: var(--color-text-muted); border: 1px dashed var(--color-border-strong); font-style: italic; }
/* The Recall control under one of my own messages. Small + quiet by default; the armed
   ("Tap again to recall") state turns warning-colored so the confirming tap is deliberate. */
.dm-recall-btn { align-self: flex-end; margin-top: 2px; border: none; background: transparent; cursor: pointer; font: 600 11px var(--font-sans); color: var(--color-text-muted); padding: 2px 4px; }
.dm-recall-btn:hover { color: var(--color-text); text-decoration: underline; }
.dm-recall-btn.is-armed { color: var(--color-danger, #b42318); text-decoration: none; }
.dm-recall-btn:disabled { opacity: .55; cursor: default; }
/* flex:0 0 auto (#14, J) — the composer keeps its natural height and never shrinks, so a
   short viewport or the on-screen keyboard can't compress it or push it under the tab bar;
   the message list absorbs all the flex give above it. */
.dm-composer { flex: 0 0 auto; display: flex; gap: var(--space-2); padding: var(--space-2) var(--space-1); border-top: 1px solid var(--color-border); }
.dm-input { flex: 1 1 auto; resize: none; min-height: 38px; max-height: 140px; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); border: 1px solid var(--color-border-strong); font: 14px var(--font-sans); }
.dm-send { font: 600 13px var(--font-sans); padding: var(--space-1) var(--space-4); border-radius: var(--radius-sm); cursor: pointer; background: var(--color-accent); color: var(--color-text-on-accent); border: 1px solid var(--color-accent); }
.dm-send:hover { background: var(--color-accent-hover); }
.dm-send:disabled { opacity: .55; cursor: default; }
/* Attach (📎) control + staged-file preview + in-bubble attachments (#184). */
.dm-attach-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 38px; min-height: 38px; border-radius: var(--radius-md); border: 1px solid var(--color-border-strong); background: var(--color-card); cursor: pointer; font-size: 18px; flex: 0 0 auto; }
.dm-attach-btn:hover { background: var(--color-bg-subtle, var(--color-card)); }
/* MSGSEND1: a send that wasn't confirmed says so right above the composer, which
   still holds the message. */
.dm-send-status { padding: var(--space-2) var(--space-3); border-top: 1px solid var(--color-border); font: 13px/1.4 var(--font-sans); color: var(--color-warning); background: var(--color-warning-soft); }
.dm-send-status.is-error { color: var(--color-danger); background: var(--color-danger-soft); }
.dm-attach-preview { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-2) var(--space-3); border-top: 1px solid var(--color-border); }
.dm-attach-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--color-border); }
.dm-attach-fileicon { font-size: 22px; }
.dm-attach-pname { flex: 1 1 auto; font: 13px var(--font-sans); color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dm-attach-clear { border: none; background: transparent; cursor: pointer; color: var(--color-text-muted); font-size: 15px; padding: 0 var(--space-1); }
/* Multi-attach staging strip (Bryan 2026-08-20): each staged file is its own
   chip with a thumb/icon, name, and remove control; a count tail follows. */
.dm-attach-item { display: inline-flex; align-items: center; gap: var(--space-1); max-width: 180px; padding: 2px var(--space-1); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-card); }
.dm-attach-item .dm-attach-pname { flex: 0 1 auto; max-width: 110px; }
.dm-attach-count { font: 600 12px var(--font-sans); color: var(--color-text-muted); }
/* Chat videos: inline player capped so a landscape recording can't take the pane. */
.dm-attach-videowrap { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 3px; max-width: 100%; }
/* Bryan 2026-08-20 — 240px max-height squeezed portrait phone videos to ~110px wide, so the
   browser collapsed all controls into its ⋮ menu, which opened off the screen edge. Let
   portrait videos stand taller and never narrower than ~200px so controls fit inline. */
.dm-attach-video { max-width: min(320px, 100%); max-height: min(55vh, 460px); min-width: min(200px, 100%); border-radius: var(--radius-md); border: 1px solid var(--color-border); background: #000; }
.dm-attach-expired { display: inline-block; font: 13px var(--font-sans); color: var(--color-text-muted); font-style: italic; padding: var(--space-1) 0; }
.dm-bubble-attach { padding: var(--space-1); }
.dm-attach-img { max-width: 220px; max-height: 220px; border-radius: var(--radius-md); display: block; }
.dm-attach-file { display: inline-block; font: 600 13px var(--font-sans); color: var(--color-accent); text-decoration: none; padding: var(--space-1) 0; }
.dm-attach-file:hover { text-decoration: underline; }
.dm-run.is-mine .dm-attach-file { color: var(--color-text-on-accent); }
/* G — photo(s) attached to a Cases-tab case render inline (not just the floor-plan chip). */
.case-photos { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.case-photo-link { display: inline-block; line-height: 0; }
.case-photo-img { max-width: 220px; max-height: 220px; border-radius: var(--radius-md); border: 1px solid var(--color-border); display: block; object-fit: cover; }
/* Bryan 2026-06-24: Save control under each case photo/sketch so it pulls to a real file. */
.case-photo-wrap { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.case-photo-save { border: 1px solid var(--color-border); background: var(--color-card); cursor: pointer; font: 600 11px var(--font-sans); color: var(--color-text); padding: 2px 10px; border-radius: var(--radius-sm); }
.case-photo-save:hover { background: var(--color-bg); }
.case-photo-save:disabled { opacity: .55; cursor: default; }
.dm-empty { padding: var(--space-4) var(--space-3); color: var(--color-text-muted); text-align: center; }
/* font-size:11px — each message now carries its OWN timestamp (Bryan 2026-06-24), so the
   meta line is smaller + quieter to keep the per-message stamp from crowding the thread. */
.dm-meta { display: flex; align-items: center; gap: var(--space-2); padding: 2px 4px; min-height: 14px; font-size: 11px; color: var(--color-text-muted); }
.dm-run-receipt { min-height: 12px; padding-top: 0; }
.dm-receipt { font-size: 11px; line-height: 1; }
.dm-receipt-sent { color: var(--color-text-muted); }
.dm-receipt-delivered { color: var(--color-text-muted); }
.dm-receipt-seen { color: var(--color-accent); }
/* #19 — no .is-mine color override here: .dm-meta sits OUTSIDE the accent bubble,
   on the pane background, so text-on-accent (white) would be invisible. */

/* ---- Emoji reactions on chat messages (#1) ----
   Each DM message is wrapped in .dm-msg (a column that inherits the run's
   left/right alignment) so a bubble can carry its own chip row + a small react
   opener. Chips render from ReactionsLogic.summarize; toggling a chip or a
   palette choice POSTs to the portal-local /api/dm/:thread/reactions store. */
.dm-msg { display: flex; flex-direction: column; align-items: inherit; gap: 2px; max-width: 100%; }
.dm-reactions { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 1px; }
.dm-reaction-chip {
  display: inline-flex; align-items: center; gap: 3px; padding: 1px 7px;
  border-radius: var(--radius-full); border: 1px solid var(--color-border);
  background: var(--color-card); color: var(--color-text);
  font: 500 12px var(--font-sans); line-height: 1.5; cursor: pointer;
}
.dm-reaction-chip.is-mine { border-color: var(--color-accent); background: var(--color-accent-faint); }
.dm-reaction-emoji { font-size: 13px; line-height: 1; }
.dm-reaction-count { font-variant-numeric: tabular-nums; }
/* Bryan 2026-06-24: "emoji reactions to only show if you hold down on a message." The
   always-on opener is gone — a long-press (onMsgPressStart) reveals the palette directly.
   The element stays in the DOM (hidden) only to carry aria-expanded state. */
.dm-react-btn { display: none; }
.dm-react-palette {
  display: flex; gap: 2px; margin-top: 2px; padding: 3px 5px;
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-full); box-shadow: var(--shadow-overlay);
}
.dm-react-palette[hidden] { display: none; }
.dm-react-choice {
  background: none; border: 0; cursor: pointer; font-size: 19px; line-height: 1;
  padding: 2px 4px; border-radius: var(--radius-md);
}
.dm-react-choice:hover, .dm-react-choice:focus-visible { background: var(--color-bg); transform: scale(1.15); }

/* Bryan 2026-06-24: hold (mobile) or right-click (desktop) a driver/George message for a
   React / Recall / Copy menu instead of the OS copy popup. That popup was hijacking the
   long-press, so text selection + the iOS callout are suppressed on driver-thread bubbles
   ONLY — .dm-msg scopes it, so homeowner, agent-log and saved-draft bubbles (which have no
   menu) keep normal selection and stay copyable the ordinary way. */
.dm-msg .dm-bubble { -webkit-touch-callout: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; }
.dm-action-menu {
  display: flex; gap: 2px; margin-top: 2px; padding: 3px 5px;
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-full); box-shadow: var(--shadow-overlay);
}
.dm-action-menu[hidden] { display: none; }
.dm-action-item {
  background: none; border: 0; cursor: pointer; font: 600 12px var(--font-sans);
  color: var(--color-text); padding: 3px 9px; border-radius: var(--radius-md); line-height: 1.4;
}
.dm-action-item:hover, .dm-action-item:focus-visible { background: var(--color-bg); }
/* Recall lives in the menu now — override its old standalone (flex-end, muted) layout. */
.dm-action-item.dm-recall-btn { align-self: auto; margin-top: 0; color: var(--color-text); }
/* Attachment Save control — under each image/file so it pulls to a real file (preview link
   alone can't force a download inside the installed PWA). */
.dm-attach-wrap { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.dm-attach-save {
  border: 1px solid var(--color-border); background: var(--color-card); cursor: pointer;
  font: 600 11px var(--font-sans); color: var(--color-text); padding: 2px 10px;
  border-radius: var(--radius-sm); -webkit-user-select: none; user-select: none;
}
.dm-attach-save:hover { background: var(--color-bg); }
.dm-attach-save:disabled { opacity: .55; cursor: default; }

/* "‹ Chats" back control — hidden on desktop two-pane, shown on phone and whenever
   a thread is open (body.inbox-thread-open) so there's always a way back. */
.dm-back { display: none; align-self: flex-start; align-items: center; gap: 2px;
  background: none; border: 0; cursor: pointer; color: var(--color-accent);
  font: 600 14px var(--font-sans); padding: var(--space-2) var(--space-3) var(--space-1); }
body.inbox-thread-open .dm-back { display: inline-flex; }

/* ---- Unified Inbox (#43 Phase 1) — filter chips + thread chrome over the .dm-* panes ---- */
.inbox-shell { display: flex; flex-direction: column; gap: var(--space-2); }
.inbox-filters { display: flex; flex-wrap: wrap; gap: var(--space-1); padding: var(--space-1) 0; }
.inbox-chip {
  font: 600 13px var(--font-sans); padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-full);
  background: var(--color-card); color: var(--color-text); cursor: pointer;
}
.inbox-chip.is-active { background: var(--color-accent); color: var(--color-text-on-accent); border-color: var(--color-accent); }
/* live count inside a filter chip (Drafts = un-sent appointment openers) */
.inbox-chip-count {
  display: inline-block; margin-left: 6px; min-width: 16px; padding: 0 5px;
  font: 700 11px var(--font-sans); line-height: 16px; text-align: center;
  border-radius: var(--radius-full); background: var(--color-warning-soft); color: var(--color-warning);
}
.inbox-chip.is-active .inbox-chip-count { background: var(--color-text-on-accent); color: var(--color-accent); }
/* small channel tag under a thread name (Agent / Driver / Homeowner) */
.inbox-channel-tag {
  display: inline-block; font: 600 10px var(--font-sans); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--color-text-muted); margin-top: 1px;
}
/* "N draft(s)" chip on a homeowner thread row */
.inbox-draft-chip {
  position: absolute; right: 10px; bottom: 10px;
  font: 600 11px var(--font-sans); padding: 1px 6px; border-radius: var(--radius-full);
  background: var(--color-warning-soft); color: var(--color-warning);
}
/* homeowner pane: outbound-only notice + per-bubble status tag */
.inbox-ro-note {
  margin: var(--space-2) auto; padding: var(--space-2) var(--space-3);
  max-width: 28em; text-align: center; font-size: 13px; font-style: italic;
  color: var(--color-text-muted); background: var(--color-bg-subtle); border-radius: var(--radius-md);
}
.inbox-status-tag { display: block; font: 600 11px var(--font-sans); color: var(--color-text-muted); margin-top: 2px; }
/* D3 — homeowner WrapDesk composer: AI-draft + editable box + Approve / Send now. Pinned
   at the bottom of the pane like .dm-composer; shown only on homeowner threads. */
.inbox-ho-composer {
  flex: 0 0 auto; display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-2) var(--space-1); border-top: 1px solid var(--color-border);
}
/* ── WorthSavingBox — AI extraction pill (Bryan 2026-06-21) ─────────────────
   Collapsed pill → expand → spinner → extracted fields → Save to contact →
   SyncedCard with Undo. Positioned between status-controls and AI draft toolbar. */
.inbox-ho-ws { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-border); }
.inbox-ho-ws-pill {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-2); cursor: pointer;
  background: var(--color-accent-faint); border-radius: var(--radius-md);
  box-shadow: 0 0 0 1px var(--color-accent-soft);
  transition: box-shadow var(--motion-fast) ease;
}
.inbox-ho-ws-pill:hover { box-shadow: 0 0 0 2px var(--color-accent-soft); }
.inbox-ho-ws-zap { font-size: 14px; flex-shrink: 0; }
.inbox-ho-ws-label { font: 600 13px var(--font-sans); color: var(--color-text); flex: 1; }
.inbox-ho-ws-new {
  font: 700 9px var(--font-sans); letter-spacing: .4px;
  padding: 1px 5px; border-radius: 8px;
  background: var(--color-accent); color: #fff;
}
.inbox-ho-ws-toggle {
  background: none; border: none; cursor: pointer; padding: 2px 4px;
  font-size: 14px; color: var(--color-muted); line-height: 1;
}
.inbox-ho-ws-pill--saved .inbox-ho-ws-label::after { content: ' · Saved'; color: var(--color-muted); font-weight: 400; }
.inbox-ho-ws-body { padding: var(--space-2) 0 var(--space-1); }
.inbox-ho-ws-loading { font: 13px var(--font-sans); color: var(--color-muted); padding: var(--space-1) 0; }
.inbox-ho-ws-msg { font: 13px var(--font-sans); color: var(--color-muted); margin: 0; }
.inbox-ho-ws-card {
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2);
}
.inbox-ho-ws-row { font: 13px var(--font-sans); display: flex; gap: var(--space-2); align-items: baseline; }
.inbox-ho-ws-lbl { font-weight: 700; font-size: 11px; letter-spacing: .3px; color: var(--color-muted); flex-shrink: 0; min-width: 48px; }
.inbox-ho-ws-summary { font: 13px/1.45 var(--font-sans); color: var(--color-text); }
.inbox-ho-ws-notes { margin: 0; padding-left: 16px; font: 13px/1.45 var(--font-sans); color: var(--color-text); }
.inbox-ho-ws-notes li { margin-bottom: 2px; }
.inbox-ho-ws-actions { margin-top: var(--space-2); }
.inbox-ho-ws-save-btn {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font: 700 12px var(--font-sans); cursor: pointer;
  padding: var(--space-1) var(--space-3); min-height: 30px;
  color: #fff; background: var(--color-accent);
  border: 1px solid var(--color-accent); border-radius: var(--radius-full);
}
.inbox-ho-ws-save-btn:hover:not([disabled]) { opacity: .88; }
.inbox-ho-ws-save-btn[disabled] { opacity: .5; cursor: not-allowed; }
.inbox-ho-ws-synced {
  border: 1px solid var(--color-accent); border-radius: var(--radius-md);
  padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2);
}
.inbox-ho-ws-synced-head { font: 700 12px var(--font-sans); color: var(--color-accent); letter-spacing: .3px; }
.inbox-ho-ws-undo-btn {
  background: none; border: 1px solid var(--color-border-strong); border-radius: var(--radius-full);
  font: 600 12px var(--font-sans); cursor: pointer; padding: 3px 12px;
  color: var(--color-muted); margin-top: var(--space-1);
}
.inbox-ho-ws-undo-btn:hover { color: var(--color-text); }
/* Worth Saving expand: Spoke note textarea + Log attempt button */
.inbox-ho-spoke-input {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 52px;
  padding: var(--space-2); border-radius: var(--radius-md);
  border: 1px solid var(--color-border-strong); background: var(--color-card);
  color: var(--color-text); font: 14px var(--font-sans);
}
.inbox-ho-spoke-input:focus { outline: none; border-color: var(--color-accent); }
.inbox-ho-ws-fb { font-size: 12px; color: var(--color-text-muted); margin-left: var(--space-2); align-self: center; }
.inbox-ho-ws-divider { border: none; border-top: 1px solid var(--color-border); margin: var(--space-2) 0; }

/* Toolbar: AI draft + status controls (Confirm / Next week) on one wrapping row.
   The "Worth saving" pill was removed (Bryan 2026-06-28 Item 4). No overflow:hidden —
   if the buttons don't fit, the row WRAPS instead of clipping a button off-page.
   Bryan (B): status buttons must be side-by-side, never stacked. */
.inbox-ho-toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
/* WS pill inside toolbar: reset standalone padding/border, allow shrink */
.inbox-ho-toolbar .inbox-ho-ws { padding: 0; border-bottom: none; flex-shrink: 1; min-width: 0; overflow: hidden; }
.inbox-ho-toolbar .inbox-ho-ws-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; }
/* Status controls share the toolbar row with WS pill + AI draft. */
.inbox-ho-thread-actions { position: relative; margin-left: auto; }
.inbox-ho-thread-actions > summary {
  display: inline-flex; align-items: center; min-height: 36px; padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border); border-radius: var(--radius-full); background: var(--color-card);
  color: var(--color-text); cursor: pointer; font-size: 13px; font-weight: 700; list-style: none;
}
.inbox-ho-thread-actions > summary::-webkit-details-marker { display: none; }
.inbox-ho-thread-actions > summary::after { content: '\2304'; margin-left: var(--space-2); }
.inbox-ho-thread-actions[open] > summary::after { transform: rotate(180deg); }
.inbox-ho-thread-actions-menu {
  position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 30;
  display: grid; gap: var(--space-2); width: min(360px, calc(100vw - 32px));
  max-height: min(55dvh, 420px); overflow-y: auto; padding: var(--space-3);
  border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-card);
  box-shadow: var(--shadow-lg);
}
.inbox-ho-thread-actions-menu .inbox-ho-worth-saving { width: 100%; }
.inbox-ho-thread-actions-menu .inbox-ho-status-controls,
.inbox-ho-thread-actions-menu .inbox-status-controls { display: flex; flex-wrap: wrap; gap: var(--space-2); width: 100%; }
.inbox-ho-thread-actions-menu .inbox-status-controls > * { flex: 1 1 130px; }
.inbox-ho-worth-saving { white-space: nowrap; }
.inbox-worth-saving-review {
  margin: var(--space-2) 0;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-subtle);
  max-height: min(42vh, 420px);
  overflow-y: auto;
}
.inbox-worth-saving-loading,
.inbox-worth-saving-empty,
.inbox-worth-saving-hint { color: var(--color-text-muted); font-size: 0.82rem; line-height: 1.4; }
.inbox-worth-saving-section { display: grid; gap: var(--space-2); }
.inbox-worth-saving-action {
  display: grid; gap: var(--space-2); padding: var(--space-3);
  border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-card);
}
.inbox-worth-saving-draft { width: 100%; min-height: 88px; resize: vertical; font-size: 16px; }
.inbox-worth-saving-buttons { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.inbox-worth-open { text-decoration: none; display: inline-flex; align-items: center; }
.inbox-ho-ai {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font: 700 13px var(--font-sans); cursor: pointer;
  padding: var(--space-1) var(--space-3); min-height: 34px;
  color: var(--color-accent); background: var(--color-accent-faint);
  border: 1px solid var(--color-accent-soft); border-radius: var(--radius-full);
  transition: background var(--motion-fast) ease;
}
.inbox-ho-ai:hover:not([disabled]) { background: var(--color-accent-soft); }
.inbox-ho-ai[disabled] { opacity: .5; cursor: not-allowed; }
.inbox-ho-hint { font-size: 12px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inbox-ho-draft, .inbox-draft-card-body {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 48px;
  font-family: var(--font-sans); font-size: 16px; line-height: 1.4;
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.inbox-ho-draft:focus, .inbox-draft-card-body:focus {
  outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint);
}
/* item C / #64 + #6 — the composer's box + its single Save button on ONE compact row, the
   .dm-composer shape Bryan praised in WrapDesk: the textarea grows, Save stays pinned to its
   bottom edge, and the whole composer is ~half its old height so the message list above shows
   more. The shared .inbox-ho-draft rule keeps font-size:16px (dodges iOS zoom); we only
   override sizing/resize here and leave .inbox-draft-card-body (the agent-thread card that
   shares that rule) untouched. */
.inbox-ho-row { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: var(--space-2); }
.inbox-ho-row .inbox-ho-draft { grid-column: 1 / -1; width: 100%; min-height: 48px; max-height: 280px; resize: none; padding: var(--space-3); line-height: 1.45; }
.inbox-ho-row .inbox-ho-send { grid-column: 3; grid-row: 2; min-height: 42px; padding: 0 var(--space-5); }
.inbox-ho-row .inbox-ho-send-later { grid-column: 2; grid-row: 2; min-height: 42px; padding: 0 var(--space-3); font-size: 13px; }
.inbox-ho-sched-picker { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) 0; flex-wrap: wrap; }
.inbox-ho-sched-picker input[type="datetime-local"] { flex: 1 1 auto; min-width: 170px; padding: var(--space-1) var(--space-2); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); font: 14px var(--font-sans); background: var(--color-card); color: var(--color-text); height: 40px; }
.inbox-ho-sched-picker .inbox-ho-btn { flex: 0 0 auto; min-height: 40px; padding: 0 var(--space-3); font-size: 13px; }
.inbox-ho-sendrow { display: flex; gap: var(--space-2); }
.inbox-ho-btn {
  flex: 1 1 0; font: 700 15px var(--font-sans); cursor: pointer;
  min-height: 46px; padding: 0 var(--space-4); border-radius: var(--radius-md);
  border: 1px solid var(--color-border-strong); background: var(--color-card); color: var(--color-text-strong);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.inbox-ho-btn:hover:not([disabled]) { background: var(--color-bg-subtle); }
.inbox-ho-btn:active:not([disabled]) { transform: scale(0.98); }
.inbox-ho-btn[disabled] { opacity: .5; cursor: not-allowed; }
.inbox-ho-btn-primary { background: var(--color-accent); color: var(--color-text-on-accent); border-color: transparent; }
.inbox-ho-btn-primary:hover:not([disabled]) { background: var(--color-accent-hover); }
.inbox-ho-feedback, .inbox-draft-fb { font-size: 13px; color: var(--color-text-muted); padding: 0 var(--space-1); }
.inbox-ho-access-contact {
  display: grid; gap: var(--space-2); padding: var(--space-3);
  border: 1px solid var(--color-accent-soft); border-radius: var(--radius-md);
  background: var(--color-accent-faint);
}
.inbox-ho-access-contact[hidden] { display: none; }
.inbox-access-contact-head { font: 700 15px var(--font-sans); color: var(--color-text-strong); }
.inbox-access-contact-sub { font: 12px/1.4 var(--font-sans); color: var(--color-text-muted); }
.inbox-access-contact-body {
  width: 100%; box-sizing: border-box; min-height: 92px; resize: vertical;
  padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md); background: var(--color-card); color: var(--color-text);
  font: 16px/1.4 var(--font-sans);
}
.inbox-access-contact-body:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.inbox-access-contact-actions { display: flex; align-items: center; gap: var(--space-2); }
.inbox-access-contact-actions .inbox-ho-btn { flex: 0 0 auto; min-height: 40px; }
.inbox-access-contact-feedback { font: 12px var(--font-sans); color: var(--color-text-muted); }
/* #39 — Approve / Send-now controls ON an appointment-opener bubble. Right-aligned to sit
   under the operator's own (is-mine) bubble; the buttons reuse .inbox-ho-btn. The send row
   is width-capped so the two buttons don't stretch the full pane like the composer's do. */
.inbox-opener-actions { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-1); margin: 2px 0 var(--space-2); }
.inbox-opener-menu-row { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); width: min(420px, 92%); }
.inbox-opener-menu { position: relative; }
.inbox-opener-menu summary { cursor: pointer; list-style: none; min-height: 38px; padding: 8px 14px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-card); font-weight: 700; }
.inbox-opener-menu summary::-webkit-details-marker { display: none; }
.inbox-opener-menu-items { position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 8; display: grid; gap: 6px; min-width: 170px; padding: 8px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-card); box-shadow: var(--shadow-lg); }
.inbox-opener-menu-items .inbox-ho-btn { width: 100%; justify-content: flex-start; }
.inbox-opener-actions .inbox-ho-sendrow { width: 100%; flex-wrap: wrap; }
.inbox-opener-actions .inbox-ho-btn { min-height: 38px; padding: 0 var(--space-2); font-size: 13px; }
.inbox-opener-danger { color: var(--color-danger, #b42318); }
.inbox-opener-editor { width: min(520px, 96%); padding: var(--space-2); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-bg-subtle); }
.inbox-opener-editor.is-open { position: sticky; bottom: 6px; z-index: 7; max-height: min(55dvh, 360px); overflow: auto; box-shadow: var(--shadow-lg); }
.inbox-opener-edit-body { display: block; width: 100%; min-height: 96px; max-height: min(32dvh, 240px); margin-bottom: var(--space-2); padding: var(--space-2); resize: vertical; font: 16px var(--font-sans); color: var(--color-text); background: var(--color-card); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); }
.inbox-opener-fb { font-size: 13px; color: var(--color-text-muted); padding: 0 var(--space-1); text-align: right; }
/* D3 — the operator's saved Approve / Send-now drafts, listed below the read-only texts. */
.inbox-ho-drafts { display: block; margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--color-border); }
.inbox-ho-drafts-h { font: 700 11px var(--font-sans); letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-muted); margin: 0 0 var(--space-2) var(--space-1); }
.inbox-ho-draft-tag { display: inline-block; font: 700 10px var(--font-sans); letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 6px; border-radius: var(--radius-full); margin-left: var(--space-1); }
.inbox-ho-draft-pending { background: var(--color-warning-soft); color: var(--color-warning); }
.inbox-ho-draft-approved { background: var(--color-info-soft); color: var(--color-info); }
/* Item B — quiet "Remove" control on a saved draft's meta row; danger-tinted on hover so
   dropping a queued reply is a deliberate tap. Sits on the pane bg, outside the bubble. */
.inbox-ho-draft-remove { border: none; background: transparent; cursor: pointer; font: 600 11px var(--font-sans); color: var(--color-text-muted); padding: 2px 4px; border-radius: var(--radius-sm); }
.inbox-ho-draft-remove:hover { color: var(--color-danger, #b42318); text-decoration: underline; }
.inbox-ho-draft-remove:disabled { opacity: .55; cursor: default; }
/* D1+D3 — the gateway-suggested homeowner text card in the agent thread. */
.inbox-draft-card {
  margin-top: var(--space-3); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2);
  background: var(--color-accent-faint); border: 1px solid var(--color-accent-soft); border-radius: var(--radius-md);
}
.inbox-draft-card-h { font: 700 13px var(--font-sans); color: var(--color-text-strong); }
.inbox-draft-why { font-size: 12px; }
/* homeowner thread: route date + arrival window (mirrored to the driver app's ETA) */
.inbox-thread-meta { display: block; font: 600 12px var(--font-sans); color: var(--color-accent); margin-top: 2px; }
/* Bryan 2026-08-19 "waisted space at the top" — tightened card paddings so the head takes
   two slim lines (address / status), not a tall banner. */
.inbox-ho-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px var(--space-3);
  padding: 6px var(--space-2); margin-bottom: var(--space-1);
  background: var(--color-accent-faint); border: 1px solid var(--color-accent-soft); border-radius: var(--radius-md); }
.inbox-ho-name { font: 700 15px var(--font-sans); color: var(--color-text-strong); }
.inbox-ho-when, .inbox-ho-window, .inbox-ho-addr { font: 600 13px var(--font-sans); color: var(--color-text); }
.inbox-ho-addr { } /* address flows inline with name */
.inbox-ho-lbl { font: 600 10px var(--font-sans); letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-muted); }
/* #5 — "live" badge next to the arrival window when it mirrors the live Spoke ETA
   (never shown on the texted-window fallback, so the label is always truthful). */
.inbox-ho-live { display: inline-block; font: 700 9px var(--font-sans); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent); background: var(--color-accent-soft); padding: 1px 5px; border-radius: var(--radius-full); vertical-align: 1px; }
/* #5 — homeowner list row: the property address on its own muted line, clipped to one line. */
.inbox-thread-addr { display: block; font: 500 12px var(--font-sans); color: var(--color-text-muted); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ── Surface 5 — schedule status (confirmed / pushed-to-next-week / in-progress) ──────
   A scannable chip on each homeowner thread row + a status line and action bar in the pane.
   The state color is shared by the list chip (.inbox-status-chip) and the pane status line
   (.inbox-ho-status) via the .inbox-status-<state> modifier. Read-only display; the controls
   call the operator-driven confirm-window / push / restore endpoints (no auto-send). */
.inbox-status-chip { display: inline-block; margin-top: 3px;
  font: 700 10px var(--font-sans); letter-spacing: 0.04em; text-transform: uppercase;
  padding: 1px 7px; border-radius: var(--radius-full); }
.inbox-status-confirmed   { background: var(--color-success-soft); color: var(--color-success); }
.inbox-status-next-week   { background: var(--color-warning-soft); color: var(--color-warning); }
.inbox-status-in-progress { background: var(--color-bg-subtle);    color: var(--color-text-muted); }
/* pane meta status line — its own row, tinted by the shared state modifier above */
.inbox-ho-status { flex-basis: 100%; font: 700 12px var(--font-sans);
  padding: 2px 6px; border-radius: var(--radius-sm); }
.inbox-ho-status .inbox-ho-lbl { margin-right: var(--space-1); }
/* Sticky thread head — pins the name/meta + the status action bar to the top of the
   #inbox-messages scroller so Mark confirmed / Push to next week / Restore are reachable at
   ANY scroll position (the pane auto-scrolls to the newest message, which used to bury them).
   Opaque var(--color-bg) (the pane bg) + z-index so message bubbles scroll cleanly underneath;
   the confirm form is a SIBLING below this head, never a child, so it can't clip when pinned. */
.inbox-ho-head { position: sticky; top: 0; z-index: 5; background: var(--color-bg);
  padding-bottom: var(--space-1); border-bottom: 1px solid var(--color-border); }
/* The scroller carries padding-top:var(--space-2); a top:0 sticky child sticks just BELOW
   that band, so a message would bleed through it above the pinned head as it scrolls under.
   Paint a bg strip over the band (anchored to the sticky head, so it's in the head's z-layer). */
.inbox-ho-head::before { content: ''; position: absolute; left: 0; right: 0;
  top: calc(var(--space-2) * -1); height: var(--space-2); background: var(--color-bg); }
/* Bryan 2026-08-20 "I still can't see any of the conversation when typing a reply":
   while the homeowner reply box has focus (inbox.js toggles body.inbox-typing), the
   address/status head and the AI-draft/Actions toolbar hand their rows to the message
   scroller. Both return the moment focus leaves the box, so nothing is lost — the
   keyboard is what actually eats the screen, and this gives the remainder to messages. */
body.inbox-typing .inbox-ho-head { display: none; }
body.inbox-typing .inbox-ho-toolbar { display: none; }
/* Status controls (Mark confirmed / Push / Restore) — inside toolbar, next to AI draft. */
.inbox-ho-status-controls { display: flex; flex: 1; min-width: 0; flex-wrap: wrap; gap: var(--space-2); }
/* Confirm form zone — shows below the toolbar when Mark confirmed is open. */
.inbox-ho-confirm-zone:not(:empty) { padding-top: var(--space-1); }
.inbox-status-controls { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.inbox-status-controls .inbox-ho-btn { flex: 0 1 auto; min-height: 38px; font-size: 13px; padding: 0 var(--space-3); }
/* Item J — "Convert to exterior only" (AFI high value): a warning-tinted action so it reads
   as a scope change, distinct from the neutral Mark-confirmed / Push buttons. Once converted,
   a calm "Exterior only" pill sits beside a plain Restore-to-interior button. */
.inbox-status-controls .inbox-exterior-convert {
  background: var(--color-warning-soft); color: var(--color-warning); border-color: transparent; }
.inbox-status-controls .inbox-exterior-convert:hover:not([disabled]) { filter: brightness(0.97); }
.inbox-exterior-tag { display: inline-flex; align-items: center; min-height: 38px;
  font: 700 12px var(--font-sans); padding: 0 var(--space-3); border-radius: var(--radius-full);
  background: var(--color-warning-soft); color: var(--color-warning); }
/* WrapDesk-style bottom action bar in homeowner threads: action chips at the bottom of the
   pane (Draft reply / Log attempt / Add note), matching WrapDesk's quickRow pattern. */
.inbox-recs {
  padding: var(--space-2) var(--space-3) 0;
  display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center;
}
.inbox-recs-label { font-size: 0.72rem; color: var(--color-muted); margin-right: 2px; }
.inbox-recs-chip {
  display: inline-flex; align-items: center; height: 30px; padding: 0 var(--space-2);
  border-radius: var(--radius-full); cursor: pointer; font: 12px var(--font-sans);
  background: var(--color-bg-subtle); border: 1px solid var(--color-border); color: var(--color-muted);
}
.inbox-recs-chip:hover { border-color: var(--color-accent); color: var(--color-accent); }
.inbox-recs-action { font-size: 0.75rem; color: var(--color-muted); padding: var(--space-1) 0 var(--space-2); width: 100%; }
.ho-action-bar {
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: 1px solid var(--color-border);
  display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-start;
  background: var(--color-card);
}
.ho-action-chip {
  display: inline-flex; align-items: center; min-height: 34px; padding: 0 var(--space-3);
  border-radius: var(--radius-full); cursor: pointer;
  background: var(--color-bg-subtle); border: 1px solid var(--color-border-strong);
  color: var(--color-text); font: 600 13px var(--font-sans); white-space: nowrap;
}
.ho-action-chip:hover:not([disabled]) { border-color: var(--color-accent); color: var(--color-accent); }
.ho-action-chip[disabled] { opacity: 0.6; cursor: not-allowed; }
.ho-note-form { display: flex; flex-direction: column; gap: var(--space-2); width: 100%; margin-top: var(--space-1); }
.ho-note-input {
  width: 100%; padding: var(--space-2); border-radius: var(--radius-md);
  border: 1px solid var(--color-border-strong); background: var(--color-card);
  color: var(--color-text); font: 14px var(--font-sans); resize: vertical; min-height: 56px;
}
.ho-note-input:focus { outline: none; border-color: var(--color-accent); }
.ho-note-actions { display: flex; gap: var(--space-2); }
.ho-note-feedback { font-size: 12px; color: var(--color-text-muted); min-height: 16px; }
/* inbox-ho-head-row: name/meta left, contact icon right — mirrors WrapDesk thread header layout. */
.inbox-ho-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.inbox-ho-head-row .inbox-ho-meta { flex: 1; min-width: 0; }
/* Contact icon button in the thread header top-right (WrapDesk Feather "user" icon position). */
.inbox-ho-contact-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex-shrink: 0;
  border-radius: var(--radius-md); cursor: pointer;
  background: transparent; border: 1px solid var(--color-border-strong);
  color: var(--color-text-muted); padding: 0;
}
.inbox-ho-contact-btn:hover { border-color: var(--color-accent); color: var(--color-accent); }
/* WrapDesk-style contact detail sections (Messages link, Files, Notes). */
.contact-detail-section { padding: var(--space-3) 0; border-top: 1px solid var(--color-border); }
.contact-detail-section:first-child { border-top: none; }
.contact-section-hd {
  font: 700 11px var(--font-sans); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-text-muted); margin-bottom: var(--space-2);
}
.contact-section-empty { font-size: 13px; color: var(--color-text-muted); margin: 0; }
.contact-note-entry { margin-bottom: var(--space-2); }
.contact-note-text { font-size: 13px; margin: 0 0 2px; }
.contact-note-date { font-size: 11px; color: var(--color-text-muted); margin: 0; }
/* #48 — contact Files section */
.contact-file-list { list-style: none; margin: var(--space-1) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.contact-file-item { display: flex; }
.contact-file-link { display: flex; align-items: center; gap: var(--space-2); text-decoration: none; color: var(--color-accent); font-size: 14px; word-break: break-all; }
.contact-file-link:hover .contact-file-name { text-decoration: underline; }
.contact-file-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--color-border); flex-shrink: 0; }
.contact-file-icon { font-size: 24px; line-height: 1; flex-shrink: 0; }
.contact-file-name { flex: 1 1 auto; min-width: 0; }
.contact-open-convo-msg {
  display: inline-flex; align-items: center; min-height: 36px; padding: 0 var(--space-3);
  border-radius: var(--radius-md); cursor: pointer;
  background: var(--color-accent); border: none; color: var(--color-text-on-accent);
  font: 600 14px var(--font-sans);
}
.contact-open-convo-msg:hover { filter: brightness(0.93); }
/* Assistant tab: the operator <-> agent chat section below the TODO panel. */
.assistant-tab-panel {
  display: flex; flex-direction: column; min-height: 100%;
  padding-bottom: calc(88px + env(safe-area-inset-bottom));
}
.assistant-suggestions { display: flex; flex-direction: column; gap: 8px; padding: 0 var(--space-3) var(--space-2); }
.assistant-suggestion { border: 1px solid var(--color-border); border-radius: 12px; background: var(--color-card); padding: 10px 12px; }
.assistant-suggestion-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.assistant-suggestion-name { font-weight: 700; font-size: 15px; }
.assistant-suggestion-name-action {
  min-height: 44px; margin: -10px 0 -10px -12px; padding: 10px 12px;
  border: 0; background: transparent; color: var(--color-text-strong);
  font-family: inherit; text-align: left; cursor: pointer;
}
.assistant-suggestion-name-action:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: -2px; border-radius: 8px;
}
.assistant-suggestion-kind { color: var(--color-text-muted); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.assistant-suggestion-address,.assistant-suggestion-meta { margin-top: 3px; font-size: 13px; color: var(--color-text-muted); }
.assistant-suggestion-evidence { margin: 8px 0 0; padding: 7px 9px; border-left: 3px solid var(--color-accent); background: var(--color-bg-subtle); border-radius: 6px; font-size: 13px; line-height: 1.35; }
.assistant-suggestion-card-action { cursor: pointer; }
.assistant-suggestion-card-action:focus-visible { outline: 3px solid var(--color-primary); outline-offset: 2px; }
.assistant-suggestion-context { margin-top: 10px; display: grid; gap: 6px; font-size: 13px; line-height: 1.4; }
.assistant-suggestion-context-turn { padding: 7px 9px; border-radius: 10px; background: var(--color-bg-subtle); }
.assistant-suggestion-context-turn.is-bryan { margin-left: 18px; }
.assistant-suggestion-context-turn.is-homeowner { margin-right: 18px; }
.assistant-suggestion-why { margin-top: 6px; font-size: 12px; line-height: 1.35; }
.assistant-suggestion-body { max-height: 84px; overflow: auto; white-space: pre-wrap; }
.assistant-suggestion-actions { display: flex; justify-content: flex-end; margin-top: 8px; }
.assistant-contact-card { margin-top: 10px; padding: 10px; border: 1px solid var(--color-border); border-radius: 12px; background: var(--color-bg-subtle); }
.assistant-contact-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.assistant-contact-head a { color: var(--color-primary); font-size: 13px; }
.assistant-contact-hint { margin-top: 4px; color: var(--color-text-muted); font-size: 12px; line-height: 1.4; }
.assistant-contact-draft { width: 100%; margin-top: 8px; min-height: 104px; resize: vertical; }
.assistant-bound-action { flex: 1 0 100%; padding: 7px 9px; border-radius: 8px; background: var(--color-accent-faint); color: var(--color-text); font-size: 12px; }
.assistant-chat-log {
  flex: 1; overflow-y: auto; padding: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-2);
  min-height: 120px; max-height: 40vh;
}
.assistant-chat-empty { font-size: 13px; margin: 0; }
.assistant-chat-bubble {
  display: flex; max-width: 80%;
}
.assistant-chat-bubble.is-mine { align-self: flex-end; }
.assistant-chat-bubble.is-agent { align-self: flex-start; }
.assistant-chat-text {
  display: inline-block; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md); font-size: 14px; line-height: 1.45; word-break: break-word;
}
.is-mine .assistant-chat-text { background: var(--color-accent); color: var(--color-text-on-accent); border-radius: var(--radius-md) var(--radius-md) 2px var(--radius-md); }
.is-agent .assistant-chat-text { background: var(--color-bg-subtle); color: var(--color-text); border: 1px solid var(--color-border); border-radius: var(--radius-md) var(--radius-md) var(--radius-md) 2px; }
.assistant-chat-form {
  position: fixed; left: 50%; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  z-index: 19; width: min(100%, var(--app-max)); transform: translateX(-50%);
  display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-end;
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: 1px solid var(--color-border);
  background: var(--color-card);
}
.assistant-chat-input {
  flex: 1; padding: var(--space-2); border-radius: var(--radius-md);
  border: 1px solid var(--color-border-strong); background: var(--color-card);
  color: var(--color-text); font: 14px var(--font-sans); resize: none;
}
.assistant-chat-input:focus { outline: none; border-color: var(--color-accent); }
.assistant-chat-send { white-space: nowrap; align-self: flex-end; }
/* the inline Mark-confirmed form: a date + a 4hr arrival block, then Save / Cancel */
.inbox-confirm-form { display: flex; flex-direction: column; gap: var(--space-2);
  margin: 0 0 var(--space-3); padding: var(--space-3);
  background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: var(--radius-md); }
.inbox-confirm-row { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.inbox-confirm-lbl { display: flex; flex-direction: column; gap: 2px; flex: 1 1 140px;
  font: 600 10px var(--font-sans); letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-muted); }
.inbox-confirm-date, .inbox-confirm-start, .inbox-confirm-end { font-family: var(--font-sans); font-size: 16px; min-height: 40px;
  padding: var(--space-1) var(--space-2); color: var(--color-text);
  background: var(--color-card); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); }
.inbox-confirm-date:focus, .inbox-confirm-start:focus, .inbox-confirm-end:focus { outline: none; border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-faint); }
.inbox-confirm-actions { display: flex; gap: var(--space-2); }
.inbox-confirm-actions .inbox-ho-btn { flex: 1 1 0; min-height: 42px; font-size: 14px; }
.inbox-confirm-fb { font-size: 13px; padding: 0 var(--space-1); }
/* #6 — Sharp "Photos labeled" button. A day-level call-to-action at the top of the
   Cases tab; hidden (and emptied) by cases.js for every driver not on the button
   workflow, so the soft accent card only appears when the button is live. */
.cases-ready-section { display: block; margin-bottom: var(--space-3);
  padding: var(--space-3); background: var(--color-accent-faint);
  border: 1px solid var(--color-accent-soft); border-radius: var(--radius-md); }
.cases-ready-btn { width: 100%; }
.cases-ready-hint { margin: var(--space-2) 0 0; font-size: 13px; }
/* Needs attention (Bryan 2026-09-27): operator-only list of every worker's stops that still need
   something. Red border when any row is a problem; rows tinted by level. attention.js renders it. */
.attention-panel { display: block; margin-bottom: var(--space-3); background: var(--color-card);
  border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
.attention-panel.has-problems { border-color: var(--color-danger); }
.attention-toggle { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; width: 100%;
  padding: var(--space-3); background: none; border: 0; text-align: left; font: inherit; color: inherit; cursor: pointer; }
.attention-toggle::after { content: '\25BE'; margin-left: auto; color: var(--color-text-muted); }
.attention-toggle[aria-expanded="false"]::after { content: '\25B8'; }
.attention-heading { font-weight: 600; }
.attention-summary { font-size: 13px; }
.attention-body { padding: 0 var(--space-3) var(--space-3); }
.attention-note { margin-bottom: var(--space-2); padding: var(--space-2); font-size: 13px;
  background: var(--color-warning-soft); color: var(--color-warning); border-radius: var(--radius-sm); }
.attention-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.attention-item { padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  border-left: 4px solid var(--color-border-strong); background: var(--color-accent-faint); }
.attention-item.is-problem { border-left-color: var(--color-danger); background: var(--color-danger-soft); }
.attention-item.is-check { border-left-color: var(--color-warning); background: var(--color-warning-soft); }
.attention-item.is-info { border-left-color: var(--color-info); background: var(--color-info-soft); }
.attention-item-head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.attention-badge { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.attention-badge.is-problem { color: var(--color-danger); }
.attention-badge.is-check { color: var(--color-warning); }
.attention-badge.is-info { color: var(--color-info); }
.attention-item-title { font-weight: 600; overflow-wrap: anywhere; }
.attention-where { font-size: 13px; margin-top: 2px; overflow-wrap: anywhere; }
.attention-why { font-size: 13px; margin-top: 2px; }
.attention-dismiss { margin-top: var(--space-1); }
.attention-empty { margin: 0; font-size: 13px; }
.attention-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-2); }
/* agent pane (#4): inline to-do panel (call-the-agent + add-number) + a message box,
   WrapDesk-assistant style. The .agent-todo-* cards bring their own styling. */
.inbox-agent-intro { padding: var(--space-2) var(--space-1) var(--space-3); }
.inbox-agent-intro p { margin: 0; }
.inbox-agent-todos { display: block; }
/* The operator's OWN notes to the agent, rendered as sent ("is-mine") bubbles below
   the to-dos (operator) / intro (intake). Never a fabricated agent reply. A quiet top
   divider sets the conversation apart from whatever sits above it. */
.inbox-agent-log { display: block; }
.inbox-agent-log:has(.dm-run) { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--color-border); }
.inbox-agent-log .inbox-agent-empty { margin: var(--space-2) auto; }
/* #17 — George's intake-assistant pane: a soft accent intro card (his own identity,
   not the operator to-do list) + a quiet conversational empty state. */
.inbox-agent-intro-intake {
  padding: var(--space-3); margin-bottom: var(--space-3);
  background: var(--color-accent-faint); border: 1px solid var(--color-accent-soft);
  border-radius: var(--radius-md);
}
.inbox-agent-intro-intake .inbox-agent-hello {
  margin: 0 0 var(--space-1); font: 700 16px var(--font-sans); color: var(--color-text-strong);
}
.inbox-agent-empty {
  margin: var(--space-4) auto; padding: var(--space-2) var(--space-3);
  max-width: 28em; text-align: center; font-size: 13px;
}
/* ---- Full-height chat (DM + Inbox): pin the composer above the fixed bottom tab bar at
   EVERY width (JF 2026-06-12, "phone open -> typing box behind the bottom tabs") ----
   The portal is ALWAYS a <=640px-wide centered column (.app-main max-width:640px) with a
   position:fixed bottom tab bar at every width (>=720px only re-centers the bar). So the
   composer must clear that bar at every width — including the unfolded Z Fold inner screen
   (>640px). These rules used to live inside @media (max-width:640px), which fixed only the
   cover screen; the unfolded screen fell through to the broken height:calc(100vh-180px)
   default and the composer dropped ~65px behind the tabs. They are width-INDEPENDENT, so
   they live unconditionally here; only the single-pane vs two-pane PRESENTATION stays
   width-gated below.

   The cap is keyed on .chat-app (stamped by switchTab on every engine — older WebViews drop
   a whole comma-rule containing an unsupported :has(), which once silently killed the cap)
   with the :has() pair kept as a belt-and-suspenders for any tab flip that bypasses switchTab.
   It only matches while a chat tab is open, so normal scrolling tabs are untouched. It is
   sized by FLEX, not position:fixed (a transform on the fading-in panel would re-root a fixed
   child), needs no dvh math, and adapts to the operator bar automatically (no magic offset).
   Chain: .view-app.chat-app (height cap) → .app-main → #tab-dm → #dm-mount → .dm-wrap. */
.view-app.chat-app { height: 100vh; height: 100dvh; }
.view-app.chat-app .app-main { display: flex; flex-direction: column; min-height: 0; }
.view-app:has(#tab-dm:not([hidden])), .view-app:has(#tab-inbox:not([hidden])) { height: 100vh; height: 100dvh; }
.app-main:has(#tab-dm:not([hidden])), .app-main:has(#tab-inbox:not([hidden])) { display: flex; flex-direction: column; min-height: 0; }
#tab-dm, #tab-inbox { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
#tab-dm .panel-head, #tab-inbox .panel-head { display: none; }
#dm-mount, #inbox-mount { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* Inbox wraps its panes in an extra .inbox-shell (filters + .dm-wrap) that dm.js lacks; it
   must also flex-fill or the .dm-wrap below can't bound its height and the composer is pushed
   below the fold. */
.inbox-shell { flex: 1 1 auto; min-height: 0; }
/* A flex item's default min-height is auto (= min-content), so a long thread could refuse to
   shrink and push the composer below the pane. min-height:0 (here; .dm-wrap above) lets the
   message list scroll INSIDE the pane, keeping the composer pinned above the tab bar. */
.dm-messages { min-height: 0; }

@media (max-width: 640px) {
  /* Phone single-pane PRESENTATION only — the conversation overlays the thread list and the
     "‹ Chats" back control returns to it. The height cap + flex-fill above are width-independent
     and now apply at every width; only this list/pane swap is phone-specific. */
  .dm-wrap { gap: 0; background: var(--color-bg); }
  .dm-list { flex: 1 1 auto; border-right: none; min-height: 0; }
  .dm-pane { position: absolute; inset: 0; background: var(--color-bg); display: none;
    padding: 0 var(--space-2) var(--space-2); }
  .dm-pane.is-open { display: flex; }
  .dm-back { display: inline-flex; }
  /* A spoke with a single conversation has no list to go back to — hide it there. */
  .dm-wrap.dm-solo .dm-back { display: none; }
  /* Item 4 (JF 2026-06-18): "inside a message thread the area where you can actually see
     messages is so small — it shows the filters (all/insured/drivers/agents/drafts) even when
     you're in a thread." On a phone the conversation overlays the list, so the filter row is
     dead space stealing height. Hide it whenever a thread is open. The filters are the pane's
     UNCLE (.inbox-shell > .inbox-filters; the pane is .inbox-shell .dm-wrap .dm-pane), so a
     :has() on the shell reaches up to them; tapping "‹ Inbox" removes .is-open and they return.
     Desktop (>=640px, two-pane) is untouched — this lives inside the phone media query only. */
  .inbox-shell:has(.dm-pane.is-open) .inbox-filters { display: none; }
}

/* ---- Work Completed log (#34) — read-only daily ledger, Bryan + George ---- */
.wa-wrap { display: flex; flex-direction: column; gap: var(--space-4); }
.wa-day { display: flex; flex-direction: column; gap: var(--space-2); }
.wa-day-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-2); padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--color-border);
}
.wa-day-head h3 { margin: 0; }
.wa-day-count { color: var(--color-text-muted); font-size: 12px; font-weight: 500; white-space: nowrap; }
/* Per-driver daily count — a chip per driver between the day header and its rows. */
.wa-driver-counts { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-1) 0 var(--space-2); }
.wa-dc-chip {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 4px 10px; border-radius: var(--radius-full);
  background: var(--color-bg-subtle); border: 1px solid var(--color-border);
  font-size: 13px; white-space: nowrap;
}
.wa-dc-name { color: var(--color-text-strong); font-weight: 600; }
.wa-dc-total { color: var(--color-text-strong); font-weight: 700; font-variant-numeric: tabular-nums; }
.wa-dc-split { color: var(--color-text-muted); font-size: 12px; }
.wa-kind-totals { display: flex; flex-direction: column; gap: 4px; padding: 0 0 var(--space-2); }
.wa-kt-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; font-size: 13px; }
.wa-kt-head { color: var(--color-text-strong); font-weight: 600; min-width: 6.5rem; }
.wa-kt-part { color: var(--color-text-strong); font-variant-numeric: tabular-nums; white-space: nowrap; }
.wa-kt-part.is-zero { color: var(--color-text-muted); }
.wa-kt-not_submitted .wa-kt-head, .wa-kt-not_submitted .wa-kt-part { color: var(--color-text-muted); }
.wa-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.wa-row {
  display: grid;
  grid-template-columns: 4.5rem 7rem auto 1fr;
  align-items: center; gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
  font-size: 14px;
}
.wa-row:last-child { border-bottom: none; }
.wa-row.is-clickable {
  cursor: pointer;
  border-radius: var(--radius-md);
}
.wa-row.is-clickable:hover,
.wa-row.is-clickable:focus-visible {
  background: var(--color-bg-subtle);
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.wa-nav-status,
.board-occurrence-error {
  margin: 0 0 var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-md);
  background: var(--color-warning-soft);
  color: var(--color-text);
}
.board-occurrence-error p { margin: var(--space-1) 0 var(--space-3); }
.sharp-batch-host[hidden] { display: none; }
.sharp-batch-card {
  margin: 0 0 14px;
  padding: 14px;
  border: 1px solid #d8ddd9;
  border-radius: 16px;
  background: #fff;
}
.sharp-batch-heading {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 10px;
}
.sharp-batch-heading span,
.sharp-batch-issue {
  color: #6b746f;
  font-size: .86rem;
}
.sharp-batch-input {
  box-sizing: border-box;
  width: 100%;
  min-height: 82px;
  resize: vertical;
}
.sharp-batch-import-row,
.sharp-batch-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.sharp-batch-import-row { margin: 8px 0 12px; }
.sharp-batch-row {
  padding: 11px 0;
  border-top: 1px solid #edf0ee;
}
.sharp-batch-label {
  margin-bottom: 7px;
  font-weight: 650;
  overflow-wrap: anywhere;
}
.sharp-batch-issue { margin: -3px 0 7px; }
.sharp-batch-preview {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: #f4f7f5;
}
.sharp-batch-preview ol {
  margin: 6px 0 0;
  padding-left: 22px;
}
.sharp-batch-preview li { overflow-wrap: anywhere; }
.sharp-batch-outcome.is-selected {
  color: #fff;
  border-color: #138473;
  background: #138473;
}
.sharp-batch-error {
  color: #a33a2b;
  font-size: .86rem;
}
.wa-time { color: var(--color-text-muted); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wa-driver { color: var(--color-text-strong); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wa-badge {
  display: inline-flex; align-items: center; justify-self: start;
  font-size: 12px; font-weight: 600; padding: 2px 10px; border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted); white-space: nowrap;
}
.wa-badge.wa-submitted { background: var(--color-success-soft); color: var(--color-success); }
.wa-badge.wa-notsubmitted { background: var(--color-warning-soft); color: var(--color-warning); }
.wa-addr { color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wa-ctx { color: var(--color-text-faint); font-size: 12px; }
.wa-status { padding: var(--space-4) 0; }
.wa-error { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }

/* On a phone the address + context wrap under the time/driver/badge row. */
@media (max-width: 560px) {
  .wa-row { grid-template-columns: 4.5rem 1fr auto; }
  .wa-addr { grid-column: 1 / -1; }
}

/* ============================================================
   Agent TODO panel (#2) — phone-gap + unpaid worklists.
   ============================================================ */
.agent-todos { display: flex; flex-direction: column; gap: var(--space-6); }
.agent-todos-loading { padding: var(--space-6) 0; }

.agent-todo-group { display: flex; flex-direction: column; gap: var(--space-3); }
.agent-todo-group-title {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0; font-size: 15px; font-weight: 700; color: var(--color-text-strong);
}
.agent-todo-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-accent-soft); color: var(--color-accent-hover);
  font-size: 12px; font-weight: 700;
}
.agent-todo-group-hint { margin: 0; font-size: 13px; }
.agent-todo-group-fresh {
  margin: 0; align-self: flex-start;
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-success-soft); color: var(--color-success);
  font-size: 12px; font-weight: 700;
}
.agent-todo-list { display: flex; flex-direction: column; gap: var(--space-3); }

.agent-todo-card {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.agent-todo-main { display: flex; flex-direction: column; gap: 2px; }
.agent-todo-who { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.agent-todo-name { font-size: 15px; font-weight: 600; color: var(--color-text-strong); }
.agent-todo-addr { font-size: 13px; font-weight: 500; color: var(--color-text); }
.agent-todo-meta { font-size: 13px; color: var(--color-text-muted); }

.agent-todo-tier {
  display: inline-flex; align-items: center; text-transform: uppercase;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 2px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
}
.agent-todo-tier-mid { background: var(--color-info-soft); color: var(--color-info); }
.agent-todo-tier-high { background: var(--color-warning-soft); color: var(--color-warning); }

.agent-todo-agent {
  display: flex; align-items: center; justify-content: flex-start; gap: var(--space-1);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  background: var(--color-bg-subtle); border-radius: var(--radius-md);
}
.agent-todo-callline { font-size: 14px; color: var(--color-text); }
.agent-todo-agent-name { font-weight: 600; color: var(--color-text-strong); }
.agent-todo-call {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: 14px; font-weight: 700; color: var(--color-accent);
  text-decoration: none; white-space: nowrap;
}
.agent-todo-call:hover { color: var(--color-accent-hover); text-decoration: underline; }
.agent-todo-call-ico { font-size: 15px; }
.agent-todo-call-none { color: var(--color-text-faint); font-weight: 500; }

.agent-todo-fill { display: flex; align-items: stretch; gap: var(--space-2); }
.agent-todo-input {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--font-sans); font-size: 16px;
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); min-height: 48px;
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.agent-todo-input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.agent-todo-input:disabled { opacity: .6; }
.agent-todo-save {
  flex: 0 0 auto;
  font-family: var(--font-sans); font-size: 15px; font-weight: 700;
  padding: 0 var(--space-5); min-height: 48px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-todo-save:hover:not([disabled]) { background: var(--color-accent-hover); }
.agent-todo-save:active:not([disabled]) { transform: scale(0.98); }
.agent-todo-save[disabled] { opacity: .5; cursor: not-allowed; }

/* Bryan 2026-09-25: a second homeowner number and an email from the agent, each logged in
   MillInfo on its own after the first number saves. Bryan's own session only. */
.agent-todo-extra { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
.agent-todo-extra-input {
  box-sizing: border-box; width: 100%;
  font-family: var(--font-sans); font-size: 16px;
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); min-height: 48px;
}
.agent-todo-extra-input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.agent-todo-extra-input:disabled { opacity: .6; }
.agent-todo-extra-adds { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.agent-todo-add {
  font-family: var(--font-sans); font-size: 14px; font-weight: 600;
  min-height: 44px; padding: 0 var(--space-3);
  border: 1px dashed var(--color-border-strong); border-radius: var(--radius-md);
  background: transparent; color: var(--color-accent); cursor: pointer;
}
.agent-todo-extra [hidden] { display: none; }
.agent-contact-progress { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-2); font-size: 14px; line-height: 1.4; }
.agent-contact-progress:empty { display: none; }
.agent-contact-row { color: var(--color-text-muted, var(--color-text)); }
.agent-contact-row[data-state="saved"], .agent-contact-row[data-state="on_file"] { color: var(--color-success); }
.agent-contact-row[data-state="failed"] { color: var(--color-danger); }
.agent-contact-row[data-state="unconfirmed"] { color: var(--color-warning); }
.agent-contact-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }

/* S1 (JF 2026-06-13) confirmation review card. Reuses the .agent-todo-card shell;
   a success-green left rail + check badge marks it as a confirmed appointment ready
   to Approve (lands the 4hr block + access note on the Spoke stop, texts nobody). The
   window itself is the agent's captured fact (read-only headline); only the four
   contact/access extras are editable inputs. */
.agent-confirm-card { border-left: 3px solid var(--color-success); }
.agent-confirm-head { display: flex; align-items: center; gap: var(--space-2); }
.agent-confirm-badge {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: var(--radius-full);
  background: var(--color-success-soft); color: var(--color-success);
  font-size: 13px; font-weight: 700;
}
.agent-confirm-title { font-size: 15px; font-weight: 700; color: var(--color-text-strong); }
.agent-confirm-who { font-size: 14px; font-weight: 600; color: var(--color-text); }
.agent-confirm-addr { font-size: 13px; color: var(--color-text-muted); }

.agent-confirm-fields { display: flex; flex-direction: column; gap: var(--space-3); }
.agent-confirm-field { display: flex; flex-direction: column; gap: var(--space-1); }
.agent-confirm-flabel {
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-muted);
}
.agent-confirm-field input {
  width: 100%; box-sizing: border-box;
  font-family: var(--font-sans); font-size: 16px;
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); min-height: 48px;
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.agent-confirm-field input:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }

.agent-confirm-actions { display: flex; }
.agent-confirm-approve {
  flex: 1 1 auto;
  font-family: var(--font-sans); font-size: 15px; font-weight: 700;
  padding: 0 var(--space-5); min-height: 48px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-success); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-confirm-approve:hover:not([disabled]) { filter: brightness(0.94); }
.agent-confirm-approve:active:not([disabled]) { transform: scale(0.98); }
.agent-confirm-approve[disabled] { opacity: .5; cursor: not-allowed; }

/* S3 (JF 2026-06-13) call-log review card. Reuses the .agent-todo-card shell with an
   accent left rail. The agent's proposed line is editable; a two-way toggle flips the
   honest result (Appointment made / Left voicemail). "Log in MillInfo" opens George's
   workbench with the line ready to PASTE — nothing is auto-written. */
.agent-call-card { border-left: 3px solid var(--color-accent); }
.agent-call-head { display: flex; align-items: center; gap: var(--space-2); }
.agent-call-name { font-size: 15px; font-weight: 700; color: var(--color-text-strong); }

/* Segmented toggle: two equal buttons, the active result filled with the accent. */
.agent-call-kind {
  display: flex; gap: var(--space-1);
  background: var(--color-bg-subtle); border-radius: var(--radius-md); padding: 3px;
}
.agent-call-kindbtn {
  flex: 1 1 0; min-height: 40px;
  font-family: var(--font-sans); font-size: 13px; font-weight: 600;
  color: var(--color-text-muted); background: transparent;
  border: 1px solid transparent; border-radius: var(--radius-sm); cursor: pointer;
  transition: background var(--motion-fast) ease, color var(--motion-fast) ease;
}
.agent-call-kindbtn.is-active {
  background: var(--color-card); color: var(--color-accent);
  border-color: var(--color-border); font-weight: 700;
}

.agent-call-flabel {
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-muted);
}
.agent-call-text {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 72px;
  font-family: var(--font-sans); font-size: 16px; line-height: 1.4;
  background: var(--color-card); color: var(--color-text);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.agent-call-text:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.agent-call-basis { font-size: 13px; color: var(--color-text-muted); }
.agent-call-why {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-faint); margin-right: var(--space-1);
}
.agent-call-actions { display: flex; }
.agent-call-log {
  flex: 1 1 auto;
  font-family: var(--font-sans); font-size: 15px; font-weight: 700;
  padding: 0 var(--space-5); min-height: 48px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-call-log:hover { background: var(--color-accent-hover); }
.agent-call-log:active { transform: scale(0.98); }

/* S4 (JF 2026-06-13) phone-write card. The card shows ONLY the first name + address —
   the obtained number is NEVER rendered here (PII); it rides in the in-memory row and
   surfaces only in the workbench's copy strip. "Write to MillInfo" opens that workbench. */
.agent-pw-card { border-left: 3px solid var(--color-accent); flex-direction: row; align-items: center; gap: var(--space-3); }
.agent-pw-main { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.agent-pw-who { font-size: 15px; font-weight: 600; color: var(--color-text-strong); }
.agent-pw-addr { font-size: 13px; color: var(--color-text-muted); }
.agent-pw-write {
  flex: 0 0 auto;
  font-family: var(--font-sans); font-size: 14px; font-weight: 700;
  padding: 0 var(--space-4); min-height: 44px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-pw-write:hover { background: var(--color-accent-hover); }
.agent-pw-write:active { transform: scale(0.98); }

/* Unpaid rows: single line, amount pinned right. */
.agent-todo-unpaid { flex-direction: row; align-items: center; justify-content: space-between; }
.agent-todo-amount {
  font-size: 16px; font-weight: 700; color: var(--color-text-strong);
  white-space: nowrap;
}

/* Stage 1 (JF 2026-06-14) — Recent activity feed. The chronological audit trail of what
   the agent and operator did (confirm, 4hr block, MillInfo write, push-to-next-week),
   newest first. Each row is a kind chip + PII-safe summary + who/when, with an Undo on
   the reversible, not-yet-undone ones. An undone row reads struck-through "Undone" so the
   history stays honest. Lighter than a to-do card: it's reference, not an action item. */
.agent-changes-group .agent-change-list { display: flex; flex-direction: column; gap: var(--space-2); }
.agent-change-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--color-bg-subtle); border-radius: var(--radius-md);
}
.agent-change-row.is-undone { opacity: .6; }
.agent-change-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.agent-change-kind {
  align-self: flex-start;
  display: inline-flex; align-items: center; text-transform: uppercase;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 2px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
}
.agent-change-kind-confirm { background: var(--color-success-soft); color: var(--color-success); }
.agent-change-kind-send { background: var(--color-accent-soft); color: var(--color-accent-hover); }
.agent-change-kind-spoke-block { background: var(--color-info-soft); color: var(--color-info); }
.agent-change-kind-push-next-week { background: var(--color-warning-soft); color: var(--color-warning); }
.agent-change-summary { font-size: 14px; color: var(--color-text); }
.agent-change-row.is-undone .agent-change-summary { text-decoration: line-through; }
.agent-change-meta { font-size: 12px; }
.agent-change-action { flex: 0 0 auto; }
.agent-change-undo {
  font-family: var(--font-sans); font-size: 13px; font-weight: 700; cursor: pointer;
  padding: var(--space-1) var(--space-3); min-height: 32px;
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  background: var(--color-card); color: var(--color-text);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-change-undo:hover:not([disabled]) { background: var(--color-bg-subtle); }
.agent-change-undo:active:not([disabled]) { transform: scale(0.98); }
.agent-change-undo[disabled] { opacity: .5; cursor: not-allowed; }
.agent-change-undone {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-text-faint);
}

/* This week's appointments roster (redesigned 2026-06-15 — Bryan: "you give me days to go
   off, just a name. make it beautiful and helpful and intuitive"). Grouped BY DAY: a weekday
   + date header (with a Today / Tomorrow cue) over tappable appointment rows; tapping a row
   opens that homeowner's conversation, where the day + 4-hour block confirm form lives.
   Status reads as a colored tag + a left accent stripe: confirmed = green, in progress =
   neutral (not yet confirmed), next week = amber (pushed off the route), converting = blue.
   Confirmed rows carry the Spoke + Millennium write markers — "✓" once the Changes feed
   records the write, "pending" until then (honest while the live-write gates are off). Per
   Bryan this panel does NOT print the 4-hour arrival block. */
.assistant-day-overview { margin: 0 0 var(--space-4); }
.assistant-week-card {
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-card);
  box-shadow: var(--shadow-card);
}
.assistant-week-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg-subtle);
}
.assistant-week-head h3 { margin: 0; font-size: 16px; color: var(--color-text-strong); }
.assistant-week-head span { font-size: 12px; color: var(--color-text-muted); }
.assistant-day-list { display: grid; grid-template-columns: 1fr; }
.assistant-day-row {
  min-height: 52px;
  display: grid; grid-template-columns: minmax(92px, .75fr) minmax(0, 1.5fr) auto;
  align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.assistant-day-row:last-child { border-bottom: 0; }
.assistant-day-row.is-today { box-shadow: inset 3px 0 0 var(--color-accent); background: var(--color-accent-faint); }
.assistant-day-when, .assistant-day-state { display: flex; flex-direction: column; min-width: 0; }
.assistant-day-weekday { font-size: 13px; font-weight: 750; color: var(--color-text-strong); }
.assistant-day-date, .assistant-day-state span { font-size: 11px; color: var(--color-text-muted); }
.assistant-day-state strong { font-size: 15px; color: var(--color-text-strong); }
.assistant-day-today {
  display: inline-flex; margin-left: 3px; padding: 1px 5px; border-radius: var(--radius-full);
  font-size: 9px; font-weight: 800; text-transform: uppercase;
  color: var(--color-accent-hover); background: var(--color-accent-soft);
}
.assistant-day-alerts { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.assistant-day-alert {
  justify-self: end; max-width: 74px; padding: 3px 6px; border-radius: var(--radius-full);
  font-size: 10px; font-weight: 750; line-height: 1.2; text-align: center;
  color: var(--color-warning); background: var(--color-warning-soft);
}
@media (min-width: 760px) {
  .assistant-day-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .assistant-day-row:nth-child(odd) { border-right: 1px solid var(--color-border); }
}

.agent-roster-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.agent-roster-head .agent-todo-group-title { margin: 0; }
.roster-summary { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.roster-chip {
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 700; letter-spacing: .01em;
  padding: 3px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
}
.roster-chip-confirmed   { background: var(--color-success-soft); color: var(--color-success); }
.roster-chip-in-progress { background: var(--color-bg-subtle);    color: var(--color-text-muted); }
.roster-chip-next-week   { background: var(--color-warning-soft); color: var(--color-warning); }
.roster-chip-rescheduled { background: var(--color-info-soft);    color: var(--color-info); }
/* Reconciliation gaps — confirmed appts still owing a Spoke note / a Millennium appt log.
   An attention color (not the muted in-progress grey) so the chore is visible at a glance. */
.roster-chip-todo        { background: var(--color-warning-soft); color: var(--color-warning); }
.agent-roster-hint { margin: var(--space-1) 0 var(--space-3); }

.agent-roster-days { display: flex; flex-direction: column; gap: var(--space-4); }
.roster-day { display: flex; flex-direction: column; gap: var(--space-2); }
.roster-day-head {
  display: flex; align-items: baseline; gap: var(--space-2);
  padding: 0 var(--space-1) var(--space-1);
  border-bottom: 1px solid var(--color-border);
}
.roster-day-weekday { font-size: 14px; font-weight: 700; color: var(--color-text-strong); }
.roster-day-date { font-size: 13px; color: var(--color-text-muted); }
.roster-day-rel {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  padding: 1px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-accent-soft); color: var(--color-accent-hover);
}
.roster-day-head-undated .roster-day-weekday { color: var(--color-text-muted); font-weight: 600; }
.roster-day-count { margin-left: auto; font-size: 12px; font-weight: 700; color: var(--color-text-faint); }
.roster-day-rows { display: flex; flex-direction: column; gap: var(--space-2); }

.roster-row-wrap { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-2); }
.roster-closeout {
  min-height: 40px; padding: var(--space-2) var(--space-3); white-space: nowrap;
  color: var(--color-warning); border-color: var(--color-warning); background: var(--color-card);
}
.roster-closeout:disabled { opacity: .55; cursor: wait; }
@media (max-width: 430px) {
  .roster-row-wrap { grid-template-columns: 1fr; }
  .roster-closeout { justify-self: end; min-height: 36px; }
}

.roster-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  width: 100%; text-align: left; cursor: pointer; font: inherit; color: inherit;
  padding: var(--space-3); margin: 0;
  background: var(--color-card);
  border: 1px solid var(--color-border); border-left: 3px solid var(--color-border-strong);
  border-radius: var(--radius-md); box-shadow: var(--shadow-card);
  transition: box-shadow .12s ease, background .12s ease, transform .04s ease;
}
.roster-row:hover { background: var(--color-card-hover); box-shadow: var(--shadow-card-hover); }
.roster-row:active { transform: translateY(1px); }
.roster-row:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent-faint), 0 0 0 4px var(--color-accent); }
.roster-row-confirmed { border-left-color: var(--color-success); }
.roster-row-in-progress { border-left-color: var(--color-border-strong); }
.roster-row-next-week { border-left-color: var(--color-warning); }
.roster-row-converting { border-left-color: var(--color-info); }
.roster-row-rescheduled { border-left-color: var(--color-info); }

.roster-row-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.roster-name { font-size: 15px; font-weight: 700; color: var(--color-text-strong); line-height: 1.25; }
.roster-addr {
  font-size: 12px; color: var(--color-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
/* Carrier + tier account chip (#62) — quiet metadata, distinct from the grey address
   and the status tags. Hugs its content; truncates rather than wrapping. */
.roster-account {
  align-self: flex-start; margin: 1px 0;
  font-size: 11px; font-weight: 600; line-height: 1.25;
  color: var(--color-accent-hover); background: var(--color-accent-faint);
  border: 1px solid var(--color-accent-soft);
  padding: 1px var(--space-2); border-radius: var(--radius-full);
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Supplement chip (#4) — amber, deliberately louder than the teal account chip because a
   supplement (fire mitigation / EHP / brush) changes what gets inspected AND paid. The
   leading "+ " marks it as extra scope so it never reads as another account label. */
.roster-supp {
  align-self: flex-start; margin: 1px 0;
  font-size: 11px; font-weight: 700; line-height: 1.25;
  color: var(--color-warning); background: var(--color-warning-soft);
  border: 1px solid var(--color-warning);
  padding: 1px var(--space-2); border-radius: var(--radius-full);
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.roster-supp::before { content: '+ '; }
.roster-marks { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); margin-top: 2px; }
.roster-mark {
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 600;
  padding: 2px var(--space-2); border-radius: var(--radius-full);
}
.roster-mark.is-done { background: var(--color-success-soft); color: var(--color-success); }
/* "Needs Spoke note" / "Needs appt log" — a chore still owed on a confirmed appt. Amber,
   not faint grey, so the gap reads as an action item (Bryan's reconciliation ask). */
.roster-mark.is-pending { background: var(--color-warning-soft); color: var(--color-warning); }

/* #63 outbound-reach chip — did our latest appointment text actually reach the homeowner?
   A read/delivery receipt is the only proxy Beeper exposes (no transport field); its
   absence past the gateway window = likely bad number / landline, which is an ACTION ITEM
   (call them) → danger styling, and it may wrap on a narrow row so it is not a pill. The
   confirmed Read/Delivered marker is deliberately quiet (muted, no fill) so only the
   "call them" case draws the eye. Never says "SMS" — we report the receipt proxy only. */
.roster-reach {
  display: inline-flex; align-items: baseline; gap: 4px;
  align-self: flex-start; margin-top: 3px; max-width: 100%;
  font-size: 11px; font-weight: 600; line-height: 1.3;
  padding: 2px var(--space-2); border-radius: var(--radius-md);
}
.roster-reach-dot { font-weight: 800; flex: none; }
.roster-reach-text { white-space: normal; overflow-wrap: anywhere; }
.roster-reach-when { font-weight: 500; opacity: .8; }
.roster-reach-warn { background: var(--color-danger-soft); color: var(--color-danger); }
.roster-reach-ok { background: transparent; color: var(--color-text-muted); padding-left: 0; }

.roster-row-meta { display: flex; align-items: center; gap: var(--space-1); flex: none; }
.roster-tag {
  display: inline-flex; align-items: center; white-space: nowrap;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 3px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
}
.roster-tag-confirmed { background: var(--color-success-soft); color: var(--color-success); }
.roster-tag-in-progress { background: var(--color-bg-subtle); color: var(--color-text-muted); }
.roster-tag-next-week { background: var(--color-warning-soft); color: var(--color-warning); }
.roster-tag-converting { background: var(--color-info-soft); color: var(--color-info); }
.roster-tag-rescheduled { background: var(--color-info-soft); color: var(--color-info); }
.roster-chevron { font-size: 20px; line-height: 1; color: var(--color-text-faint); flex: none; }

/* Stage 3 (JF 2026-06-14) — per-send approval queue. The agent drafted these replies
   and is HOLDING them: nothing is texted to an insured until the operator taps Send.
   An amber left rail flags "needs your tap" (distinct from the green confirm and blue
   call/phone-write rails). The card shows who it's going to + the insured's last message
   (context) + the EXACT outbound text, so the tap is informed. pre-wrap keeps any line
   breaks in the draft (same lesson as .dm-bubble). Send = the gateway performs the real
   Beeper send; Skip cancels the hold. Phone shows in this operator-only view only. */
.agent-send-card { border-left: 3px solid var(--color-warning); }
.agent-send-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1) var(--space-2); }
.agent-send-to { font-size: 15px; font-weight: 700; color: var(--color-text-strong); }
.agent-send-phone { font-size: 13px; color: var(--color-text-muted); }
.agent-send-kind {
  margin-left: auto;
  display: inline-flex; align-items: center; text-transform: uppercase;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 2px var(--space-2); border-radius: var(--radius-full);
  background: var(--color-warning-soft); color: var(--color-warning);
}
.agent-send-inbound { font-size: 13px; color: var(--color-text-muted); }
.agent-send-inlabel {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-faint); margin-right: var(--space-1);
}
.agent-send-body {
  white-space: pre-wrap; overflow-wrap: anywhere;
  font-size: 16px; line-height: 1.4; color: var(--color-text);
  background: var(--color-card);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}
.agent-send-actions { display: flex; gap: var(--space-2); }
.agent-send-go {
  flex: 1 1 auto;
  font-family: var(--font-sans); font-size: 15px; font-weight: 700;
  padding: 0 var(--space-5); min-height: 48px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-send-go:hover:not([disabled]) { background: var(--color-accent-hover); }
.agent-send-go:active:not([disabled]) { transform: scale(0.98); }
.agent-send-go[disabled] { opacity: .5; cursor: not-allowed; }
.agent-send-skip {
  flex: 0 0 auto;
  font-family: var(--font-sans); font-size: 15px; font-weight: 700; cursor: pointer;
  padding: 0 var(--space-5); min-height: 48px;
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  background: var(--color-card); color: var(--color-text);
  transition: background var(--motion-fast) ease, transform 80ms ease;
}
.agent-send-skip:hover:not([disabled]) { background: var(--color-bg-subtle); }
.agent-send-skip:active:not([disabled]) { transform: scale(0.98); }
.agent-send-skip[disabled] { opacity: .5; cursor: not-allowed; }

/* #1 (JF 2026-06-11) — full-size image lightbox. Tapping an uploaded photo thumb or the
   county-assessor sketch opens this singleton overlay (board.js openLightbox) showing the
   SAME src at natural size. Sits above every other layer (toasts/menus top out at z 50).
   A white mat keeps black-on-white sketches visible on the dark backdrop; behind a full-
   bleed photo the mat is simply covered. Closes on a backdrop tap, the ×, or Escape. */
.img-lightbox {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
  background: rgba(15, 11, 6, .82);
  -webkit-tap-highlight-color: transparent;
}
.img-lightbox[hidden] { display: none; }
.img-lightbox-img {
  max-width: 94vw; max-height: 88vh; width: auto; height: auto;
  object-fit: contain; background: #fff;
  border-radius: var(--radius-sm); box-shadow: 0 12px 48px rgba(0, 0, 0, .5);
}
.img-lightbox-close {
  position: absolute; top: max(12px, env(safe-area-inset-top)); right: 14px;
  width: 44px; height: 44px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-full);
  background: rgba(0, 0, 0, .5); color: #fff;
  font: 300 28px var(--font-sans); cursor: pointer;
}
.img-lightbox-close:hover { background: rgba(0, 0, 0, .72); }
/* Lock the page scroll behind the modal so a tap-drag moves the overlay, not the board. */
body.has-lightbox { overflow: hidden; }

/* ============================================================
   Settings tab (operator) — per-driver work-config rows + the
   "Customize how the agent opens" disclosure (#16, JF 2026-06-12).
   The scheduling agent has a REAL conversation in Bryan's voice to
   book the appointment (D-47) — NOT a template bot. This gear only
   seeds the agent's FIRST booking message, per driver, per kind.
   The insured-name + policy-number tokens are LOCKED (filled per
   stop, can't be removed); everything else is editable.
   ============================================================ */
#settings-root { display: flex; flex-direction: column; gap: var(--space-4); }
.portal-whats-new { background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-4); }
.portal-whats-new-head h3, .portal-whats-new-entry h4 { margin: 0; color: var(--color-text-strong); }
.portal-whats-new-head p { margin: var(--space-1) 0 0; }
.portal-whats-new-entry { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
.portal-whats-new-date { margin: 0 0 var(--space-1); color: var(--color-primary); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.portal-whats-new-entry ul { margin: var(--space-2) 0 0; padding-left: var(--space-5); color: var(--color-text); }
.portal-whats-new-entry li + li { margin-top: var(--space-2); }
.portal-whats-new-badge { position: absolute; top: 3px; right: 3px; min-width: 26px; padding: 2px 5px; border-radius: 999px; background: var(--color-danger); color: #fff; font-size: 9px; font-weight: 800; line-height: 1.2; }
.settings-loading, .settings-empty, .settings-error { padding: var(--space-6) 0; color: var(--color-text-muted); }
.settings-error { color: var(--color-danger); }
/* v158 — sign out moved from global header to Settings tab */
.settings-signout-section { padding: var(--space-6) 0 var(--space-4); border-top: 1px solid var(--color-border); margin-top: var(--space-4); }
.settings-signout-btn { color: var(--color-danger); border-color: var(--color-danger); }

.settings-row {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.settings-row-name { font-size: 16px; font-weight: 700; color: var(--color-text-strong); }
.settings-toggle { display: flex; align-items: center; gap: var(--space-2); font-size: 14px; color: var(--color-text); cursor: pointer; }
.settings-toggle input { width: 18px; height: 18px; accent-color: var(--color-accent); flex: 0 0 auto; }
.settings-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--color-text-muted); }
.settings-field select {
  font-family: var(--font-sans); font-size: 16px; color: var(--color-text);
  background: var(--color-card); border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md); padding: var(--space-2) var(--space-3); min-height: 44px;
}
.settings-save {
  align-self: flex-start;
  font-family: var(--font-sans); font-size: 14px; font-weight: 700;
  padding: var(--space-2) var(--space-5); min-height: 40px;
  border-radius: var(--radius-md); border: 1px solid transparent; cursor: pointer;
  background: var(--color-accent); color: var(--color-text-on-accent);
  transition: background var(--motion-fast) ease;
}
.settings-save:hover:not([disabled]) { background: var(--color-accent-hover); }
.settings-save[disabled] { opacity: .6; cursor: progress; }
.settings-status { font-size: 13px; color: var(--color-text-muted); }

.spoke-connection-card,
.spoke-owner-section {
  border: 1px solid var(--color-border);
  border-radius: 16px;
  background: var(--color-surface);
  padding: 18px;
}
.spoke-connection-head,
.spoke-owner-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.spoke-connection-head h3 { margin: 0; font-size: 20px; }
.spoke-connection-head p,
.spoke-owner-head span { display: block; margin: 3px 0 0; color: var(--color-text-muted); font-size: 13px; }
.spoke-connection-state { display: inline-flex; align-items: center; border-radius: 999px; padding: 5px 9px; background: #eef2f7; color: #334155; font-size: 12px; font-weight: 700; white-space: nowrap; }
.spoke-connection-state--connected { background: #dcfce7; color: #166534; }
.spoke-connection-state--login_pending,
.spoke-connection-state--verification_pending { background: #fef3c7; color: #92400e; }
.spoke-connection-state--needs_attention { background: #fee2e2; color: #991b1b; }
.spoke-connection-copy { color: var(--color-text); line-height: 1.5; margin: 18px 0; }
.spoke-connection-detail { margin: 7px 0 0; color: var(--color-text-muted); font-size: 12px; text-align: right; }
.spoke-connect-feedback { min-height: 18px; margin: 9px 0 0; color: var(--color-text-muted); font-size: 13px; }
.spoke-owner-section { margin-top: 14px; background: var(--color-bg-subtle, #f8fafc); }
.spoke-owner-head strong { display: block; }
.spoke-owner-actions { display: flex; gap: 8px; margin-top: 12px; }
@media (max-width: 520px) {
  .spoke-connection-head,
  .spoke-owner-head { flex-direction: column; }
  .spoke-connection-detail { text-align: left; }
}

/* The disclosure spans the row full-width, separated by a hairline. */
.settings-openings {
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-1); padding-top: var(--space-3);
}
.settings-openings-summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: var(--space-2);
  font-size: 14px; font-weight: 600; color: var(--color-accent);
  padding: var(--space-1) 0;
  -webkit-tap-highlight-color: transparent;
}
.settings-openings-summary::-webkit-details-marker { display: none; }
.settings-openings-summary::before {
  content: '\25B8'; /* ▸ */ font-size: 11px; color: var(--color-text-muted);
  transition: transform var(--motion-fast) ease;
}
.settings-openings[open] > .settings-openings-summary::before { transform: rotate(90deg); }
.settings-openings-summary:hover { color: var(--color-accent-hover); }

.settings-openings-note {
  margin: var(--space-2) 0 var(--space-4);
  font-size: 13px; line-height: 1.5; color: var(--color-text-muted);
}
.settings-openings-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px;
  background: var(--color-bg-subtle); color: var(--color-text);
  padding: 1px 5px; border-radius: var(--radius-sm); border: 1px solid var(--color-border);
  white-space: nowrap;
}

.opening-field {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3) 0; border-top: 1px solid var(--color-border);
}
.opening-field:first-of-type { border-top: none; padding-top: 0; }
.opening-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.opening-label { font-size: 14px; font-weight: 700; color: var(--color-text-strong); }
.opening-badge {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 2px var(--space-2); border-radius: var(--radius-full); white-space: nowrap;
}
.opening-badge.is-default { background: var(--color-bg-subtle); color: var(--color-text-muted); }
.opening-badge.is-custom { background: var(--color-accent-soft); color: var(--color-accent-hover); }
.opening-hint { margin: 0; font-size: 12px; color: var(--color-text-faint); }

.opening-text {
  width: 100%; resize: vertical; min-height: 140px;
  font-family: var(--font-sans); font-size: 16px; line-height: 1.5;
  color: var(--color-text); background: var(--color-card);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: var(--space-3);
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.opening-text:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.opening-field.is-invalid .opening-text { border-color: var(--color-danger); box-shadow: 0 0 0 3px var(--color-danger-soft); }

.opening-tokens { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.opening-tokens-label { font-size: 12px; font-weight: 600; color: var(--color-text-muted); }
.opening-token {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-sans); font-size: 12px; font-weight: 600;
  color: var(--color-text); background: var(--color-bg-subtle);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-full);
  padding: 4px 10px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.opening-token:hover { background: var(--color-accent-faint); border-color: var(--color-accent); color: var(--color-accent-hover); }
.opening-token.is-locked { background: var(--color-accent-faint); border-color: var(--color-accent-soft); }
.opening-token-req {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-accent-hover); background: var(--color-card);
  padding: 1px 5px; border-radius: var(--radius-full); border: 1px solid var(--color-accent-soft);
}

.opening-field-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); flex-wrap: wrap; min-height: 22px;
}
.opening-error { font-size: 12px; font-weight: 600; color: var(--color-danger); flex: 1 1 auto; min-width: 0; }
.opening-reset {
  flex: 0 0 auto;
  font-family: var(--font-sans); font-size: 12px; font-weight: 600;
  color: var(--color-text-muted); background: transparent;
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: 4px var(--space-3); cursor: pointer;
}
.opening-reset:hover { color: var(--color-text); background: var(--color-bg-subtle); }

.settings-openings-foot {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
/* The Save button only carries `.btn` (base) — paint it the primary accent here. */
.opening-save { flex: 0 0 auto; background: var(--color-accent); color: var(--color-text-on-accent); font-weight: 700; }
.opening-save:hover:not([disabled]) { background: var(--color-accent-hover); }

/* ---- Live workbench (George's interactive Steel browser, #126) ----
   The passive watch view reuses .card/.empty-state. These cover the live JPEG
   frame (responsive, never wider than the column) and the interactive wrapper. */
.live-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
.live-card-title { font-weight: 600; color: var(--color-text-strong); }

/* Chrome-style tab strip: one tab per open driver browser (Bryan 2026-06-19 — "a tab
   open for each driver ... multiple tabs at once like a normal chrome"). The active tab
   reads as joined to the live-card below; background tabs are muted but their engine
   sessions stay alive, so switching back is instant. The engine logged each one in
   server-side (George never sees a password). */
.live-tabs {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-1);
  margin-bottom: -1px; padding: 0 var(--space-1);
}
.live-tab {
  display: inline-flex; align-items: center; gap: var(--space-1);
  max-width: 220px; padding: var(--space-1) var(--space-2);
  background: var(--color-bg-subtle); color: var(--color-text-muted);
  border: 1px solid var(--color-border); border-bottom: none;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.live-tab.active {
  background: var(--color-card); color: var(--color-text-strong);
  border-color: var(--color-border-strong);
}
.live-tab-label {
  flex: 1 1 auto; min-width: 0; max-width: 168px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 600; color: inherit; text-align: left;
}
.live-tab-close {
  flex: 0 0 auto; width: 20px; height: 20px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: var(--radius-sm);
  cursor: pointer; font-size: 16px; color: var(--color-text-muted);
}
.live-tab-close:hover { background: var(--color-danger-soft); color: var(--color-danger); }

.live-frame {
  display: block; width: 100%; height: auto; max-width: 100%;
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  background: var(--color-bg-subtle);
}
.live-frame-err { opacity: .45; transition: opacity var(--motion-fast) ease; }

/* Interactive: the frame IS the input surface — crosshair cursor, focus ring,
   and touch-action:none so a drag/scroll on it relays instead of panning the page. */
.live-interactive .live-stage { position: relative; }
.live-frame-interactive { cursor: crosshair; touch-action: none; }
.live-frame-interactive:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent-faint), 0 0 0 4px var(--color-accent); }
.live-hint { margin-top: var(--space-3); font-size: 13px; }
.live-login-note {
  margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3);
  font-size: 13px; color: var(--color-warning);
  background: var(--color-warning-soft); border-radius: var(--radius-sm);
}

/* S3/S4 prefill strip: the exact line (call-log text) or number (insured phone)
   George should PASTE into MillInfo when he opens the browser from a to-do.
   Read-only BY CONTRACT — nothing is auto-typed; George copies, pastes, and
   presses save himself. Accent-tinted so it reads as "here is your text". */
.live-prefill {
  margin: 0 0 var(--space-3); padding: var(--space-3);
  background: var(--color-accent-faint); border: 1px solid var(--color-accent-soft);
  border-radius: var(--radius-md);
}
.live-prefill-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.live-prefill-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--color-accent-hover);
}
.live-prefill-copy { flex: 0 0 auto; }
.live-prefill-text {
  width: 100%; resize: vertical; min-height: 52px;
  font-family: var(--font-sans); font-size: 15px; line-height: 1.5;
  color: var(--color-text); background: var(--color-card);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  transition: border-color var(--motion-fast) ease, box-shadow var(--motion-fast) ease;
}
.live-prefill-text:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.live-prefill-hint { margin: var(--space-2) 0 0; font-size: 12px; color: var(--color-text-muted); }

.opening-save-status { font-size: 13px; color: var(--color-text-muted); }

/* ---------- Contacts tab (Bryan 2026-06-19: "each insured becomes a contact") ----------
   A read-only insured directory. Search bar on top, a tappable card per insured. Built by
   contacts.js; the data identity is the Inbox's (one insured per phone). */
.contacts-bar {
  display: flex; align-items: center; gap: var(--space-3);
  margin: 0 0 var(--space-3);
}
.contacts-search {
  flex: 1 1 auto; min-width: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  background: var(--color-card); color: var(--color-text); font-size: 15px;
}
.contacts-search:focus { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.contacts-count { flex: 0 0 auto; font-size: 13px; color: var(--color-text-muted); white-space: nowrap; }
.contacts-list { display: flex; flex-direction: column; gap: var(--space-2); }
.contacts-empty { margin: var(--space-4) 0; }

/* The two folders (Bryan items 2 + 7): Everyone (full book, default) | Saved (the shortlist).
   Segmented pills; the active one fills with the accent. A count chip rides inside each pill so the
   operator sees how many are in each folder before tapping. */
.contacts-folders { display: flex; gap: var(--space-2); margin: 0 0 var(--space-3); }
.contacts-folder {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: var(--space-2) var(--space-3);
  background: var(--color-card); color: var(--color-text-muted);
  border: 1px solid var(--color-border-strong); border-radius: 999px;
  font: 600 13px var(--font-sans);
}
.contacts-folder:hover { border-color: var(--color-accent); color: var(--color-text); }
.contacts-folder.is-active { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-text-on-accent); }
.contacts-folder:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-accent-faint); }
.contacts-folder-n {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: var(--color-bg-subtle); color: var(--color-text-muted);
  font-size: 11px; font-weight: 700;
}
.contacts-folder.is-active .contacts-folder-n { background: rgba(255, 255, 255, 0.25); color: var(--color-text-on-accent); }

/* The "worth saving" star (Bryan items 2 + 7). One glyph, two states: outline by default, filled
   gold when its wrapper carries .is-saved. Shared by the card marker and the detail toggle. */
.star-ico { fill: none; flex: 0 0 auto; }
.contact-star .star-ico,
.contact-save-toggle.is-saved .star-ico { fill: var(--color-warning); stroke: var(--color-warning); }

/* One insured = one card. Whole card is the tap target (opens the contact's detail sheet). */
.contact-card {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  padding: var(--space-3) var(--space-3); cursor: pointer;
  background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  color: var(--color-text);
}
.contact-card:hover { background: var(--color-card-hover); border-color: var(--color-border-strong); }
.contact-card:focus-visible { outline: none; border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-faint); }
.contact-avatar {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-accent-soft); color: var(--color-accent-hover);
  font-weight: 700; font-size: 14px; letter-spacing: 0.02em;
}
.contact-avatar.is-anon { background: var(--color-bg-subtle); color: var(--color-text-faint); }
.contact-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.contact-name { font-weight: 600; color: var(--color-text-strong); font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-sub { font-size: 13px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-addr { font-size: 12px; color: var(--color-text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-star { flex: 0 0 auto; display: inline-flex; align-items: center; }
.contact-go { flex: 0 0 auto; color: var(--color-text-faint); font-size: 22px; line-height: 1; padding: 0 var(--space-1); }

/* The WrapDesk-style BIG contact detail sheet (Bryan items 6 + 8). Fills the Contacts tab,
   replacing the directory; the Back button returns. */
.contact-detail { display: flex; flex-direction: column; gap: var(--space-3); padding-bottom: var(--space-5); }
.contact-detail-top { display: flex; align-items: center; }
.contact-back {
  background: none; border: none; color: var(--color-accent); cursor: pointer;
  font: 600 14px var(--font-sans); padding: var(--space-2) 0; display: inline-flex; align-items: center; gap: 4px;
}
.contact-back:hover { color: var(--color-accent-hover); }
.contact-detail-hero { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-2); padding: var(--space-2) 0; }
.contact-detail-avatar {
  width: 76px; height: 76px; border-radius: 999px; display: flex; align-items: center; justify-content: center;
  background: var(--color-accent-soft); color: var(--color-accent-hover); font-weight: 700; font-size: 26px; letter-spacing: 0.02em;
}
.contact-detail-avatar.is-anon { background: var(--color-bg-subtle); color: var(--color-text-faint); }
.contact-detail-avatar svg { width: 34px; height: 34px; }
.contact-detail-name { margin: 0; font: 700 22px var(--font-sans); color: var(--color-text-strong); word-break: break-word; }
.contact-detail-subphone { font-size: 14px; color: var(--color-text-muted); }
/* Status bar — wraps the shared .stepper + an honest one-line status. */
.contact-detail-status {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-3); background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
.contact-detail-statusline { font: 700 14px var(--font-sans); text-align: center; }
.contact-detail-statusline.contact-status-confirmed   { color: var(--color-success); }
.contact-detail-statusline.contact-status-next-week   { color: var(--color-warning); }
.contact-detail-statusline.contact-status-in-progress { color: var(--color-text-muted); }
/* Fields — phone (tap-to-call), address, case. */
.contact-detail-fields { display: flex; flex-direction: column; gap: 1px; background: var(--color-border); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
.contact-detail-row { display: flex; align-items: baseline; gap: var(--space-3); padding: var(--space-3); background: var(--color-card); }
.contact-detail-lbl { flex: 0 0 64px; font: 600 12px var(--font-sans); color: var(--color-text-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.contact-detail-val { flex: 1 1 auto; min-width: 0; font-size: 15px; color: var(--color-text); word-break: break-word; }
.contact-detail-call { color: var(--color-accent); font-weight: 600; text-decoration: none; }
.contact-detail-call:hover { text-decoration: underline; }
.contact-detail-actions { display: flex; flex-direction: row; gap: var(--space-2); margin-top: var(--space-1); }
.contact-detail-actions .btn { flex: 1; min-width: 0; justify-content: center; font-size: 0.875rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* "Worth saving" toggle (Bryan items 2 + 7): a secondary, outline button under the primary CTA.
   Unsaved = neutral outline + "Worth saving"; saved = gold star + soft-gold fill + "Saved". */
.contact-save-toggle {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--color-card); border: 1px solid var(--color-border-strong); color: var(--color-text);
}
.contact-save-toggle:hover { border-color: var(--color-warning); }
.contact-save-toggle.is-saved { border-color: var(--color-warning); background: var(--color-warning-soft, rgba(180, 83, 9, 0.10)); color: var(--color-text-strong); }
.contact-save-toggle[disabled] { opacity: 0.6; cursor: default; }
.contact-save-label { font-weight: 600; }
/* ── contact detail messages section (#48) ── */
.contact-detail-msgs { display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-1); border-top: 1px solid var(--color-border); }
.contact-msgs-hd { font: 600 12px var(--font-sans); color: var(--color-text-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.contact-msgs-more { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 11px; }
.contact-msgs-list { display: flex; flex-direction: column; gap: var(--space-2); overflow-y: auto; padding: 2px 0; min-height: 200px; }
.contact-msgs-empty, .contact-msgs-loading { font-size: 14px; color: var(--color-text-muted); margin: 0; }
.contact-msg { display: flex; flex-direction: column; gap: 2px; }
.contact-msg-mine { align-items: flex-end; }
.contact-msg-ho { align-items: flex-start; }
.contact-msg-bubble { max-width: 82%; padding: 8px 12px; border-radius: 14px; font-size: 14px; line-height: 1.45; word-break: break-word; white-space: pre-wrap; }
.contact-msg-mine .contact-msg-bubble { background: var(--color-accent); color: #fff; border-bottom-right-radius: 3px; }
.contact-msg-ho .contact-msg-bubble { background: var(--color-bg-subtle); color: var(--color-text); border-bottom-left-radius: 3px; }
.contact-msg-at { font-size: 11px; color: var(--color-text-faint); padding: 0 2px; }

/* ── contact thread link card (v157: WrapDesk ConversationCard pattern) ── */
.contact-thread-card {
  border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  padding: var(--space-3); margin-top: var(--space-2);
  background: var(--color-card);
  display: flex; flex-direction: column; gap: var(--space-2);
  cursor: pointer;
}
.contact-thread-card-meta { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.contact-thread-badge {
  font: 600 11px var(--font-sans); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  padding: 2px 8px; border-radius: var(--radius-full);
}
.contact-thread-date { font-size: 12px; color: var(--color-text-muted); }
.contact-thread-preview { display: flex; flex-direction: column; gap: 2px; }
.contact-thread-preview-line { margin: 0; font-size: 13px; color: var(--color-text); line-height: 1.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-thread-preview-line.muted { color: var(--color-text-muted); }
.contact-thread-footer { display: flex; justify-content: flex-end; padding-top: var(--space-1); border-top: 1px solid var(--color-border); }
.contact-open-convo-msg {
  background: none; border: none; padding: 0; cursor: pointer;
  font: 600 13px var(--font-sans); color: var(--color-accent);
}
.contact-open-convo-msg:hover { text-decoration: underline; }

/* ── contact detail tab bar (Bryan 2026-06-21: WrapDesk-style Messages|Files|Notes tabs) ── */
.contact-tabs {
  display: flex; gap: 0;
  border-bottom: 1px solid var(--color-border);
  margin: 0 calc(-1 * var(--space-3));
  padding: 0 var(--space-3);
}
.contact-tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  font: 600 14px var(--font-sans); color: var(--color-text-muted);
  padding: 10px var(--space-4); cursor: pointer;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.contact-tab.is-active { color: var(--color-accent); border-bottom-color: var(--color-accent); }
.contact-tab-panels { flex: 1; min-height: 0; }
.contact-tab-panel { display: none; flex-direction: column; gap: var(--space-3); padding-top: var(--space-3); }
.contact-tab-panel.is-active { display: flex; }
/* note labels inside the Notes panel */
.contact-note-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; margin: 0; }
.contact-note-case { border-left: 3px solid var(--color-accent); padding-left: var(--space-2); }
.contact-note-entry { display: flex; flex-direction: column; gap: 2px; }
.contact-inspection-stepper.stepper { flex-wrap: wrap; }

/* #42 route approval cards in agent panel (2026-06-21) */
.agent-routes-group { display: flex; flex-direction: column; gap: var(--space-3); }
.agent-routes-approve-all {
  align-self: flex-start;
  font-size: 14px; font-weight: 700; padding: var(--space-2) var(--space-4);
}
.agent-route-card {
  background: var(--color-card); border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.agent-route-card-head {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
}
.agent-route-day { font-size: 15px; font-weight: 700; color: var(--color-text-strong); }
.agent-route-drivers { font-size: 13px; color: var(--color-text-muted); }
.agent-route-card-actions {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
}
.agent-route-link {
  font-size: 13px; color: var(--color-accent); text-decoration: none;
}
.agent-route-link:hover { text-decoration: underline; }

/* ── Gmail connect section (v160, Settings tab, operator-only) ──────────────── */
.gmail-connect-section {
  padding: 16px 0 20px;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 20px;
}
.gmail-connect-head h3 {
  font-size: 15px; font-weight: 600; margin: 0 0 4px;
  color: var(--color-text-strong);
}
.gmail-connect-head .muted { margin: 0 0 12px; }
.gmail-connect-btn {
  display: inline-block; padding: 7px 14px;
  background: var(--color-accent); color: #fff; border-radius: var(--radius-md);
  font-size: 14px; font-weight: 500; text-decoration: none; border: none; cursor: pointer;
}
.gmail-connect-btn:hover { opacity: 0.9; }
.gmail-connected-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.gmail-connected-email { font-size: 14px; color: var(--color-text); }
.gmail-disconnect-btn { font-size: 13px; color: var(--color-text-muted); }
.gmail-connect-error { color: var(--color-danger, #dc2626); font-size: 13px; }
.gmail-status-saving { font-size: 13px; color: var(--color-text-muted); }
/* Draft review board: the Drafts chip groups pending appointment openers by
   provider and lets Bryan select precisely the rows he intends to send/delete. */
.inbox-draft-board { padding: 10px; display: grid; gap: 12px; }
.inbox-draft-board-head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.inbox-draft-board-head > span { color: var(--muted, #667085); font-size: .86rem; margin-right: auto; }
.inbox-draft-board-group { border: 1px solid #e4e7ec; border-radius: 12px; overflow: hidden; }
.inbox-draft-board-group h3 { margin: 0; padding: 9px 12px; font-size: .9rem; background: #f8fafc; }
.inbox-draft-board-group h3 span { color: #667085; font-weight: 500; }
.inbox-draft-board-card { display: grid; grid-template-columns: 1fr auto; gap: 8px 10px; padding: 10px 12px; border-top: 1px solid #eef2f6; }
.inbox-draft-board-select { display: flex; gap: 10px; cursor: pointer; min-width: 0; }
.inbox-draft-board-card input { margin-top: 3px; }
.inbox-draft-board-card span { display: block; }
.inbox-draft-board-status { display: inline-block !important; margin-left: 7px; color: #667085; font-size: .78rem; text-transform: capitalize; }
.inbox-draft-board-body { margin-top: 3px; color: #475467; font-size: .85rem; white-space: pre-wrap; }
.inbox-draft-board-editor { grid-column: 1 / -1; }
.inbox-draft-board-editor textarea { width: 100%; box-sizing: border-box; resize: vertical; }
.inbox-draft-board-feedback { min-height: 1.2em; margin-top: 4px; color: #667085; font-size: .82rem; }
/* "Draft again" copies go to the insured's phone, not the saved group chat. */
.inbox-draft-target { margin-top: 3px; color: #b54708; font-size: .8rem; font-weight: 600; }
.dm-run .inbox-draft-target { text-align: right; padding: 0 var(--space-1); }
.inbox-draft-again .inbox-opener-menu-row { justify-content: flex-end; }

.case-import-panel {
  margin: 0 0 22px;
  padding: 16px;
  border: 1px solid var(--border, #e4e7ec);
  border-radius: 14px;
  background: var(--surface-soft, #f7faf9);
}

.payroll-section {
  margin: 0 0 var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.payroll-current { padding-top: var(--space-2); }
.payroll-amount { font-size: 30px; line-height: 1.1; font-weight: 800; color: var(--color-text-strong); }
.payroll-paydate { margin-top: 4px; font-weight: 700; color: var(--color-accent); }
.payroll-period, .payroll-empty { margin-top: 4px; color: var(--color-text-muted); font-size: 14px; }
.payroll-warning {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-warning-soft, #fff4ce);
  color: var(--color-text);
  font-size: 13px;
}
.payroll-search { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
.payroll-search label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 700; }
.payroll-search-row { display: flex; gap: var(--space-2); }
.payroll-search-row input {
  min-width: 0;
  flex: 1;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font: inherit;
}
.payroll-policy-result { margin-top: var(--space-2); font-size: 13px; color: var(--color-text-muted); }
.case-import-panel h4 { margin: 0 0 4px; }
.case-import-preview { display: grid; gap: 10px; margin: 12px 0; }
.case-import-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: start;
}
.case-import-row input[type="text"] { width: 100%; box-sizing: border-box; }
.case-import-row .form-error { display: block; margin-top: 4px; }

/* George work-state controls and Bryan's append-only audit view. */
.gw-card,
.gw-owner-card {
  display: grid;
  gap: 16px;
}
.gw-status-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.gw-status-head > div {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.gw-state {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2, #eef2f7);
  font-weight: 700;
}
.gw-state-working { background: #dcfce7; color: #166534; }
.gw-state-break,
.gw-state-lunch { background: #fef3c7; color: #92400e; }
.gw-state-off_clock { background: #e5e7eb; color: #374151; }
.gw-timer {
  font-size: clamp(1.35rem, 4vw, 2rem);
  font-variant-numeric: tabular-nums;
}
.gw-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.gw-actions .btn {
  min-height: 44px;
}
.gw-card.is-overdue {
  border-color: #dc2626;
  box-shadow: 0 0 0 2px rgb(220 38 38 / 12%);
}
.gw-callout,
.sharp-reminder {
  padding: 12px;
  border-left: 4px solid var(--accent, #2563eb);
  border-radius: 8px;
  background: var(--surface-2, #f8fafc);
}
.gw-owner-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin: 0;
}
.gw-owner-grid > div,
.gw-timeline-row,
.gw-sharp-audit-row {
  padding: 10px;
  border: 1px solid var(--border, #dbe2ea);
  border-radius: 8px;
}
.gw-owner-grid dt,
.gw-timeline-row time {
  color: var(--muted, #64748b);
  font-size: .8rem;
}
.gw-owner-grid dd { margin: 4px 0 0; }
.gw-owner-card-compact {
  gap: 10px;
}
.gw-owner-summary {
  margin: 3px 0 0;
  color: var(--muted, #64748b);
  font-size: .9rem;
}
.gw-owner-details {
  border-top: 1px solid var(--border, #dbe2ea);
}
.gw-owner-details > summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  cursor: pointer;
  font-weight: 700;
  list-style: none;
}
.gw-owner-details > summary::-webkit-details-marker { display: none; }
.gw-owner-details > summary::after {
  content: '⌄';
  margin-left: auto;
  transition: transform .15s ease;
}
.gw-owner-details[open] > summary::after { transform: rotate(180deg); }
.gw-owner-details:not([open]) > .gw-owner-details-body { display: none; }
.gw-owner-details-body {
  display: grid;
  gap: 16px;
  padding-top: 6px;
}
.gw-schedule {
  border: 1px solid var(--border, #dbe2ea);
  border-radius: 10px;
  background: var(--surface-2, #f8fafc);
}
.gw-schedule > summary {
  min-height: 44px; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 0 12px; cursor: pointer; list-style: none; font-weight: 750;
}
.gw-schedule > summary::-webkit-details-marker { display: none; }
.gw-schedule > summary span:last-child { color: var(--muted, #64748b); font-size: .8rem; font-weight: 600; }
.gw-schedule-body { display: grid; gap: 8px; padding: 0 12px 12px; }
.gw-schedule-row { display: grid; grid-template-columns: minmax(82px, .7fr) minmax(0, 1.3fr); gap: 10px; }
.gw-schedule-row span { color: var(--muted, #64748b); }
.gw-schedule-row.is-occasional { padding-top: 8px; border-top: 1px solid var(--border, #dbe2ea); }
.gw-schedule-body p { margin: 0; color: var(--muted, #64748b); font-size: .82rem; line-height: 1.4; }
.gw-timeline,
.gw-sharp-audit {
  display: grid;
  gap: 8px;
}
.gw-timeline-row.is-excluded,
.gw-sharp-audit-row.is-voided {
  opacity: .7;
  text-decoration: line-through;
}
.gw-sharp-audit-row > div:first-child {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}
.gw-sharp-history {
  margin: 8px 0;
  color: var(--muted, #64748b);
}
.gw-error,
.sharp-batch-error {
  color: #b91c1c;
  font-weight: 650;
}
.sharp-batch-address-label {
  display: grid;
  gap: 6px;
}
.sharp-batch-input {
  width: 100%;
}

@media (max-width: 640px) {
  .gw-owner-grid { grid-template-columns: 1fr; }
  .gw-actions .btn { flex: 1 1 100%; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .gw-card *,
  .gw-owner-card * {
    scroll-behavior: auto !important;
    transition: none !important;
    animation: none !important;
  }
}

.paperwork-trigger {
  display: grid;
  gap: .45rem;
  margin-top: .65rem;
  padding: .75rem;
  border: 1px solid #d8e2df;
  border-radius: .8rem;
  background: #f7faf9;
}
.paperwork-trigger p { margin: 0; color: #52615d; }
.paperwork-trigger.is-waiting { border-color: #e7c66a; background: #fff9e8; }
.paperwork-trigger.is-ready { border-color: #75c8bb; background: #eefaf7; }
.paperwork-trigger.is-running { border-color: #9abbd8; background: #f0f7fc; }
.paperwork-trigger.is-done { border-color: #73bd8a; background: #eef9f1; }
.paperwork-trigger.is-blocked { border-color: #d99191; background: #fff1f1; }
.paperwork-trigger [data-role="run-delivered-status"] { color: #52615d; font-size: .85rem; }
.paperwork-trigger .paperwork-rerun { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .2rem; }

/* Bryan 2026-08-17 - Log call chip shares the AI-draft toolbar row */
.inbox-ho-toolbar .ho-action-bar { padding: 0; border-top: none; background: transparent; }
.inbox-ho-toolbar .ho-action-bar .ho-note-form { flex-basis: 100%; }

/* Bryan 2026-10-07 - Convert to exterior only email box on Cases high-value cards (restored from the 2026-08-31 release) */
.field-exterior-convert .field-exterior-button { width: 100%; min-height: 40px; color: var(--color-warning); border: 1px solid var(--color-warning); background: var(--color-card); border-radius: var(--radius-md); font-weight: 600; }
.field-exterior-convert .field-exterior-button:disabled { opacity: .7; }
.field-conv-panel { margin-top: 8px; display: grid; gap: 8px; }
.field-conv-status { margin: 0; font-size: 13px; color: var(--color-text-muted); min-height: 1em; }
.field-conv-status:empty { display: none; }
.field-conv-field { display: block; font-size: 13px; color: var(--color-text-muted); }
.field-conv-field input, .field-conv-field textarea { display: block; width: 100%; box-sizing: border-box; margin-top: 4px; padding: 8px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-card); color: var(--color-text); font: inherit; }
.field-conv-subjline { margin: 0; font-size: 13px; color: var(--color-text-muted); overflow-wrap: anywhere; }
.field-conv-subject { font-weight: 600; color: var(--color-text); }
.field-conv-recipients { margin: 0; font-size: 12px; color: var(--color-text-muted); overflow-wrap: anywhere; }
.field-conv-actions { display: flex; gap: 8px; }
.field-conv-send { flex: 1; min-height: 40px; font-weight: 600; color: var(--color-text-on-accent); background: var(--color-accent); border: 1px solid var(--color-accent); border-radius: var(--radius-md); }
.field-conv-send:disabled { opacity: .7; }
.field-conv-cancel { min-height: 40px; padding: 0 12px; color: var(--color-text-muted); background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--radius-md); }