/**
 * RBP Group — mono theme.
 * Loaded AFTER app.css (AdminKit) to override the look for auth/onboarding.
 * Monochrome surfaces + a single blue accent for primary actions.
 */
:root {
  --rbpg-bg: #f4f4f5;
  --rbpg-surface: #ffffff;
  --rbpg-border: #e4e4e7;
  --rbpg-border-strong: #d4d4d8;
  --rbpg-text: #18181b;
  --rbpg-muted: #71717a;
  --rbpg-grey: #f4f4f5;
  --rbpg-grey-hover: #e4e4e7;
  --rbpg-primary: #2563eb;
  --rbpg-primary-hover: #1d4ed8;

  /* ── Status tones ──────────────────────────────────────────────────────────
   * One ramp per meaning, for every blip on every page. There were four
   * near-duplicate palettes before this — three greens, three reds, and a
   * "mono-pastel" set whose amber (#85693a) was olive and failed AA at 4.49:1 —
   * so the same estimate could read a different colour in the cockpit, the list
   * and the builder. Verify any change with
   * `node scripts/dev/check-tone-contrast.mjs`.
   *
   * Three steps, because a blip has three jobs and one colour cannot do all of
   * them on a mono canvas:
   *
   *   tint  the field. Held at the same luminance across all five tones (spread
   *         0.057), so no pill shouts louder than another for want of meaning.
   *   ink   the text. Dark enough to clear 4.5:1 on its own tint — these are
   *         11-12px caps, which WCAG counts as normal text, not large.
   *   dot   the signal. Full chroma, and the reason the ink is allowed to be
   *         dark: dark amber IS brown, physically, so hue has to live somewhere
   *         that does not have to be read. The dot is that place.
   *
   * `lit` is the same tone for dark chrome, where a pale tint washes out against
   * near-black. It matches the -300 inks the cockpit header already uses for save
   * state, so the header keeps one language.
   */
  --tone-info-tint: #eff6ff;
  --tone-info-ink: #1d4ed8;
  --tone-info-dot: #2563eb;
  --tone-info-lit: #93c5fd;

  --tone-ok-tint: #f0fdf4;
  --tone-ok-ink: #15803d;
  --tone-ok-dot: #16a34a;
  --tone-ok-lit: #86efac;

  --tone-warn-tint: #fffbeb;
  --tone-warn-ink: #b45309;
  --tone-warn-dot: #f59e0b;
  --tone-warn-lit: #fcd34d;

  --tone-bad-tint: #fef2f2;
  --tone-bad-ink: #b91c1c;
  --tone-bad-dot: #ef4444;
  --tone-bad-lit: #fca5a5;

  --tone-neutral-tint: #f4f4f5;
  --tone-neutral-ink: #52525b;
  --tone-neutral-dot: #a1a1aa;
  --tone-neutral-lit: #d4d4d8;
  --rbpg-radius: 12px;
  --rbpg-radius-lg: 16px;
  --rbpg-appbar-h: 64px;
  --rbpg-footer-h: 76px;
  --rbpg-max: 1080px;
}

/* Reserve the scrollbar gutter so opening a modal (which hides overflow) never
   changes layout width — no sideways shift of content, fixed footer or dialogs. */
html { scrollbar-gutter: stable; }

body {
  background: var(--rbpg-bg);
  color: var(--rbpg-text);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* ---- Typography ------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 { color: var(--rbpg-text); font-weight: 700; }
.text-muted { color: var(--rbpg-muted) !important; }
a { color: var(--rbpg-primary); }

/* ---- Buttons: mono by default, blue for primary ---------------------- */
.btn {
  border-radius: var(--rbpg-radius);
  font-weight: 600;
  padding: 0.7rem 1.4rem;
  font-size: 1rem;
  line-height: 1.2;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}
.btn-lg { padding: 0.85rem 1.6rem; font-size: 1.05rem; }
/* Bootstrap's `-sm` sizes are declared before this stylesheet loads, so the base
   rule above was silently winning and `.btn-sm` rendered at full size on every
   page in the app. Restated here, after the base, so asking for a small control
   gets one — which is what a dozen call sites already thought they were doing. */
.btn-sm { padding: 0.42rem 0.85rem; font-size: 0.875rem; }

/* Primary = the one attention-grabbing action */
.btn-primary,
.btn-success {
  background: var(--rbpg-primary);
  border: 1px solid var(--rbpg-primary);
  color: #fff;
}
.btn-primary:hover, .btn-primary:focus,
.btn-success:hover, .btn-success:focus {
  background: var(--rbpg-primary-hover);
  border-color: var(--rbpg-primary-hover);
  color: #fff;
  box-shadow: none;
}

/* Default / secondary = light grey fill, no border, bold black text */
.btn-secondary,
.btn-light,
.btn-default {
  background: var(--rbpg-grey);
  border: 1px solid transparent;
  color: var(--rbpg-text);
  font-weight: 700;
}
.btn-secondary:hover, .btn-light:hover, .btn-default:hover {
  background: var(--rbpg-grey-hover);
  color: var(--rbpg-text);
}

/* Outline = white fill, soft grey border, softens on hover (no hard border) */
.btn-outline-secondary,
.btn-outline-primary {
  background: var(--rbpg-surface);
  border: 1px solid var(--rbpg-border-strong);
  color: var(--rbpg-text);
  font-weight: 600;
}
.btn-outline-secondary:hover, .btn-outline-secondary:focus,
.btn-outline-primary:hover, .btn-outline-primary:focus {
  background: var(--rbpg-grey);
  border-color: var(--rbpg-grey);
  color: var(--rbpg-text);
  box-shadow: none;
}

.btn:focus, .btn:focus-visible { box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.15); }
.btn:disabled { opacity: 0.55; }

/* ---- Inputs: bigger, rounder ---------------------------------------- */
.form-control,
.form-select {
  border-radius: var(--rbpg-radius);
  border: 1px solid var(--rbpg-border-strong);
  padding: 0.7rem 0.9rem;
}
/* Reserve room on the right for the dropdown chevron so it never overlaps the
   text (esp. on width:auto selects like the per-page picker). */
.form-select {
  padding-right: 2.25rem;
  background-position: right 0.75rem center;
  font-size: 1rem;
  color: var(--rbpg-text);
  background-color: var(--rbpg-surface);
}
.form-control-lg, .form-select-lg { padding: 0.85rem 1rem; font-size: 1.05rem; }
/* Same story as `.btn-sm` above. */
.form-control-sm, .form-select-sm { padding: 0.42rem 0.7rem; font-size: 0.875rem; }
.form-select-sm { padding-right: 2rem; background-position: right 0.55rem center; }
.form-control:focus,
.form-select:focus {
  border-color: var(--rbpg-primary);
  box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.12);
}
.form-control::placeholder { color: #a1a1aa; }
.form-label { font-weight: 600; color: var(--rbpg-text); margin-bottom: 0.35rem; }
.input-group > .form-control { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.input-group > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; }

.form-check-input:checked { background-color: var(--rbpg-primary); border-color: var(--rbpg-primary); }
.form-check-input:focus { box-shadow: 0 0 0 0.2rem rgba(37, 99, 235, 0.15); }

/* ---- Cards ----------------------------------------------------------- */
.card {
  background: var(--rbpg-surface);
  border: 1px solid var(--rbpg-border);
  border-radius: var(--rbpg-radius-lg);
  box-shadow: 0 1px 2px rgba(24, 24, 27, 0.04);
}

/* ---- Alerts (muted, mono) ------------------------------------------- */
.alert { border-radius: var(--rbpg-radius); border: 1px solid var(--rbpg-border); }
.alert-info { background: #f4f4f5; color: var(--rbpg-text); }

/* ---- Badges --------------------------------------------------------- */
.badge.bg-primary-subtle { background: #eff3fe !important; color: var(--rbpg-primary) !important; font-weight: 600; }

/* =====================================================================
   Fixed app shell (header + footer) used by onboarding
   ===================================================================== */
.rbpg-appbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--rbpg-appbar-h);
  background: var(--rbpg-surface);
  border-bottom: 1px solid var(--rbpg-border);
  display: flex;
  align-items: center;
  padding: 0 1.25rem;
  z-index: 1030;
}
.rbpg-appbar .rbpg-exit {
  background: transparent;
  border: 1px solid var(--rbpg-border-strong);
  color: var(--rbpg-text);
  font-weight: 600;
  padding: 0.45rem 1.05rem;
  border-radius: var(--rbpg-radius);
}
.rbpg-appbar .rbpg-exit:hover { background: var(--rbpg-grey); }
.rbpg-brand {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-weight: 800;
  letter-spacing: 0.06em;
  font-size: 0.95rem;
  color: var(--rbpg-text);
  text-transform: uppercase;
  white-space: nowrap;
}

.rbpg-footer {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  min-height: var(--rbpg-footer-h);
  background: var(--rbpg-surface);
  border-top: 1px solid var(--rbpg-border);
  display: flex;
  align-items: center;
  padding: 0.9rem 1.25rem;
  z-index: 1030;
}
.rbpg-footer-inner {
  width: 100%;
  max-width: var(--rbpg-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.rbpg-shell {
  min-height: 100vh;
  padding-top: var(--rbpg-appbar-h);
  padding-bottom: calc(var(--rbpg-footer-h) + 1.5rem);
}
.rbpg-body {
  max-width: var(--rbpg-max);
  margin: 0 auto;
  padding: 2.25rem 1.25rem 1rem;
}

@media (max-width: 575.98px) {
  .rbpg-brand { font-size: 0.78rem; }
  .rbpg-body { padding-top: 1.25rem; }
}

/* =====================================================================
   Supplier app shell (sidebar + topbar + content) — monochrome
   Used by supplier.html and the supplier-* pages via supplier-shell.js
   ===================================================================== */
:root { --rbpg-sidebar-w: 248px; }

.app { min-height: 100vh; }

.app-sidebar {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--rbpg-sidebar-w);
  background: var(--rbpg-surface);
  border-right: 1px solid var(--rbpg-border);
  display: flex;
  flex-direction: column;
  z-index: 1040;
}
.app-sidebar .app-brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.25rem 1.4rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.9rem;
  color: var(--rbpg-text);
  text-decoration: none;
  border-bottom: 1px solid var(--rbpg-border);
}
.app-brand .app-brand-mark {
  width: 30px; height: 30px; border-radius: 9px;
  background: var(--rbpg-text); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.85rem; font-weight: 800; letter-spacing: 0;
}
/* White, because a Brand logo is somebody else's artwork and almost all of it is
   dark ink drawn for white paper. The band used to be near-black — right for the
   one white REPIID mark it was built for, wrong for every Brand since.
   Height matches the main-content header (--rbpg-appbar-h) so the divider lines align */
.app-sidebar .app-brand {
  height: var(--rbpg-appbar-h);
  padding: 0 1rem;
  justify-content: center;
  background: #fff;
  border-bottom: 1px solid var(--rbpg-border);
  box-sizing: border-box;
}
/* Scaled by whichever dimension runs out first, so a tall square mark and a wide
   wordmark both land inside the band without being stretched or cropped. */
.app-brand .app-brand-logo {
  display: none; height: auto; width: auto;
  max-height: 60px; max-width: 100%;
  object-fit: contain; flex: 0 1 auto; min-width: 0;
}
.app-brand .app-brand-logo.is-loaded { display: block; }
/* Shared logo/name chrome: the name is also the stable fallback while an image
   loads or fails, and neither can widen the fixed sidebar. */
.app-sidebar .app-brand--text,
.app-sidebar .app-brand--chrome { color: var(--rbpg-text); overflow: hidden; }
.app-brand-name {
  min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
/* A logo that has loaded already says the name — printing it again beside the
   artwork is the same word twice. The span stays in the markup because it is
   what shows when the image is missing, slow or broken. */
.app-brand .app-brand-logo.is-loaded + .app-brand-name { display: none; }
.app-nav { padding: 0.9rem 0.75rem; overflow-y: auto; flex: 1; }
.app-nav .nav-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--rbpg-muted); font-weight: 700; padding: 0.4rem 0.75rem 0.5rem;
}
/* A group with no heading, which in the admin tree is Settings sitting on its own
   at the bottom. A rule over it would read as a divider between two halves of a
   list; the gap says "and separately, this" without claiming there is a category. */
.app-nav .nav-gap { height: 1.1rem; }
.app-nav a {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.7rem 0.8rem; margin-bottom: 0.15rem;
  border-radius: 10px;
  color: var(--rbpg-text); text-decoration: none;
  font-weight: 600; font-size: 0.95rem;
}
.app-nav a svg, .app-nav a i { width: 18px; height: 18px; color: var(--rbpg-muted); }
.app-nav a:hover { background: var(--rbpg-grey); }
.app-nav a.active { background: var(--rbpg-text); color: #fff; }
.app-nav a.active svg, .app-nav a.active i { color: #fff; }
.app-nav .nav-badge {
  margin-left: auto;
  min-width: 20px; height: 18px; padding: 0 6px;
  border-radius: 999px;
  background: #dc2626; color: #fff;
  font-size: 0.7rem; font-weight: 800; line-height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
}
.app-nav a.active .nav-badge { background: #fff; color: var(--rbpg-text); }

/* ---- Message center -------------------------------------------------- */
/* Folder rail reuses .rail-* (My repairs). Icons need sizing here. */
.rail-stage svg, .rail-stage i { width: 16px; height: 16px; color: var(--rbpg-muted); flex: 0 0 auto; }
.rail-stage.active svg, .rail-stage.active i { color: #fff; }

.msg-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }

/* Conversation list — Gmail single-row density: avatar · name · subject+snippet
   (one truncated line) · date right. Bold only on unread name+subject; no tint. */
.msg-list { display: flex; flex-direction: column; }
.msg-conv {
  display: flex; gap: 0.85rem; align-items: center; width: 100%; text-align: left; cursor: pointer;
  border: 0; background: none; border-bottom: 1px solid var(--rbpg-border);
  padding: 0.55rem 1.25rem;
}
.msg-conv:hover { background: #fafafa; }
.msg-conv:last-child { border-bottom: 0; }
.msg-avatar {
  width: 32px; height: 32px; border-radius: 50%; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: #f1f1f4; color: #52525b;
}
.msg-avatar svg { width: 16px; height: 16px; }
/* Channel tint — calm pastel, carries the transport signal (no separate blip). */
.msg-avatar--email { background: #eef2ff; color: #4f46e5; }
.msg-avatar--sms { background: #ecfdf5; color: #059669; }
.msg-avatar--note { background: #fffbeb; color: #d97706; }
.msg-avatar.sk { background: #ececec; }
/* Name shrinks to its content (no reserved dead space after short names). */
.msg-conv-who {
  flex: 0 1 auto; min-width: 0; max-width: 32%;
  color: var(--rbpg-text); font-weight: 500; font-size: 0.88rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Subject + snippet share one truncating line (subject first, snippet greys out). */
.msg-conv-main { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.msg-conv-subject { color: var(--rbpg-text); font-size: 0.87rem; font-weight: 400; }
.msg-conv-preview { color: var(--rbpg-muted); font-size: 0.87rem; }
.msg-conv-preview::before { content: " — "; }
.msg-conv-meta { flex: 0 0 auto; padding-left: 0.5rem; display: flex; align-items: center; gap: 0.5rem; }
.msg-conv-time { flex: 0 0 auto; color: var(--rbpg-muted); font-size: 0.75rem; white-space: nowrap; }
.msg-chip { flex: 0 0 auto; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; padding: 0.05rem 0.36rem; border-radius: 4px; }
.msg-chip--overdue { color: #b91c1c; background: #fef2f2; border: 1px solid #fecaca; }
.msg-chip--ai { color: #fff; background: #0d0d0d; border: 1px solid #0d0d0d; }

/* Row 3-dot actions — subtle, appears on row hover/focus (Gmail-style). */
.msg-conv-menu { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 0; border-radius: 6px; background: transparent; color: var(--rbpg-muted); cursor: pointer; opacity: 0.5; transition: opacity 0.12s, background 0.12s; }
.msg-conv:hover .msg-conv-menu, .msg-conv:focus-within .msg-conv-menu { opacity: 1; }
.msg-conv-menu:hover { background: #ececef; color: var(--rbpg-text); }
.msg-conv-menu:focus-visible { opacity: 1; outline: 2px solid var(--rbpg-text); outline-offset: 1px; }

/* Row action dropdown (appended to body, fixed-positioned). */
.msg-menu { z-index: 1200; min-width: 200px; background: #fff; border: 1px solid var(--rbpg-border); border-radius: 10px; box-shadow: 0 16px 40px rgba(0,0,0,0.16); padding: 0.3rem; }
.msg-menu-item { display: block; width: 100%; text-align: left; background: none; border: 0; border-radius: 7px; padding: 0.5rem 0.6rem; font-size: 0.86rem; color: var(--rbpg-text); cursor: pointer; }
.msg-menu-item:hover, .msg-menu-item:focus-visible { background: #f4f4f5; outline: none; }

/* Route supplier picker (modal). */
.msg-picker-back { position: fixed; inset: 0; z-index: 1300; background: rgba(17,17,17,0.28); display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh; }
.msg-picker { width: 100%; max-width: 460px; background: #fff; border-radius: 14px; box-shadow: 0 24px 60px rgba(0,0,0,0.3); overflow: hidden; display: flex; flex-direction: column; }
.msg-picker-title { padding: 0.7rem 1rem 0; font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--rbpg-muted); }
.msg-picker-input { border: 0; border-bottom: 1px solid var(--rbpg-border); padding: 0.8rem 1rem; font-size: 0.95rem; outline: none; }
.msg-picker-list { max-height: 320px; overflow-y: auto; padding: 0.4rem; }
.msg-picker-item { display: block; width: 100%; text-align: left; background: none; border: 0; border-radius: 8px; padding: 0.6rem 0.7rem; font-size: 0.9rem; cursor: pointer; }
.msg-picker-item:hover, .msg-picker-item:focus-visible { background: var(--rbpg-text); color: #fff; outline: none; }
.msg-picker-empty { color: var(--rbpg-muted); font-size: 0.88rem; padding: 1rem; text-align: center; }
.msg-pill {
  flex: 0 0 auto; min-width: 1.2rem; text-align: center;
  background: var(--rbpg-text); color: #fff; font-size: 0.7rem; font-weight: 700;
  padding: 0.03rem 0.38rem; border-radius: 999px;
}
.msg-conv.unread .msg-conv-who { font-weight: 700; }
.msg-conv.unread .msg-conv-subject { font-weight: 700; color: var(--rbpg-text); }
.msg-conv.unread .msg-conv-preview { color: var(--rbpg-text); }

.msg-empty { padding: 3.5rem 1.25rem; text-align: center; color: var(--rbpg-muted); }
.msg-conv--skel { pointer-events: none; }
.msg-conv--skel .sk-line { height: 11px; border-radius: 6px; background: #ececec; }
.msg-conv--skel .msg-conv-who { width: 130px; }

.app-main {
  margin-left: var(--rbpg-sidebar-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.app-topbar {
  height: var(--rbpg-appbar-h);
  background: var(--rbpg-surface);
  border-bottom: 1px solid var(--rbpg-border);
  display: flex; align-items: center;
  padding: 0 1.75rem; gap: 1rem;
  position: sticky; top: 0; z-index: 1030;
}
.app-topbar .topbar-title { font-size: 1.15rem; font-weight: 700; margin: 0; }
.app-topbar .topbar-right { margin-left: auto; display: flex; align-items: center; gap: 0.6rem; }
.app-hamburger { display: none; background: none; border: 0; padding: 0.4rem; }

.app-content { padding: 1.9rem 1.75rem 3rem; max-width: 1280px; width: 100%; }
.page-head { margin-bottom: 1.5rem; }
.page-head h1 { font-size: 1.5rem; margin: 0 0 0.25rem; }
.page-head p { color: var(--rbpg-muted); margin: 0; }

/* Cards get more breathing room in the app */
.app-content .card { margin-bottom: 1.5rem; }
.app-content .card-header {
  background: transparent; border-bottom: 1px solid var(--rbpg-border);
  padding: 1.15rem 1.4rem;
}
.app-content .card-header .card-title { font-size: 1.02rem; font-weight: 700; }
.app-content .card-body { padding: 1.4rem; }

/* Stat cards */
.stat-card .stat-label { color: var(--rbpg-muted); font-weight: 600; font-size: 0.85rem; margin-bottom: 0.35rem; }
.stat-card .stat-value { font-size: 2rem; font-weight: 800; line-height: 1; }
.stat-card .stat-sub { color: var(--rbpg-muted); font-size: 0.8rem; margin-top: 0.4rem; }

/* Equal-height cards on the same row (no need for per-card h-100) */
.app-content .row > [class*="col"] { display: flex; flex-direction: column; }
.app-content .row > [class*="col"] > .card { flex: 1 1 auto; }

/* Toolbar row above tables/lists */
.rbpg-toolbar { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.rbpg-toolbar .spacer { margin-left: auto; }

/* ---- Padded, readable tables ---------------------------------------- */
.table.rbpg-table { margin: 0; }
.table.rbpg-table thead th {
  background: var(--rbpg-grey);
  color: var(--rbpg-muted);
  font-weight: 700; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.04em;
  border-bottom: 1px solid var(--rbpg-border);
  padding: 0.85rem 1.1rem; white-space: nowrap;
}
.table.rbpg-table tbody td {
  padding: 0.95rem 1.1rem;
  border-bottom: 1px solid var(--rbpg-border);
  vertical-align: middle; font-size: 0.92rem;
}
.table.rbpg-table tbody tr:last-child td { border-bottom: 0; }
.table.rbpg-table tbody tr:hover td { background: #fafafa; }

/* Fixed table layout so column widths stay locked when sorting re-renders rows
   (content no longer dictates width → no horizontal shift). Every column gets an
   explicit width except the Item column, which flexes to fill the remainder. */
.table.rbpg-table--fixed { table-layout: fixed; width: 100%; min-width: 54rem; }
.table.rbpg-table--fixed td, .table.rbpg-table--fixed th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.table.rbpg-table--fixed td.rbpg-thumb-cell { overflow: visible; }

/* Thumbnail cell: tight vertical padding so the image fills the row height
   (roughly the normal text-cell height) — a bigger avatar without taller rows.
   Specificity must beat `.table.rbpg-table tbody td`, hence the doubled classes. */
.table.rbpg-table td.rbpg-thumb-cell { padding-top: 0.4rem; padding-bottom: 0.4rem; padding-left: 2rem; padding-right: 1rem; width: 6rem; }
.rbpg-table .rbpg-thumb { width: 48px; height: 48px; object-fit: cover; display: block; border-radius: 8px; border: 1px solid var(--rbpg-border); background: var(--rbpg-grey); }

/* Soft neutral grey→white ramp for the content page (matches the process-page
   message thread), instead of the flat grey app background. */
.rbpg-ramp-bg {
  background: linear-gradient(180deg, #f4f4f5 0%, #f8f8f9 40%, #fcfcfc 70%, #ffffff 100%);
  background-attachment: fixed;
}

/* Clickable case title in list tables (opens the case, like the dashboard).
   Also matches a <button>: something that opens an overlay instead of
   navigating is still a title you click, and should read the same. */
.rbpg-table .rbpg-case-link { color: var(--rbpg-text); text-decoration: none; cursor: pointer; vertical-align: middle; }
.rbpg-table .rbpg-case-link:hover { color: var(--rbpg-primary); text-decoration: underline; }
.rbpg-table button.rbpg-case-link {
  background: none; border: 0; padding: 0; max-width: 100%;
  display: inline-block; text-align: left;
}
/* An identifier that is also the way in — the Estimate column's reference. Blue
   at rest, unlike `.rbpg-case-link` beside it, which is a title first and a link
   second. The underline waits for hover so fifty rows are not fifty underlines. */
.rbpg-table a.rbpg-ref-link { color: var(--rbpg-primary); text-decoration: none; }
.rbpg-table a.rbpg-ref-link:hover { text-decoration: underline; }

/* Sortable headers */
th[data-sort] { cursor: pointer; user-select: none; }
th[data-sort]::after { content: '⇅'; opacity: 0.35; margin-left: 0.4rem; font-size: 0.8em; }
th.sort-active::after { opacity: 1; }
th.sort-asc::after { content: '↑'; }
th.sort-desc::after { content: '↓'; }

/* Skeleton loading rows */
.skeleton-row td > span {
  display: block; height: 0.9rem; border-radius: 5px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e4e4e7 50%, #f0f0f0 75%);
  background-size: 200% 100%; animation: rbpg-shimmer 1.4s infinite;
}
@keyframes rbpg-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* Pagination */
.rbpg-pager { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.4rem; gap: 0.75rem; flex-wrap: wrap; }
.rbpg-pager .page-info { color: var(--rbpg-muted); font-size: 0.88rem; }

/* ---- Verification center -------------------------------------------- */
.verify-progress { display: flex; align-items: center; gap: 1rem; }
.verify-progress .progress { flex: 1; height: 10px; border-radius: 999px; background: var(--rbpg-grey); }
.verify-progress .progress-bar { background: var(--rbpg-text); border-radius: 999px; }

.doc-card { display: flex; align-items: flex-start; gap: 1rem; padding: 1.25rem; border: 1px solid var(--rbpg-border); border-radius: var(--rbpg-radius); background: var(--rbpg-surface); height: 100%; }
.doc-card .doc-icon { width: 42px; height: 42px; border-radius: 11px; background: var(--rbpg-grey); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.doc-card .doc-icon svg, .doc-card .doc-icon i { width: 20px; height: 20px; color: var(--rbpg-text); }
.doc-card .doc-body { flex: 1; min-width: 0; }
.doc-card .doc-title { font-weight: 700; margin: 0 0 0.15rem; }
.doc-card .doc-desc { color: var(--rbpg-muted); font-size: 0.85rem; margin: 0 0 0.75rem; }

/* Status pills (mono) */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.28rem 0.6rem;
  border-radius: 999px;
  /* An inline-flex box hands its baseline to its FIRST flex item, which here is
     a textless dot — so a pill set beside a label used to sit off the line it
     belonged on. Centring it is both correct and what the eye expects. */
  vertical-align: middle;
  --blip: currentColor;
}
/* `flex: none` because a dot is not a negotiable part of a pill: in a narrow
   table cell a long label was squeezing the circle into an ellipse. */
.pill::before {
  content: '';
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--blip);
}
.pill-verified { background: var(--tone-ok-tint);      color: var(--tone-ok-ink);      --blip: var(--tone-ok-dot); }
.pill-pending  { background: var(--tone-warn-tint);    color: var(--tone-warn-ink);    --blip: var(--tone-warn-dot); }
.pill-rejected { background: var(--tone-bad-tint);     color: var(--tone-bad-ink);     --blip: var(--tone-bad-dot); }
.pill-missing  { background: var(--tone-neutral-tint); color: var(--tone-neutral-ink); --blip: var(--tone-neutral-dot); }
/* In flight — an estimate that is with the customer. Estimates already speak
   blue for this in .pq-est-status and .eb-pill, so without a blue in the shared
   set the same estimate would read amber on the Estimates page and blue in the
   cockpit and the builder. Colour only for meaning; the "one blue per screen"
   rule governs competing CTAs, not status. */
.pill-info     { background: var(--tone-info-tint);    color: var(--tone-info-ink);    --blip: var(--tone-info-dot); }

/* Status blips read as caps across every page (P3 UI). */
.pill,
[class*="rbpg-pill--"],
.rbpg-table .badge { text-transform: uppercase; letter-spacing: 0.03em; }

/* ---- Tabs (mono) ---------------------------------------------------- */
.rbpg-tabs.nav-tabs { border-bottom: 1px solid var(--rbpg-border); gap: 0.25rem; }
.rbpg-tabs .nav-link {
  border: 0; border-bottom: 2px solid transparent;
  color: var(--rbpg-muted); font-weight: 600;
  padding: 0.65rem 1rem; border-radius: 0; background: none;
}
.rbpg-tabs .nav-link:hover { color: var(--rbpg-text); border-bottom-color: var(--rbpg-border-strong); }
.rbpg-tabs .nav-link.active { color: var(--rbpg-text); background: none; border-bottom-color: var(--rbpg-text); }

/* Hide lower-priority table columns on smaller screens */
@media (max-width: 991.98px) { .hide-sm { display: none !important; } }
@media (max-width: 1199.98px) { .hide-md { display: none !important; } }

/* Row-menu dropdown trigger */
.rbpg-table .dropdown-toggle::after { display: none; }
.rbpg-table td .dropdown-item { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; font-weight: 600; }
.rbpg-table td .dropdown-item svg { width: 16px; height: 16px; }

/* List cards: full-height card so short lists don't collapse, the table scrolls
   INSIDE the card (overflow-x stays on .table-responsive), and the pager is
   pinned to the bottom. Row menus escape the scroll box via Popper "fixed". */
/* overflow:hidden clips the square grey <thead> background to the card's rounded
   corners so the border curve shows (row menus escape via position:fixed). */
.rbpg-list-card { min-height: 460px; display: flex; flex-direction: column; overflow: hidden; }
.rbpg-list-card > .card-header { flex: 0 0 auto; }
.rbpg-list-card > .table-responsive { flex: 1 1 auto; }
.rbpg-list-card > .rbpg-pager { flex: 0 0 auto; border-top: 1px solid var(--rbpg-border); }

/* Shared workspace footer (P4-ACTIONS-1) — FIXED to the bottom of the content
   area (right of the sidebar). Pinned independent of table height: a short table
   won't pull it up, a tall table won't push it down; content scrolls underneath.
   One row: page-info left, Actions dead-center (absolute), Prev/Next right. */
.rbpg-ws-footer {
  position: fixed; left: var(--rbpg-sidebar-w); right: 0; bottom: 0; z-index: 900;
  display: flex; align-items: center;
  min-height: var(--rbpg-footer-h);
  padding: 0.6rem 0;
  background: var(--rbpg-surface);
  border-top: 1px solid var(--rbpg-border);
  box-shadow: 0 -6px 18px rgba(0,0,0,0.05);
}
/* Inner wrapper matches .app-content (max-width 1280, left-aligned, 1.75rem pad)
   so the pager + centered Actions align with the content instead of centering on
   the full-width footer (which sits right of the capped content on wide screens). */
.rbpg-ws-footer-inner {
  position: relative;
  flex: 1 1 auto; width: 100%; max-width: 1280px;
  padding: 0 1.75rem;
  display: flex; align-items: center;
}
.rbpg-ws-footer .rbpg-pager--flat {
  flex: 1 1 auto;
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: nowrap; gap: 0.75rem; padding: 0;
}
.rbpg-ws-footer-inner #rbpg-ws-actions {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
}
/* Reserve space so the pager / last rows aren't hidden behind the fixed footer. */
.app-content--footer { padding-bottom: calc(var(--rbpg-footer-h) + 1.5rem); }
@media (max-width: 991.98px) {
  .rbpg-ws-footer { left: 0; }               /* sidebar goes off-canvas */
}
@media (max-width: 720px) {
  .rbpg-ws-footer-inner { padding-left: 1rem; padding-right: 1rem; }
}
/* Row menu must layer above sticky table headers / neighbouring rows */
.dropdown-menu { z-index: 1056; }

/* Modals go above the workspace shells.
 *
 * Bootstrap puts a modal at 1055, which is fine on a normal page and wrong on
 * every `.pq` one: those shells are fixed at 2000, the action palette at 3000
 * and the file menu at 3500, so a modal opened from a cockpit or the estimate
 * builder rendered BEHIND the page that opened it — visible to the DOM,
 * invisible to the person who asked for it.
 *
 * Raised as a pair so Bootstrap's own ordering is untouched: a modal still sits
 * one step above its backdrop, and no other page changes. Above the file menu
 * too, because a modal is a question and nothing should be able to cover one. */
.modal { z-index: 3600; }
.modal-backdrop { z-index: 3590; }

/* Mobile off-canvas backdrop for the sidebar */
.app-scrim { position: fixed; inset: 0; background: rgba(24, 24, 27, 0.45); z-index: 1039; opacity: 0; pointer-events: none; transition: opacity .2s ease; }
.app-scrim.show { opacity: 1; pointer-events: auto; }
@media (min-width: 992px) { .app-scrim { display: none !important; } }

/* Impersonation banner (44px, fixed) sits above the app.
   Offset the fixed sidebar + sticky topbar below it, and push the main content
   flow down by the same amount so the pinned topbar doesn't overlap the body. */
.app-impersonation {
  position: fixed; top: 0; left: 0; right: 0; height: 44px;
  padding: 0 .75rem; background: #f4f4f5; color: #18181b;
  border-bottom: 1px solid #e4e4e7; z-index: 1060;
  display: flex; align-items: center; justify-content: center; gap: 1rem;
  font-size: .85rem; font-weight: 600;
}
.app-impersonation span {
  min-width: 0; max-width: 28vw; overflow: hidden;
  white-space: nowrap; text-overflow: ellipsis;
}
.app-impersonation .btn { flex: 0 0 auto; padding: .2rem .7rem; }
.app-workshop-name {
  max-width: 18rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app.impersonating .app-sidebar,
.app.impersonating .app-topbar { top: 44px; }
.app.impersonating .app-main { padding-top: 44px; }
@media (max-width: 720px) {
  .app-impersonation {
    height: 72px; display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: repeat(3, 1fr); gap: 0 .5rem;
    justify-content: stretch; font-size: .72rem;
  }
  .app-impersonation strong { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .app-impersonation span { max-width: none; }
  .app-impersonation .btn { grid-column: 2; grid-row: 1 / span 3; }
  .app.impersonating .app-sidebar,
  .app.impersonating .app-topbar { top: 72px; }
  .app.impersonating .app-main { padding-top: 72px; }
}

/* ---- Vendor detail: logo + per-card settings ------------------------ */
.rbpg-logo-thumb {
  width: 96px; height: 96px; border-radius: 16px; object-fit: cover;
  background: #f3f4f6; border: 1px solid #e5e7eb; flex: 0 0 auto;
}
.rbpg-logo-lg { width: 120px; height: 120px; border-radius: 18px; }
.rbpg-logo-empty {
  display: flex; align-items: center; justify-content: center;
  font-size: .72rem; color: #9ca3af; text-align: center;
}
.rbpg-setting-card .card-footer {
  background: transparent; border-top: 1px solid #eef0f2;
}
.rbpg-save-msg.is-ok { color: #4f6f5e; }
.rbpg-save-msg.is-err { color: #9b3b32; }

/* Status pills on the vendor detail page. These were a second, deliberately
   desaturated palette — sage #3f6b54, olive #85693a, a blue with no blue in it
   (#566079) — which is what "muddy" looks like in hex, and the olive failed AA.
   Same tones as `.pill` now; only the geometry differs, because this page sets
   them in denser rows. */
.rbpg-pill {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .2rem .6rem; border-radius: 999px;
  font-size: .75rem; font-weight: 600; line-height: 1.4;
  background: var(--tone-neutral-tint); color: var(--tone-neutral-ink);
  white-space: nowrap; vertical-align: middle;
  --blip: var(--tone-neutral-dot);
}
/* No opacity on the dot: dimming a signal colour is how it became mud. */
.rbpg-pill::before {
  content: ""; flex: none; width: .45rem; height: .45rem; border-radius: 999px;
  background: var(--blip);
}
.rbpg-pill--ok    { background: var(--tone-ok-tint);      color: var(--tone-ok-ink);      --blip: var(--tone-ok-dot); }
.rbpg-pill--warn  { background: var(--tone-warn-tint);    color: var(--tone-warn-ink);    --blip: var(--tone-warn-dot); }
.rbpg-pill--info  { background: var(--tone-info-tint);    color: var(--tone-info-ink);    --blip: var(--tone-info-dot); }
.rbpg-pill--bad   { background: var(--tone-bad-tint);     color: var(--tone-bad-ink);     --blip: var(--tone-bad-dot); }
.rbpg-pill--muted { background: var(--tone-neutral-tint); color: var(--tone-neutral-ink); --blip: var(--tone-neutral-dot); }
.rbpg-pill-row { display: flex; flex-wrap: wrap; gap: .4rem; }

/* Onboarding progress */
.rbpg-progress { height: 8px; background: #eceef0; border-radius: 999px; overflow: hidden; }
.rbpg-progress > span { display: block; height: 100%; background: #111827; border-radius: 999px; transition: width .3s ease; }
.rbpg-steps { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem; }
.rbpg-steps li { display: flex; align-items: center; gap: .6rem; font-size: .85rem; color: #6b7280; }
.rbpg-steps li .dot {
  width: 18px; height: 18px; border-radius: 999px; flex: 0 0 auto;
  border: 1.5px solid #d1d5db; display: inline-flex; align-items: center; justify-content: center;
  font-size: .6rem; color: #fff;
}
.rbpg-steps li.done { color: #1f2937; }
.rbpg-steps li.done .dot { background: #111827; border-color: #111827; }
.rbpg-steps li.current { color: #1f2937; font-weight: 600; }
.rbpg-steps li.current .dot { border-color: #111827; }

/* ---- Modals (mono, commercial) -------------------------------------- */
/* Add class `rbpg-modal` to the outer `.modal` element. Optional size
   modifiers on the same element: `rbpg-modal--wide` / `rbpg-modal--narrow`.
   Primary action = dark grey (.btn-mono); secondary = light grey (.btn-light). */
.rbpg-modal .modal-dialog { max-width: 620px; }
.rbpg-modal.rbpg-modal--wide .modal-dialog { max-width: 860px; }
.rbpg-modal.rbpg-modal--narrow .modal-dialog { max-width: 480px; }

.rbpg-modal .modal-content {
  border: 1px solid var(--rbpg-border);
  border-radius: var(--rbpg-radius-lg);
  box-shadow: 0 24px 64px rgba(24, 24, 27, .20);
  overflow: hidden;
}
.rbpg-modal .modal-header {
  align-items: flex-start;
  gap: 1rem;
  padding: 1.35rem 1.6rem;
  border-bottom: 1px solid var(--rbpg-border);
  background: var(--rbpg-surface);
}
.rbpg-modal .modal-title { font-size: 1.2rem; font-weight: 700; line-height: 1.3; }
.rbpg-modal .modal-subtitle { margin: .3rem 0 0; font-size: .875rem; color: var(--rbpg-muted); line-height: 1.45; }
.rbpg-modal .modal-header { justify-content: space-between; }
.rbpg-modal .btn-close { margin: -.25rem -.25rem 0 auto; padding: .65rem; opacity: .55; }

/* Shared: tag chips (used by CustomerEdit + customer view) */
.tag-chip { display: inline-flex; align-items: center; gap: .4rem; background: #e4e4e7; color: #3f3f46; border-radius: 999px; padding: .2rem .6rem; font-size: .78rem; font-weight: 600; }
.tag-chip button { border: 0; background: transparent; color: #71717a; line-height: 1; font-size: .9rem; cursor: pointer; padding: 0; }

/* ── Masked customer contact + Reveal (PRIV-1) ─────────────────────────────────
   `ma****@gmail.com` is the resting state of every contact row in the app, so it must
   not read as a warning or an error: the mask is muted and the button quieter still.
   `None on file` is styled as absence rather than as a hidden value — the two facts are
   different, and a screen that renders them alike is how an agent comes to tell a
   customer we have no address for them when we do. */
.rbpg-cr { display: inline-flex; align-items: baseline; gap: .35rem; flex-wrap: wrap; }
.rbpg-cr-mask { color: var(--rbpg-muted); user-select: none; }
.rbpg-cr-none { color: var(--rbpg-muted); font-style: italic; }
/* Selectable on purpose: the reason to reveal an address is almost always to use it. */
.rbpg-cr-open { user-select: all; }
.rbpg-cr-btn {
  display: inline-flex; align-items: center; gap: .25rem;
  border: 1px solid var(--rbpg-border); background: #fff; color: var(--rbpg-muted);
  border-radius: 999px; padding: .05rem .45rem; font-size: .72rem; font-weight: 600;
  line-height: 1.4; cursor: pointer;
}
.rbpg-cr-btn:hover, .rbpg-cr-btn:focus-visible { color: var(--rbpg-ink); border-color: var(--rbpg-ink); }
.rbpg-cr-btn.is-busy { opacity: .5; cursor: default; }
.rbpg-cr-btn.is-done { color: #15803d; border-color: #15803d; }
.rbpg-cr-copy { padding: .05rem .3rem; }
.rbpg-cr-note { color: var(--rbpg-muted); font-size: .7rem; }
.rbpg-cr-note.is-error { color: #b91c1c; }

/* Shared: audit history list (CustomerEdit history modal) */
.hist-item { padding: .85rem 0; border-bottom: 1px solid var(--rbpg-border); }
.hist-item:last-child { border-bottom: 0; }
.hist-actor { width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: .7rem; font-weight: 800; flex: 0 0 auto; }
.hist-actor.human { background: #e4e4e7; color: #3f3f46; }
.hist-actor.ai { background: #dbeafe; color: #1e40af; }
.hist-actor.system { background: #e5e5e5; color: #525252; }
.hist-diff { font-size: .8rem; background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 8px; padding: .5rem .7rem; margin-top: .4rem; }
.hist-old { color: #b91c1c; text-decoration: line-through; }
.hist-new { color: #15803d; }
.actor-badge { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: .05rem .4rem; border-radius: 999px; }
.actor-badge.ai { background: #dbeafe; color: #1e40af; }
.rbpg-modal .btn-close:hover { opacity: .9; }

/* Bootstrap's scrollable modal caps the height of `.modal-content` and expects
   the header, body and footer to be its direct children — the body scrolls and
   the other two stay put. Most of our forms wrap all three in a `<form>` so the
   Enter key submits, which leaves one unconstrained child holding the lot: the
   content runs past the capped height and `overflow: hidden` takes the footer,
   and the submit button in it, off the bottom of the dialog. Passing the flex
   column through the form puts it back. */
.modal-dialog-scrollable .modal-content > form {
  display: flex;
  flex-direction: column;
  max-height: 100%;
  overflow: hidden;
}
.modal-dialog-scrollable .modal-content > form > .modal-body { overflow-y: auto; }

.rbpg-modal .modal-body { padding: 1.6rem; }
.rbpg-modal .modal-footer {
  padding: 1.1rem 1.6rem;
  border-top: 1px solid var(--rbpg-border);
  background: var(--rbpg-grey);
  gap: .6rem;
}
.rbpg-modal .modal-footer .btn { padding-inline: 1.25rem; }

/* Comfortable inputs + labels inside modals */
.rbpg-modal .modal-body .form-label { font-weight: 600; font-size: .82rem; margin-bottom: .35rem; }
/* `:not()` so a form that asks for compact controls still gets them: this rule
   is three classes deep and would otherwise outrank `.form-control-sm`. */
.rbpg-modal .modal-body .form-control:not(.form-control-sm),
.rbpg-modal .modal-body .form-select:not(.form-select-sm) { padding: .6rem .85rem; font-size: .95rem; }
.rbpg-modal .modal-body .row + .row { margin-top: .25rem; }

/* A confirmation whose consequences are worth listing rather than describing.
   Tighter than a default list: this is a short set of facts inside a dialog
   somebody is reading in a hurry, not prose. */
.rbpg-modal .rc-list { margin: .65rem 0 0; padding-left: 1.15rem; }
.rbpg-modal .rc-list li { font-size: .9rem; line-height: 1.5; }
.rbpg-modal .rc-list li + li { margin-top: .35rem; }

/* The optional note a confirmation can collect. Set off from the explanation
   above it, because it is the one thing on the dialog that is asking rather
   than telling. */
.rbpg-modal .rc-field { margin-top: 1rem; }
.rbpg-modal .rc-field .form-label { font-size: .85rem; font-weight: 600; margin-bottom: .35rem; }
.rbpg-modal .rc-field textarea { resize: vertical; }

/* Grouped sections with a hint of separation and breathing room */
.rbpg-modal .form-block { margin-bottom: 1.5rem; }
.rbpg-modal .form-block:last-child { margin-bottom: 0; }
.rbpg-modal .form-hint { font-size: .8rem; color: var(--rbpg-muted); margin: .3rem 0 0; }

/* ---- Payment method picker -------------------------------------------
   Four tiles rather than a select: the method is the first decision and the
   one that changes what the operator is looking for on their screen — a bank
   reference, a terminal receipt — so it is worth seeing all four at once
   instead of opening a list to find out what the options are. */
.rp-methods {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
  margin-bottom: .25rem;
}
.rp-methods button {
  display: block;
  text-align: left;
  padding: .5rem .7rem;
  border: 1px solid var(--rbpg-border);
  border-radius: var(--rbpg-radius);
  background: #fff;
  cursor: pointer;
}
.rp-methods button:hover { background: var(--rbpg-grey); }
.rp-methods button.is-on {
  border-color: #3f3f46;
  box-shadow: inset 0 0 0 1px #3f3f46;
  background: #fff;
}
.rp-m-label { display: block; font-size: .875rem; font-weight: 600; color: var(--rbpg-text); }
.rp-m-hint { display: block; font-size: .75rem; color: var(--rbpg-muted); }

/* ---- Recording a decision on the customer's behalf ---------------------
   The approve/decline toggle borrows the method picker's tiles: it is the same
   kind of choice — the first one, and the one that changes what the rest of the
   form asks for — so it should not look like a different mechanism. */
.rd-toggle { margin-bottom: 1rem; }

/* The work, read back. Not a table: the agent is reading these down a phone,
   and a name against a price in a quiet list is easier to keep your place in
   than a grid. */
.rd-line, .rd-opt {
  display: flex; align-items: center; gap: .6rem;
  padding: .45rem 0; border-bottom: 1px solid var(--rbpg-border);
  font-size: .875rem;
}
.rd-line:last-child, .rd-opt:last-child { border-bottom: 0; }
.rd-line > span, .rd-opt-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.rd-line > b, .rd-opt > b { flex: 0 0 auto; font-variant-numeric: tabular-nums; }

/* The whole row is the hit target, because ticking a £120 upgrade on somebody
   else's behalf should not depend on hitting a 14px box. */
.rd-opt { cursor: pointer; margin: 0; }
.rd-opt:hover { background: var(--rbpg-grey); }
.rd-opt .form-check-input { flex: 0 0 auto; margin: 0; cursor: pointer; }

/* The same three rows the Total card on the page shows, so the button's figure
   can be reconciled against the line prices above it rather than taken on
   trust. Right-aligned values on tabular numerals, as everywhere else money is
   totalled. */
.rd-sum {
  margin-top: .9rem;
  padding-top: .7rem;
  border-top: 1px solid var(--rbpg-border);
}
.rd-sum-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .18rem 0;
  font-size: .85rem;
}
.rd-sum-row > b { font-variant-numeric: tabular-nums; font-weight: 600; }
.rd-sum-row--grand {
  margin-top: .3rem;
  padding-top: .4rem;
  border-top: 1px solid var(--rbpg-border);
}
.rd-sum-row--grand > span, .rd-sum-row--grand > b { font-weight: 700; }

/* "optional", beside a label. Quiet enough not to compete with the label and
   present enough that nobody hunts for the asterisk that is not there. */
.rd-opt-tag {
  font-size: .7rem;
  font-weight: 500;
  color: var(--rbpg-muted);
  text-transform: none;
  letter-spacing: 0;
}

/* The consequence sits ABOVE the buttons rather than beside them: it is the
   sentence that should be read before the click, and a footnote to the right of
   a primary button is read afterwards, if at all. */
.rbpg-modal .modal-footer.rd-foot {
  display: block;
  text-align: left;
}
.rd-consequence {
  margin: 0 0 .7rem;
  font-size: .78rem;
  line-height: 1.45;
  color: var(--rbpg-muted);
}
.rd-foot-actions { display: flex; justify-content: flex-end; gap: .5rem; }
.rbpg-modal .modal-lead {
  margin: 0 0 1.4rem;
  padding: .85rem 1rem;
  font-size: .875rem;
  color: var(--rbpg-text);
  background: var(--rbpg-grey);
  border: 1px solid var(--rbpg-border);
  border-radius: var(--rbpg-radius);
  line-height: 1.5;
}

/* Two-column page layout — single column until 1280px, then split.
   Use `.rbpg-split` on a wrapper with two direct child <div> columns.
   Add `.rbpg-split--even` for a 50/50 split instead of the default 1.35 / 1. */
.rbpg-split { display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: start; }
@media (min-width: 1280px) {
  .rbpg-split { grid-template-columns: 1.35fr 1fr; }
  .rbpg-split.rbpg-split--even { grid-template-columns: 1fr 1fr; }
  /* Fixed identity sidebar (left) + wide activity panel (right) */
  .rbpg-split.rbpg-split--sidebar { grid-template-columns: 340px 1fr; }
}

/* `--match`: two LISTS side by side rather than a panel beside a detail.
   The default `align-items: start` is right for the common case — a short
   sidebar next to a long feed should not stretch to match it — and wrong when
   both columns are card-wrapped tables with a pager at the bottom, because the
   shorter card's pager then floats halfway up the row next to the taller one's.
   Stretching both and letting the card fill its column puts the two pagers on
   one line, which is the whole point of them matching. */
.rbpg-split--match { align-items: stretch; }
.rbpg-split--match > * { display: flex; min-width: 0; }
.rbpg-split--match > * > .card { flex: 1 1 auto; width: 100%; }

/* Success view inside a modal (post-submit confirmation) */
.rbpg-modal-success { text-align: center; padding: .25rem 0 .25rem; }
.rbpg-success-badge {
  width: 56px; height: 56px; border-radius: 999px;
  margin: .25rem auto 1rem;
  display: inline-flex; align-items: center; justify-content: center;
  color: #3f6b54; background: #eaf1ec; border: 1px solid #d8e6dd;
}

/* Mono primary button (dark grey, not black, not blue) */
.btn-mono {
  background: #3f3f46;
  border: 1px solid #3f3f46;
  color: #fff;
  font-weight: 600;
}
.btn-mono:hover, .btn-mono:focus, .btn-mono:active {
  background: #27272a !important;
  border-color: #27272a !important;
  color: #fff !important;
}
.btn-mono:disabled { background: #a1a1aa; border-color: #a1a1aa; color: #fff; opacity: 1; }

/* Stripe's own indigo, on the one button that connects a Stripe account.
   The admin palette is deliberately mono, and this is the exception worth
   making: a payment processor's colour is recognised before it is read, so
   somebody landing on the Payments tab looking for "where do the keys go"
   finds it without scanning the card. It stays the only coloured button on
   that card, which is what keeps it meaning "this one". */
.btn-stripe {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: #635bff;
  border: 1px solid #635bff;
  color: #fff;
  font-weight: 600;
}
.btn-stripe:hover, .btn-stripe:focus, .btn-stripe:active {
  background: #4f46e5 !important;
  border-color: #4f46e5 !important;
  color: #fff !important;
}
.btn-stripe:disabled { background: #b4afff; border-color: #b4afff; color: #fff; opacity: 1; }
.btn-stripe svg { flex: none; }

/* ---- Responsive ----------------------------------------------------- */
@media (max-width: 991.98px) {
  .app-sidebar { transform: translateX(-100%); transition: transform .2s ease; }
  .app-sidebar.open { transform: translateX(0); }
  .app-main { margin-left: 0; }
  .app-hamburger { display: inline-flex; }
  .app-content { padding: 1.25rem 1rem 3rem; }
}

@media (max-width: 575.98px) {
  .rbpg-modal .modal-header,
  .rbpg-modal .modal-body,
  .rbpg-modal .modal-footer { padding-left: 1.15rem; padding-right: 1.15rem; }
  .rbpg-modal .modal-footer { flex-direction: column-reverse; }
  .rbpg-modal .modal-footer .btn { width: 100%; margin: 0; }
}

/* ---- Cases console: contextual pipeline rail ------------------------- */
.rail-nav .rail-back {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.55rem 0.8rem; margin-bottom: 0.5rem;
  border-radius: 10px; color: var(--rbpg-muted); text-decoration: none;
  font-weight: 600; font-size: 0.85rem;
}
.rail-nav .rail-back:hover { background: var(--rbpg-grey); color: var(--rbpg-text); }
.rail-nav .rail-back svg, .rail-nav .rail-back i { width: 16px; height: 16px; }
.rail-stage {
  display: flex; align-items: center; gap: 0.6rem; width: 100%;
  padding: 0.55rem 0.7rem; margin-bottom: 0.15rem;
  border: 0; background: transparent; border-radius: 10px;
  color: var(--rbpg-text); font-weight: 600; font-size: 0.9rem;
  text-align: left; cursor: pointer;
}
.rail-stage:hover { background: var(--rbpg-grey); }
.rail-stage.active { background: var(--rbpg-text); color: #fff; }
/* A rail row that NAVIGATES is an <a>, and `.app-nav a` (0,1,1) outweighs
   `.rail-stage` (0,1,0) on padding and font size — 45px against 39px, measured.
   Every rail built so far is buttons, so this had never shown; the Settings rail
   (NAV-2) is entirely links and would have been visibly taller row by row. */
.app-nav a.rail-stage {
  padding: 0.55rem 0.7rem;
  font-size: 0.9rem;
  gap: 0.6rem;
}
.app-nav a.rail-stage.active { color: #fff; }
.rail-stage-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.rail-dot--muted { background: var(--rbpg-border) !important; }
.rail-count {
  min-width: 1.6rem; text-align: center; font-size: 0.75rem; font-weight: 700;
  padding: 0.05rem 0.4rem; border-radius: 999px;
  background: var(--rbpg-grey); color: var(--rbpg-muted);
}
.rail-stage.active .rail-count { background: rgba(255,255,255,0.2); color: #fff; }
.rail-count--alert { background: #fee2e2; color: #b91c1c; }
.rail-stage.active .rail-count--alert { background: #dc2626; color: #fff; }

/* ---- `hidden` has to actually hide ----------------------------------- */
/* Bootstrap's display utilities are `!important`, so on any element carrying both a `d-*`
   class and the `hidden` attribute the class wins and `hidden` silently does nothing. The
   workshop's "Process queue" button is `d-inline-flex`, so it sat on the dashboard where
   WF-15 had correctly hidden it, and `#raise-vat-row` in billing has the same shape.

   The UA stylesheet's `[hidden] { display: none }` cannot compete with `!important`, so this
   raises it to match. It has to come before the utilities in specificity terms, hence
   `!important` here too — an attribute that means "not on the page" should not be losing an
   argument to a layout class. */
[hidden] { display: none !important; }

/* ---- Cases console: KPI / health strip ------------------------------- */
.kpi-strip {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.9rem;
  margin-bottom: 1.1rem;
}
.kpi-card {
  background: var(--rbpg-surface); border: 1px solid var(--rbpg-border);
  border-radius: 14px; padding: 0.9rem 1.1rem;
}
.kpi-card--alert { border-color: #fca5a5; background: #fef2f2; }

/* ---- Workshop floor: the summary card (WF-22 §3, §4) -----------------
   Three labelled figures in three fixed rows, so a column of cards can be scanned down
   rather than re-read one by one. An empty queue prints an em dash in each row instead of
   collapsing, which is what keeps the shape still between visits. */
.ws-card { display: flex; flex-direction: column; cursor: pointer; transition: border-color .15s ease; }
.ws-card:hover { border-color: var(--rbpg-text); }
.ws-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }

.ws-flags { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.ws-flag {
  font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  padding: 0.1rem 0.4rem; border-radius: 999px;
  background: var(--rbpg-grey); color: var(--rbpg-muted); white-space: nowrap;
}
.ws-flag--late { background: #fee2e2; color: #b91c1c; }

.ws-metrics { margin-top: 0.5rem; display: grid; gap: 0.1rem; }
/* Fixed first column so the values line up across every card in the grid, not just within
   one. `max-content` on the target keeps "of 4w" tight against the figure it qualifies. */
.ws-metric {
  display: grid; grid-template-columns: 4.2rem auto max-content;
  gap: 0.4rem; align-items: baseline;
  font-size: 0.74rem; line-height: 1.5;
}
.ws-metric-k { color: var(--rbpg-muted); }
.ws-metric-v {
  font-variant-numeric: tabular-nums; font-weight: 600; color: var(--rbpg-text);
  text-align: right;
}
.ws-metric-t { color: var(--rbpg-muted); font-size: 0.68rem; }
/* Past the promise. The figure reddens, the label does not — the label is furniture. */
.ws-metric--over .ws-metric-v, .ws-metric--over .ws-metric-t { color: #b91c1c; }

.ws-card-link {
  margin-top: 0.55rem; padding: 0; border: 0; background: none;
  align-self: flex-start; font-size: 0.72rem; font-weight: 600;
  color: var(--rbpg-text); text-decoration: underline; text-underline-offset: 2px;
}
.ws-card-link:hover { color: #b91c1c; }
.kpi-label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--rbpg-muted); font-weight: 700; margin-bottom: 0.35rem;
}
.kpi-value { font-size: 1.6rem; font-weight: 800; line-height: 1.1; color: var(--rbpg-text); }
/* Billing (EST-26): whose money a figure is. Two ledgers run in opposite
   directions on that page and the amounts alone cannot say which is which. */
.kpi-sub { margin-top: 0.3rem; font-size: 0.72rem; color: var(--rbpg-muted); }
.kpi-healthbar {
  margin-top: 0.55rem; height: 7px; border-radius: 999px;
  background: var(--rbpg-grey); overflow: hidden;
}
.kpi-healthbar span { display: block; height: 100%; width: 0; border-radius: 999px; transition: width .3s ease, background .3s ease; }

@media (max-width: 991.98px) { .kpi-strip { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 479.98px) { .kpi-strip { grid-template-columns: 1fr 1fr; } }

.hdr-count { font-size: 1.1rem; font-weight: 600; color: var(--rbpg-muted); }
.hdr-blip { width: 10px; height: 10px; border-radius: 50%; display: inline-block; background: var(--rbpg-border); }

/* ===================================================================== */
/* Process cockpit — full-screen queue-clearing workspace (P4-CASE-CONSOLE) */
/* ===================================================================== */
/* Lock the page: the cockpit owns the whole viewport, nothing scrolls behind it. */
body:has(.pq) { overflow: hidden; margin: 0; }
.pq {
  position: fixed; inset: 0; z-index: 2000;
  height: 100vh; height: 100dvh;
  display: grid; grid-template-rows: 56px auto minmax(0, 1fr) 66px;
  background: var(--rbpg-bg, #f5f5f5); color: var(--rbpg-text);
}
/* Pin each direct child to its row: a display:none warnings row is removed from
   the grid, so without this the body/footer auto-shift up a track. */
.pq-top { grid-row: 1; }
.pq-warnings { grid-row: 2; }
.pq-body { grid-row: 3; }
.pq-foot { grid-row: 4; }
/* Thin, rounded, monochrome scrollbars for every scroll area in the cockpit. */
.pq-queue, .pq-info, .pq-thread, .pq-stage-list, .pq-palette-list, .pq-composer > textarea {
  scrollbar-width: thin; scrollbar-color: #c7c7c7 transparent;
}
.pq-queue::-webkit-scrollbar, .pq-info::-webkit-scrollbar, .pq-thread::-webkit-scrollbar,
.pq-stage-list::-webkit-scrollbar, .pq-palette-list::-webkit-scrollbar,
.pq-composer > textarea::-webkit-scrollbar { width: 8px; height: 8px; }
.pq-queue::-webkit-scrollbar-thumb, .pq-info::-webkit-scrollbar-thumb, .pq-thread::-webkit-scrollbar-thumb,
.pq-stage-list::-webkit-scrollbar-thumb, .pq-palette-list::-webkit-scrollbar-thumb,
.pq-composer > textarea::-webkit-scrollbar-thumb {
  background: #cccccc; border-radius: 999px; border: 2px solid transparent; background-clip: content-box;
}
.pq-queue::-webkit-scrollbar-thumb:hover, .pq-info::-webkit-scrollbar-thumb:hover, .pq-thread::-webkit-scrollbar-thumb:hover { background: #a8a8a8; background-clip: content-box; }
.pq-queue::-webkit-scrollbar-track, .pq-info::-webkit-scrollbar-track, .pq-thread::-webkit-scrollbar-track { background: transparent; }
.pq-top {
  display: flex; align-items: center; gap: 1rem;
  height: 56px; padding: 0 1.1rem;
  background: var(--rbpg-text); color: #fff;
}
.pq-crumb { font-weight: 700; letter-spacing: 0.01em; display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem; }
.pq-crumb .sep { opacity: 0.5; }
.pq-crumb .muted { color: #d4d4d8; font-weight: 600; }
/* Header stage selector: black bg, white border, red label, chevron */
.pq-stage-btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: transparent; color: #f87171; font-weight: 800; font-size: 0.9rem;
  border: 1px solid rgba(255,255,255,0.55); border-radius: 8px;
  padding: 0.2rem 0.6rem; cursor: pointer; line-height: 1.4;
}
.pq-stage-btn:hover { border-color: #fff; background: rgba(255,255,255,0.08); }
.pq-stage-btn:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.pq-stage-caret { color: #fff; opacity: 0.85; font-size: 0.7rem; }
/* The same fact in the side panel's Case block (WF-19). Quieter than the crumb pill — the crumb has
   to survive a glance, this sits in a list of fields being read deliberately. */
.pq-kind-inline {
  display: inline-block; padding: 0.1rem 0.4rem;
  border-radius: 6px; background: #fdf1cf; color: #6b4e00;
  font-weight: 700; font-size: 0.78rem; line-height: 1.35;
}

/* A warranty return, beside the bench (WF-19). Louder than the lane on purpose: the lane is where
   the job is, this is whether our own last repair failed, and the person reading it is about to
   open the parcel. Amber rather than red — it is a fact to register, not an alarm. */
.pq-kind {
  display: inline-flex; align-items: center;
  margin-left: 0.5rem; padding: 0.2rem 0.55rem;
  border-radius: 8px; background: #f2c14e; color: #3a2c05;
  font-weight: 800; font-size: 0.78rem; line-height: 1.4;
  letter-spacing: 0.02em; text-transform: uppercase; white-space: nowrap;
}
/* The bench, beside the stage (WF-24). Quieter than the stage button because it is a fact rather
   than a control, but not muted grey — for a workshop this is the line that matters. */
.pq-lane {
  display: inline-flex; align-items: center;
  margin-left: 0.5rem; padding: 0.2rem 0.55rem;
  border-radius: 8px; background: rgba(255,255,255,0.14); color: #fff;
  font-weight: 700; font-size: 0.82rem; line-height: 1.4; white-space: nowrap;
}
/* Status pill mid-change: pulse until the server confirms the new stage. */
.pq-stage--moving { animation: pq-stage-pulse 0.9s ease-in-out infinite; }
@keyframes pq-stage-pulse { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }

/* Warning banners (between header and body) — full-bleed blocks, no margin/border. */
.pq-warnings { display: flex; flex-direction: column; gap: 0; padding: 0; }
.pq-warn { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1.1rem; font-size: 0.84rem; font-weight: 600; }
.pq-warn-ico { font-size: 0.9rem; opacity: 0.9; }
.pq-warn--urgent { background: #fbeded; color: #9f2f2f; }
.pq-warn--warn { background: #f7f4ec; color: #6f5a1f; }
/* P3-AIV-2: the same strip, carrying an offer rather than a problem — AIVERIE has
   read the photos and is asking whether the record should say so. Deliberately the
   quietest of the three: a case is not in trouble because a machine found something,
   so it reads as neutral chrome with the two verbs on the right. */
.pq-warn--action { background: var(--rbpg-surface); color: var(--rbpg-text); border-bottom: 1px solid var(--rbpg-border); font-weight: 500; padding: 0.42rem 1.1rem; }
/* Drawn icon, optically sized to the text and dropped a hair so its centre sits on
   the text's centre rather than on the line box's. */
.pq-warn--action .pq-warn-ico { display: inline-flex; color: var(--rbpg-muted); }
.pq-warn--action .pq-warn-ico svg { width: 15px; height: 15px; display: block; }
.pq-warn-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-warn-short { display: none; }
.pq-warn-acts { margin-left: auto; display: flex; align-items: center; gap: 0.35rem; flex: none; }
/* Small, and centred by the flexbox rather than by line-height: a button's text
   sitting a pixel high is the thing that makes a strip like this look homemade. */
.pq-warn-acts .pq-btn {
  display: inline-flex; align-items: center; justify-content: center;
  align-self: center; padding: 0.24rem 0.62rem; font-size: 0.75rem; line-height: 1.35;
}
/* On a phone the sentence goes, the count stays. */
@media (max-width: 900px) {
  .pq-warn--action .pq-warn-long { display: none; }
  .pq-warn--action .pq-warn-short { display: inline; }
}
/* Thread-trailing blip (e.g. "customer not messaged for N days"). */
.pq-blip { align-self: center; margin: 0.5rem 0 0.1rem; font-size: 0.74rem; font-weight: 600; color: var(--rbpg-muted); background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 999px; padding: 0.25rem 0.75rem; }
.pq-blip--warn { background: #f7f4ec; color: #6f5a1f; border-color: #e3d9c1; }
.pq-progress { margin-left: auto; display: flex; align-items: center; gap: 0.8rem; font-size: 0.85rem; color: #e4e4e7; }
.pq-progress-bar { width: 160px; height: 6px; border-radius: 999px; background: rgba(255,255,255,0.2); overflow: hidden; }
.pq-progress-bar span { display: block; height: 100%; background: #fff; width: 0; transition: width .25s ease; }
.pq-close {
  border: 0; background: rgba(255,255,255,0.12); color: #fff;
  width: 34px; height: 34px; border-radius: 9px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.pq-close:hover { background: rgba(255,255,255,0.25); }

.pq-body { display: grid; grid-template-columns: 280px minmax(0, 1fr) clamp(280px, 22vw, 400px); min-height: 0; overflow: hidden; }
.pq-queue, .pq-info { background: var(--rbpg-surface); overflow-y: auto; min-height: 0; }
.pq-queue { border-right: 1px solid var(--rbpg-border); padding: 0.6rem; }
.pq-info { border-left: 1px solid var(--rbpg-border); padding: 1.1rem; }
.pq-queue-head { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--rbpg-muted); font-weight: 700; padding: 0.4rem 0.5rem 0.6rem; }
.pq-queue-toggle {
  display: none; align-items: center; justify-content: center;
  border: 0; background: rgba(255,255,255,0.12); color: #fff;
  width: 34px; height: 34px; border-radius: 9px; cursor: pointer; flex: 0 0 auto;
}
.pq-queue-toggle:hover { background: rgba(255,255,255,0.25); }
.pq-scrim { display: none; }

.pq-card {
  display: block; width: 100%; text-align: left;
  border: 1px solid #eee; background: #fff;
  border-radius: 11px; padding: 0.6rem 0.7rem; margin-bottom: 0.3rem; cursor: pointer;
}
.pq-card:hover { background: var(--rbpg-grey); border-color: #e2e2e2; }
.pq-card.active { background: #fff; border-color: var(--rbpg-text); box-shadow: 0 1px 0 rgba(0,0,0,0.04); }
.pq-card.done { opacity: 0.5; }
.pq-card.done .pq-card-ref::before { content: '✓ '; color: #16a34a; font-weight: 800; }
.pq-card-top { display: flex; align-items: center; gap: 0.5rem; }
.pq-card-ref { font-family: ui-monospace, monospace; font-size: 0.72rem; color: var(--rbpg-muted); }
.pq-card-age { margin-left: auto; font-size: 0.72rem; color: var(--rbpg-muted); }
.pq-card-age.over { color: #dc2626; font-weight: 700; }
.pq-card-cust { font-weight: 700; font-size: 0.9rem; margin-top: 0.15rem; }
.pq-card-item { font-size: 0.8rem; color: var(--rbpg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Realtime: unread new-message dot + "moved on" fade (docs/design/30). */
.pq-card { position: relative; }
.pq-card-dot { display: none; position: absolute; top: 0.6rem; right: 0.6rem; width: 8px; height: 8px; border-radius: 50%; background: #2563eb; box-shadow: 0 0 0 2px #fff; }
.pq-card--unread .pq-card-dot { display: block; }
.pq-card--unread .pq-card-cust { font-weight: 800; }
.pq-card--moved { opacity: 0.45; }
/* Case labels (AUTO-7) — Trello-style colour bars, rendered app-wide by
   RBPGLabels (lib/rbpg-labels.js). No words: the name shows on hover (.rbpg-tip).
   `--dots` is the compact variant used on queue cards. */
.rbpg-labels { display: inline-flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.rbpg-labels--dots { gap: 5px; margin-top: 0.35rem; }
/* Flat bars — no border, no shadow. Pure colour floating on a transparent strip. */
.rbpg-lbar { width: 42px; height: 11px; border-radius: 999px; display: inline-block; box-shadow: none; border: 0; cursor: default; }
.rbpg-ldot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; box-shadow: none; border: 0; cursor: default; }
/* In-list variant: label bars sit under the item title in the cases/board tables.
   Slightly smaller than the message-window strip so dense rows stay tight; the
   wrapper collapses when a case has no labels so it never adds blank space. */
.rbpg-cell-labels { display: block; margin-top: 0.28rem; }
.rbpg-cell-labels:empty { display: none; }
.rbpg-cell-labels .rbpg-lbar { width: 26px; height: 8px; }
/* Reusable styled hover tooltip ("blip") — any element with .rbpg-tip[data-tip]. */
.rbpg-tip { position: relative; }
.rbpg-tip::after, .rbpg-tip::before { opacity: 0; visibility: hidden; transition: opacity .12s ease; pointer-events: none; position: absolute; z-index: 60; }
.rbpg-tip::after { content: attr(data-tip); left: 50%; bottom: calc(100% + 7px); transform: translateX(-50%); background: #1f2430; color: #fff; font-size: 0.72rem; font-weight: 600; line-height: 1; white-space: nowrap; padding: 5px 8px; border-radius: 6px; box-shadow: 0 4px 14px rgba(0,0,0,0.22); }
.rbpg-tip::before { content: ''; left: 50%; bottom: calc(100% + 2px); transform: translateX(-50%); border: 5px solid transparent; border-top-color: #1f2430; }
.rbpg-tip:hover::after, .rbpg-tip:hover::before { opacity: 1; visibility: visible; }
/* The pinned strip at the bottom-left of the message window (above the composer).
   Transparent so the bars float directly on the message area, with clear spacing
   above the composer so it never crowds the message box. */
.pq-msg-labels { display: flex; align-items: center; gap: 7px; padding: 0 0.9rem; background: transparent; }
.pq-msg-labels:not(:empty) { padding-top: 0.55rem; padding-bottom: 0.7rem; }
.pq-muted { color: var(--rbpg-muted); font-weight: 400; }
/* Priority pill */
.pq-prio { display: inline-block; padding: 1px 9px; border-radius: 999px; font-size: 0.72rem; font-weight: 700; line-height: 1.5; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06); }
.pq-prio--low { background: #eef2f7; color: #64748b; }
.pq-prio--normal { background: #e8f0fe; color: #2563eb; }
.pq-prio--high { background: #fff1e6; color: #c2410c; }
.pq-prio--urgent { background: #fde8e8; color: #dc2626; }
.pq-card--moved .pq-card-cust { text-decoration: line-through; }

/* Center: message feed + composer */
.pq-center { display: grid; grid-template-rows: 1fr auto; min-height: 0; min-width: 0; background: #fff; }
.pq-thread {
  overflow-y: auto; padding: 1.2rem 1.4rem; display: flex; flex-direction: column; gap: 0.7rem;
  /* Soft neutral ramp — barely tinted at top, nearly white below. No cool/blue cast. */
  background:
    linear-gradient(
      180deg,
      #f4f4f5 0%,
      #f8f8f9 40%,
      #fcfcfc 70%,
      #ffffff 100%
    );
  background-attachment: fixed;
}
.pq-thread-empty { margin: auto; color: var(--rbpg-muted); font-size: 0.9rem; }
/* Inline system log line (routed / snoozed / stage moved) — quiet, centered. */
.pq-sys { align-self: center; display: inline-flex; align-items: center; gap: 0.5rem; margin: 0.55rem 0 0.15rem; font-size: 0.72rem; color: var(--rbpg-muted); background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 999px; padding: 0.22rem 0.7rem; max-width: 90%; }
.pq-sys-text { font-weight: 600; }
.pq-sys-time { opacity: 0.7; }
.pq-sys-ico { opacity: 0.8; font-size: 0.78rem; line-height: 1; }
.pq-sys-actor { opacity: 0.85; font-weight: 600; }
.pq-sys-actor::before { content: '\00b7'; margin: 0 0.4rem 0 0; opacity: 0.6; }
.pq-sys-ai { margin-left: 0.3rem; font-size: 0.6rem; font-weight: 800; letter-spacing: 0.03em; color: #6d28d9; background: #efe7fd; border-radius: 4px; padding: 0.02rem 0.28rem; vertical-align: middle; }
/* A log line whose whole row is the link (a payment: it names no reference to
   underline, but it knows the estimate it settled). Reads as the pill it
   already was until pointed at. */
a.pq-sys--link { text-decoration: none; color: var(--rbpg-muted); cursor: pointer; }
a.pq-sys--link:hover { background: #fff; border-color: #c9d4e5; color: var(--rbpg-text); }

/* References inside notes and log lines: an estimate number, a case, a payment.
   Understated on purpose — the note is the message, the link is an offer. */
.pq-ref {
  color: inherit; text-decoration: underline; text-underline-offset: 2px;
  text-decoration-thickness: 1px; text-decoration-color: rgba(37, 99, 235, 0.5);
  border-radius: 3px; padding: 0 1px; transition: background 0.12s ease;
}
.pq-ref:hover { color: #1d4ed8; text-decoration-color: #1d4ed8; background: rgba(37, 99, 235, 0.09); }

/* Message rows: customer left, our messages + notes right (WhatsApp/Intercom).
   Bubble width shrinks to content up to max-width; date sits below the bubble. */
.pq-msg { max-width: 78%; display: flex; flex-direction: column; gap: 0.22rem; }
.pq-msg--out { align-self: flex-end; align-items: flex-end; }
.pq-msg--in { align-self: flex-start; align-items: flex-start; }

.pq-bubble { display: inline-block; max-width: 100%; border-radius: 15px; overflow: hidden; font-size: 0.9rem; line-height: 1.45; box-shadow: 0 1px 1px rgba(0,0,0,0.04); }
.pq-msg--in .pq-bubble { background: #fff; border: 1px solid var(--rbpg-border); color: var(--rbpg-text); border-bottom-left-radius: 5px; }
.pq-msg--out .pq-bubble { background: #eaf1fb; border: 1px solid #d7e3f6; color: #1c2a44; border-bottom-right-radius: 5px; }
.pq-msg--note .pq-bubble, .pq-bubble--note { background: #fdf6d8; border: 1px solid #f0dfa0; color: #5a4708; border-bottom-right-radius: 5px; }
/* text/doc area — padded. Media (photos) sit flush above with no inner margin. */
.pq-bubble-pad { padding: 0.55rem 0.75rem; }
.pq-bubble--media { width: 300px; max-width: 100%; }
.pq-bubble-subj { font-weight: 700; font-size: 0.82rem; margin-bottom: 0.2rem; }
.pq-bubble-body { white-space: pre-wrap; word-break: break-word; }
.pq-bubble-pad > .pq-atts:first-child { margin-top: 0; }

/* Intercom-style quoted-history unfold: collapse the reply's quoted tail +
   signature behind a small ••• affordance so the thread shows only the new text. */
.pq-quote-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 0.3rem; padding: 0 0.3rem; height: 12px; min-width: 22px;
  border: 1px solid rgba(0,0,0,0.14); border-radius: 999px;
  background: rgba(0,0,0,0.045); color: inherit; cursor: pointer;
  font-size: 0.6rem; line-height: 1; letter-spacing: 0.04em; opacity: 0.7;
}
.pq-quote-toggle .pq-quote-dots { position: relative; top: -1px; }
.pq-quote-toggle:hover { opacity: 1; background: rgba(0,0,0,0.08); }
.pq-quote-toggle.is-open { background: rgba(0,0,0,0.1); opacity: 1; }
.pq-quoted {
  white-space: pre-wrap; word-break: break-word;
  margin-top: 0.4rem; padding: 0.5rem 0.65rem;
  border-left: 2px solid rgba(0,0,0,0.14);
  background: rgba(0,0,0,0.03); border-radius: 4px;
  font-size: 0.82rem; opacity: 0.72;
}

/* full-bleed photos inside a bubble (WhatsApp-style) */
.pq-media { display: block; background: rgba(0,0,0,0.04); }
.pq-media-item { display: block; width: 100%; border: 0; padding: 0; margin: 0; background: none; cursor: pointer; }
.pq-media--one .pq-media-item img { display: block; width: 100%; max-height: 300px; object-fit: cover; }
.pq-media--multi { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.pq-media--multi .pq-media-item { aspect-ratio: 1; }
.pq-media--multi .pq-media-item img { display: block; width: 100%; height: 100%; object-fit: cover; }

.pq-msg-foot { display: flex; align-items: center; gap: 0.4rem; font-size: 0.66rem; color: var(--rbpg-muted); padding: 0 0.15rem; }
.pq-msg-foot time { color: var(--rbpg-muted); }
.pq-msg-foot .pq-who { font-weight: 600; color: #6b6b70; }
.pq-chan { text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; font-size: 0.58rem; padding: 0.07rem 0.38rem; border-radius: 5px; background: var(--rbpg-grey); color: var(--rbpg-muted); }
.pq-chan--email { background: #e7effb; color: #2563eb; }
.pq-chan--sms { background: #e6f6ec; color: #16a34a; }
.pq-chan--note { background: #fbf0cf; color: #a16207; }

/* Cursor-style composer: bordered card, controls tucked lower-right */
.pq-composer {
  border-top: 1px solid var(--rbpg-border); background: var(--rbpg-surface); padding: 0.7rem 0.9rem;
}
.pq-composer > textarea {
  width: 100%; border: 1px solid var(--rbpg-border); border-top-left-radius: 12px; border-top-right-radius: 12px;
  border-bottom: 0; padding: 0.6rem 0.75rem; resize: none; font-size: 0.9rem;
  min-height: 42px; max-height: 168px; overflow-y: hidden; display: block;
}
.pq-composer > textarea:focus { outline: none; border-color: var(--rbpg-text); }
.pq-composer--note > textarea { background: #fffdf3; }
.pq-composer-bar {
  display: flex; align-items: center; gap: 0.5rem;
  border: 1px solid var(--rbpg-border); border-top: 0;
  border-bottom-left-radius: 12px; border-bottom-right-radius: 12px;
  padding: 0.35rem 0.4rem 0.35rem 0.5rem; background: #fff;
}
.pq-composer:focus-within .pq-composer-bar,
.pq-composer:focus-within > textarea { border-color: var(--rbpg-text); }
.pq-composer--note .pq-composer-bar { background: #fffdf3; }
.pq-composer-hint { margin-left: auto; font-size: 0.72rem; color: var(--rbpg-muted); }

/* channel drop-up */
.pq-chan-wrap { position: relative; }
.pq-chan-btn {
  display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer;
  background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 8px;
  padding: 0.3rem 0.55rem; font-size: 0.82rem; font-weight: 600; color: var(--rbpg-text);
}
.pq-chan-btn:hover { border-color: var(--rbpg-text); }
.pq-chan-caret { font-size: 0.6rem; color: var(--rbpg-muted); }
.pq-chan-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--rbpg-text); display: inline-block; }
.pq-chan-dot--email { background: #2563eb; }
.pq-chan-dot--sms { background: #16a34a; }
.pq-chan-dot--note { background: #d97706; }
.pq-chan-menu {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30;
  background: #fff; border: 1px solid var(--rbpg-border); border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.14); padding: 0.3rem; min-width: 190px;
}
.pq-chan-opt {
  display: flex; align-items: center; gap: 0.55rem; width: 100%; text-align: left;
  background: transparent; border: 0; border-radius: 7px; padding: 0.55rem 0.6rem; cursor: pointer; font-size: 0.88rem;
}
.pq-chan-opt:hover { background: var(--rbpg-grey); }

/* circular send with up-arrow */
.pq-send {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--rbpg-text); color: #fff; display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.05rem; line-height: 1; transition: background .15s, opacity .15s;
}
.pq-send:hover:not(:disabled) { background: #000; }
.pq-send:disabled { opacity: 0.4; cursor: default; }
.pq-send--busy { position: relative; color: transparent; }
.pq-send--busy::after {
  content: ''; position: absolute; width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.4); border-top-color: #fff; animation: pq-spin 0.7s linear infinite;
}
@keyframes pq-spin { to { transform: rotate(360deg); } }

/* Right: case + customer info */
.pq-info-block { margin-bottom: 1.15rem; }
.pq-info-block h4 { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--rbpg-muted); font-weight: 600; margin-bottom: 0.6rem; }
/* Hairline dividers between stacked sections (Case ▸ Customer ▸ Supplier). */
#pq-panel-case .pq-info-block + .pq-info-block { border-top: 1px solid var(--rbpg-border); padding-top: 1.15rem; }
.pq-kv { display: grid; grid-template-columns: 96px 1fr; gap: 0.15rem 0.7rem; font-size: 0.85rem; margin-bottom: 0.4rem; align-items: baseline; }
.pq-kv .k { color: var(--rbpg-muted); font-weight: 400; }
.pq-kv .v { font-weight: 500; color: var(--rbpg-text); word-break: break-word; }
.pq-info-title { font-size: 1rem; font-weight: 700; margin-bottom: 0.35rem; letter-spacing: -0.01em; }
.pq-brand-fact-row { display: flex; align-items: center; gap: 0.55rem; font-size: 0.88rem; font-weight: 600; }
.pq-brand-logo { width: 28px; height: 28px; border-radius: 7px; object-fit: contain; border: 1px solid var(--rbpg-border); background: #fff; }
.pq-brand-logo--empty { display: inline-flex; align-items: center; justify-content: center; color: var(--rbpg-muted); background: var(--rbpg-grey); }
/* Machine case id — dulled, monospace; only shown on the case detail view. */
.pq-caseid { margin-top: 0.6rem; font-family: ui-monospace, monospace; font-size: 0.68rem; color: var(--rbpg-muted); opacity: 0.55; user-select: all; }
.cse-machine-id { font-family: ui-monospace, monospace; font-size: 0.8em; color: var(--rbpg-muted); opacity: 0.55; user-select: all; }
.cse-case-no { font-weight: 700; }
.pq-info-head { display: flex; align-items: center; justify-content: space-between; }
/* Always there, just quiet.
 *
 * These were `opacity: 0` until the block was hovered, which made the only way
 * to edit a case or a customer invisible until you happened to move the mouse
 * over the right area — and on a panel that is mostly read-only text, there is
 * no reason to. An affordance you have to discover by accident is not an
 * affordance. Muted at rest and full contrast on hover says "you can change
 * this" without competing with the values it sits beside. */
/* Centred by flex with the padding zeroed, not by a button's default text
   alignment. A button keeps 6px of side padding, which left these 26px squares
   with a 12px content box — narrower than the 13.3px glyphs in them. An
   overflowing inline box aligns to the start rather than centring, so the icon
   sat a subpixel right of centre, and display scaling rounds that up to a
   visible pixel. */
.pq-edit {
  background: transparent; border: 1px solid transparent; border-radius: 7px; cursor: pointer;
  color: var(--rbpg-muted); width: 26px; height: 26px; line-height: 1; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.45; transition: opacity .12s, background .12s, color .12s, border-color .12s;
}
/* A plain ASCII plus, sized up to match the pencil's weight. The fullwidth U+FF0B
   it replaces is absent from Inter, so it fell through to whichever CJK-capable
   font the machine happened to offer — a glyph nobody chose, at a width that
   varied by device. */
.pq-edit--add { font-size: 1.05rem; }
.pq-info-block:hover .pq-edit, .pq-edit:focus-visible { opacity: 1; }
.pq-edit:hover { opacity: 1; background: var(--rbpg-grey); color: var(--rbpg-text); border-color: var(--rbpg-border); }

/* The item name and its pencil. Baseline-aligned rather than centred: the name
   wraps to two lines on a long one and a centred button would drift into the
   middle of it. */
/* Only on the Estimate card, whose title is a reference with a revision blip
   beside it. The base `.pq-info-title` is also the case item's name, which wraps
   to two lines and must not be turned into a single row. */
.pq-info-block--est .pq-info-title { display: flex; align-items: center; gap: 0.45rem; }
/* Where the estimate stands, ruled off from the provenance above it. */
.pq-info-money { margin-top: 0.6rem; padding-top: 0.6rem; border-top: 1px solid var(--rbpg-border); }
.pq-info-money .pq-kv:last-child { margin-bottom: 0; }
.pq-info-title--act { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.5rem; }
.pq-info-title--act .pq-title-text { min-width: 0; overflow-wrap: anywhere; }
.pq-info-title--act .pq-edit { flex: 0 0 auto; margin-top: -2px; }

/* estimates block in the case rail — one row per estimate, then the money */
/* Edged at rest, not only on hover. The grey wash arrives when the pointer does,
   which left the rows reading as loose lines of text until you touched one — the
   dotted rule says "card" while it is being scanned. A shade darker than
   `--rbpg-border`: dots cover about half the edge, so the token that reads as a
   hairline when solid all but disappears when broken up. */
.pq-est {
  display: grid; grid-template-columns: 1fr auto; gap: 0.1rem 0.6rem; align-items: baseline;
  padding: 0.4rem 0.5rem; margin: 0 -0.5rem 0.35rem; border-radius: 8px;
  border: 1px dotted #ddd;
  text-decoration: none; color: inherit;
  transition: background .12s, border-color .12s;
}
.pq-est:hover { background: var(--rbpg-grey); border-color: var(--rbpg-border); }
/* The blip's own fill IS the hover fill, so it dissolved into the row exactly
   when the row was being pointed at. Inverted to the surface colour instead. */
.pq-est:hover .pq-est-rev { background: var(--rbpg-surface); }
.pq-est-ref { font-size: 0.85rem; font-weight: 600; color: var(--rbpg-text); }
.pq-est-total { font-size: 0.85rem; font-weight: 600; }
/* Which revision of the family this row is, opposite the reference. Quiet grey
   and no border so it never competes with the status below it — it is an
   identifier, not a state. A pill rather than a fixed circle: at R3 the two read
   the same, but a circle sized for two characters spills its background at R10. */
.pq-est-rev {
  justify-self: end; align-self: center;
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--rbpg-grey); color: var(--rbpg-muted);
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.02em;
  display: inline-flex; align-items: center; justify-content: center;
}
.pq-est-status {
  grid-column: 1 / -1; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--rbpg-muted);
}
/* Bare caps, no field behind them, so these take the ink step straight. */
.pq-est-status[data-status="approved"] { color: var(--tone-ok-ink); }
.pq-est-status[data-status="sent"], .pq-est-status[data-status="viewed"] { color: var(--tone-info-ink); }
.pq-est-status[data-status="rejected"], .pq-est-status[data-status="expired"] { color: var(--tone-bad-ink); }
/* An unsent revision on a family whose live row is the one shown. Full width
   under the status, because it is a different document from the one on the line
   above and must not read as part of it. */
.pq-est-note {
  grid-column: 1 / -1; font-size: 0.72rem; color: var(--tone-warn-ink, var(--rbpg-muted));
}
.pq-est-empty { font-size: 0.85rem; color: var(--rbpg-muted); margin-bottom: 0.5rem; }
#pq-estimates .pq-kv:first-of-type { margin-top: 0.7rem; padding-top: 0.7rem; border-top: 1px solid var(--rbpg-border); }

/* case cover image + camera */
.pq-cover { position: relative; border-radius: 12px; overflow: hidden; background: var(--rbpg-grey); margin-bottom: 0.7rem; aspect-ratio: 4 / 3; }
.pq-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pq-cover--empty { display: flex; align-items: center; justify-content: center; border: 1px dashed var(--rbpg-border); }
.pq-cover-ph { color: var(--rbpg-muted); font-size: 0.82rem; }
.pq-cover-cam {
  position: absolute; right: 8px; bottom: 8px; width: 34px; height: 34px; border-radius: 50%;
  border: 0; background: rgba(0,0,0,0.62); color: #fff; cursor: pointer; font-size: 0.95rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.pq-cover-cam:hover { background: rgba(0,0,0,0.82); }

.pq-more { background: transparent; border: 0; color: var(--rbpg-muted); font-weight: 600; font-size: 0.8rem; cursor: pointer; padding: 0.35rem 0; }
.pq-more:hover { color: var(--rbpg-text); }
.pq-more-body { margin-top: 0.3rem; }
.pq-kv--block { grid-template-columns: 1fr; gap: 0.15rem; }
.pq-kv--block .k { min-width: 0; }
.pq-kv--addr .v { display: block; }
.pq-addr { line-height: 1.35; }
.pq-add-addr { background: transparent; border: 1px dashed var(--rbpg-border); border-radius: 8px; color: var(--rbpg-text); font-size: 0.8rem; font-weight: 600; padding: 0.3rem 0.55rem; cursor: pointer; }
.pq-add-addr:hover { border-color: var(--rbpg-text); background: var(--rbpg-grey); }

/* Customer name → full customer page */
.pq-cust-link { color: var(--rbpg-text); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
.pq-cust-link:hover { text-decoration-thickness: 2px; }

/* In-button busy spinner (approve/send/apply) */
.pq-btn.is-busy { pointer-events: none; opacity: 0.85; }
.pq-btn-spin { display: inline-block; width: 13px; height: 13px; border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%; animation: pq-spin 0.7s linear infinite; vertical-align: -2px; }

/* Feed avatars + AI tag (Slice D) */
.pq-avatar { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; overflow: hidden; flex: 0 0 auto; vertical-align: middle; }
.pq-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Slightly smaller + tighter so two initials never touch the circle edge. */
.pq-avatar--init { font-size: 0.5rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1; color: #fff; background: hsl(var(--h, 210), 42%, 46%); }
.pq-avatar[data-ai="1"] { box-shadow: 0 0 0 1.5px #0d0d0d; }
.pq-msg-foot .pq-avatar { margin-left: 0.15rem; }
.pq-chip-ai { display: inline-flex; align-items: center; font-size: 0.6rem; font-weight: 700; letter-spacing: 0.04em; color: #fff; background: #0d0d0d; border-radius: 4px; padding: 0 4px; height: 14px; margin-left: 0.15rem; }

/* AI typing indicator (drafting a reply) */
.pq-msg--typing { opacity: 0.9; }
.pq-bubble--typing { display: inline-flex; align-items: center; gap: 0.5rem; }
.pq-bubble--typing .pq-bubble-pad { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.7rem; }
.pq-typing { display: inline-flex; align-items: center; gap: 3px; }
.pq-typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--rbpg-muted, #888); display: inline-block; animation: pq-typing-bounce 1.2s infinite ease-in-out; }
.pq-typing span:nth-child(2) { animation-delay: 0.18s; }
.pq-typing span:nth-child(3) { animation-delay: 0.36s; }
.pq-typing-txt { font-size: 0.76rem; color: var(--rbpg-muted, #777); }
@keyframes pq-typing-bounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.5; } 30% { transform: translateY(-3px); opacity: 1; } }

/* Composer lock — assistant owns the reply (prevents double-handling). */
.pq-ai-lock {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.9rem; border-top: 1px solid var(--rbpg-border);
  background: #f6f6f7; color: var(--rbpg-muted, #666); font-size: 0.8rem;
}
.pq-ai-lock-dots { flex: 0 0 auto; }
.pq-ai-lock-txt { flex: 1 1 auto; }
.pq-ai-lock-take {
  flex: 0 0 auto; font-size: 0.76rem; font-weight: 600; cursor: pointer;
  color: #0d0d0d; background: #fff; border: 1px solid var(--rbpg-border);
  border-radius: 999px; padding: 0.18rem 0.7rem;
}
.pq-ai-lock-take:hover { border-color: var(--rbpg-text); }
.pq-ai-lock-take:disabled { opacity: 0.6; cursor: default; }
.pq-composer--ailock > textarea { background: #fafafa; color: var(--rbpg-muted, #999); }

/* Message-center "AI working…" chip (assistant is drafting). */
.msg-chip--ai-working { color: #0d0d0d; background: #ececed; border: 1px solid #d9d9db; }

/* AIVERIE panel (P3-AIV-1) */
.pq-info-head { display: flex; align-items: center; gap: 0.5rem; }
.pq-info-head h4 { display: inline-flex; align-items: center; gap: 0.4rem; margin-bottom: 0.6rem; }
.pq-av .pq-info-head { justify-content: space-between; }
.pq-av-chip, .pq-av-tag { display: inline-flex; align-items: center; font-size: 0.68rem; color: var(--rbpg-muted); background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 999px; padding: 0.12rem 0.5rem; text-transform: uppercase; letter-spacing: 0.04em; }
.pq-av-chip { margin-bottom: 0.6rem; }
.pq-conf { display: inline-flex; align-items: center; font-size: 0.68rem; font-weight: 700; border-radius: 999px; padding: 0.1rem 0.44rem; letter-spacing: 0.02em; }
.pq-conf--hi { background: #123a20; color: #7ee2a0; }
.pq-conf--mid { background: #3a3312; color: #e6d27a; }
.pq-conf--lo { background: #3a1a12; color: #e89a7a; }
.pq-av-thumb { width: 64px; height: 64px; border-radius: 10px; overflow: hidden; background: var(--rbpg-grey); margin: 0 0 0.6rem; }
.pq-av-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pq-av-sub { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--rbpg-muted); font-weight: 600; margin: 0.7rem 0 0.4rem; }
/* Hairline-divided subsections inside the AIVERIE panel (mirror case sections). */
.pq-av-group + .pq-av-group { border-top: 1px solid var(--rbpg-border); margin-top: 0.9rem; padding-top: 0.9rem; }
.pq-av-group .pq-av-sub:first-child { margin-top: 0; }
.pq-av-sub--tight { margin-top: 0.6rem; }
.pq-av-tags { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.pq-av-tag { text-transform: none; letter-spacing: 0; color: var(--rbpg-text); }
.pq-av-miss { font-size: 0.8rem; color: var(--rbpg-muted); margin-top: 0.6rem; line-height: 1.4; }
.pq-av-links { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 0.7rem; font-size: 0.82rem; }
.pq-av-links a { font-weight: 600; }
.pq-av-timeline { display: flex; flex-direction: column; gap: 0.3rem; }
.pq-av-tl { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; }
.pq-av-tl .t { color: var(--rbpg-text); flex: 1 1 auto; }
.pq-av-note { font-size: 0.85rem; color: var(--rbpg-text); line-height: 1.45; margin: 0 0 0.7rem; }
.pq-av-muted { font-size: 0.82rem; color: var(--rbpg-muted); }
.pq-av-spin { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--rbpg-muted); line-height: 1.4; }
.pq-av--empty { display: flex; flex-direction: column; min-height: 140px; }
.pq-spinner { width: 14px; height: 14px; border: 2px solid var(--rbpg-border); border-top-color: var(--rbpg-text); border-radius: 50%; animation: pq-spin 0.8s linear infinite; flex: 0 0 auto; }
@keyframes pq-spin { to { transform: rotate(360deg); } }
.pq-btn { background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 8px; color: var(--rbpg-text); font-size: 0.82rem; font-weight: 600; padding: 0.4rem 0.7rem; cursor: pointer; align-self: flex-start; }
.pq-btn:hover { border-color: var(--rbpg-text); }
.pq-btn--primary { background: var(--rbpg-text); color: var(--rbpg-bg, #0d0d0d); border-color: var(--rbpg-text); }
.pq-btn--primary:hover { opacity: 0.9; }
.pq-link-btn { background: transparent; border: 0; color: var(--rbpg-text); font-weight: 700; font-size: 0.8rem; cursor: pointer; padding: 0; text-decoration: underline; }
.pq-sub-dull { font-size: 0.76rem; color: var(--rbpg-muted); margin-bottom: 0.4rem; }

/* footer actions grouping */
.pq-actions-context { display: inline-flex; gap: 0.5rem; }
.pq-actions-btn .pq-nav-key, .pq-foot .pq-nav-key { opacity: 0.8; }

/* ---- overlays (stage modal + action palette) ---- */
.pq-overlay { position: fixed; inset: 0; z-index: 3000; background: rgba(15,15,15,0.4); display: flex; align-items: center; justify-content: center; padding: 1rem; }
.pq-overlay--bottom { align-items: flex-end; justify-content: center; padding-bottom: 82px; }
/* The workspace Actions palette aligns its centering region to the content area
   (right of the sidebar) so the card sits directly above the footer Actions
   button, not offset toward the whole-viewport center. */
#rbpg-actions.pq-overlay { left: var(--rbpg-sidebar-w); }
@media (max-width: 991.98px) { #rbpg-actions.pq-overlay { left: 0; } }
.pq-overlay-card { background: #fff; border-radius: 14px; box-shadow: 0 24px 60px rgba(0,0,0,0.28); width: 100%; max-width: 420px; display: flex; flex-direction: column; max-height: 78vh; }
.pq-overlay-card--mid { max-width: 500px; }
.pq-overlay-head { display: flex; align-items: flex-start; justify-content: space-between; padding: 1rem 1.1rem 0.5rem; }
.pq-overlay-head h3 { font-size: 1.05rem; font-weight: 800; margin: 0; }
.pq-overlay-sub { font-size: 0.82rem; color: var(--rbpg-muted); margin: 0.15rem 0 0; }
.pq-overlay-x { background: transparent; border: 0; font-size: 1rem; color: var(--rbpg-muted); cursor: pointer; }

/* ---- AIVERIE run photo picker ---- */
.pq-av-pick-card { max-width: 680px; }
.pq-av-pick-body { overflow-y: auto; padding: 0.6rem 1.1rem 0.9rem; }
.pq-av-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); gap: 0.55rem; }
.pq-av-pick { position: relative; padding: 0; margin: 0; border: 2px solid var(--rbpg-border); border-radius: 10px; overflow: hidden; background: var(--rbpg-grey); cursor: pointer; aspect-ratio: 1 / 1; }
.pq-av-pick img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0.5; transition: opacity 0.12s ease; }
.pq-av-pick.is-on { border-color: var(--rbpg-text); }
.pq-av-pick.is-on img { opacity: 1; }
.pq-av-pick-check { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border-radius: 50%; background: var(--rbpg-text); color: #fff; display: none; align-items: center; justify-content: center; font-size: 0.78rem; }
.pq-av-pick.is-on .pq-av-pick-check { display: flex; }
.pq-av-pick-empty { color: var(--rbpg-muted); font-size: 0.88rem; line-height: 1.5; text-align: center; padding: 2rem 1rem; }
.pq-av-pick-foot { display: flex; align-items: center; gap: 0.8rem; padding: 0.8rem 1.1rem; border-top: 1px solid var(--rbpg-border); }
.pq-av-pick-foot .pq-btn { align-self: auto; }
.pq-av-pick-count { margin-left: auto; font-size: 0.8rem; color: var(--rbpg-muted); }
.pq-btn:disabled { opacity: 0.5; cursor: default; }

/* ---- AIVERIE brand tag in the picker title ---- */
.pq-av-brand {
  font-size: 0.6rem; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--rbpg-muted); border: 1px solid var(--rbpg-border); border-radius: 4px;
  padding: 0.05rem 0.32rem; margin-left: 0.45rem; vertical-align: middle; position: relative; top: -1px;
}

/* ---- AIVERIE run mode card (AUTO-8): Basic (default) vs Full ---- */
/* Sits below the photo grid with even breathing room above and below. Monotone. */
.pq-av-mode {
  display: flex; align-items: center; gap: 0.9rem; margin: 0.85rem 1.1rem 0.95rem;
  padding: 0.55rem 0.7rem; border: 1px solid var(--rbpg-border); border-radius: 8px;
  background: #fafafa; cursor: pointer; user-select: none;
}
.pq-av-mode input { position: absolute; opacity: 0; width: 0; height: 0; }
.pq-av-mode-sw {
  flex: 0 0 auto; margin-left: auto; width: 28px; height: 16px; border-radius: 999px;
  background: var(--rbpg-border); position: relative; transition: background 0.15s ease;
}
.pq-av-mode-sw::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 1px rgba(0,0,0,0.2);
  transition: transform 0.15s ease;
}
.pq-av-mode input:checked + .pq-av-mode-sw { background: var(--rbpg-text); }
.pq-av-mode input:checked + .pq-av-mode-sw::after { transform: translateX(12px); }
.pq-av-mode input:focus-visible + .pq-av-mode-sw { outline: 2px solid var(--rbpg-text); outline-offset: 2px; }
.pq-av-mode-txt { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.3; }
.pq-av-mode-txt strong { font-size: 0.82rem; }
.pq-av-mode-txt span { font-size: 0.73rem; color: var(--rbpg-muted); }
/* Fixed width so the label flip (Run basic ⇄ Run full) never shifts the layout. */
#pq-av-pick-run { min-width: 104px; text-align: center; }

/* ---- Upgrade banner (basic -> full) shown above the facts ---- */
.pq-av-upgrade {
  display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.7rem;
  padding: 0.6rem 0.75rem; border: 1px solid var(--rbpg-border); border-radius: 10px;
  background: linear-gradient(0deg, #faf7ff, #fbfbfb);
}
.pq-av-upgrade-txt { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.3; }
.pq-av-upgrade-txt strong { font-size: 0.82rem; }
.pq-av-upgrade-txt span { font-size: 0.74rem; color: var(--rbpg-muted); }
.pq-av-upgrade .pq-btn { margin-left: auto; }
.pq-av-chip--depth { color: var(--rbpg-text); background: #eef6ef; border-color: #cfe6d2; }
.pq-av-cust { font-size: 0.74rem; color: var(--rbpg-muted); margin: -0.15rem 0 0.5rem; font-style: italic; }
.pq-av-actions { margin-top: 0.7rem; display: flex; justify-content: flex-start; }

/* ---- AIVERIE per-photo view badges in the file gallery ---- */
.pq-file-views { position: absolute; top: 6px; left: 6px; display: flex; flex-wrap: wrap; gap: 3px; pointer-events: none; z-index: 2; }
.pq-file-view {
  font-size: 0.6rem; font-weight: 600; letter-spacing: 0.02em; line-height: 1;
  padding: 0.18rem 0.34rem; border-radius: 4px; color: #fff;
  background: rgba(20,20,20,0.72); backdrop-filter: blur(2px);
}
.pq-file-view--warn { background: rgba(203,32,39,0.85); }

/* ---- AI suggestion card (in-thread) ---- */
.pq-ai-card {
  margin: 0.6rem 0 0.2rem; padding: 0.85rem 0.95rem; border: 1px solid var(--rbpg-border);
  border-left: 3px solid var(--rbpg-text); border-radius: 12px; background: #fbfbfb;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}
.pq-ai-head { display: flex; align-items: center; gap: 0.5rem; }
.pq-ai-badge { font-size: 0.62rem; font-weight: 800; letter-spacing: 0.06em; background: var(--rbpg-text); color: #fff; border-radius: 5px; padding: 0.1rem 0.34rem; }
.pq-ai-agent { font-size: 0.82rem; font-weight: 700; }
.pq-ai-cat { font-size: 0.72rem; color: var(--rbpg-muted); text-transform: capitalize; }
.pq-ai-head-right { margin-left: auto; display: flex; align-items: center; gap: 0.4rem; }
.pq-ai-conf { font-size: 0.72rem; color: var(--rbpg-muted); border: 1px solid var(--rbpg-border); border-radius: 999px; padding: 0.05rem 0.45rem; }
/* Customer sentiment chip in the AI card header — emoji + label, colour by tier.
   Tight vertical padding so the emoji can be larger without inflating the chip. */
/* Flat, borderless pastel pills. Colour maps to valence (green=positive,
   amber=caution, red=negative); the neutral baseline stays a calm slate so the
   eye is only drawn to tones that actually need attention. */
.pq-ai-tone { display: inline-flex; align-items: center; gap: 4px; font-size: 0.68rem; font-weight: 600; border-radius: 999px; padding: 0.06rem 0.46rem 0.06rem 0.36rem; border: none; }
.pq-ai-tone-emo { font-size: 1.25em; line-height: 1; }
.pq-ai-tone > :not(.pq-ai-tone-emo) { margin-right: 1px; }
.pq-ai-tone--pos  { background: #d1fadf; color: #05603a; }
.pq-ai-tone--warn { background: #fdf1c7; color: #8a5a00; }
.pq-ai-tone--bad  { background: #fdd8d8; color: #a81f1f; }
.pq-ai-tone--neutral { background: #eef1f6; color: #5b6675; }

/* Admin re-run: a quiet, muted test-tool button (left of Dismiss). */
.pq-ai-rerun { color: var(--rbpg-muted); }
.pq-ai-rerun:hover { color: var(--rbpg-text); }

/* Sentiment as a status pill on the right of the name row (a health signal, not
   a data field). Name may wrap/truncate; the pill holds the top-right. */
.pq-info-title--mood { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.5rem; }
.pq-info-title--mood .pq-title-text { min-width: 0; overflow-wrap: anywhere; }
.pq-info-title--mood .pq-ai-tone { flex: 0 0 auto; margin-top: 1px; }
.pq-ai-summary { font-size: 0.82rem; color: var(--rbpg-text); margin: 0.5rem 0 0.2rem; line-height: 1.4; }
.pq-ai-label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); margin: 0.6rem 0 0.25rem; }
.pq-ai-reply {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 90px;
  border: 1px solid var(--rbpg-border); border-radius: 8px; padding: 0.55rem 0.65rem;
  font: inherit; font-size: 0.85rem; line-height: 1.45; color: var(--rbpg-text); background: #fff;
}
.pq-ai-reply:focus { outline: none; border-color: var(--rbpg-text); }
.pq-ai-actions { margin-top: 0.6rem; border-top: 1px solid var(--rbpg-border); padding-top: 0.55rem; }
.pq-ai-actions-h { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); margin-bottom: 0.35rem; }
.pq-ai-act { display: flex; align-items: center; gap: 0.45rem; font-size: 0.82rem; padding: 0.15rem 0; cursor: pointer; }
.pq-ai-act-conf { color: var(--rbpg-muted); font-size: 0.72rem; }
.pq-ai-foot { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.7rem; }
.pq-ai-spacer { flex: 1; }

/* ---- AI card: detected-updates + actions rows ---- */
.pq-ai-sec { margin-top: 0.7rem; border-top: 1px solid var(--rbpg-border); padding-top: 0.55rem; }
.pq-ai-sec-h { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); margin-bottom: 0.4rem; }

/* Internal-only "why this reply" brief panel (doc 35) — admin, collapsed */
.pq-ai-brief { margin-top: 0.6rem; border-top: 1px dashed var(--rbpg-border); padding-top: 0.5rem; }
.pq-ai-brief > summary { cursor: pointer; font-size: 0.7rem; color: var(--rbpg-muted); letter-spacing: 0.03em; list-style: none; }
.pq-ai-brief > summary::-webkit-details-marker { display: none; }
.pq-ai-brief > summary::before { content: '\25B8'; display: inline-block; margin-right: 0.3rem; transition: transform 0.15s; }
.pq-ai-brief[open] > summary::before { transform: rotate(90deg); }
.pq-brief-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.45rem 0 0.35rem; }
.pq-brief-chip { font-size: 0.62rem; padding: 0.05rem 0.35rem; border-radius: 999px; background: rgba(0,0,0,0.06); color: var(--rbpg-muted); }
.pq-brief-chip--warn { background: rgba(180,83,9,0.12); color: #b45309; }
.pq-brief-chip--esc { background: #0d0d0d; color: #fff; }
.pq-brief-list { list-style: none; margin: 0.2rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.pq-brief-list li { display: flex; align-items: flex-start; gap: 0.4rem; font-size: 0.76rem; color: var(--rbpg-text); line-height: 1.35; }
.pq-brief-tag { flex: 0 0 auto; font-size: 0.56rem; font-weight: 700; letter-spacing: 0.04em; padding: 0.05rem 0.28rem; border-radius: 4px; margin-top: 0.08rem; color: #fff; }
.pq-brief-tag--say { background: #2563eb; }
.pq-brief-tag--ask { background: #7c3aed; }

.pq-ai-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0; }
.pq-ai-row + .pq-ai-row { border-top: 1px dashed var(--rbpg-border); }
.pq-ai-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.pq-ai-row-t { font-size: 0.83rem; font-weight: 600; color: var(--rbpg-text); }
.pq-ai-row-v { font-size: 0.8rem; color: var(--rbpg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-ai-done { font-size: 0.8rem; color: #16a34a; font-weight: 600; white-space: nowrap; }
.pq-btn--sm { padding: 0.28rem 0.6rem; font-size: 0.78rem; white-space: nowrap; }

/* ---- Simulate inbound modal ---- */
.pq-sim-body { padding: 0.8rem 1.1rem; }
.pq-sim-body textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 96px;
  border: 1px solid var(--rbpg-border); border-radius: 8px; padding: 0.6rem 0.7rem;
  font: inherit; font-size: 0.9rem; line-height: 1.45; color: var(--rbpg-text);
}
.pq-sim-body textarea:focus { outline: none; border-color: var(--rbpg-text); }

/* ---- AI entity blips (in-thread, doc 34) ---- */
.pq-ent {
  cursor: pointer; border-radius: 3px; padding: 0 1px;
  text-decoration: underline dotted; text-underline-offset: 2px;
  text-decoration-thickness: 1.5px; text-decoration-color: #6366f1;
  background: rgba(99, 102, 241, 0.08); transition: background 0.12s ease;
}
.pq-ent:hover { background: rgba(99, 102, 241, 0.18); }
.pq-ent--phone { text-decoration-color: #0891b2; background: rgba(8, 145, 178, 0.08); }
.pq-ent--phone:hover { background: rgba(8, 145, 178, 0.18); }
.pq-ent.is-confirm { text-decoration-color: #d97706; background: rgba(217, 119, 6, 0.1); }
.pq-ent.is-confirm:hover { background: rgba(217, 119, 6, 0.2); }
.pq-ent.is-applied {
  text-decoration-color: #16a34a; background: rgba(22, 163, 74, 0.08); cursor: default;
}
.pq-msg--out .pq-ent { text-decoration-color: rgba(255,255,255,0.7); background: rgba(255,255,255,0.14); }

/* ---- AI entity compare modal ---- */
.pq-ent-body { padding: 0.8rem 1.1rem; }
.pq-ent-cmp { display: grid; grid-template-columns: 1fr 1fr; gap: 0.7rem; }
.pq-ent-col {
  border: 1px solid var(--rbpg-border); border-radius: 10px; padding: 0.6rem 0.7rem; background: #fbfbfb;
}
.pq-ent-col--new { border-color: #6366f1; background: rgba(99, 102, 241, 0.05); }
.pq-ent-h { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); margin-bottom: 0.3rem; }
.pq-ent-val { font-size: 0.86rem; line-height: 1.45; color: var(--rbpg-text); }
.pq-ent-none { color: var(--rbpg-muted); font-style: italic; }

/* ---- AIVERIE suggested details (P3-AIV-2) ----
   One row per detail, and the two values ARE the control: two cards, pick one. A
   tickbox beside two panels asked the question twice — the panels showed the choice,
   then a checkbox elsewhere decided it. Radio inputs underneath, so arrow keys work
   and the thing is one question with two answers. Shared by the cockpit and the
   estimate builder through lib/rbpg-fact-suggest.js. */
.pq-sug-body { padding: 0.4rem 1.1rem 0.8rem; overflow-y: auto; }
.pq-sug-row { padding: 0.75rem 0; border-bottom: 1px solid var(--rbpg-border); }
.pq-sug-row:last-child { border-bottom: 0; }
.pq-sug-label { font-size: 0.86rem; font-weight: 700; margin-bottom: 0.4rem; }
.pq-sug-cmp { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
.pq-sug-card {
  display: block; border: 1px solid var(--rbpg-border); border-radius: 10px;
  padding: 0.45rem 0.6rem 0.55rem; background: #fff; min-width: 0; cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.pq-sug-card:hover { border-color: #b8b8b8; }
/* Chosen: a second inner line rather than a fill, so the value stays the most
   legible thing in the card and no tinted background is needed to say "selected". */
.pq-sug-card.is-on { border-color: var(--rbpg-text); box-shadow: inset 0 0 0 1px var(--rbpg-text); }
.pq-sug-h {
  display: flex; align-items: center; gap: 0.35rem; font-size: 0.66rem;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); margin-bottom: 0.2rem;
}
.pq-sug-card.is-on .pq-sug-h { color: var(--rbpg-text); }
.pq-sug-h input[type="radio"] { width: 13px; height: 13px; margin: 0; accent-color: var(--rbpg-text); cursor: pointer; }
.pq-sug-val { display: block; font-size: 0.86rem; line-height: 1.4; word-break: break-word; }
.pq-sug-held { font-size: 0.74rem; font-weight: 500; color: var(--rbpg-muted); }
.pq-sug-input {
  display: block; width: 100%; border: 0; padding: 0; background: transparent; font: inherit;
  font-size: 0.86rem; line-height: 1.4; color: var(--rbpg-text); outline: none;
  resize: none; overflow: hidden;
}
.pq-sug-input:focus { box-shadow: 0 1px 0 0 var(--rbpg-text); }
/* Already dealt with, still shown — dimmer, because it is here to be recoverable
   rather than to be asked again. */
.pq-sug-row.is-set-aside { opacity: 0.72; }
.pq-sug-row.is-set-aside:hover, .pq-sug-row.is-set-aside:focus-within { opacity: 1; }
/* Notes under a label. Grey, not amber: a caveat about the evidence is information,
   and washing the row in colour made it look like a fault. */
.pq-sug-note { margin: 0 0 0.4rem; font-size: 0.76rem; line-height: 1.45; color: var(--rbpg-muted); }
.pq-sug-note--care { color: #4a4a4a; font-weight: 600; }
.pq-sug-none { margin-top: 0.45rem; font-size: 0.78rem; }
/* A write that failed says so next to the button that failed, since there is no
   toast to carry it. It takes the slack in the row rather than squeezing the
   buttons, which otherwise wrap their labels to make room for the bad news. */
.pq-sug-err { flex: 1 1 auto; min-width: 0; font-size: 0.76rem; line-height: 1.35; color: #9d1c1c; align-self: center; }
.pq-sug-body ~ .pq-av-pick-foot .pq-btn { white-space: nowrap; }
/* The permanent way back in, in the AIVERIE panel, after a banner is dismissed. */
.pq-av-upgrade--sug { border-style: solid; }
@media (max-width: 560px) {
  .pq-sug-cmp { grid-template-columns: 1fr; }
}
.pq-ent-e164 { color: var(--rbpg-muted); font-size: 0.78rem; }

/* Phone: mobile/landline segmented toggle + SMS hint */
.pq-phone-kind { display: flex; align-items: center; flex-wrap: wrap; gap: 0.55rem; margin-top: 0.85rem; }
.pq-phone-kind-lbl { font-size: 0.8rem; color: var(--rbpg-muted); }
.pq-seg { display: inline-flex; border: 1px solid var(--rbpg-border); border-radius: 8px; overflow: hidden; }
.pq-seg-btn {
  border: 0; background: #fff; color: var(--rbpg-text); cursor: pointer;
  font-size: 0.8rem; padding: 0.32rem 0.7rem; line-height: 1;
}
.pq-seg-btn + .pq-seg-btn { border-left: 1px solid var(--rbpg-border); }
.pq-seg-btn.is-on { background: #0d0d0d; color: #fff; }
.pq-phone-sms { flex-basis: 100%; font-size: 0.76rem; margin-top: 0.15rem; }
.pq-phone-sms.is-ok { color: #15803d; }
.pq-phone-sms.is-warn { color: #b45309; }
.pq-phone-sms.is-muted { color: var(--rbpg-muted); }

.pq-stage-list { overflow-y: auto; padding: 0.5rem 0.7rem 0.9rem; }
.pq-stage-item {
  display: flex; align-items: center; gap: 0.6rem; width: 100%; text-align: left; cursor: pointer;
  background: transparent; border: 1px solid transparent; border-radius: 10px; padding: 0.6rem 0.7rem; margin-bottom: 0.2rem;
}
.pq-stage-item:hover, .pq-stage-item:focus-visible { background: var(--rbpg-grey); outline: none; }
.pq-stage-item.current { border-color: var(--rbpg-text); background: #fff; }
.pq-stage-item-main { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.pq-stage-item-label { font-weight: 700; font-size: 0.92rem; }
.pq-stage-item-meta { font-size: 0.72rem; color: var(--rbpg-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.pq-stage-count { background: var(--rbpg-grey); border-radius: 999px; font-size: 0.75rem; font-weight: 700; padding: 0.1rem 0.5rem; color: var(--rbpg-muted); }
.pq-stage-here { font-size: 0.68rem; font-weight: 800; text-transform: uppercase; color: #dc2626; }

.pq-palette-card { background: #fff; border-radius: 14px; box-shadow: 0 24px 60px rgba(0,0,0,0.3); width: 100%; max-width: 520px; display: flex; flex-direction: column; overflow: hidden; position: relative; }
/* Busy overlay: shown while an async action (e.g. status change) is in flight, so
   the palette acknowledges the click instead of vanishing instantly. */
.pq-palette-busy { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; gap: 0.6rem; background: rgba(255,255,255,0.82); backdrop-filter: blur(1px); font-size: 0.9rem; font-weight: 700; color: var(--rbpg-text); }
.pq-palette-spin { width: 18px; height: 18px; border: 2px solid var(--rbpg-border); border-top-color: var(--rbpg-text); border-radius: 50%; animation: pq-palette-spin 0.7s linear infinite; }
@keyframes pq-palette-spin { to { transform: rotate(360deg); } }
.pq-palette-input { border: 0; border-bottom: 1px solid var(--rbpg-border); padding: 0.85rem 1rem; font-size: 0.95rem; outline: none; }
.pq-palette-list { max-height: 320px; overflow-y: auto; overscroll-behavior: contain; padding: 0.4rem; }
.pq-palette-item { display: flex; align-items: center; gap: 0.7rem; width: 100%; text-align: left; cursor: pointer; background: transparent; border: 0; border-radius: 8px; padding: 0.6rem 0.7rem; font-size: 0.9rem; }
.pq-palette-item.sel { background: var(--rbpg-text); color: #fff; }
.pq-palette-ico { width: 20px; flex: 0 0 auto; text-align: center; }
/* An inline-SVG icon (`iconHtml`) inherits the row's colour, so it flips to white
   with the selected row instead of staying dark on dark like an emoji does. */
.pq-palette-ico svg { display: block; margin: 0 auto; color: inherit; }
.pq-palette-empty { color: var(--rbpg-muted); font-size: 0.88rem; padding: 1rem; text-align: center; }
/* A view fetching its rows. The spinner sits with the word rather than replacing
   the card, because the palette is still usable while it loads. */
.pq-palette-loading { display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
.pq-palette-foot { border-top: 1px solid var(--rbpg-border); padding: 0.45rem 1rem; font-size: 0.72rem; color: var(--rbpg-muted); }
.pq-palette-group { padding: 0.55rem 0.7rem 0.25rem; font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); }
.pq-palette-text { display: flex; flex-direction: column; min-width: 0; }
.pq-palette-label { line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-palette-sub { font-size: 0.76rem; color: var(--rbpg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-palette-item.sel .pq-palette-sub { color: rgba(255,255,255,0.72); }
/* Paged palette: header breadcrumb + back, and the "opens a sub-view" chevron. */
.pq-palette-head { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem 0.85rem 0; }
.pq-palette-back { border: 0; background: transparent; cursor: pointer; font-size: 1.2rem; line-height: 1; color: var(--rbpg-muted); padding: 0.1rem 0.45rem; border-radius: 7px; }
.pq-palette-back:hover { background: var(--rbpg-surface-2, #f4f4f5); color: var(--rbpg-text); }
.pq-palette-crumb { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--rbpg-muted); }
.pq-palette-more { margin-left: auto; color: var(--rbpg-muted); font-size: 1rem; line-height: 1; }
.pq-palette-item.sel .pq-palette-more { color: rgba(255,255,255,0.72); }
.pq-picker-title { padding: 0.7rem 1rem 0; font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--rbpg-muted); }

/* Footer */
.pq-foot {
  display: flex; align-items: center; gap: 1rem;
  height: 66px; padding: 0 1.1rem;
  background: var(--rbpg-surface); border-top: 1px solid var(--rbpg-border);
}
.pq-foot .pq-actions { margin: 0 auto; display: flex; gap: 0.6rem; align-items: center; }
.pq-nav-key { font-size: 0.68rem; color: var(--rbpg-muted); margin-left: 0.35rem; }

/* ---- right sidebar tabs (Case | Files) ---- */
.pq-info { padding: 0; display: flex; flex-direction: column; }
.pq-tabs { position: sticky; top: 0; z-index: 5; display: flex; align-items: stretch; flex: 0 0 auto; background: var(--rbpg-surface); border-bottom: 1px solid var(--rbpg-border); padding: 0 0.6rem; }
.pq-tab { flex: 0 0 auto; background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 0.8rem 0.7rem; font-size: 0.8rem; font-weight: 700; color: var(--rbpg-muted); cursor: pointer; text-transform: uppercase; letter-spacing: 0.04em; }
.pq-tab:hover { color: var(--rbpg-text); }
.pq-tab.active { color: var(--rbpg-text); border-bottom-color: var(--rbpg-text); }
.pq-tab-count { color: var(--rbpg-muted); font-weight: 600; letter-spacing: 0; }
.pq-tab-add { margin-left: auto; align-self: center; width: 28px; height: 28px; border-radius: 7px; border: 1px solid var(--rbpg-border); background: var(--rbpg-grey); color: var(--rbpg-muted); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.pq-tab-add:hover { border-color: var(--rbpg-text); color: var(--rbpg-text); }
.pq-tab-add svg { display: block; }
.pq-tab-panel { padding: 1.1rem; flex: 1 1 auto; min-height: 0; }
#pq-panel-files { display: flex; flex-direction: column; }

/* ---- QC: the reports tab, and the timeline card (QC epic) ----
   Here rather than in qc.css because these two render on the case cockpit and
   the estimate builder, which do not load the bench view's stylesheet. Built
   from the existing .pq- vocabulary — no new buttons, no new colours. */
.qc-list { display: flex; flex-direction: column; }
.qc-list-row {
  display: flex; align-items: flex-start; gap: 0.6rem; width: 100%;
  padding: 0.6rem 0; border: 0; border-bottom: 1px solid var(--rbpg-border);
  background: transparent; text-align: left; cursor: pointer;
  color: inherit; text-decoration: none;
}
.qc-list-row:last-child { border-bottom: 0; }
.qc-list-row:hover .qc-list-title { text-decoration: underline; }
.qc-list-text { display: flex; flex-direction: column; min-width: 0; }
.qc-list-title { font-size: 0.85rem; font-weight: 700; }
.qc-list-sub { font-size: 0.75rem; color: var(--rbpg-muted); }
/* The dot is the whole status language in the rail: four states, no text needed
   until you read the line under it. */
.qc-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 999px; margin-top: 0.32rem; background: #16a34a; }
.qc-dot--warning { background: #f59e0b; }
.qc-dot--alert { background: #dc2626; }
.qc-dot--skipped { background: var(--rbpg-muted); }
/* Unfinished, and the two ways of being unfinished: hollow for a draft somebody
   is part-way through, amber for one deliberately left open. */
.qc-dot--open { background: transparent; box-shadow: inset 0 0 0 2px var(--rbpg-muted); }
.qc-dot--parked { background: #f59e0b; }
.qc-list-row--open .qc-list-title { color: #b45309; }
.qc-list-go {
  margin-left: auto; align-self: center; flex: 0 0 auto;
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: #b45309;
}
.qcq-verdict { display: inline-flex; align-items: center; gap: .45rem; font-weight: 600; font-size: .85rem; }
.qcq-verdict .qc-dot { margin-top: 0; }

.pq-qc {
  align-self: flex-start;
  /* .pq-thread is a column flex container, so a tall card is a shrinkable item:
     it gets squeezed below its content height when the thread overflows. The
     other card types survive that because the overspill simply draws outside
     them — this one clips, to keep the cover photo inside the rounded corners,
     so what got squeezed off was the View report button. */
  flex: 0 0 auto;
  max-width: 460px;
  margin: 0.5rem 0;
  padding: 0.75rem 0.9rem;
  background: var(--rbpg-surface);
  border: 1px solid var(--rbpg-border);
  border-left: 3px solid #16a34a;
  border-radius: 10px;
}
.pq-qc--warning { border-left-color: #f59e0b; }
.pq-qc--alert { border-left-color: #dc2626; }
/* Paused is not a verdict. Amber like a warning, but dashed, so a card that has
   not finished saying anything cannot be mistaken at a glance for one that has. */
.pq-qc--paused { border-left-color: #f59e0b; border-left-style: dashed; }
/* With a photograph the card leads with it, edge to edge. The padding moves off
   the card and onto the blocks below the image, which is why this is a modifier
   rather than a margin on the image. */
.pq-qc--shot { padding: 0 0 .75rem; overflow: hidden; }
.pq-qc--shot > *:not(.pq-qc-cover) { padding-left: .9rem; padding-right: .9rem; }
.pq-qc-cover {
  display: block; width: 100%; padding: 0; border: 0; margin: 0 0 .7rem;
  background: var(--rbpg-grey); cursor: pointer;
}
.pq-qc-cover img { display: block; width: 100%; height: 150px; object-fit: cover; }
.pq-qc-top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.pq-qc-title { font-size: 0.85rem; font-weight: 800; }
.pq-qc-pill { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--rbpg-muted); }
.pq-qc--warning .pq-qc-pill { color: #b45309; }
.pq-qc--alert .pq-qc-pill { color: #b91c1c; }
.pq-qc--paused .pq-qc-pill { color: #b45309; }
.pq-qc-why { margin: 0.45rem 0 0; font-size: 0.78rem; color: var(--rbpg-text); }
.pq-qc-meta { display: flex; align-items: center; gap: 0.35rem; font-size: 0.72rem; color: var(--rbpg-muted); margin-top: 0.25rem; }
.pq-qc-meta .pq-avatar { width: 20px; height: 20px; }
.pq-qc-meta .pq-avatar--init { font-size: 0.55rem; }
.pq-qc-steps { list-style: none; margin: 0.6rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.pq-qc-steps li { display: flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; color: var(--rbpg-text); }
.pq-qc-steps li.is-bad { color: #b91c1c; font-weight: 600; }
/* The sections nobody answered, rolled into one line. Muted and unticked: a tick
   claims somebody looked. */
.pq-qc-steps li.is-quiet { color: var(--rbpg-muted); font-size: 0.74rem; padding-left: 1.05rem; }
.pq-qc-steps svg { flex: 0 0 auto; width: 13px; height: 13px; color: #16a34a; }
.pq-qc-steps li.is-bad svg { color: #dc2626; }
.pq-qc-actions { margin-top: 0.6rem; }
/* rbpg-theme defines .btn and .btn-lg but not .btn-sm, and it loads after
   Bootstrap — so `btn-sm` in the markup is overridden back to full size, here
   and everywhere else in the app. Sized locally rather than by adding the
   missing .btn-sm, which would resize every small button on every page. */
.pq-qc-actions .btn { padding: 0.4rem 0.85rem; font-size: 0.8rem; }

/* ---- QC report viewer (RBPGQCView) ----
   A document, over whatever you were doing. Deliberately plain: this is the
   thing that gets printed and the thing that gets quoted back months later. */
.qcv { align-items: center; justify-content: center; }
.qcv[hidden] { display: none; }
.qcv-card {
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.28);
  width: 100%;
  max-width: 720px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  position: relative;
}
.qcv-x {
  position: absolute; top: .5rem; right: .7rem;
  background: transparent; border: 0; line-height: 1;
  font-size: 1.6rem; color: var(--rbpg-muted); cursor: pointer;
}
.qcv-x:hover { color: var(--rbpg-text); }
.qcv-body { overflow-y: auto; padding: 1.6rem 1.8rem .5rem; }
.qcv-load { display: flex; justify-content: center; padding: 3rem 0; }
.qcv-fail { color: #b91c1c; padding: 2rem 0; text-align: center; }
.qcv-foot {
  display: flex; justify-content: flex-end; gap: .5rem;
  padding: .9rem 1.3rem; border-top: 1px solid var(--rbpg-border);
}

.qcv-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding-right: 1.6rem; }
.qcv-head h2 { font-size: 1.2rem; font-weight: 800; margin: 0; }
.qcv-sub { margin: .15rem 0 0; font-size: .82rem; color: var(--rbpg-muted); }
.qcv-verdict {
  flex: 0 0 auto; font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
  padding: .25rem .55rem; border-radius: 999px; background: #dcfce7; color: #166534;
}
.qcv-verdict--warning { background: #fef3c7; color: #92400e; }
.qcv-verdict--alert { background: #fee2e2; color: #991b1b; }
.qcv-verdict--skipped { background: var(--rbpg-grey); color: var(--rbpg-muted); }
.qcv-signed { display: flex; align-items: center; gap: .5rem; margin: .9rem 0 0; font-size: .78rem; }
.qcv-face { flex: 0 0 auto; width: 28px; height: 28px; font-size: .68rem; }
.qcv-signed-who { display: block; color: var(--rbpg-text); font-weight: 600; }
.qcv-signed-when { display: block; font-size: .72rem; color: var(--rbpg-muted); }
.qcv-skip { margin: .3rem 0 0; font-size: .82rem; color: #92400e; }

.qcv-step { margin-top: 1.5rem; }
.qcv-step h3 {
  font-size: .72rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  color: var(--rbpg-muted); margin: 0 0 .6rem; padding-bottom: .35rem;
  border-bottom: 1px solid var(--rbpg-border);
}
.qcv-qa { padding: .6rem 0; border-bottom: 1px solid var(--rbpg-border); }
.qcv-qa:last-child { border-bottom: 0; }
.qcv-q { font-size: .85rem; color: var(--rbpg-text); }
.qcv-ans { margin-top: .25rem; }
.qcv-a { font-size: .88rem; font-weight: 700; color: #166534; }
.qcv-a--yes { color: #166534; }
.qcv-a--warning { color: #b45309; }
.qcv-a--alert { color: #b91c1c; }
.qcv-a--blank { color: var(--rbpg-muted); font-weight: 600; }
.qcv-blank { font-size: .85rem; color: var(--rbpg-muted); font-style: italic; }
.qcv-free { font-size: .88rem; }
/* What a corrected fact replaced, said quietly beside the answer: it modifies
   the answer rather than being it. */
.qcv-was { margin-left: .4rem; font-size: .78rem; color: var(--rbpg-muted); text-decoration: line-through; }
.qcv-said {
  margin: .3rem 0 0; padding-left: .7rem;
  border-left: 2px solid var(--rbpg-border-strong);
  font-size: .84rem; white-space: pre-wrap;
}
/* What they wrote, quoted — so it is legible as the operator's words rather
   than as more of the form. */
.qcv-note {
  margin: .4rem 0 0; padding-left: .7rem;
  border-left: 2px solid var(--rbpg-border);
  font-size: .84rem; color: var(--rbpg-text);
}
.qcv-lines { list-style: none; margin: .2rem 0 0; padding: 0; }
.qcv-lines li { display: flex; justify-content: space-between; gap: 1rem; padding: .2rem 0; font-size: .85rem; }
/* The conclusion of an assessment, printed as two lists rather than fourteen
   sections holding one line each. Same required/optional language as the chips
   that recorded it and the estimate it feeds. */
.qcv-work-group { margin: 0 0 .9rem; }
.qcv-work-group h4 {
  font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  margin: 0 0 .35rem; color: var(--rbpg-text);
}
.qcv-work-group--optional h4 { color: #92400e; }
.qcv-work-list { list-style: none; margin: 0; padding: 0; }
.qcv-work-list li {
  padding: .28rem 0 .28rem .85rem; font-size: .88rem; position: relative;
  border-bottom: 1px dashed var(--rbpg-border);
}
.qcv-work-list li:last-child { border-bottom: 0; }
.qcv-work-list li::before {
  content: ''; position: absolute; left: 0; top: .72rem;
  width: 5px; height: 5px; border-radius: 50%; background: var(--rbpg-text);
}
.qcv-work-group--optional .qcv-work-list li::before { background: #d97706; }
.qcv-work-note { margin-top: .7rem; }
.qcv-work-note-q { font-size: .72rem; font-weight: 700; color: var(--rbpg-muted); }

.qcv-shots { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .6rem; }
.qcv-shots img { width: 92px; height: 92px; object-fit: cover; border-radius: 8px; border: 1px solid var(--rbpg-border); display: block; }

/* Printing the overlay prints the REPORT, not the page behind it. This is what
   the Download PDF button relies on. */
@media print {
  body.qcv-open > *:not(.qcv) { display: none !important; }
  body.qcv-open .qcv { position: static; background: none; padding: 0; }
  body.qcv-open .qcv-card { box-shadow: none; max-height: none; max-width: none; }
  body.qcv-open .qcv-body { overflow: visible; }
  body.qcv-open .qcv-x, body.qcv-open .qcv-foot { display: none !important; }
}

/* ---- files grid + drop zone ---- */
.pq-files-zone { position: relative; flex: 1 1 auto; min-height: 0; }
.pq-files-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem; align-content: start; }
.pq-files-zone--empty { display: flex; align-items: center; justify-content: center; text-align: center; border: 1px dashed var(--rbpg-border); border-radius: 12px; }
.pq-files-empty-inner { color: var(--rbpg-muted); padding: 1.2rem; display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.pq-files-empty-inner svg { color: var(--rbpg-border); margin-bottom: 0.3rem; }
.pq-files-empty-title { font-size: 0.9rem; font-weight: 700; color: var(--rbpg-text); margin: 0; }
.pq-files-empty-sub { font-size: 0.8rem; margin: 0; line-height: 1.5; }
/* Drop-to-upload overlay — only visible while dragging OS files over the zone. */
.pq-files-overlay { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; background: rgba(255,255,255,0.82); border: 2px dashed var(--rbpg-text); border-radius: 12px; z-index: 6; }
.pq-files-zone.pq-drop-over .pq-files-overlay { display: flex; }
.pq-files-overlay-card { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; font-size: 0.9rem; font-weight: 700; color: var(--rbpg-text); }
.pq-file { position: relative; border: 1px solid var(--rbpg-border); border-radius: 11px; overflow: hidden; background: #fff; display: flex; flex-direction: column; transition: border-color .12s, box-shadow .12s; }
.pq-file:hover { border-color: #d0d0d3; box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
.pq-file--cover { border-color: var(--rbpg-text); }
.pq-file-cover-badge { position: absolute; top: 5px; left: 5px; z-index: 2; background: var(--rbpg-text); color: #fff; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.12rem 0.4rem; border-radius: 6px; pointer-events: none; }
.pq-file-thumb { border: 0; background: var(--rbpg-grey); padding: 0; cursor: pointer; aspect-ratio: 4 / 3; display: block; width: 100%; overflow: hidden; position: relative; }
.pq-file-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pq-file-thumb--doc { display: flex; align-items: center; justify-content: center; }
.pq-file-fallback { position: absolute; inset: 0; align-items: center; justify-content: center; color: var(--rbpg-muted); background: var(--rbpg-grey); }
.pq-file-thumb--doc .pq-file-fallback { position: static; width: 100%; height: 100%; }
.pq-file-fallback .pq-glyph { filter: drop-shadow(0 1px 2px rgba(0,0,0,0.06)); }
/* play badge overlaid on video tiles */
.pq-file-play { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 30px; height: 30px; border-radius: 50%; background: rgba(0,0,0,0.55); pointer-events: none; }
.pq-file-play::after { content: ""; position: absolute; left: 52%; top: 50%; transform: translate(-50%,-50%); border-style: solid; border-width: 6px 0 6px 10px; border-color: transparent transparent transparent #fff; }
.pq-file-meta { padding: 0.45rem 0.55rem 0.55rem; min-width: 0; }
.pq-file-name { display: block; font-size: 0.76rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-file-sub { display: flex; align-items: center; gap: 0.25rem; font-size: 0.64rem; color: var(--rbpg-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.pq-file-dot { opacity: 0.6; }
/* three-dot menu button (top-right of the card) */
.pq-file-menu-btn { position: absolute; top: 5px; right: 5px; width: 26px; height: 26px; border-radius: 7px; border: 0; background: rgba(255,255,255,0.86); color: var(--rbpg-text); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; opacity: 0; transition: opacity .12s, background .12s; box-shadow: 0 1px 3px rgba(0,0,0,0.18); }
.pq-file:hover .pq-file-menu-btn, .pq-file-menu-btn:focus-visible { opacity: 1; }
.pq-file-menu-btn:hover { background: #fff; }
.pq-file-menu-btn svg { display: block; }
@media (hover: none) { .pq-file-menu-btn { opacity: 1; } }

/* body-mounted file action menu */
.pq-file-menu { position: fixed; z-index: 3500; background: #fff; border: 1px solid var(--rbpg-border); border-radius: 10px; box-shadow: 0 12px 34px rgba(0,0,0,0.18); padding: 0.3rem; min-width: 180px; }
.pq-fm-item { display: block; width: 100%; text-align: left; background: transparent; border: 0; border-radius: 7px; padding: 0.55rem 0.65rem; font-size: 0.85rem; color: var(--rbpg-text); cursor: pointer; text-decoration: none; }
.pq-fm-item:hover { background: var(--rbpg-grey); }
.pq-fm-item--danger { color: #b91c1c; }
.pq-fm-item--danger:hover { background: #fbeded; }

/* ---- attachment strips under thread bubbles ---- */
.pq-atts { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.4rem; }
.pq-msg--out .pq-atts { justify-content: flex-end; }
.pq-att { border: 1px solid var(--rbpg-border); border-radius: 10px; background: #fff; cursor: pointer; padding: 0; overflow: hidden; }
.pq-att--img { width: 118px; height: 118px; }
.pq-att--img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pq-att--doc { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.45rem 0.6rem; max-width: 230px; }
.pq-att-ico { font-size: 1.1rem; }
.pq-att-name { font-size: 0.78rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-att-size { font-size: 0.66rem; color: var(--rbpg-muted); flex: 0 0 auto; }
/* Our own paper, already sent: the same chip squared off and inked, matching the
   composer's staged chip so the thing staged and the thing sent look like the same
   object. Wider, because the name IS the reference (Estimate-EST-2026-000006). */
.pq-att--paper { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.45rem 0.6rem; max-width: 320px; border-radius: 6px; border-color: var(--rbpg-text); }
.pq-att--paper:hover { background: var(--rbpg-grey); }
.pq-att--paper[aria-busy="true"] { opacity: 0.55; cursor: progress; }
/* Sent without its paperclip — the words went, the file did not. */
.pq-att--dropped { border-color: #d99; border-style: dashed; }
.pq-att--dropped .pq-att-size { color: #b91c1c; }

/* ---- composer: paperclip + pending chips ---- */
.pq-attach { width: 32px; height: 32px; flex: 0 0 auto; border-radius: 8px; border: 1px solid var(--rbpg-border); background: var(--rbpg-grey); cursor: pointer; color: var(--rbpg-muted); display: inline-flex; align-items: center; justify-content: center; }
.pq-attach:hover { border-color: var(--rbpg-text); color: var(--rbpg-text); }
.pq-attach svg { display: block; }
.pq-attach--off, .pq-attach:disabled { opacity: 0.4; cursor: not-allowed; }
.pq-attach--off:hover, .pq-attach:disabled:hover { border-color: var(--rbpg-border); color: var(--rbpg-muted); }
.pq-pending { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0 0 0.55rem; }
.pq-chip { display: inline-flex; align-items: center; gap: 0.4rem; background: var(--rbpg-grey); border: 1px solid var(--rbpg-border); border-radius: 999px; padding: 0.2rem 0.35rem 0.2rem 0.5rem; font-size: 0.76rem; max-width: 210px; }
.pq-chip--img { padding-left: 0.2rem; }
.pq-chip img { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; }
.pq-chip-ico { font-size: 0.85rem; }
.pq-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-chip-x { border: 0; background: transparent; cursor: pointer; color: var(--rbpg-muted); font-size: 0.7rem; padding: 0 2px; line-height: 1; }
.pq-chip-x:hover { color: var(--rbpg-text); }
/* Our own paper, staged for the send: squared off and inked so a generated
   invoice never reads as something the customer uploaded. Wider, because the
   name it carries is a reference the operator wants to see in full. */
.pq-chip--doc { border-radius: 6px; border-color: var(--rbpg-text); background: #fff; max-width: 320px; }
.pq-drop-over { outline: 2px dashed var(--rbpg-text); outline-offset: -4px; }

/* ---- file viewer (lightbox over the message column) ---- */
.pq-center { position: relative; }
.pq-viewer { position: absolute; inset: 0; z-index: 40; background: rgba(20,20,20,0.93); display: flex; align-items: center; justify-content: center; }
.pq-viewer-top { position: absolute; top: 0; left: 0; right: 0; display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.8rem; }
.pq-viewer-cap { font-size: 0.8rem; color: #e4e4e7; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-viewer-actions { margin-left: auto; display: flex; gap: 0.4rem; align-items: center; }
.pq-viewer-btn { background: rgba(255,255,255,0.14); color: #fff; border: 0; border-radius: 8px; padding: 0.35rem 0.6rem; font-size: 0.78rem; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; }
.pq-viewer-btn:hover { background: rgba(255,255,255,0.28); color: #fff; }
.pq-viewer-stage { max-width: 88%; max-height: 78%; display: flex; align-items: center; justify-content: center; }
.pq-viewer-stage img { max-width: 100%; max-height: 74vh; object-fit: contain; border-radius: 6px; }
.pq-viewer-stage iframe { width: 78vw; max-width: 720px; height: 74vh; border: 0; background: #fff; border-radius: 6px; }
.pq-viewer-stage video { max-width: 100%; max-height: 74vh; border-radius: 6px; background: #000; }
.pq-viewer-doc { text-align: center; color: #fff; display: flex; flex-direction: column; gap: 0.7rem; align-items: center; }
.pq-viewer-doc audio { margin-top: 0.4rem; }
.pq-viewer-doc-name { font-size: 0.9rem; color: #e4e4e7; max-width: 60vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-viewer-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 40px; height: 40px; border-radius: 50%; border: 0; background: rgba(255,255,255,0.16); color: #fff; font-size: 1.4rem; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.pq-viewer-nav:hover { background: rgba(255,255,255,0.3); }
.pq-viewer-prev { left: 12px; }
.pq-viewer-next { right: 12px; }
/* bottom info strip: file name + mime + size + uploaded date */
.pq-viewer-bar { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: baseline; gap: 0.8rem; padding: 0.6rem 0.9rem; background: linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0)); color: #f2f2f4; }
.pq-vbar-name { font-size: 0.82rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pq-vbar-meta { margin-left: auto; font-size: 0.72rem; color: #c9c9cf; white-space: nowrap; text-transform: uppercase; letter-spacing: 0.03em; }

/* Skeletons */
.pq-skel { background: linear-gradient(90deg,#ececec 25%,#f5f5f5 37%,#ececec 63%); background-size: 400% 100%; animation: pq-shimmer 1.2s ease infinite; border-radius: 8px; }
@keyframes pq-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

@media (max-width: 1199.98px) {
  /* Intercom-style: keep case context; queue becomes a left drawer. */
  .pq-body { display: flex; flex-direction: row; min-height: 0; overflow: hidden; }
  .pq-center { flex: 1 1 auto; min-width: 0; }
  .pq-info { flex: 0 0 clamp(260px, 34vw, 360px); width: clamp(260px, 34vw, 360px); }
  .pq-queue-toggle { display: inline-flex; }
  .pq-queue {
    position: fixed; top: 0; left: 0; bottom: 0; width: min(300px, 86vw); z-index: 1040;
    transform: translateX(-105%); transition: transform .2s ease;
    box-shadow: 4px 0 24px rgba(0,0,0,.12); border-right: 1px solid var(--rbpg-border);
  }
  .pq.queue-open .pq-queue { transform: translateX(0); }
  .pq-scrim {
    display: block; position: fixed; inset: 0; z-index: 1039;
    background: rgba(24, 24, 27, 0.45); opacity: 0; pointer-events: none; transition: opacity .2s ease;
  }
  .pq.queue-open .pq-scrim { opacity: 1; pointer-events: auto; }
}
@media (max-width: 639.98px) {
  .pq-info { flex-basis: min(200px, 42vw); width: min(200px, 42vw); }
  .pq-progress-bar { width: 72px; }
  .pq-crumb .muted:first-child, .pq-crumb .sep:first-of-type { display: none; }
}

/* The customer's rating, on the case where the repair happened (REV-7).
   Structured like .pq-qc deliberately — same left-accent card, same place in the
   feed — because it is the same kind of thing: an outcome somebody needs to see
   without going looking for it. The accent carries the verdict, so a 2-star is
   visible while scrolling past. */
.pq-rv {
  align-self: flex-start;
  flex: 0 0 auto;
  max-width: 460px;
  margin: 0.5rem 0;
  padding: 0.7rem 0.9rem;
  background: var(--rbpg-surface);
  border: 1px solid var(--rbpg-border);
  border-left: 3px solid #16a34a;
  border-radius: 10px;
}
.pq-rv--mixed { border-left-color: #f59e0b; }
.pq-rv--poor { border-left-color: #dc2626; }
.pq-rv-top { display: flex; align-items: center; gap: 0.5rem; }
.pq-rv-stars { display: inline-flex; gap: 0.05rem; font-size: 0.95rem; line-height: 1; }
.pq-rv-star { color: var(--rbpg-star-off); }
.pq-rv-star.is-on { color: var(--rbpg-star); }
.pq-rv-score { font-size: 0.85rem; font-weight: 800; }
.pq-rv-pill {
  margin-left: auto; font-size: 0.68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--rbpg-muted);
}
.pq-rv-comment {
  margin-top: 0.5rem; font-size: 0.875rem; line-height: 1.5;
  color: var(--rbpg-text); overflow-wrap: anywhere;
}
.pq-rv-meta { margin-top: 0.4rem; font-size: 0.72rem; color: var(--rbpg-muted); }
.pq-rv-who { font-weight: 600; }

/* Rich reply rendering (thread bubbles) */
.pq-rich a { color: #2563eb; text-decoration: underline; word-break: break-word; }
.pq-rich p { margin: 0 0 0.5rem; }
.pq-rich p:last-child { margin-bottom: 0; }
.pq-rich ul, .pq-rich ol { margin: 0 0 0.5rem 1.1rem; padding: 0; }

/* Designed emails, framed at the width they were designed for.
   Phone-width on purpose: these templates are built mobile-first and that is how
   nearly every customer opens them, so the bubble shows what they actually saw
   rather than a stretched desktop variant nobody receives. */
/* A framed email brings its own card: its own canvas colour, its own white sheet,
   its own border and radius. Wrapping that in the outbound bubble's blue card and
   then bordering the frame on top produced three nested rectangles around one
   email. So a document message drops the bubble entirely — no background, no
   border, no padding — and the email is simply the thing in the thread. */
.pq-bubble--doc { background: none !important; border: 0 !important; box-shadow: none !important; }
.pq-bubble--doc .pq-bubble-pad { padding: 0; }
.pq-msg--doc .pq-bubble { overflow: visible; }
.pq-bubble--doc .pq-bubble-subj {
  font-size: 0.78rem; font-weight: 600; color: var(--rbpg-muted);
  margin: 0 0 0.3rem; padding: 0;
}

.pq-doc { position: relative; width: 390px; max-width: 100%; }
.pq-doc-frame {
  display: block; width: 100%; height: 220px; /* replaced on load by measured height */
  border: 0; background: transparent;
  color-scheme: light; /* emails are designed on a light canvas; don't invert them */
  /* A picture of what was sent, not a copy you can use. Suppressing pointer
     events kills the link cursor as well as the click, so nothing inside invites
     a press — including the review email's five stars, each of which files a
     rating on the customer's behalf when followed. The "show full email" button
     sits outside the frame and still works. */
  pointer-events: none;
}
.pq-doc-more { display: none; }
.pq-doc--clipped::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 1.9rem; height: 4rem;
  background: linear-gradient(to bottom, rgba(255,255,255,0), #fff); pointer-events: none;
}
.pq-doc--clipped .pq-doc-more {
  display: block; width: 100%; padding: 0.4rem 0.6rem;
  border: 1px solid var(--rbpg-border); border-radius: 8px; background: #fff;
  font: inherit; font-size: 0.8rem; font-weight: 600; color: #2563eb;
  cursor: pointer; text-align: center;
}
.pq-doc--clipped .pq-doc-more:hover { background: #f7f9fc; }

/* ==========================================================================
   RBPGEditor — reusable Gmail/Intercom-style rich editor (Trix)
   One connected box: content on top, a compact toolbar docked at the bottom.
   ========================================================================== */
.rbpg-editor {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--rbpg-border, #d7dbe0);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.rbpg-editor:focus-within {
  border-color: var(--rbpg-accent, #2563eb);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
}
/* Editing area on top */
.rbpg-editor .rbpg-editor__area {
  order: 1;
  border: 0;
  border-radius: 0;
  min-height: 150px;
  max-height: 420px;
  overflow-y: auto;
  padding: 0.75rem 0.85rem;
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.55;
  color: #1f2430;
}
.rbpg-editor .rbpg-editor__area:focus { outline: none; }
.rbpg-editor .rbpg-editor__area a { color: #2563eb; text-decoration: underline; }
.rbpg-editor .rbpg-editor__area:empty::before,
.rbpg-editor .rbpg-editor__area[data-trix-empty]::before {
  color: #98a2b3;
}

/* Toolbar docked at the BOTTOM, compact */
.rbpg-editor trix-toolbar {
  order: 2;
  padding: 0.2rem 0.35rem;
  background: #f7f8fa;
  border-top: 1px solid var(--rbpg-border, #e6e9ee);
}
.rbpg-editor trix-toolbar .trix-button-row { align-items: center; flex-wrap: nowrap; }
.rbpg-editor trix-toolbar .trix-button-group {
  border: 0;
  margin: 0 0.15rem 0 0;
  box-shadow: none;
}
.rbpg-editor trix-toolbar .trix-button-group:not(:first-child) { margin-left: 0.15rem; }
.rbpg-editor trix-toolbar .trix-button {
  height: 1.7rem;
  width: 1.85rem;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #475467;
}
.rbpg-editor trix-toolbar .trix-button:not(:disabled):hover { background: #e9edf3; }
.rbpg-editor trix-toolbar .trix-button.trix-active { background: #e2e8f5; color: #1d4ed8; }
.rbpg-editor trix-toolbar .trix-button::before { margin: 22% !important; opacity: 0.85; }

/* Keep only the basics: bold, italic, link, bullet + numbered list.
   Hide heading/quote/code/strike, indentation, attachments and history. */
.rbpg-editor trix-toolbar .trix-button--icon-strike,
.rbpg-editor trix-toolbar .trix-button--icon-heading-1,
.rbpg-editor trix-toolbar .trix-button--icon-quote,
.rbpg-editor trix-toolbar .trix-button--icon-code,
.rbpg-editor trix-toolbar .trix-button--icon-decrease-nesting-level,
.rbpg-editor trix-toolbar .trix-button--icon-increase-nesting-level,
.rbpg-editor trix-toolbar .trix-button-group--file-tools,
.rbpg-editor trix-toolbar .trix-button-group--history-tools { display: none !important; }

/* Link dialog: keep it tidy above the bottom toolbar */
.rbpg-editor trix-toolbar .trix-dialog {
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(16, 24, 40, 0.16);
  font-size: 0.85rem;
}
.rbpg-editor trix-toolbar .trix-dialog .trix-input--dialog {
  border-radius: 6px;
  border: 1px solid var(--rbpg-border, #d7dbe0);
}

/* ── Ratings ────────────────────────────────────────────────────────────────
 * REV-7. The markup comes from `lib/rbpg-stars.js`, which is the one renderer
 * for every screen showing a score. These are the styles for it.
 *
 * Amber is the ONE exception to the status-tone rule in the design system, and
 * it is deliberate: a star is not a status. The five tones mean "in flight",
 * "settled", "waiting", "ended badly" and "no state yet", and a rating is none
 * of those — it is a quantity. Rendering it in `--tone-ok` would say a 2-star
 * review is good news, and in `--tone-warn` that a 5-star one needs attention.
 * A grey star is not a star, so it gets its own value, declared once here.
 */
/*
 * Google's review yellow, deliberately. Our reviews sit next to our Google
 * Business rating in a customer's mind — the thank-you email sends promoters
 * straight there — and a star that is a different yellow in the two places reads
 * as a different, lesser kind of rating. Matching it costs nothing and buys the
 * association.
 *
 * This is the ONLY place either value is written. Three copies of the amber
 * existed before (here, the public survey page, and the case timeline card), and
 * two of them would still be the old colour now.
 */
:root {
  --rbpg-star: #fbbc04;
  --rbpg-star-off: #d4d4d8;
}

.rbpg-rating {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  white-space: nowrap;
  line-height: 1;
}

.rbpg-stars { display: inline-flex; align-items: center; gap: 1px; }

.rbpg-star { display: inline-flex; line-height: 0; }

.rbpg-star svg {
  width: 15px;
  height: 15px;
  display: block;
  fill: var(--rbpg-star-off);
}

.rbpg-star.is-on svg { fill: var(--rbpg-star); }

/* A half star by clipping the same path, so there is one star shape in the
   codebase rather than a second half-width asset that drifts from it. */
.rbpg-star.is-half svg { fill: var(--rbpg-star); clip-path: inset(0 50% 0 0); }

.rbpg-stars--sm .rbpg-star svg { width: 12px; height: 12px; }
.rbpg-stars--lg .rbpg-star svg { width: 22px; height: 22px; }

.rbpg-rating-score {
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--rbpg-text);
  font-variant-numeric: tabular-nums;
}

.rbpg-stars--lg + .rbpg-rating-score { font-size: 1.5rem; }

.rbpg-rating-count {
  font-size: 0.75rem;
  color: var(--rbpg-muted);
  font-variant-numeric: tabular-nums;
}

/* A score on its own line under a name in the case rail — the supplier's
   (REV-7) and the customer's (REV-11) — in the shape every rating a customer has
   ever seen uses: glyphs, then the number, then how many. Trailing the name
   instead meant it competed with a long workshop name for the rail's width and
   wrapped mid-component.

   Both selectors carry the same rules because both lines are the same component
   in the same column; only the number underneath differs, and that is a backend
   distinction rather than a visual one.

   `align-items: baseline` rather than center: the stars are 12px and the score
   is 13px, and centring them leaves the numeral sitting visibly low. */
.pq-supplier-rating,
.pq-cust-rating {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  margin: -0.15rem 0 0.4rem;
}
.pq-supplier-rating .rbpg-rating,
.pq-cust-rating .rbpg-rating { gap: 0.3rem; }
.pq-supplier-rating .rbpg-stars,
.pq-cust-rating .rbpg-stars { align-self: center; }
.pq-supplier-rating .rbpg-rating-score,
.pq-cust-rating .rbpg-rating-score { font-size: 0.82rem; font-weight: 700; }
.pq-supplier-rating .rbpg-rating-count,
.pq-cust-rating .rbpg-rating-count { font-size: 0.72rem; }
/* The count is a link to the reviews behind it, but it is still a quiet caption:
   underlining only on hover keeps the line reading as a rating rather than as a
   row of navigation. */
.pq-supplier-rating .rbpg-rating-count a,
.pq-cust-rating .rbpg-rating-count a { color: inherit; text-decoration: none; }
.pq-supplier-rating .rbpg-rating-count a:hover,
.pq-cust-rating .rbpg-rating-count a:hover { color: #2563eb; text-decoration: underline; }

/* Nobody rated yet. Neutral, and deliberately NOT a pill — a pill is a status,
   and "we have not asked anybody yet" is the absence of one.

   On a customer this line is the point rather than a fallback: the question the
   rail is answering is "has this person ever reviewed us", and an empty space
   answers that with silence, which reads identically to a panel that has not
   finished loading. */
.rbpg-rating--none {
  font-size: 0.75rem;
  color: var(--rbpg-muted);
  font-style: italic;
}

/* ── The reviews inbox (REV-8) ──────────────────────────────────────────────*/

/* Fixed layout, and it has to be. Under the default `auto` algorithm a column
   can never be narrower than its own content, so a nowrap cell — five stars, a
   date that must not wrap — sets a floor that no `width` can talk it below, and
   the table answers a container it does not fit by scrolling sideways. Once the
   row menu took a column of its own that is exactly what happened here, and
   trimming the other five moved the number without ever reaching zero.

   Fixed layout inverts it: the declared widths are the truth and the content
   fits them, so this table is the width of its card by construction. The cost is
   that overflow becomes the cell's problem, which is what the truncation below
   is for.

   `.rbpg-table--fixed` above is the same idea and is NOT reused here, for one
   reason: it carries `min-width: 54rem` for wide full-page tables, and this card
   is half a split row at about 43rem — so adopting it would reintroduce the
   sideways scroll it otherwise cures.

   Small text across every column, and tighter gutters than the app default. Both
   buy width, and the first also settles an inconsistency: Case and When were
   `small` while Customer and Supplier were not, so one table was being read at
   two sizes.

   The doubled `.table.rv-table` is not decoration: the defaults it is overriding
   are `.table.rbpg-table tbody td`, and a single class loses to them. */
.table.rv-table { table-layout: fixed; }
.table.rv-table thead th { padding-left: 0.75rem; padding-right: 0.75rem; }
.table.rv-table tbody td {
  font-size: 0.82rem;
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}

/* Reviews is a dense operational inbox. Keep the paired card headers the same
   height and make their controls genuinely small: the theme's base `.btn` rule
   loads after Bootstrap and otherwise overrides `btn-sm` back to full size. */
.rv-page .rbpg-list-card > .card-header {
  min-height: 3.25rem;
  padding: 0.7rem 1rem;
}
.rv-page .rbpg-list-card > .card-header .card-title { font-size: 0.95rem; }
.rv-page #rv-recompute,
.rv-page .rbpg-pager .btn {
  padding: 0.3rem 0.65rem;
  border-radius: 0.45rem;
  font-size: 0.75rem;
  line-height: 1.2;
}
.rv-page .rbpg-pager {
  padding: 0.6rem 1rem;
  gap: 0.5rem;
}
.rv-page .rbpg-pager .page-info { font-size: 0.75rem; }
.rv-page .rbpg-pager .d-flex { gap: 0.4rem !important; }

/* A scanning table, so every row is one line high and the columns line up all
   the way down. Names truncate rather than wrap: a long supplier name that wraps
   sets its whole row two lines tall and breaks the rhythm the table exists for.
   The full name stays readable via the link itself.

   No max-width: under fixed layout the column is already the limit, and a second
   one here would only stop a name using the room it has been given. */
.rv-cell-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Stars and the comment marker on one line. No number beside them: a customer
   rating is a whole number, so the glyphs already say it exactly. */
.rv-cell-rating { white-space: nowrap; }

/* A case reference is one token and must stay on one line. Under fixed layout a
   column no longer widens to fit its content, so "5041086" in a 5rem column
   broke across two lines and set that one row 23px taller than the rest — the
   exact fault the truncation rules above exist to prevent, arriving through the
   one cell that had no rule of its own. */
.rv-cell-ref {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* "14 Jul 2026" is three words, and this table sits next to the by-supplier
   panel with barely 500px to divide five ways. Let the date wrap and it stacks
   into three lines, which sets the row three times taller than its neighbours —
   the single worst offender against a table whose whole job is to be scanned. */
.rv-cell-when { white-space: nowrap; }

/* The five-bar spread. Five numbers do not need a charting library, and this
   reads at a glance in a card that is 4rem tall. */
.rv-dist { display: grid; gap: 0.25rem; }

.rv-dist-row { display: flex; align-items: center; gap: 0.5rem; }

.rv-dist-label {
  font-size: 0.7rem;
  color: var(--rbpg-muted);
  width: 1.7rem;
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

.rv-dist-track {
  flex: 1 1 auto;
  height: 6px;
  border-radius: 3px;
  background: var(--rbpg-grey);
  overflow: hidden;
}

/* Amber, matching the stars, so the bars and the glyphs above them are
   obviously about the same quantity. */
.rv-dist-fill { display: block; height: 100%; background: var(--rbpg-star); border-radius: 3px; }

.rv-dist-n {
  font-size: 0.7rem;
  color: var(--rbpg-muted);
  width: 2.5rem;
  flex: 0 0 auto;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* One review, read in full (RBPGReviewModal). Ordered the way a public review
   card is — who, how many stars, when, then the words — because that is the
   order somebody already expects to read them in. */
.rvm-who { font-size: 1.05rem; font-weight: 700; line-height: 1.2; }
.rvm-stars { margin-top: 0.45rem; }
.rvm-when { margin-top: 0.35rem; font-size: 0.8rem; color: var(--rbpg-muted); }

/* The gap above is the point: it separates the customer from their sentence, so
   the prose reads as a quotation rather than another field in a form. */
.rvm-comment {
  margin-top: 1.35rem;
  font-size: 1rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.rvm-comment--empty { color: var(--rbpg-muted); font-style: italic; }

.rvm-meta {
  margin-top: 1.35rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rbpg-border);
  font-size: 0.8rem;
  color: var(--rbpg-muted);
}

/* Visually hidden, still read aloud. The stars are aria-hidden, so this is the
   whole accessible name for a rating: "4.9 out of 5, from 156 reviews". */
.rbpg-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* SET-2/3 — Settings hub and compact Brand administration. */
.set-identity {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

/* Settings hub cards (NAV-2). The whole card is the link, so the hit target is the
   card and not a word at the bottom of it. */
.set-card {
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.set-card:hover, .set-card:focus-visible {
  border-color: var(--rbpg-accent, #0d6efd);
  transform: translateY(-2px);
  color: inherit;
}
.set-card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.set-card-head svg { color: var(--rbpg-muted); flex: 0 0 auto; }
/* Empty on every card but Brands, which fills it with its counts. Reserving the
   space keeps the explanations on one baseline across the row. */
.set-card-foot { padding-top: 0.75rem; min-height: 1.25rem; }

.br-search { width: min(22rem, 100%); }
.br-status { width: 10rem; }
.br-table { table-layout: fixed; }
.br-table th:nth-child(1) { width: 28%; }
.br-table th:nth-child(2) { width: 25%; }
.br-table th:nth-child(3) { width: 18%; }
.br-table th:nth-child(4) { width: 15%; }
.br-table th:nth-child(5) { width: 14%; }

.br-logo {
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--rbpg-border);
  border-radius: 0.4rem;
  object-fit: contain;
  background: #fff;
  flex: 0 0 auto;
}

.br-logo--empty {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--rbpg-muted);
  background: var(--rbpg-grey);
  font-weight: 700;
}

.br-url {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.br-url a {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 767.98px) {
  .br-table th:nth-child(2), .br-table td:nth-child(2),
  .br-table th:nth-child(4), .br-table td:nth-child(4) { display: none; }
  .br-table th:nth-child(1) { width: 45%; }
  .br-table th:nth-child(3) { width: 32%; }
  .br-table th:nth-child(5) { width: 23%; }
}

/* Automations — what the system may say to a customer unattended, and what
   waits for a person. The step rows are a list rather than a table because the
   only column that matters is the switch on the right. */
.au-hint {
  color: var(--rbpg-muted);
  font-size: 0.82rem;
  margin-bottom: 1rem;
}

.au-strong { font-weight: 600; }

/* The pointer to the approval queue in the Message center. Deliberately a
   single row: the drafts are actioned there, not here. */
.au-queue-link {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1.1rem;
  color: inherit;
  text-decoration: none;
}

.au-queue-link:hover { border-color: var(--rbpg-primary, #2563eb); color: inherit; }

.au-queue-figure {
  flex: 0 0 auto;
  min-width: 2.4rem;
  padding: 0.15rem 0.5rem;
  border-radius: 0.5rem;
  background: var(--rbpg-surface-2, #f1f5f9);
  color: var(--rbpg-muted);
  font-weight: 700;
  font-size: 1.05rem;
  text-align: center;
}

/* Something is actually waiting on a person — say so in the colour used for
   everything else that is overdue. */
.au-queue-link--waiting .au-queue-figure { background: #fee2e2; color: #b91c1c; }

.au-queue-text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.au-queue-text span { font-size: 0.82rem; }

.au-wf { border-top: 1px solid var(--rbpg-border); }
.au-wf:first-child { border-top: 0; }
/* An off workflow sends nothing, so its step switches are context, not controls. */
.au-wf--off .au-step { opacity: 0.55; }

.au-wf-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1rem;
  background: var(--rbpg-surface-2, #f8fafc);
}

.au-step {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem 0.7rem 2rem;
  border-top: 1px solid var(--rbpg-border);
}

.au-step-main { min-width: 0; }
.au-step-main .au-strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.au-step-mode { flex: 0 0 11rem; }

@media (max-width: 767.98px) {
  .au-step { align-items: flex-start; flex-direction: column; padding-left: 1rem; }
  .au-step-mode { flex: none; }
}

/* ==========================================================================
   Card payments panel (RBPGPaymentAccount, EST-4 §3.12)

   One row per capability Stripe was asked about, each carrying its own pill.
   The shape is the QC checklist's, because an operator reading "did this pass?"
   down a column should not have to learn a second layout to do it.
   ========================================================================== */

.pa-checks {
  margin-top: 1rem;
  border-top: 1px solid var(--rbpg-border);
}

/* After the facts there is already a hairline closing the last row, so the
   checklist continues the same ruled column instead of drawing a second line
   1px away from the first. */
.pa-facts + .pa-checks { margin-top: 0; border-top: 0; }

.pa-check {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rbpg-border);
  font-size: 0.8125rem;
}

.pa-check-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.pa-check-note {
  color: var(--rbpg-muted);
  font-size: 0.75rem;
}

.pa-check-state { flex: none; }

/* A second checklist under an unrelated one (the print card carries the printers
   and then what the traveller shows) needs air and a heading, or the two read as
   one list of six things. The margin is deliberately larger than the row gap. */
.pa-checks-sep { margin-top: 1.6rem; }

.pa-check-head {
  padding-bottom: 0.45rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--rbpg-muted);
}

/* A switch used as a row's control, rather than beside its own label: the label
   is the row's text on the left, so the switch itself must not reserve the
   indent Bootstrap's form-check keeps for one. */
.pa-check-state .pa-switch .form-check { padding-left: 0; min-height: 0; }

/* ---- The facts, once connected ------------------------------------------
   Label left, answer right, hairline between. This replaced a Bootstrap
   `dl.row`, whose col-sm-4 / col-sm-8 split put a two-word answer at the far
   end of an eight-column cell — so a one-word status appeared to span the card
   with nothing to anchor it. A two-column flex row sizes to its content. */
.pa-facts {
  margin-top: 0.9rem;
  border-top: 1px solid var(--rbpg-border);
  font-size: 0.8125rem;
}

.pa-fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--rbpg-border);
}

.pa-fact-k { color: var(--rbpg-muted); }
.pa-fact-v { flex: none; text-align: right; }
/* A date is a fact, not a finding — muted, so the eye passes over it and stops
   on the two rows that say whether this thing works. */
.pa-fact-quiet, .pa-fact-none { color: var(--rbpg-muted); }

/* Status as a mark and a word. `align-items: center` on an inline-flex keeps the
   icon on the text's optical centre rather than its baseline, where a check
   glyph sits noticeably low. */
.pa-ok, .pa-bad, .pa-none {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 500;
  white-space: nowrap;
}
.pa-ok { color: #15803d; }
.pa-bad { color: #b45309; }
.pa-none { color: var(--rbpg-muted); font-weight: 400; }
.pa-i { flex: none; }

/* The last four of the stored key. Monospaced so the digits line up against the
   same four read off the Stripe dashboard, which is the only thing it is for. */
.pa-mask {
  font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: 0.05em;
}

.pa-warn-list {
  padding-left: 1.1rem;
  font-size: 0.875rem;
  color: var(--rbpg-muted);
}
.pa-warn-list li { margin-bottom: 0.35rem; }

/* ---- Taking cards: on or off --------------------------------------------
   A switch, not the three-way select this used to be. Test versus live is a
   property of the key and was never this control's to change, so what is left
   is one decision and it gets the shape of one. */
.pa-switch { display: inline-flex; align-items: center; }
.pa-switch .form-check { display: inline-flex; align-items: center; gap: 0.55rem; padding-left: 0; }
.pa-switch .form-check-input { margin: 0; flex: none; cursor: pointer; }
.pa-switch-label {
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

/* Which Stripe environment the keys are for. Only test earns a chip: live is
   the unremarkable case, and "no real money is moving" is the part worth
   saying out loud beside the account name. */
.pa-kind-chip {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid rgba(99, 91, 255, 0.35);
  border-radius: 0.25rem;
  background: rgba(99, 91, 255, 0.08);
  color: #4f46e5;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  vertical-align: middle;
}

.pa-actions { margin-top: 1.25rem; }

/* ---- Not connected: an empty state, not a table with one row ------------
   Deliberately no status pill. A badge reading "Not connected" above a button
   reading "Connect Stripe" restates the button and spends the full width of
   the card on one grey word. What earns the space is what connecting does. */
.pa-empty {
  padding: 0.25rem 0 0;
}
.pa-empty-lead {
  margin: 0 0 0.35rem;
  font-weight: 600;
  color: var(--rbpg-ink, #18181b);
}
.pa-empty-note {
  margin: 0 0 1.1rem;
  max-width: 60ch;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--rbpg-muted);
}

/* ---- Connected: identity and state in one strip -------------------------
   The two things worth seeing without reading — whose account, and whether it
   is taking real money. Everything you only look up when something is wrong
   stays in the table below. */
.pa-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--rbpg-border);
}
.pa-head-id {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
}
/* Stripe's indigo on the mark only. The button is the coloured thing on this
   card; a second filled block of it would compete with the action. */
.pa-head-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: rgba(99, 91, 255, 0.1);
  color: #635bff;
}
.pa-head-text { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.pa-head-name {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--rbpg-ink, #18181b);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pa-head-acct {
  font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.75rem;
  color: var(--rbpg-muted);
}
.pa-head-mode { flex: none; }

/* On a phone the pill drops under the label rather than squeezing the sentence
   that explains what failed into a two-word column. */
@media (max-width: 575.98px) {
  .pa-check { align-items: flex-start; flex-direction: column; gap: 0.35rem; }
  /* The switch drops under the account name rather than being squeezed beside
     it, which on a narrow phone would truncate whichever of the two is longer. */
  .pa-head { align-items: flex-start; }
  .pa-head-mode { width: 100%; }
}

/* ---- Workshop lanes (WF-3 settings) --------------------------------------
   The lane editor is a form laid out as a table, which is why it needs its own
   rules rather than the list-table defaults.

   It shipped on a bare `.table.table-sm` inside `.card-body.p-0`, so the first
   column's inputs sat flush against the card's left edge with nothing between
   text and border. `.rbpg-table` is the app's padded-table convention and fixes
   that at a stroke; everything below is what a table of CONTROLS needs on top of
   a table of text. */
.wl-table { table-layout: fixed; min-width: 40rem; }

/* Widths are declared once here rather than as inline `style` on each `th`, so
   the header and the body cannot disagree. Fixed layout means these are the
   truth and the inputs fit them, instead of the inputs setting the width. */
.wl-table th:nth-child(1), .wl-table td:nth-child(1) { width: 34%; }
.wl-table th:nth-child(2), .wl-table td:nth-child(2) { width: 32%; }
.wl-table th:nth-child(3), .wl-table td:nth-child(3) { width: 16%; }
.wl-table th:nth-child(4), .wl-table td:nth-child(4) { width: 18%; text-align: right; }

/* Rows of inputs need less vertical padding than rows of text: a `form-control`
   brings its own, and the convention's 0.95rem on top of it makes the six lanes
   taller than the screen. The horizontal 1.1rem is the point of the exercise and
   is left exactly as it is. */
.wl-table tbody td { padding-top: 0.6rem; padding-bottom: 0.6rem; vertical-align: middle; }

/* The number and its unit are one answer — "four weeks" — so they are one
   control. Roughly a third of the column for the figure leaves the unit enough
   room that "weeks" never clips. */
.wl-target { max-width: 15rem; }
.wl-target > .form-control { flex: 0 0 36%; }

/* Locked lane (`parts`): readable, and visibly not yours to change. Bootstrap
   leaves a readonly input white, which invites the click that does nothing. */
.wl-row--locked input[readonly] {
  background: var(--rbpg-grey);
  color: var(--rbpg-muted);
  cursor: default;
}

/* Switched off. Enough fade to read as inactive at a glance, without dimming the
   switch itself — that is the control you need in order to undo it, and a faded
   toggle is the one thing on the row that must not look unavailable.
   The exclusion is by column because opacity cannot be cancelled by a child. */
.wl-row--hidden td:not(:nth-child(3)) { opacity: 0.5; }

/* Job counts are compared down the column, so they line up on the digit. */
.wl-jobs { font-variant-numeric: tabular-nums; font-weight: 600; }

/* The tray's turnaround: stated, not editable. Reads as a value rather than an empty
   input, because a box here would imply this screen owns a number it does not (WF-14). */
.wl-fixed-target {
  font-size: 0.85rem;
  color: var(--rbpg-muted);
}

/* The card's one-line explanation, under its title. Matches `.modal-subtitle` so
   a heading with a sentence beneath it looks the same wherever it appears. */
.wl-subtitle {
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--rbpg-muted);
  max-width: 62ch;
}

/* Matched to `.app-content .card-header`: the theme restyles the header but leaves
   the footer on Bootstrap's tighter padding and tinted cap, so a card with both
   is lopsided top to bottom. */
.wl-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1.15rem 1.4rem;
  background: transparent;
  border-top: 1px solid var(--rbpg-border);
}

/* Sits beside Save and changes as you type, so it needs a fixed idea of its own
   size — otherwise the button shifts sideways when the hiding warning appears. */
.wl-status { font-size: 0.82rem; line-height: 1.4; color: var(--rbpg-muted); flex: 1 1 18rem; }
.wl-status--warn { color: var(--tone-warn-ink); font-weight: 600; }

/* Explanatory prose, not a form. Wider line height and a measure that stops the
   lines running the full width of a 1280px page. Darker than `--rbpg-muted`,
   which is sized for one-line hints: three paragraphs of it is hard going. */
.wl-note { font-size: 0.875rem; line-height: 1.6; color: #52525b; }
.wl-note p { max-width: 68ch; margin-bottom: 0.75rem; }
.wl-note strong { color: var(--rbpg-text); font-weight: 600; }

/* One control on a line of its own, so `d-flex` with a gap is all it needs. */
.wl-picker { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.wl-picker .form-select { max-width: 22rem; }

/* Below this the four columns cannot hold an input each, so the table scrolls
   sideways inside its card rather than crushing the controls. */
@media (max-width: 767.98px) {
  .wl-table { min-width: 34rem; }
  .wl-table tbody td { padding-left: 0.75rem; padding-right: 0.75rem; }
  .wl-table thead th { padding-left: 0.75rem; padding-right: 0.75rem; }
}

