/*
 * Hand-written CSS: no build step (no Node on this host). Same visual
 * language (palette, spacing, radii) as the Next.js sibling implementation's
 * Tailwind config, just written directly instead of generated.
 */
:root {
  /* ── Lumen Ecosystem design system (2026 redesign) ──────────────────
     The shell is always navy, the primary action is always the gold
     gradient, and the tenant/vertical color survives only as an identity
     cue (--accent): the workspace mark's dot, the active-nav rail, KPI
     dots and stage markers. Switching workspaces changes rooms, not
     products. */
  --ink: #0f1b2d;
  --ink-soft: #3c4a5c;
  --ink-muted: #5a6b82;   /* AA on paper: do not lighten */
  --ink-faint: #8fa0b5;
  --bg: #f5f6f8;
  --surface: #ffffff;
  --surface-sunken: #eef0f5;
  --surface-raised: #fafbfc;
  --surface-hover: #fafbfc;

  /* Navy scale (the shell + every Lumen-authored block). */
  --navy: #0f1b2d;
  --navy-deep: #0b1420;
  --navy-band: #0a1220;
  --navy-inset: #122034;
  --navy-raised: #16273f;
  --navy-glow: #1c3a5e;
  --navy-edge: #1e3251;
  --navy-line: #26405f;
  --on-navy: #eef3fa;
  --on-navy-body: #c7d5e8;
  --on-navy-muted: #8ea6c4;
  --on-navy-micro: #7e9bbd;

  /* Gold: the one action color, always the 135° gradient, never flat. */
  --gold: #c9a227;
  --gold-2: #e0b93a;
  --gold-grad: linear-gradient(135deg, #c9a227, #e0b93a);
  --gold-ink: #8a6d1f;      /* gold ink on paper */
  --gold-warm-ink: #7d6113; /* warm badge ink */
  --gold-nav-ink: #f3e4b4;  /* active-nav ink on navy */
  --gold-tint: #fdf3dc;     /* warm tint */
  --gold-paper: #fffdf6;    /* unread row */

  /* Tenant accent: an identity CUE, not a paint bucket. The layout
     injects the vertical's value; text never sits on it. */
  --accent: #34C759;
  --accent-soft: #e4f7ec;
  --accent-ink: #14663c;
  /* Legacy alias: --brand once painted large areas per vertical. It now
     resolves to navy so every old "--brand-filled" control reads as the
     universal chrome color with white ink, which is the redesign. */
  --brand: var(--navy);
  --brand-hover: var(--navy-raised);
  --on-brand: #ffffff;

  /* Semantic status. Status color outranks brand color everywhere. */
  --green: #2fbf71;
  --green-soft: #e4f7ec;
  --green-ink: #1a7a48;
  --red: #e05a5a;
  --red-soft: #fdeceb;
  --red-ink: #a8342f;
  --danger: var(--red);
  --danger-ink: var(--red-ink);
  --amber: #7d6113;
  --amber-soft: #fdf3dc;
  --warn: var(--amber);
  --cool-soft: #eef0f5;
  --cool-ink: #4a5a70;
  --purple: #7c3aed;
  --purple-soft: #ede9fe;

  --border: #e4e7ee;
  --border-soft: #f1f3f7;
  /* --line / --line-2 are the names the standalone public documents (a sent
     quote, a sealed agreement) use for the same hairline, and several blocks
     in here were written against them by hand. An undefined custom property
     makes the whole `border: 1px solid var(--x)` declaration invalid, so those
     rules drew no border at all rather than a wrong one, which is why a card
     could look like floating text. Defined as aliases so both names resolve,
     and so they follow the theme when --border is repainted.  */
  --line: var(--border);
  --line-2: var(--border);
  --hairline: #e4e7ee;
  --radius-sm: 11px;  /* buttons, inputs, nav items */
  --radius: 16px;     /* cards */
  --radius-lg: 18px;  /* bands, sheets */
  --shadow-sm: 0 8px 24px rgba(15, 27, 45, .05);
  --shadow-md: 0 14px 34px rgba(15, 27, 45, .1);
  --shadow-lg: 0 30px 80px rgba(0, 0, 0, .6);
  --shadow-gold: 0 10px 30px rgba(201, 162, 39, .35);
  --shadow-gold-hover: 0 14px 36px rgba(201, 162, 39, .45);
  --focus-ring: 0 0 0 3px rgba(201, 162, 39, .14);
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-8: 32px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
h1, h2, h3, p { margin: 0; }
button { font-family: inherit; }

/* ── Layout shell ─────────────────────────────────────────────────────── */
.app-shell { display: flex; min-height: 100vh; }
.sidebar {
  width: var(--sidebar-w); flex-shrink: 0; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #0f1b2d 0%, #0b1420 100%);
  border-right: 1px solid var(--navy-edge);
}
/* Workspace switcher: the sidebar's top block IS the tenant identity now
   (the old top genre-tab strip folded into it). */
.sidebar-brand { padding: 16px 14px 12px; border-bottom: 1px solid rgba(38, 64, 95, .6); }
.ws-switch {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 9px; border-radius: 12px; cursor: pointer; text-align: left;
  background: rgba(255, 255, 255, .03); border: 1px solid rgba(38, 64, 95, .8);
  transition: background .15s ease;
}
.ws-switch:hover { background: rgba(255, 255, 255, .06); }
.brand-mark {
  position: relative; width: 34px; height: 34px; border-radius: 10px; flex: none;
  background: linear-gradient(135deg, #16273f, #1e3251); border: 1px solid var(--navy-line);
  color: var(--on-navy); display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 14px;
}
/* The one place the tenant accent lives on the shell: the mark's dot. */
.brand-dot {
  position: absolute; right: -2px; bottom: -2px; width: 10px; height: 10px;
  border-radius: 999px; background: var(--accent); border: 2px solid #0f1b2d;
}
.brand-name { font-size: 13px; font-weight: 700; line-height: 1.25; color: var(--on-navy); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.brand-tenant { font-size: 11px; color: var(--on-navy-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ws-caret { margin-left: auto; color: var(--on-navy-micro); font-size: 12px; flex: none; }
/* The switcher's menu: every licensed business type, one per row. */
.ws-menu {
  position: absolute; z-index: 60; left: 12px; right: 12px; top: calc(100% + 6px);
  background: var(--navy-raised); border: 1px solid var(--navy-line); border-radius: 14px;
  box-shadow: var(--shadow-lg); padding: 6px; max-height: 60vh; overflow: auto;
}
.ws-menu-item {
  display: flex; align-items: center; gap: 9px; padding: 9px 10px; border-radius: 10px;
  color: var(--on-navy-body); font-size: 12.5px; font-weight: 600;
}
.ws-menu-item:hover { background: rgba(255, 255, 255, .06); color: var(--on-navy); }
.ws-menu-item.on { background: rgba(201, 162, 39, .16); color: var(--gold-nav-ink); }
.ws-menu-label { padding: 9px 10px 4px; font-size: 9.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase; color: var(--on-navy-micro); }

.nav-section-label {
  padding: 15px 12px 5px; font-size: 9.5px; font-weight: 800; text-transform: uppercase;
  letter-spacing: 1.5px; color: var(--on-navy-micro);
}
.nav-link {
  display: flex; align-items: center; gap: 9px; margin: 1px 10px; padding: 8px 10px;
  border-radius: 10px; color: var(--on-navy-muted); font-size: 13px; font-weight: 600;
  transition: background 0.12s ease, color 0.12s ease;
}
.nav-link:hover { background: rgba(255, 255, 255, .05); color: var(--on-navy); }
.nav-link.active {
  background: rgba(201, 162, 39, .16); color: var(--gold-nav-ink); font-weight: 700;
  box-shadow: inset 2px 0 0 var(--accent);
}
.nav-icon { width: 16px; text-align: center; opacity: 0.85; font-size: 11px; flex: none; }
.nav-count { margin-left: auto; font-size: 10.5px; font-weight: 700; color: var(--on-navy-micro); font-variant-numeric: tabular-nums; }
.nav-count.hot { background: var(--red); color: #fff; border-radius: 999px; min-width: 17px; text-align: center; padding: 1px 5px; }
.nav-count.gold { background: var(--gold-grad); color: #0b1420; border-radius: 999px; min-width: 17px; text-align: center; padding: 1px 5px; }

.sidebar-footer {
  margin-top: auto; border-top: 1px solid rgba(38, 64, 95, .6); padding: 12px 14px;
  font-size: 12px; display: flex; align-items: center; gap: 10px;
}
.sidebar-footer .avatar {
  width: 30px; height: 30px; border-radius: 999px; background: var(--navy-raised);
  border: 1px solid var(--navy-line);
  color: var(--gold-2); display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 12px; flex-shrink: 0;
}
.sidebar-footer .who { min-width: 0; flex: 1; }
.sidebar-footer .name { font-weight: 600; color: var(--on-navy); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar-footer .role { color: var(--on-navy-micro); font-size: 10.5px; }
.sidebar-footer form { margin-top: 2px; }
.sidebar-footer button {
  background: none; border: none; padding: 0; color: var(--on-navy-micro);
  cursor: pointer; font-size: 11px;
}
.sidebar-footer button:hover { color: var(--on-navy); text-decoration: underline; }

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--bg); }
/* Retired top strip: workspaces switch from the sidebar now. The class
   stays only as the platform owner's test-drive picker home. */
.genre-tabs {
  display: flex; gap: 4px; padding: 8px 24px; background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.genre-tab { padding: 7px 14px; border-radius: var(--radius-sm); font-size: 13px; font-weight: 600; color: var(--ink-muted); transition: background 0.12s ease; }
.genre-tab.active { background: var(--navy); color: #fff; }
.genre-tab:not(.active):hover { background: var(--border-soft); }
.genre-dot { display: inline-block; width: 7px; height: 7px; border-radius: 999px; margin-right: 7px; vertical-align: middle; }
main.content {
  flex: 1; min-width: 0; max-width: 1292px; width: 100%; margin: 0 auto;
  padding: 26px 26px calc(var(--bottombar-h) + var(--safe-bottom) + 30px);
}

.flash {
  margin: 0 auto 20px; max-width: 1280px; padding: 10px 16px; border-radius: var(--radius-sm);
  font-size: 13.5px; background: var(--green-soft); color: #14532d; border: 1px solid rgba(22,163,74,0.2);
}
.flash-error { background: var(--red-soft); color: #7f1d1d; border-color: rgba(220,38,38,0.2); }

/* ── Page header ──────────────────────────────────────────────────────── */
.page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 20px; flex-wrap: wrap; }
.page-eyebrow { font-size: 9.5px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; color: var(--gold-ink); margin-bottom: 7px; }
.page-title { font-size: 25px; font-weight: 800; letter-spacing: -0.024em; margin: 0; color: var(--ink); }
.page-subtitle { margin: 6px 0 0; font-size: 13.5px; color: var(--ink-soft); max-width: 68ch; line-height: 1.65; }
.page-actions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
.breadcrumb { font-size: 11px; color: var(--ink-muted); margin-bottom: 8px; }
.breadcrumb a { color: var(--ink-muted); font-weight: 600; }
.breadcrumb a:hover { color: var(--ink); text-decoration: underline; }
.section-title { font-size: 13px; font-weight: 750; margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between; }

/* ── Cards / grid ─────────────────────────────────────────────────────── */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.card.pad { padding: 18px; }
.card-hover { transition: transform .2s, box-shadow .2s; }
.card-hover:hover { box-shadow: var(--shadow-md); transform: translateY(-4px); }

/* Navy panel: every Lumen-authored block. */
.navy-panel {
  background: linear-gradient(160deg, #16273f, #0f1b2d); border: 1px solid var(--navy-line);
  border-radius: var(--radius); padding: 18px; color: var(--on-navy);
}
.navy-panel .np-title { font-size: 13px; font-weight: 700; color: var(--on-navy); }
.navy-panel .np-body { font-size: 12.5px; color: var(--on-navy-body); line-height: 1.6; }
/* Feature band: the dark hero strip (Today's attention bar, contact header). */
.feature-band { position: relative; background: linear-gradient(160deg, #0f1b2d, #0a1220); border-radius: var(--radius-lg); overflow: hidden; color: var(--on-navy); }
.feature-band::after { content: ""; position: absolute; inset: 0; background: radial-gradient(600px 300px at 88% 10%, rgba(201, 162, 39, .14), transparent); pointer-events: none; }
.feature-band > * { position: relative; z-index: 1; }
/* On-navy chips (Lumen suggestion pills). */
.np-chip { display: inline-flex; align-items: center; padding: 7px 13px; border-radius: 999px; background: rgba(255, 255, 255, .05); border: 1px solid var(--navy-line); color: var(--on-navy); font-size: 11.5px; font-weight: 650; transition: border-color .15s ease; }
.np-chip:hover { border-color: var(--gold); }
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.col-span-1 { grid-column: span 1; }
.col-span-2 { grid-column: span 2; }
.col-span-3 { grid-column: span 3; }
.col-span-4 { grid-column: span 4; }
@media (max-width: 900px) { .grid { grid-template-columns: repeat(2, 1fr); } .col-span-2, .col-span-3, .col-span-4 { grid-column: span 2; } }
.stack { display: flex; flex-direction: column; gap: 16px; }
.row { display: flex; gap: 16px; align-items: flex-start; }
.row-between { display: flex; align-items: center; justify-content: space-between; }

/* Compact square tiles: the value is the point, so it leads; as many fit
   per row as the width allows instead of three wide slabs. The explanation
   lives in a hover tooltip, a dashboard is for reading numbers, not prose. */
/* auto-FIT, not auto-fill: auto-fill keeps empty tracks alive, so hiding a
   stat left a hole and the group always claimed the widget's full width.
   auto-fit collapses empty tracks to zero, so the tiles occupy exactly the
   room they need and the widget can be narrowed to sit beside another. */
/* max:1fr, not a fixed 128px. A fixed track maximum always leaves a
   remainder the grid cannot use, whatever the widget's width, the gap
   between "tiles that fit" and "space available" shows as dead space on the
   right. 1fr lets the tracks share the full width, so the row always ends
   flush; aspect-ratio on the slot keeps every tile square regardless. */
.kpi-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 10px; align-content: start; }
.kpi-slot { position: relative; display: flex; min-width: 0; aspect-ratio: 1 / 1; }
.kpi-slot > .kpi-card, .kpi-slot > .kpi-card-link { flex: 1; min-width: 0; }
.kpi-hide-btn {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  width: 22px; height: 22px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-muted); font-size: 12px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.kpi-hide-btn:hover { border-color: var(--brand); color: var(--brand); }
/* Drag handle for rearranging stats inside their container. Sits opposite the
   hide button so the two never overlap on the smallest tile. */
.kpi-drag {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  width: 22px; height: 22px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-muted); font-size: 12px; line-height: 1; cursor: grab;
  display: flex; align-items: center; justify-content: center; user-select: none;
}
.kpi-drag:active { cursor: grabbing; }
.kpi-slot.sortable-ghost { opacity: 0.35; }
.kpi-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: 8px;
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 4px;
  height: 100%; min-height: 0; min-width: 0; text-align: center;
  transition: transform .2s, box-shadow .2s;
}
.kpi-card-link:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.kpi-label {
  font-size: 9.5px; font-weight: 800; color: var(--ink-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi-value { font-size: 22px; font-weight: 800; letter-spacing: -0.028em; line-height: 1.05; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; color: var(--ink); }
.kpi-label { max-width: 100%; }
/* Retired from the card face; the component now puts it in a tooltip. */
.kpi-desc { display: none; }

/* ── Buttons ──────────────────────────────────────────────────────────── */
/* Full pill, like the marketing site's own CTAs, not the 8px "form button"
   radius this used to carry. Weight comes from a real shadow under the
   primary action, not from a darker fill. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: var(--radius-sm); padding: 9px 16px; font-size: 12.5px; font-weight: 800;
  cursor: pointer; border: 1px solid transparent; transition: background 0.12s ease, border-color 0.12s ease, opacity 0.12s ease, box-shadow 0.18s ease, transform 0.12s ease;
  line-height: 1.2;
}
/* The primary action is ALWAYS the gold gradient, on every screen, in every
   workspace. It never takes the tenant color. */
.btn-primary { background: var(--gold-grad); color: #0b1420; box-shadow: var(--shadow-gold); }
.btn-primary:hover { box-shadow: var(--shadow-gold-hover); }
.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--border); font-weight: 700; padding: 9px 15px; }
.btn-secondary:hover { border-color: var(--gold); }
.btn-ghost { background: none; color: var(--ink-muted); font-weight: 700; }
.btn-ghost:hover { background: var(--border-soft); color: var(--ink); }
.btn-danger { background: var(--red-soft); color: var(--red-ink); }
.btn-danger:hover { background: #fbd9d7; }
.btn-sm { padding: 7px 13px; font-size: 11.5px; }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Badges ───────────────────────────────────────────────────────────── */
/* Four semantic tints. Status is never color alone: every badge carries a
   word. Uppercase, tracked, 10px/800. */
.badge { display: inline-flex; align-items: center; border-radius: 999px; padding: 2px 9px; font-size: 10px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; }
.badge-neutral { background: var(--cool-soft); color: var(--cool-ink); }       /* new, neutral, draft */
.badge-accent { background: var(--accent-soft); color: var(--accent-ink); }
.badge-amber { background: var(--gold-tint); color: var(--gold-warm-ink); }     /* today, in progress, awaiting */
.badge-red { background: var(--red-soft); color: var(--red-ink); }              /* urgent, overdue, stalling */
.badge-green { background: var(--green-soft); color: var(--green-ink); }        /* signed, paid, complete */
.badge-purple { background: var(--purple-soft); color: var(--purple); }
.badge-dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; margin-right: 5px; display: inline-block; }

/* ── Avatars ──────────────────────────────────────────────────────────── */
.avatar { width: 26px; height: 26px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-ink); display: inline-flex; align-items: center; justify-content: center; font-weight: 600; font-size: 11px; flex-shrink: 0; }
.avatar-lg { width: 44px; height: 44px; font-size: 15px; }

/* ── Toggle switch (settings) ─────────────────────────────────────────── */
.toggle { position: relative; display: inline-block; width: 38px; height: 22px; flex-shrink: 0; }
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-track { position: absolute; inset: 0; background: var(--border); border-radius: 999px; transition: background 0.15s ease; cursor: pointer; }
.toggle-track::before { content: ""; position: absolute; width: 16px; height: 16px; left: 3px; top: 3px; background: #fff; border-radius: 999px; transition: transform 0.15s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.2); }
.toggle input:checked + .toggle-track { background: var(--gold-grad); }
.toggle input:checked + .toggle-track::before { transform: translateX(16px); }

/* ── Tabs (record detail) ────────────────────────────────────────────── */
.tabbar { display: flex; gap: 2px; border-bottom: 1px solid var(--surface-sunken); margin-bottom: 18px; }
.tab-btn { padding: 13px 4px; margin-right: 18px; font-size: 12.5px; font-weight: 650; color: var(--ink-muted); border-bottom: 2px solid transparent; cursor: pointer; background: none; border-left: none; border-right: none; border-top: none; transition: color .15s ease; }
.tab-btn:hover { color: var(--ink); }
.tab-btn.active { color: var(--ink); font-weight: 750; border-bottom-color: var(--gold); }

/* ── Timeline ─────────────────────────────────────────────────────────── */
.timeline { position: relative; padding-left: 20px; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: 1px; background: var(--border); }
.timeline-item { position: relative; padding-bottom: 16px; }
.timeline-item::before { content: ""; position: absolute; left: -20px; top: 3px; width: 8px; height: 8px; border-radius: 999px; background: var(--accent); border: 2px solid var(--surface); }
.timeline-item .t-title { font-size: 12.5px; font-weight: 650; }
.timeline-item .t-meta { font-size: 11.5px; color: var(--ink-muted); margin-top: 2px; }

/* ── Progress bar ─────────────────────────────────────────────────────── */
.progress-track { height: 6px; border-radius: 999px; background: var(--surface-sunken); overflow: hidden; }
.progress-fill { height: 100%; background: var(--gold-grad); border-radius: 999px; }
.progress-fill.over { background: var(--red); }

/* Stage stepper: reached stages take the gold gradient, unreached the bed. */
.stepper { display: flex; gap: 6px; flex-wrap: wrap; }
.stepper-step { flex: 1 1 120px; min-width: 0; }
.stepper-bar { height: 5px; border-radius: 999px; background: var(--border); }
.stepper-step.reached .stepper-bar { background: var(--gold-grad); }
.stepper-name { margin-top: 7px; font-size: 11px; font-weight: 700; color: var(--ink-muted); }
.stepper-step.reached .stepper-name { color: var(--ink); }
.stepper-date { font-size: 10.5px; color: var(--ink-muted); margin-top: 1px; }

/* ── Record workspace: list + kanban toggle ──────────────────────────── */
.workspace-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.workspace-title { font-size: 13.5px; font-weight: 700; }
.view-toggle { display: flex; border: 1px solid var(--border); border-radius: 999px; padding: 3px; font-size: 12px; background: var(--surface); }
.view-toggle button { border: none; background: none; padding: 6px 14px; border-radius: 999px; cursor: pointer; color: var(--ink-soft); font-weight: 650; text-transform: capitalize; transition: background 0.15s ease, color 0.15s ease; }
.view-toggle button.active { background: var(--navy); color: #fff; font-weight: 700; }

.kanban-scroll { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 10px; }
.kanban-col { flex-shrink: 0; width: 282px; background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px; }
.kanban-col-header { display: flex; align-items: center; gap: 7px; padding: 4px 6px 8px; font-size: 12px; }
.kanban-col-header .name { font-weight: 750; }
.kanban-col-header .count { color: var(--ink-muted); background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; font-size: 10.5px; font-weight: 700; }
.kanban-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 13px;
  padding: 12px; margin-bottom: 9px; box-shadow: 0 1px 2px rgba(15, 27, 45, .05); cursor: grab;
  transition: box-shadow .2s, transform .2s;
}
.kanban-card:hover { box-shadow: var(--shadow-md); transform: translateY(-3px); }
.kanban-card.dragging { opacity: 0.5; }
.kanban-card .title { font-size: 12.5px; font-weight: 700; line-height: 1.35; }
.kanban-card .title a:hover { text-decoration: underline; }
.kanban-card .subtitle { margin-top: 3px; font-size: 11.5px; color: var(--ink-muted); }
.kanban-card .meta { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--border-soft); display: flex; align-items: center; justify-content: space-between; }
.kanban-card .amount { font-size: 12.5px; font-weight: 750; font-variant-numeric: tabular-nums; }
.kanban-card .owner { font-size: 11px; color: var(--ink-muted); }
.kanban-empty { border: 1px dashed var(--cool-ink); opacity: .5; border-radius: 12px; padding: 22px 0; text-align: center; font-size: 11px; color: var(--ink-muted); }

table.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data-table thead th { text-align: left; font-size: 9.5px; font-weight: 800; color: var(--ink-muted); padding: 10px 18px; background: var(--surface-raised); text-transform: uppercase; letter-spacing: 0.06em; }
table.data-table thead th:first-child { border-top-left-radius: var(--radius); }
table.data-table thead th:last-child { border-top-right-radius: var(--radius); }
table.data-table tbody tr { transition: background 0.12s ease; }
table.data-table tbody tr:hover { background: var(--surface-hover); }
table.data-table tbody td { padding: 14px 18px; border-top: 1px solid var(--border-soft); }
table.data-table .muted { color: var(--ink-muted); font-size: 11.5px; }
.empty-row { padding: 36px; text-align: center; color: var(--ink-muted); }
.empty-state { text-align: center; padding: 48px 24px; color: var(--ink-faint); }
.empty-state .icon { font-size: 28px; margin-bottom: 8px; opacity: 0.6; }
.empty-state .title { font-size: 13.5px; font-weight: 500; color: var(--ink-muted); }
.empty-state .sub { font-size: 12px; margin-top: 4px; }

/* ── Forms ────────────────────────────────────────────────────────────── */
/* The glow behind the sign-in card. Was a hardcoded near-white radial, which
   on the dark public palette showed as a bright white blob at the top of the
   screen. Built from tokens now: the accent, faintly, on the page's own ground. */
.auth-wrap { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px;
  background: radial-gradient(44% 34% at 50% 0%, color-mix(in srgb, var(--accent) 14%, transparent) 0%, transparent 70%), var(--bg); }
.auth-wrap > a:first-child { align-self: flex-start; }
.auth-card { width: 100%; max-width: 380px; background: color-mix(in srgb, var(--surface) 55%, transparent); border: 1px solid var(--border); border-radius: 20px; padding: 34px; box-shadow: var(--shadow-md);
  -webkit-backdrop-filter: blur(34px) saturate(170%); backdrop-filter: blur(34px) saturate(170%); }
.auth-brand { display: flex; align-items: center; gap: 10px; margin-bottom: 24px; }
.field-label { display: block; margin-bottom: 4px; font-size: 12px; font-weight: 500; color: var(--ink-muted); }
/* Somewhere to actually write.
   A note about a roof or a foundation is prose, and a one line box that scrolls
   sideways hides what you already typed. These start at three comfortable lines
   and grow as you type (see the autogrow handler in app.js), with a ceiling so a
   long note never swallows the screen. */
textarea.field-input {
  min-height: 84px;
  line-height: 1.5;
  resize: vertical;
  overflow-y: auto;
  max-height: 60vh;
}
@media (max-width: 900px) {
  /* Thumbs, not a mouse: a little taller and never a sideways scroll. */
  textarea.field-input { min-height: 96px; }
}

.field-input, select.field-input, textarea.field-input {
  width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 13px;
  font-size: 13px; margin-bottom: 16px; font-family: inherit; background: var(--surface); color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.field-input:focus, select.field-input:focus, textarea.field-input:focus { outline: none; border-color: var(--gold); box-shadow: var(--focus-ring); }
/* A hint tucks under the input it belongs to, which carries a 16px bottom
   margin, so it pulls up by 12px. Only then: a hint anywhere else (after a
   paragraph, after a whole form) is ordinary text and sits where it falls.
   The old rule pulled every hint up, and three of them landed on top of the
   line above. */
.field-hint { margin-top: 0; margin-bottom: 16px; font-size: 11.5px; color: var(--ink-muted); }
.field-input + .field-hint, select.field-input + .field-hint, textarea.field-input + .field-hint,
.money-field + .field-hint, .money-wrap + .field-hint { margin-top: -12px; }

/* Settings as an index + detail: a left rail of tabs (grouped You / Company /
   Connections), and one panel shown at a time on the right. Clicking a tab
   swaps the panel, it never scroll-jumps a long page. */
.set-shell { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: 16px; align-items: start; }
.set-index { padding: 8px; position: sticky; top: 8px; align-self: start; }
.set-detail { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.set-group-label { font-size: 9.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase; color: var(--gold-ink); padding: 11px 10px 5px; }
.set-tab {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 9px 11px; border-radius: 11px; border: none; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink-soft);
  transition: background .12s ease, color .12s ease;
}
.set-tab:hover { background: var(--surface-sunken); color: var(--ink); }
.set-tab.active { background: var(--navy); color: #fff; font-weight: 750; }
.set-tab-ico { width: 18px; text-align: center; font-size: 13px; flex: none; opacity: .9; }
.set-tab > span:nth-child(2) { flex: 1; min-width: 0; }
.set-tab-go { color: var(--ink-faint); font-size: 14px; flex: none; }
.set-tab.active .set-tab-go { color: rgba(255,255,255,.6); }
/* Stack the rail above the panel on a phone; the rail is no longer sticky. */
@media (max-width: 720px) {
  .set-shell { grid-template-columns: 1fr; }
  .set-index { position: static; }
}
.btn-primary.btn-block { width: 100%; }
.error-text { color: var(--red); font-size: 13px; margin-bottom: 12px; }
.hint-text { text-align: center; font-size: 12px; color: var(--ink-muted); margin-top: 16px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

.list-row { display: flex; align-items: center; justify-content: space-between; padding: 13px 16px; gap: 12px; transition: background 0.12s ease; }
.list-row + .list-row { border-top: 1px solid var(--border-soft); }
a.list-row:hover, .list-row.card-hover:hover { background: var(--border-soft); }
.list-row .primary { font-size: 13.5px; font-weight: 500; letter-spacing: -0.003em; }
.list-row .secondary { font-size: 12px; color: var(--ink-muted); margin-top: 2px; }

.checkbox-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; font-size: 13.5px; }
.checkbox-row input { width: 16px; height: 16px; }
.checkbox-row.done .label { text-decoration: line-through; color: var(--ink-faint); }

/* ── Lumen QI floating assistant ──────────────────────────────────────── */
[x-cloak] { display: none !important; }
.lumen-dock-btn {
  position: fixed; z-index: 46;
  bottom: calc(var(--safe-bottom) + (var(--bottombar-h) - 58px) / 2);
  left: calc(50% + var(--dock-shift)); transform: translateX(-50%);
  width: 58px; height: 58px; border: none; cursor: pointer; padding: 0; background: none;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.15s ease, filter 0.15s ease;
}
.lumen-dock-btn:hover { transform: translateX(-50%) scale(1.06); }
.lumen-dock-logo { width: 100%; height: 100%; object-fit: contain; }
/* --glow (0..1, set from live-voice output-audio level) pulses around the bare
   logo while a live call is running, even if the sheet is minimized/closed. */
.lumen-dock-btn.lumen-dock-live { filter: drop-shadow(0 0 calc(4px + var(--glow, 0) * 16px) color-mix(in srgb, var(--brand) 65%, transparent)); }
.lumen-logo-sm { width: 24px; height: 24px; border-radius: 50%; object-fit: contain; vertical-align: middle; margin-right: 6px; }
.lumen-brand { display: flex; align-items: center; }
.lumen-sheet {
  position: fixed; bottom: calc(var(--bottombar-h) + var(--safe-bottom) + 14px);
  left: calc(50% + var(--dock-shift)); transform: translateX(-50%); z-index: 47;
  width: min(420px, calc(100vw - 32px)); height: 520px; max-height: 70vh;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); display: flex; flex-direction: column; overflow: hidden;
}
/* Live-voice mode: the composer/history are hidden and only the glowing orb
   shows, so give the sheet a tighter, brand-tinted frame to signal the mode. */
.lumen-sheet.lumen-sheet-voice {
  height: auto; min-height: 360px;
  border-color: color-mix(in srgb, var(--brand) 45%, var(--border));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent), var(--shadow-md);
}
.lumen-sheet-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border-soft); font-weight: 600; font-size: 13.5px; }
/* Wrapper around history/chat/composer while NOT in live-voice mode. Must be
   a flex column filling the fixed-height sheet, the body's flex:1 scroll and
   the bottom-pinned composer stop working inside a plain block div. */
.lumen-mode-chat { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.lumen-sheet-body { flex: 1; overflow-y: auto; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
.lumen-msg { display: flex; }
.lumen-msg.user { justify-content: flex-end; }
.lumen-bubble { max-width: 85%; padding: 8px 12px; border-radius: 12px; font-size: 13.5px; line-height: 1.4; }
/* Her line breaks are kept, the markup's own indentation is not: pre-wrap
   on the bubble itself renders every newline in the template as real space. */
.lumen-bubble-text { white-space: pre-wrap; }
.lumen-msg.assistant .lumen-bubble { background: var(--border-soft); color: var(--ink); border-bottom-left-radius: 4px; }
.lumen-msg.user .lumen-bubble { background: var(--brand); color: var(--on-brand); border-bottom-right-radius: 4px; }
.lumen-sheet-input { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--border-soft); }
.lumen-sheet-input input { flex: 1; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 12px; font-size: 13.5px; }
.lumen-sheet-input input:focus { outline: none; border-color: var(--accent); }

/* ── Lumen header actions: history, new chat, voice toggle ────────────── */
.lumen-head-actions { display: flex; align-items: center; gap: 2px; }
.lumen-icon-btn { border: none; background: transparent; cursor: pointer; font-size: 15px; line-height: 1; padding: 5px 7px; border-radius: 8px; color: var(--ink-muted); transition: background .12s, color .12s; }
.lumen-icon-btn:hover { background: var(--border-soft); color: var(--ink); }
.lumen-icon-btn.on { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }

/* ── Conversation history panel ───────────────────────────────────────── */
.lumen-history { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.lumen-history-bar { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--border-soft); flex-wrap: wrap; }
.lumen-selectall { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-muted); cursor: pointer; }
.lumen-history-list { flex: 1; overflow-y: auto; padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.lumen-history-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 10px; }
.lumen-history-row:hover { background: var(--border-soft); }
.lumen-history-row.active { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.lumen-history-row input[type=checkbox] { flex: none; }
.lumen-history-open { flex: 1; min-width: 0; text-align: left; border: none; background: transparent; cursor: pointer; display: flex; flex-direction: column; gap: 2px; padding: 0; }
.lumen-history-title { font-size: 13.5px; color: var(--ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lumen-history-meta { font-size: 11.5px; color: var(--ink-muted); }

/* ── Voice mic button ─────────────────────────────────────────────────── */
.lumen-mic { flex: none; border: 1px solid var(--border); background: transparent; cursor: pointer; border-radius: var(--radius-sm); width: 38px; font-size: 15px; color: var(--ink-muted); transition: background .12s, color .12s, border-color .12s; }
.lumen-mic:hover { border-color: var(--accent); color: var(--accent); }
.lumen-mic.listening { background: var(--danger); border-color: var(--danger); color: #fff; animation: lumen-pulse 1.1s ease-in-out infinite; }

/* Screenshots in the assistant: attach button, staging tray, sent thumbnails. */
.lumen-attach { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid var(--border); border-radius: 10px; background: transparent; color: var(--ink-muted); cursor: pointer; }
.lumen-attach:hover { border-color: var(--accent); color: var(--accent); }
.lumen-shots { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 14px 0; }
.lumen-shot { position: relative; width: 54px; height: 54px; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
.lumen-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lumen-shot button { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; display: grid; place-items: center; border: 0; border-radius: 50%; background: rgba(0,0,0,.62); color: #fff; font-size: 13px; line-height: 1; cursor: pointer; }
.lumen-shot-error { font-size: 12px; color: var(--danger); }
.lumen-bubble-shots { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.lumen-bubble-shots img { max-width: 160px; max-height: 160px; border-radius: 8px; display: block; }
@keyframes lumen-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 55%, transparent); } 50% { box-shadow: 0 0 0 5px transparent; } }

/* ── Live voice session status line ───────────────────────────────────── */
.lumen-live-status { display: flex; align-items: center; gap: 6px; padding: 6px 16px; font-size: 11.5px; color: var(--ink-muted); border-top: 1px solid var(--border-soft); }
.lumen-live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-faint); flex: none; }
.lumen-live-dot.on { background: var(--danger); animation: lumen-pulse 1.1s ease-in-out infinite; }

/* ── Live voice stage: replaces the chat window while a real speech-to-
   speech session with Ara is open. --glow (0..1) is her live output-audio
   level, updated continuously as her voice streams in, so the ring pulses to
   the rhythm of her speaking rather than a generic canned animation. ── */
.lumen-voice-stage { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; padding: 24px; }
.lumen-voice-orb {
  width: 152px; height: 152px;
  position: relative;
  /* drop-shadow traces the logo's own alpha, so the glow hugs the orb
     instead of haloing an invisible square around it. */
  filter: drop-shadow(0 0 calc(6px + var(--glow, 0.15) * 34px) color-mix(in srgb, var(--brand) 60%, transparent));
  transition: filter 90ms linear;
}
.lumen-voice-orb img { width: 100%; height: 100%; object-fit: contain; }
.lumen-voice-caption { font-size: 13px; color: var(--ink-muted); text-align: center; }
.lumen-voice-end { color: var(--danger); }

/* ── Editable dashboard: true grid-unit packing engine ──────────────────
   Widgets occupy w×h grid cells on an 8-column grid with a small fixed row
   height: twice the resolution of the old 4-column/118px grid, so drag and
   preset resizing move in noticeably smaller increments. Positions are NOT
   left to the browser's own auto-flow: the `dashboardGrid` Alpine component
   computes explicit --x/--y for every cell with a deterministic skyline
   packer (see public/js/app.js), which is what guarantees cells never
   overlap or hide one another, on every edit and after every save/reload. */
.dash-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-auto-rows: 59px;
  gap: 12px;
  grid-auto-flow: row dense;
}
.dash-cell {
  grid-column: calc(var(--x, 0) + 1) / span var(--w, 4);
  grid-row: calc(var(--y, 0) + 1) / span var(--h, 4);
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  transition: grid-column 120ms ease, grid-row 120ms ease;
}
.dash-cell > .dash-inner { flex: 1 1 auto; min-height: 0; display: flex; }
.dash-cell > .dash-inner > * { flex: 1 1 auto; min-height: 0; width: 100%; }
/* Card-bodied widgets scroll inside their own footprint instead of
   overflowing into the grid row below, this (not the packer) was the
   real source of widgets visually "hiding" each other after a resize. */
.dash-cell > .dash-inner > .card { overflow: auto; }
/* The KPI group SCALES to its footprint instead of scrolling: the stat cards
   share the height (grid rows stretch) and their padding + type shrink with
   the widget via container-query units, so dragging the corner shrinks the
   contents rather than clipping them. */
/* Tiles are FIXED perfect squares now; the widget footprint no longer
   stretches them (that stretch is what made rectangles). Rows size to the
   squares and extra widget height stays empty below. */
.dash-cell > .dash-inner > .kpi-group {
  overflow: hidden;
  height: 100%;
  grid-auto-rows: min-content;
  align-content: start;
}
.dash-cell > .dash-inner > .kpi-group .kpi-slot { min-height: 0; }
.dash-cell > .dash-inner > .kpi-group .kpi-card {
  display: flex; flex-direction: column; justify-content: center;
  overflow: hidden;
  padding: 8px;
}
.dash-cell > .dash-inner > .kpi-group .kpi-label { font-size: 10px; }
.dash-cell > .dash-inner > .kpi-group .kpi-value { font-size: 18px; line-height: 1.1; }
/* The per-stat hide toggle must stay clickable even though edit mode sets
   pointer-events:none on the widget body to prevent stray card clicks. */
.kpi-hide-btn { pointer-events: auto; }
/* The drag handle needs the same escape hatch the hide button got. Without it
   the widget body's pointer-events:none swallowed every grab, so a stat could
   be hidden while editing but never moved. The stats themselves stay live so
   the drop target can be worked out; only the click-through link is switched
   off, which is what the pointer-events:none was guarding against. */
.dash-grid.editing .kpi-group,
.dash-grid.editing .kpi-slot,
.kpi-drag { pointer-events: auto; }
.dash-grid.editing .kpi-card-link { pointer-events: none; }

/* "Choose stats" picker: which stats live in a KPI widget. */
.dash-stats-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  min-width: 210px; padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
}
.dash-stats-head {
  padding: 6px 8px 8px; font-size: 11px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-muted);
}
.dash-stats-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 8px; border-radius: 6px; cursor: pointer;
  font-size: 13px; color: var(--ink); font-weight: 500;
}
.dash-stats-item:hover { background: var(--surface-hover); }
.dash-stats-item input { cursor: pointer; }
/* The calendar fills its footprint and scales its cells to fit, it must NOT
   scroll like a list widget, or month view gets clipped when the widget is
   resized small. Clip + flex so the internal grid shrinks to show all days. */
.dash-cell > .dash-inner > .cal-card { overflow: hidden; display: flex; flex-direction: column; }
.dash-cell > .dash-inner > .cal-card > .cal-root { flex: 1; min-height: 0; }

.dash-grid.editing .dash-cell { outline: 1.5px dashed var(--border); outline-offset: 2px; border-radius: var(--radius); }
.dash-controls {
  display: flex; align-items: center; gap: 4px;
  background: var(--surface-hover); border-radius: 8px;
  padding: 4px 8px; margin-bottom: 6px; font-size: 12px;
}
.dash-controls .dash-handle { cursor: grab; font-weight: 600; margin-right: auto; user-select: none; }
.dash-controls button { padding: 2px 8px; border-radius: 4px; background: var(--border-soft); border: none; cursor: pointer; font: inherit; }
.dash-resize {
  position: absolute; right: 2px; bottom: 2px;
  width: 22px; height: 22px; padding: 0; border: none;
  background: transparent; cursor: nwse-resize; z-index: 6; touch-action: none;
}
.dash-resize::after {
  content: ''; position: absolute; right: 5px; bottom: 5px;
  width: 11px; height: 11px;
  border-right: 2.5px solid var(--brand); border-bottom: 2.5px solid var(--brand);
  border-bottom-right-radius: 3px;
}

/* Resizing with a thumb.
   Two things make the corner grip wrong on a phone. It is a 22px target, well
   under the ~44px a finger needs, and at this width every widget is forced to
   span the full row, so dragging sideways does nothing and the control feels
   broken even once you find it.
   Height is the only axis that can move here, so it becomes a wide bar centred
   on the bottom edge: an obvious handle, big enough to hit, pointing the way it
   actually travels. */
@media (max-width: 900px) {
  .dash-resize {
    right: 0; left: 0; margin: 0 auto;
    bottom: -2px;
    width: 76px; height: 30px;
    cursor: ns-resize;
    border-radius: 999px 999px 0 0;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-bottom: none;
    box-shadow: var(--shadow-sm);
    display: flex; align-items: center; justify-content: center;
  }
  .dash-resize::after {
    content: ''; position: static;
    width: 30px; height: 7px;
    border: none;
    border-top: 2.5px solid var(--ink-faint);
    border-bottom: 2.5px solid var(--ink-faint);
    border-radius: 0;
  }
  .dash-resize:active { background: var(--surface-hover); }
  .dash-resize:active::after { border-color: var(--brand); }
  .dash-grid.editing .dash-cell { padding-bottom: 6px; }
}
.dash-resizing { user-select: none; }
.dash-resizing .dash-cell { transition: none !important; }

@media (max-width: 900px) {
  .dash-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 54px; }
  /* Full-width, naturally-flowed on phones. grid-row:auto is REQUIRED, the
     base rule pins each cell to an explicit --y computed for the 8-col
     desktop packing, and leaving that in place made every widget stack onto
     the same rows and visually overlap on mobile. */
  .dash-cell { grid-column: 1 / -1; grid-row: auto / span var(--h, 4); }
}

/* ── Mobile-first responsive + Capacitor-ready (safe-area insets) ─────────
 * The app is wrapped by Capacitor in a native WebView for iOS/Android, so the
 * shell must respect notch / home-indicator safe areas and collapse the fixed
 * sidebar into an off-canvas drawer on phones. See Docs/CAPACITOR.md.
 * ---------------------------------------------------------------------- */
:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* Bottom dock: the bar's height, and how far its centre sits right of the
     viewport centre (half the sidebar width) so the orb centres in the
     CONTENT area rather than the whole window. */
  --bottombar-h: 68px;
  /* One number for the sidebar's width: the dock starts where the sidebar
     ends, so a bar hard-coded to an older width ran under the sidebar. */
  --sidebar-w: 248px;
  --dock-shift: calc(var(--sidebar-w) / 2);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

/* No horizontal page scroll / iOS rubber-band on the app shell. */
html, body { overflow-x: hidden; }
body { overscroll-behavior-y: none; }

/* Sign-out / footer clears the home indicator. */
.sidebar-footer { padding-bottom: calc(16px + var(--safe-bottom)); }

/* Desktop-only chrome hidden by default; revealed at the mobile breakpoint. */
.mobile-topbar { display: none; }
.sidebar-overlay { display: none; }

@media (max-width: 900px) {
  /* Stack the shell so the sticky top bar spans full width above content
     (the sidebar is fixed/off-canvas, out of normal flow). */
  .app-shell { display: block; }

  /* Sticky mobile top bar with the drawer trigger. */
  .mobile-topbar {
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: 0; z-index: 45;
    padding: calc(9px + var(--safe-top)) calc(14px + var(--safe-right)) 9px calc(14px + var(--safe-left));
    background: var(--surface); border-bottom: 1px solid var(--border);
  }
  .mobile-topbar .hamburger {
    width: 40px; height: 40px; flex-shrink: 0; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--surface); color: var(--ink);
    font-size: 18px; display: flex; align-items: center; justify-content: center;
  }
  .mobile-topbar .mt-title { font-weight: 600; font-size: 15px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Sidebar becomes an off-canvas drawer. */
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 60;
    width: 268px; max-width: 84vw; height: 100dvh; overflow-y: auto;
    padding-top: var(--safe-top); padding-left: var(--safe-left);
    transform: translateX(-100%); transition: transform 0.22s ease;
    box-shadow: var(--shadow-md);
  }
  .sidebar.open { transform: none; }
  .sidebar-overlay {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(0, 0, 0, 0.38);
  }

  main.content { padding: 18px calc(16px + var(--safe-right)) calc(var(--bottombar-h) + var(--safe-bottom) + 28px) calc(16px + var(--safe-left)); }
  .genre-tabs { padding: 8px 12px; flex-wrap: nowrap; gap: 6px; }
  .genre-tabs-scroll { flex-wrap: nowrap; -webkit-overflow-scrolling: touch; }
  .genre-tab { flex-shrink: 0; }
  .page-title { font-size: 19px; }
  /* Vertical placement now comes from --bottombar-h; no phone override. */
}

@media (max-width: 640px) {
  /* One column on phones; every span collapses to full width. */
  .grid { grid-template-columns: 1fr; }
  .col-span-1, .col-span-2, .col-span-3, .col-span-4 { grid-column: 1 / -1; }
  .card.pad { padding: 14px; }
  /* Wide tables scroll inside their card instead of overflowing the page. */
  .card { overflow-x: auto; }
  .row { flex-wrap: wrap; }
  .page-header { margin-bottom: 18px; }
  .page-actions { width: 100%; }
  /* Comfortable touch targets. */
  .nav-link { padding: 10px 12px; }
  .btn { min-height: 40px; }
  .field-input, input, select, textarea { font-size: 16px; } /* iOS: avoid input zoom */
}

/* Inline stage click-and-dropdown on cards & list rows */
.stage-select { margin-top: 6px; font-size: 11px; padding: 3px 6px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); max-width: 100%; cursor: pointer; }
.stage-select:focus { outline: none; border-color: var(--accent); }
.kanban-card .stage-select { width: 100%; }

/* Inbound "Get an Offer" leads, highlighted until worked */
.kanban-card.card-new { border-left: 3px solid var(--accent); background: var(--accent-soft); }
.new-badge { display:inline-block; margin-left:6px; font-size:9.5px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--on-brand); background:var(--brand); border-radius:999px; padding:1px 7px; vertical-align:middle; }

/* Clickable KPI cards + admin back button */
.kpi-card-link { display:block; cursor:pointer; }
.kpi-card-link:hover { border-color: var(--brand); box-shadow: var(--shadow-md); }
.admin-back { display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--ink-muted); margin-bottom:12px; }
.admin-back:hover { color: var(--ink); }

/* ---------------------------------------------------------------------
   Messages: chat threads, bubbles, media, link previews
   --------------------------------------------------------------------- */
.chat-thread-item:hover { background: var(--surface-hover, rgba(0,0,0,0.035)); }
.chat-bubble {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px 14px 14px 4px;
  padding: 8px 12px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}
.chat-bubble.mine {
  background: var(--brand);
  color: var(--on-brand);
  border: none;
  border-radius: 14px 14px 4px 14px;
}
.chat-bubble.mine a { color: var(--on-brand) !important; }
.chat-bubble.mine [style*="color:var(--ink-faint)"],
.chat-bubble.mine [style*="color:var(--ink-muted)"] { color: rgba(255,255,255,0.75) !important; }
@media (max-width: 900px) {
  .chat-shell { grid-template-columns: 1fr !important; }
  .chat-threads { display: none !important; } /* thread list collapses; back via Messages nav */
  .chat-shell:has(> div:only-child) .chat-threads { display: flex !important; }
}

/* Topbar action cluster (message bubble, bell, avatar) */
.topbar-actions {
  position: fixed;
  top: 14px;
  right: 18px;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 60;
}
.topbar-icon-btn {
  position: relative;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
  transition: box-shadow .18s ease, border-color .18s ease;
  text-decoration: none;
  color: var(--ink);
}
.topbar-icon-btn:hover { border-color: var(--brand); }
/* Illuminated state: glows with the brand color while anything is unread. */
.topbar-icon-btn.lit {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--accent-soft, rgba(0,0,0,0.06)), 0 0 14px var(--brand);
}
.topbar-count {
  position: absolute;
  /* Rides the bubble's top-right corner rather than sitting on top of it. */
  top: -1px;
  right: -1px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  /* Not --brand: the bubble itself is now the business colour, so a matching
     badge would disappear into it. Unread counts are red by convention. */
  background: #e0245e;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--surface);
  font-variant-numeric: tabular-nums;
}
/* .topbar-dot retired: the bell now carries the same red count as the message
   bubble, so the two read as one system and "how many" is answered at a
   glance rather than just "some". */
/* Ring, then rest. Swinging without pause reads as decoration, not an alert. */
@keyframes bell-ring {
  0%, 34%, 100% { transform: rotate(0); }
  5% { transform: rotate(13deg); }
  11% { transform: rotate(-11deg); }
  17% { transform: rotate(7deg); }
  23% { transform: rotate(-5deg); }
  29% { transform: rotate(2deg); }
}
.topbar-icon-btn.ringing .bell-glyph { display:inline-block; animation: bell-ring 1s ease-in-out infinite; transform-origin: top center; }

/* --- Message bubble ---------------------------------------------------
   The glyph IS the button: no coloured blob behind an outline icon, and no
   corner faked into a tail. The hit area stays a comfortable 38px while the
   bubble draws at 26px, and it takes the active business colour like the
   rest of the chrome. */
.topbar-bubble {
  position: relative;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; text-decoration: none;
  background: none;
  color: var(--brand);
  transition: transform .14s cubic-bezier(.34,1.4,.64,1), filter .14s ease;
  /* drop-shadow follows the bubble's silhouette; box-shadow would draw a
     rectangle around a shape that isn't one. */
  filter: drop-shadow(0 2px 5px color-mix(in srgb, var(--brand) 38%, transparent));
  -webkit-tap-highlight-color: transparent;
}
.topbar-bubble-glyph { display: block; }
.topbar-bubble:hover {
  transform: translateY(-1px) scale(1.06);
  filter: drop-shadow(0 4px 9px color-mix(in srgb, var(--brand) 52%, transparent));
}
.topbar-bubble:active { transform: scale(.94); }

/* Heartbeat while messages are unread: two beats, then rest. A scale reads
   on the bubble's own shape, where the old expanding ring could not. */
@keyframes msg-pulse {
  0%, 42%, 100% { transform: scale(1); }
  12% { transform: scale(1.13); }
  24% { transform: scale(1); }
  33% { transform: scale(1.09); }
}
.topbar-bubble.pulsing { animation: msg-pulse 2.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .topbar-bubble.pulsing { animation: none; }
  .topbar-bubble, .topbar-bubble:hover, .topbar-bubble:active { transition: none; transform: none; }
}

/* --- Notification bell -------------------------------------------------
   Matched to the message bubble: a drawn glyph that IS the button, in the
   active business colour, with a silhouette-following shadow. Sits quieter
   than the bubble at rest, a bell that shouts with nothing to say teaches
   people to stop looking at it. */
.topbar-bell {
  position: relative;
  width: 38px; height: 38px;
  border: none;
  background: none;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
  color: var(--ink-muted);
  transition: transform .14s cubic-bezier(.34,1.4,.64,1), color .14s ease, filter .14s ease;
  -webkit-tap-highlight-color: transparent;
}
.bell-glyph { display: block; line-height: 0; }
.topbar-bell:hover { transform: translateY(-1px) scale(1.06); color: var(--ink); }
.topbar-bell:active { transform: scale(.94); }

/* Something to report: takes the business colour and lifts off the bar. */
.topbar-bell.ringing {
  color: var(--brand);
  filter: drop-shadow(0 2px 5px color-mix(in srgb, var(--brand) 38%, transparent));
}
.topbar-bell.ringing .bell-glyph {
  animation: bell-ring 2.4s ease-in-out infinite;
  transform-origin: top center;
}
@media (prefers-reduced-motion: reduce) {
  .topbar-bell.ringing .bell-glyph { animation: none; }
  .topbar-bell, .topbar-bell:hover, .topbar-bell:active { transition: none; transform: none; }
}

/* Right-click context menus for the bubble & bell */
.topbar-menu {
  position: absolute;
  top: 44px; right: 0;
  z-index: 80;
  min-width: 210px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 5px;
  overflow: hidden;
}
.topbar-menu-item {
  display: flex; align-items: center; gap: 8px;
  width: 100%; text-align: left;
  padding: 9px 11px; border: none; background: none;
  font: inherit; font-size: 13px; color: inherit; cursor: pointer;
  border-radius: 8px; text-decoration: none;
}
.topbar-menu-item:hover { background: var(--surface-hover); }
.topbar-panel {
  position: fixed;
  top: 58px;
  right: 18px;
  width: 340px;
  max-width: calc(100vw - 24px);
  max-height: 70vh;
  overflow-y: auto;
  z-index: 70;
  border-radius: 14px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  box-shadow: 0 16px 48px rgba(0,0,0,0.22);
}
.topbar-panel .panel-head { padding: 12px 14px; font-weight: 700; font-size: 13px; border-bottom: 1px solid var(--hairline); display:flex; justify-content:space-between; align-items:center; }
.notif-item { display:flex; gap:10px; padding:11px 14px; border-bottom:1px solid var(--hairline); text-decoration:none; color:inherit; font-size:13px; }
.notif-item:hover { background: var(--surface-hover, rgba(0,0,0,0.03)); }
.notif-item.unread { background: var(--accent-soft); }
.notif-item .notif-time { font-size: 11px; color: var(--ink-faint); }
@media (max-width: 900px) {
  /* The cluster is NOT a child of .mobile-topbar in the DOM, so making it
     static dropped it into normal flow, the avatar landed on its own row
     above the business tabs. Keep it fixed and park it on the topbar's right. */
  .topbar-actions {
    position: fixed;
    top: calc(env(safe-area-inset-top) + 10px);
    right: 12px;
    left: auto;
    margin-left: 0;
    z-index: 60;
  }
  .mobile-topbar { display: flex; align-items: center; gap: 8px; padding-right: calc(112px + var(--safe-right)); }
  .topbar-panel { top: 62px; right: 8px; }
}

/* ---------------------------------------------------------------------
   Calendar: high-contrast, professional grid
   --------------------------------------------------------------------- */
.cal-root { --cal-line: color-mix(in srgb, var(--ink) 16%, transparent); }
.cal-frame {
  border: 1.5px solid var(--cal-line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
  min-height: 0;
  min-width: 0;
}
/* display lives in a class (not inline) because Alpine x-show drives
   style.display and would otherwise wipe an inline display value, collapsing
   the grid back to block. When shown, x-show clears style.display and these win. */
.cal-month { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.cal-week { display: block; }
.cal-week-grid { display: grid; height: 100%; }
.cal-dow-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  background: color-mix(in srgb, var(--ink) 5%, var(--surface));
  border-bottom: 1.5px solid var(--cal-line);
}
.cal-dow {
  padding: 8px 6px;
  text-align: center;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
}
.cal-cell {
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  border-right: 1px solid var(--cal-line);
  border-bottom: 1px solid var(--cal-line);
  padding: 5px;
  cursor: pointer;
  transition: background .12s ease;
}
.cal-cell:hover { background: color-mix(in srgb, var(--ink) 4%, var(--surface)); }
.cal-cell.out-month { background: color-mix(in srgb, var(--ink) 3%, var(--surface)); }
.cal-cell.out-month .cal-daynum { color: var(--ink-faint); }
.cal-cell.is-today { background: color-mix(in srgb, var(--brand) 7%, var(--surface)); box-shadow: inset 0 2.5px 0 var(--brand); }
.cal-daynum {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  width: 24px;
  border-radius: 50%;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
}
.cal-daynum.today { background: var(--brand); color: var(--on-brand); box-shadow: 0 1px 4px color-mix(in srgb, var(--brand) 55%, transparent); }
.cal-chip {
  display: block;
  width: 100%;
  text-align: left;
  color: #fff;
  border: none;
  border-radius: 5px;
  padding: 2.5px 6px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  text-shadow: 0 1px 1px rgba(0,0,0,0.28);
  box-shadow: 0 1px 2px rgba(0,0,0,0.14);
  transition: filter .12s ease, transform .12s ease;
}
.cal-chip:hover { filter: brightness(1.08); transform: translateY(-1px); }
.cal-chip-lg { border-radius: 7px; padding: 5px 8px; white-space: normal; }
.cal-more {
  border: none;
  background: none;
  text-align: left;
  padding: 1px 4px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--brand);
  cursor: pointer;
}
.cal-more:hover { text-decoration: underline; }
.cal-col { min-width: 0; border-right: 1px solid var(--cal-line); display: flex; flex-direction: column; overflow: hidden; }
.cal-col-body { padding: 8px; flex: 1; min-height: 0; overflow-y: auto; cursor: pointer; display: flex; flex-direction: column; gap: 5px; }
.cal-col-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: color-mix(in srgb, var(--ink) 5%, var(--surface));
  border-bottom: 1.5px solid var(--cal-line);
  padding: 8px 6px;
  text-align: center;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
}
.cal-col-head.today { color: var(--brand); box-shadow: inset 0 -2.5px 0 var(--brand); }

/* ---------------------------------------------------------------------
   Inbound-lead celebration + gold/green priority glow
   --------------------------------------------------------------------- */
.lumen-celebrate {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  pointer-events: auto; cursor: pointer;
  background: radial-gradient(circle at center, rgba(0,0,0,0.28), rgba(0,0,0,0.5));
  animation: celebrate-in .25s ease;
}
.lumen-celebrate.fading { opacity: 0; transition: opacity .4s ease; }
@keyframes celebrate-in { from { opacity: 0; } to { opacity: 1; } }
.lumen-confetti { position: absolute; inset: 0; width: 100%; height: 100%; }
.lumen-celebrate-text { position: relative; text-align: center; z-index: 1; }
.lumen-celebrate-emoji { font-size: 64px; animation: coin-bounce .8s ease infinite alternate; }
@keyframes coin-bounce { from { transform: translateY(0) scale(1);} to { transform: translateY(-14px) scale(1.08);} }
.lumen-celebrate-flash {
  font-size: clamp(36px, 8vw, 84px);
  font-weight: 900;
  letter-spacing: 0.04em;
  background: linear-gradient(180deg, #fde68a 0%, #f59e0b 45%, #b45309 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 3px 10px rgba(0,0,0,0.5));
  animation: gold-flash 0.5s steps(1) infinite;
  margin-top: 6px;
}
@keyframes gold-flash { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.lumen-celebrate-sub { margin-top: 10px; color: #eafce9; font-size: 15px; font-weight: 600; text-shadow: 0 1px 6px rgba(0,0,0,0.6); }

/* A new inbound lead card glows gold + green until it's worked. */
@keyframes inbound-glow {
  0%, 100% { box-shadow: 0 0 0 1px #16a34a, 0 0 14px 2px rgba(22,163,74,0.55); }
  50% { box-shadow: 0 0 0 1px #eab308, 0 0 20px 4px rgba(234,179,8,0.6); }
}
.kanban-card.card-new, .list-row.card-new, .card.card-new {
  border-left: 3px solid #16a34a;
  background: linear-gradient(90deg, rgba(234,179,8,0.10), rgba(22,163,74,0.08));
  animation: inbound-glow 1.8s ease-in-out infinite;
}
.new-badge {
  background: linear-gradient(135deg, #eab308, #16a34a) !important;
  color: #fff !important;
}
@media (prefers-reduced-motion: reduce) {
  .lumen-celebrate-emoji, .lumen-celebrate-flash, .kanban-card.card-new, .topbar-bubble.pulsing, .topbar-bell.ringing .bell-glyph { animation: none !important; }
}

/* ── Platform owner's business-type selector (sits in the tab strip) ──────
   With 50+ business types a flat tab row is unusable, so the operator gets a
   grouped dropdown instead. Also keeps the strip from colliding with the
   top-right icons. */
/* Only the TABS scroll. The picker is a sibling of the scroller, never a
   child: an overflow container clips absolutely positioned descendants
   regardless of z-index, which is what buried the dropdown. */
/* The workspaces get the row; the test picker gets what is left.
   padding-right was a fixed 68px reserved for a picker that used to be
   absolutely positioned. It is in the flow now, so that was 68px of nothing
   on every screen, and the scroller would not grow into it either. */
.genre-tabs { overflow: visible; align-items: center; }
.genre-tabs-scroll { display: flex; gap: 4px; min-width: 0; flex: 1 1 auto; overflow-x: auto; scrollbar-width: thin; }
.bt-picker { position: relative; flex: 0 0 auto; }
.bt-picker-btn { white-space: nowrap; cursor: pointer; border: 1px dashed var(--border); background: none; font: inherit; display: inline-flex; align-items: center; gap: 6px; }
.bt-picker-caret { opacity: .6; }
.bt-picker-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 200;
  width: 300px; max-width: calc(100vw - 32px); max-height: 70vh; overflow-y: auto; padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
}
.bt-picker-group {
  padding: 8px 8px 4px; font-size: 10.5px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-muted);
}
.bt-picker-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; padding: 7px 8px; border: none; background: none; cursor: pointer;
  border-radius: 6px; font: inherit; font-size: 13px; color: var(--ink); text-align: left;
}
.bt-picker-item:hover { background: var(--surface-hover); }
.bt-picker-item.on { font-weight: 650; }
.bt-picker-on { font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--brand); }

/* Phone: the picker is its icon and nothing else.
   Spelled out it took 190px of a 375px screen and left the business types in
   a 101px sliver, which is the row somebody actually navigates with.
   It sits after every .bt-picker rule above on purpose: same specificity, so
   source order decides, and higher up it lost to the very declarations it is
   meant to replace. */
@media (max-width: 900px) {
  .bt-picker-btn { padding: 7px 9px; gap: 0; }
  .bt-picker-label, .bt-picker-caret { display: none; }
  /* Anchored to its right edge, or a 300px menu opens off the screen. The
     base rule sets left: 0, and left wins over right when both are set on a
     fixed-width box, so this has to clear it rather than just add to it. */
  .bt-picker-menu { left: auto; right: 0; }
}


/* ── Bottom dock bar ──────────────────────────────────────────────────
   A permanent strip the Lumen assistant lives in, with Messages and the
   notification bell on either side of it. Starts after the sidebar so the
   orb centres on the CONTENT, not the window. */
.bottombar {
  position: fixed; left: var(--sidebar-w); right: 0; bottom: 0; z-index: 45;
  height: calc(var(--bottombar-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.05);
}
.bottombar-inner {
  height: var(--bottombar-h);
  display: flex; align-items: center; justify-content: center;
  /* The gap element reserves the orb's footprint between the two icons. */
  gap: 26px;
}
.bb-slot { position: relative; display: flex; align-items: center; }
.bb-orb-gap { width: 58px; flex: 0 0 auto; }

/* Menus and panels anchored to the dock open UPWARD. */
.topbar-menu.menu-up { top: auto; bottom: 50px; right: auto; left: 50%; transform: translateX(-50%); }
.topbar-panel.panel-up {
  position: absolute; top: auto; bottom: 50px;
  right: auto; left: 50%; transform: translateX(-50%);
  width: 340px; max-width: calc(100vw - 24px);
}

@media (max-width: 900px) {
  /* Sidebar is a drawer on phones, so the bar spans the full width and the
     orb centres on the window. */
  :root { --dock-shift: 0px; --bottombar-h: 62px; }
  .bottombar { left: 0; }
}

/* In the dock the bell sits opposite the solid blue message bubble, so give it
   the same visual weight instead of a bare glyph on the bar. */
/* The bell used to need a chip to stand out on the old bottom bar. It is now
   a drawn glyph that carries itself, and the chip fought that everywhere the
   bell was re-homed: most visibly on phones, where it read as a stray box
   next to the bare profile avatar. */
.bottombar .topbar-bell:hover { transform: translateY(-1px) scale(1.06); }
.bottombar .topbar-bell.ringing {
  filter: drop-shadow(0 2px 5px color-mix(in srgb, var(--brand) 38%, transparent));
}

/* A test-driven workspace carries a × so the operator can close it from the
   tab strip instead of going to Settings and toggling it off. */
.genre-tab-wrap { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; }
.genre-tab-wrap .genre-tab.is-test { padding-right: 28px; border: 1px dashed var(--border); }
.genre-tab-close-form { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); line-height: 0; }
.genre-tab-close {
  border: none; background: none; cursor: pointer; padding: 0;
  width: 18px; height: 18px; border-radius: 999px;
  font: inherit; font-size: 15px; line-height: 1; color: var(--ink-muted);
  display: inline-flex; align-items: center; justify-content: center;
}
.genre-tab-close:hover { background: var(--border-soft); color: var(--ink); }
.genre-tab.active.is-test + .genre-tab-close-form .genre-tab-close { color: var(--on-brand); }

/* ── Estimates / quotes ───────────────────────────────────────────────
   The estimate is a document a customer sees, so it is laid out like one:
   a paper-ish sheet with a ruled line table, right-aligned money, and a
   totals block that reads top-to-bottom the way an invoice does. */
.qt-status {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
}
.qt-status::before { content: ''; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
.qt-status-draft { background: var(--cool-soft); color: var(--cool-ink); }
.qt-status-sent { background: var(--gold-tint); color: var(--gold-warm-ink); }
.qt-status-accepted { background: var(--green-soft); color: var(--green-ink); }
.qt-status-declined { background: var(--red-soft); color: var(--red-ink); }
.qt-status-expired { background: var(--gold-tint); color: var(--gold-warm-ink); }

/* List */
.qt-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.qt-table th {
  text-align: left; padding: 10px 16px; white-space: nowrap;
  font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-muted); border-bottom: 1px solid var(--border);
}
.qt-table td { padding: 12px 16px; border-bottom: 1px solid var(--hairline); vertical-align: middle; }
.qt-table tbody tr:last-child td { border-bottom: none; }
.qt-table tbody tr:hover { background: var(--surface-hover, rgba(0,0,0,.02)); }
.qt-num { font-weight: 600; text-decoration: none; color: var(--ink); }
.qt-num:hover { color: var(--brand); }
.qt-money { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.qt-empty { padding: 44px 20px; text-align: center; color: var(--ink-muted); font-size: 13.5px; }
.qt-empty strong { display: block; color: var(--ink); font-size: 15px; margin-bottom: 6px; }

/* Document layout */
.qt-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px; align-items: start; }
@media (max-width: 1080px) { .qt-layout { grid-template-columns: 1fr; } }

.qt-doc { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.qt-doc-head { padding: 22px 24px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.qt-doc-title { font-size: 19px; font-weight: 650; letter-spacing: -0.01em; margin: 0 0 3px; }
.qt-doc-sub { font-size: 12.5px; color: var(--ink-muted); margin: 0; }
.qt-doc-meta { text-align: right; font-size: 12.5px; color: var(--ink-muted); line-height: 1.8; }
.qt-doc-meta strong { color: var(--ink); font-weight: 600; }

.qt-lines { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.qt-lines th {
  text-align: left; padding: 10px 24px; font-size: 11px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-muted);
  border-bottom: 1px solid var(--border); background: var(--surface-2, rgba(0,0,0,.015));
}
.qt-lines td { padding: 12px 24px; border-bottom: 1px solid var(--hairline); }
.qt-lines tbody tr:last-child td { border-bottom: none; }
.qt-line-desc { font-weight: 500; }
.qt-line-tag {
  margin-left: 7px; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  background: var(--border-soft); color: var(--ink-muted); vertical-align: middle;
}
.qt-x { border: none; background: none; cursor: pointer; color: var(--ink-faint); font-size: 17px; line-height: 1; padding: 2px 6px; border-radius: 6px; }
.qt-x:hover { background: var(--red-soft, #fdecea); color: var(--danger, #c0392b); }

/* Builder */
.qt-builder { padding: 18px 24px; border-top: 1px solid var(--border); background: var(--surface-2, rgba(0,0,0,.015)); }
.qt-builder + .qt-builder { border-top: 1px solid var(--hairline); }
.qt-builder-label { font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-muted); margin-bottom: 10px; }
.qt-presets { display: flex; flex-wrap: wrap; gap: 7px; }
.qt-preset {
  display: inline-flex; align-items: baseline; gap: 7px;
  padding: 7px 12px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); cursor: pointer; font: inherit; font-size: 12.5px; color: var(--ink);
  transition: border-color .12s ease, transform .12s ease;
}
.qt-preset:hover { border-color: var(--brand); transform: translateY(-1px); }
.qt-preset-price { color: var(--ink-muted); font-variant-numeric: tabular-nums; font-size: 11.5px; }
.qt-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.qt-row .field { margin: 0; }
.qt-row .field-input { margin-bottom: 0; }
.qt-check { display: inline-flex; gap: 6px; align-items: center; font-size: 12.5px; color: var(--ink-muted); padding-bottom: 9px; white-space: nowrap; }

/* Totals */
.qt-totals { font-size: 13.5px; }
.qt-total-row { display: flex; justify-content: space-between; gap: 16px; padding: 7px 0; }
.qt-total-row span:last-child { font-variant-numeric: tabular-nums; }
.qt-total-row.muted { color: var(--ink-muted); }
/* The grand total on its own navy plate with a gold "TOTAL" cue, so the one
   number that matters reads as the document's conclusion. */
.qt-total-row.grand {
  margin-top: 10px; padding: 13px 15px; border-radius: 13px;
  background: linear-gradient(160deg, #0f1b2d, #0a1220); color: var(--on-navy);
  font-size: 21px; font-weight: 800; letter-spacing: -0.02em;
}
.qt-total-row.grand span:first-child { font-size: 10px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; color: var(--gold-2); align-self: center; }
.qt-total-row.optional { border-top: 1px dashed var(--cool-ink); opacity: .85; margin-top: 6px; padding-top: 10px; color: var(--ink-muted); font-size: 12.5px; }

/* Printing an estimate should produce the document, not the app. */
@media print {
  .sidebar, .genre-tabs, .topbar-actions, .bottombar, .lumen-dock-btn,
  .page-actions, .qt-builder, .qt-side-form, .qt-x { display: none !important; }
  main.content { padding: 0 !important; max-width: none !important; }
  .qt-layout { grid-template-columns: 1fr !important; }
  .qt-doc, .card { border: none !important; box-shadow: none !important; }
}

/* Invoice states reuse the estimate pills; these three are invoice-only. */
.qt-status-partial { background: var(--gold-tint); color: var(--gold-warm-ink); }
.qt-status-overdue { background: var(--red-soft); color: var(--red-ink); }
.qt-status-void { background: var(--border-soft); color: var(--ink-faint); }

/* "Needs attention": a dark feature band above the grid, money owed and work
   due, so the first thing each morning is what to act on. The band is navy
   with a gold spotlight; each tile encodes urgency in its border + value tone. */
.attn-bar {
  position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px; margin-bottom: 18px; padding: 18px 20px; border-radius: var(--radius-lg);
  background: linear-gradient(160deg, #0f1b2d, #0a1220); overflow: hidden;
}
.attn-bar::after { content: ""; position: absolute; inset: 0; background: radial-gradient(600px 300px at 88% 10%, rgba(201, 162, 39, .14), transparent); pointer-events: none; }
.attn-item {
  position: relative; z-index: 1; display: flex; flex-direction: column; gap: 3px;
  padding: 13px 14px; border-radius: 13px;
  background: rgba(255, 255, 255, .04); border: 1px solid rgba(38, 64, 95, .9);
  text-decoration: none; transition: background .15s ease;
}
.attn-item:hover { background: rgba(255, 255, 255, .08); }
.attn-value { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--on-navy); line-height: 1.1; }
.attn-label { font-size: 11.5px; color: var(--on-navy-muted); line-height: 1.35; }
/* Urgency: an alarm tile takes a red edge and red value; money-at-stake takes
   the gold edge. */
.attn-item.attn-urgent { border-color: rgba(224, 90, 90, .5); }
.attn-item.attn-urgent .attn-value { color: #ff9c9c; }
.attn-item.attn-money { border-color: rgba(201, 162, 39, .45); }
.attn-item.attn-money .attn-value { color: var(--gold-2); }


/* ── Modern polish ────────────────────────────────────────────────────
   Deliberately small. The app's character, calm, dense, per-business-type
   colour: is right; these close the gaps that make an otherwise clean UI
   feel unfinished, and every one of them inherits the active business
   type's --brand rather than introducing a new palette. */

:root {
  /* Native UI the stylesheet cannot style, scrollbars, date pickers, the
     select popup, autofill backgrounds: follows this. Emitted per theme by
     ThemeCatalog, so a dark workspace stops getting white scrollbars. */
  color-scheme: var(--scheme, light);

  /* One line that themes every checkbox, radio, range and progress bar to the
     business type you are standing in. Previously they were browser blue in
     all 53 workspaces. */
  accent-color: var(--brand);
}

/* Keyboard focus. There was none: inputs killed the outline and replaced it
   only on :focus, and buttons/links/tabs had nothing at all, so tabbing
   through the app was invisible. :focus-visible keeps mouse clicks clean
   while making keyboard navigation obvious. */
:where(a, button, [role="button"], input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.field-input:focus-visible,
select.field-input:focus-visible,
textarea.field-input:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* Buttons acknowledge the press. 1px is enough to feel responsive without
   turning into an animation. */
.btn { transition: background .12s ease, border-color .12s ease, opacity .12s ease, transform .08s ease; }
.btn:active { transform: translateY(1px); }

/* Money and metrics must not jitter between rows. */
.kpi-value, .data-table td, .qt-money, .attn-value, .stat-value {
  font-variant-numeric: tabular-nums;
}

/* Headlines rag better when the browser balances them; harmless where
   unsupported. */
.page-title, .section-title, .ld-hero h1, .ld-section-head h2 { text-wrap: balance; }
.page-subtitle, .field-hint { text-wrap: pretty; }

/* Selection picks up the workspace colour instead of system blue. */
::selection { background: var(--accent-soft); color: var(--accent-ink); }

/* Scrollbars inside panels, matched to the theme rather than the OS default. */
.bt-list, .topbar-panel, .dash-grid, .board-col { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }

/* Honour the system setting, the app already animates lightly, but a press
   state and hover lift should still stop when asked. */
@media (prefers-reduced-motion: reduce) {
  .btn:active { transform: none; }
  .card-hover:hover, .attn-card:hover, .ld-card:hover, .bt-trigger:hover { transform: none; }
}

/* ── Phone shell ──────────────────────────────────────────────────────
   Hidden above 900px, where the sidebar and desktop dock take over. Every
   colour here is the active business type's --brand, so a phone feels like
   the workspace it is in rather than like a generic app. */
.mob { display: none; }

@media (max-width: 900px) {
  .mob { display: block; }

  /* The desktop dock's BAR would collide with the phone tab bar, but hiding
     the whole thing took the notification bell with it, and the phone dock
     has no bell, so notifications became unreachable. Strip the bar instead
     and re-home the bell beside the profile, where there is room. */
  .bottombar {
    position: static;
    height: auto;
    padding: 0;
    background: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
    pointer-events: none;   /* only the re-homed bell stays interactive */
  }
  .bottombar-inner { display: contents; }

  /* Messages already has its own dock tab, a second entry point is clutter.
     Targeted by content rather than position so reordering the dock can't
     silently hide the wrong control. */
  .bottombar .bb-slot:has(.topbar-bubble) { display: none; }

  /* The bell rides the sticky top bar, immediately left of the avatar:
     38px avatar + 10px gap, inside the bar's own 14px + safe-area padding. */
  .bottombar .bb-slot:has(.topbar-bell) {
    position: fixed;
    top: calc(var(--safe-top) + 9px);
    right: calc(62px + var(--safe-right));
    z-index: 46;
    pointer-events: auto;
  }
  .bottombar .topbar-menu { top: 44px; right: 0; }
  main.content { padding-bottom: calc(var(--mob-dock-h) + var(--safe-bottom) + 24px); }

  :root { --mob-dock-h: 62px; }

  .mob-dock {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    height: calc(var(--mob-dock-h) + var(--safe-bottom));
    padding-bottom: var(--safe-bottom);
    display: grid; grid-template-columns: repeat(5, 1fr); align-items: center;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    border-top: 1px solid var(--hairline);
  }
  .mob-tab {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    height: var(--mob-dock-h); position: relative;
    font: inherit; font-size: 10.5px; font-weight: 550; letter-spacing: .01em;
    color: var(--ink-faint); text-decoration: none; background: none; border: none; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .mob-tab .mob-ico { font-size: 19px; line-height: 1; }
  .mob-tab.on { color: var(--brand); }
  .mob-tab:active { opacity: .6; }
  .mob-orb-gap { display: block; }
  .mob-badge {
    position: absolute; top: 6px; left: 50%; margin-left: 6px;
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
    background: var(--brand); color: var(--on-brand);
    font-size: 10px; font-weight: 700; display: grid; place-items: center;
  }

  /* Create. Above the dock and to the right, thumb-reachable, and clear of
     Lumen in the centre. What it makes follows the section you are on. */
  .mob-fab-form { position: fixed; right: 16px; bottom: calc(var(--mob-dock-h) + var(--safe-bottom) + 16px); z-index: 51; }

  /* One object, not a button with a caption floating beside it, detached,
     the label read as a stray tooltip sitting on the page's content. */
  .mob-fab {
    position: fixed; right: 16px;
    bottom: calc(var(--mob-dock-h) + var(--safe-bottom) + 16px);
    z-index: 51;
    height: 46px; padding: 0 18px 0 14px;
    display: inline-flex; align-items: center; gap: 7px;
    border-radius: 999px; border: none; cursor: pointer;
    background: var(--brand); color: var(--on-brand);
    font: inherit; font-size: 14px; font-weight: 600; white-space: nowrap;
    text-decoration: none;
    box-shadow: 0 6px 18px color-mix(in srgb, var(--brand) 40%, transparent), 0 2px 6px rgba(0,0,0,.2);
    transition: transform .12s ease;
  }
  .mob-fab-plus { font-size: 20px; line-height: 1; font-weight: 400; }
  .mob-fab-text { line-height: 1; }
  .mob-fab-form .mob-fab { position: static; }
  .mob-fab:active { transform: scale(.94); }


  /* Everything else, as a sheet. */
  .mob-scrim { position: fixed; inset: 0; z-index: 58; background: rgba(0,0,0,.42); }
  .mob-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 59;
    max-height: 88vh; display: flex; flex-direction: column;
    background: var(--surface); border-top-left-radius: 20px; border-top-right-radius: 20px;
    box-shadow: 0 -8px 40px rgba(0,0,0,.28);
    padding-bottom: var(--safe-bottom);
  }
  .mob-sheet-grip { width: 38px; height: 4px; border-radius: 999px; background: var(--border); margin: 10px auto 4px; flex: none; }
  .mob-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 18px 14px; border-bottom: 1px solid var(--hairline); flex: none; }
  .mob-sheet-title { font-size: 16px; font-weight: 650; letter-spacing: -.01em; }
  .mob-sheet-sub { font-size: 12.5px; color: var(--ink-muted); margin-top: 1px; }
  .mob-sheet-x { border: none; background: var(--border-soft); color: var(--ink-muted); width: 32px; height: 32px; border-radius: 999px; font-size: 14px; cursor: pointer; }
  .mob-sheet-body { overflow-y: auto; overscroll-behavior: contain; padding: 6px 12px 22px; }

  .mob-group { margin-top: 14px; }
  .mob-group-head { font-size: 10.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-faint); padding: 6px 8px; }
  .mob-row {
    display: flex; align-items: center; gap: 12px; padding: 13px 10px; border-radius: 12px;
    font-size: 15px; color: var(--ink); text-decoration: none; min-height: 46px;
  }
  .mob-row:active { background: var(--surface-hover); }
  .mob-row-ico { width: 22px; text-align: center; font-size: 15px; color: var(--ink-muted); flex: none; }
  .mob-row-go { margin-left: auto; color: var(--ink-faint); }

  .mob-switch { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 8px 0; }
  .mob-switch-item {
    display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; border-radius: 999px;
    border: 1px solid var(--border); font-size: 14px; color: var(--ink); text-decoration: none;
  }
  .mob-switch-item.on { border-color: var(--brand); background: var(--accent-soft); }
  .mob-dot { width: 8px; height: 8px; border-radius: 999px; flex: none; }

  /* Lumen keeps the centre of the dock on every screen. */
  .lumen-dock-btn { bottom: calc(var(--safe-bottom) + 4px); left: 50%; width: 54px; height: 54px; z-index: 52; }
  .lumen-sheet { bottom: calc(var(--mob-dock-h) + var(--safe-bottom) + 12px); left: 50%; }
}

/* Swipe feedback on phone kanban cards: the edge you are flinging toward
   tints with what will happen, brand for advance, amber for snooze. */
@media (max-width: 900px) {
  .kanban-card { touch-action: pan-y; will-change: transform; }
  .kanban-card.swipe-advance { box-shadow: inset 4px 0 0 var(--brand); }
  .kanban-card.swipe-snooze { box-shadow: inset -4px 0 0 var(--amber); }
}

/* The orb acknowledges the first tap instantly, so the short wait that lets
   a double-tap register is never felt as lag. */
.lumen-dock-btn { transition: transform .12s ease, box-shadow .12s ease; }
.lumen-dock-btn.lumen-dock-armed {
  transform: translateX(-50%) scale(.92);
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--brand) 55%, transparent));
}
@media (prefers-reduced-motion: reduce) {
  .lumen-dock-btn { transition: none; }
  .lumen-dock-btn.lumen-dock-armed { transform: none; }
}

/* ── Native shell ──────────────────────────────────────────────────────
   Only applies inside the iOS/Android app, where <html> carries .is-native.
   The web build is untouched. */
.native-offline {
  position: fixed; left: 0; right: 0; top: 0; z-index: 200;
  padding: calc(var(--safe-top, env(safe-area-inset-top)) + 8px) 16px 9px;
  background: var(--amber, #b45309); color: #fff;
  font-size: 12.5px; font-weight: 550; text-align: center;
  transform: translateY(-100%); transition: transform .22s ease;
}
.native-offline.on { transform: translateY(0); }

.is-native {
  /* Inside an app, a text selection lasso on chrome feels broken; inputs and
     record content still select normally below. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.is-native input, .is-native textarea, .is-native [contenteditable],
.is-native .card, .is-native table {
  -webkit-user-select: text;
  user-select: text;
  /* The callout is the long-press menu on a phone: Copy, Paste, Look Up,
     Replace. Turning it off on the chrome above turned it off everywhere,
     because it inherits, so a field had no menu at all. Anywhere there is
     text to work with, it comes back. */
  -webkit-touch-callout: default;
}
/* Rubber-band scrolling past the top reveals the page background behind a
   fixed dock, which reads as a rendering fault rather than a gesture. */
.is-native body { overscroll-behavior-y: none; }

/* ── Phone KPIs: four true squares, two by two ─────────────────────────
   A phone screen can't read eight tiles; it gets the four stats the genre
   marked primary, as genuine 1:1 squares. The rest are still on desktop and
   in the edit-layout picker: hidden here, not gone. */
@media (max-width: 900px) {
  /* Four small even squares in ONE line. The dashboard's widget grid gives
     every cell its desktop height and stretches the stat cards to fill it
     these rules match that specificity and win by cascade order, so the
     squares stay squares inside the widget system. */
  /* Four across, but capped: on a phone (≈390px) the cap is never hit and
     they fill the row; at tablet widths the same rule would otherwise make
     ~190px giants that clip inside the widget's two-row footprint. */
  .kpi-group { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
  .kpi-slot:not(.kpi-primary) { display: none; }

  /* The KPI widget's cell shrinks to two grid rows instead of its desktop
     footprint: the stats strip, not a stats block. */
  .dash-cell:has(> .dash-inner > .kpi-group) { grid-row: auto / span 2; }

  .dash-cell > .dash-inner > .kpi-group {
    height: 100%;
    grid-auto-rows: auto;
    align-content: center;
    overflow: visible;
    /* container-type stays: cq units in the desktop rules would otherwise
       fall back to viewport units and inflate the type. The overrides below
       simply out-cascade them with fixed sizes. */
  }
  /* The square is enforced on the grid SLOT, not the flexed card: WebKit
     (the iOS app) collapses aspect-ratio on a stretched flex child to zero
     height, which rendered the tiles as slivers. A grid item's aspect-ratio
     is solid in both engines; the card then just fills its slot. */
  .kpi-slot.kpi-primary { aspect-ratio: 1 / 1; }
  .kpi-card,
  .dash-cell > .dash-inner > .kpi-group .kpi-card {
    aspect-ratio: auto;
    height: 100%; min-height: 0;
    align-items: center; text-align: center;
    justify-content: center; gap: 3px;
    padding: 6px 4px;
    border-radius: 9px;
  }
  .kpi-card .kpi-value,
  .dash-cell > .dash-inner > .kpi-group .kpi-value { font-size: 15px; line-height: 1.1; }
  .kpi-card .kpi-label,
  .dash-cell > .dash-inner > .kpi-group .kpi-label { font-size: 7.5px; letter-spacing: 0.03em; white-space: normal; line-height: 1.2; }
}

/* ── Email client ──────────────────────────────────────────────────────
   Two panes and a compose slide-over. Plain on purpose: an inbox earns
   trust by being boring, so it borrows the app's cards and type wholesale
   and adds nothing decorative. */
/* ── Mailbox ────────────────────────────────────────────────────────────────
   Read like a mail client, not a chat app. The bubbles that used to be here
   put outbound mail on the right and inbound on the left, which is iMessage;
   a mail thread is a column of messages, each one headed by who sent it, so
   a long reply is a paragraph rather than a lozenge. */
.mail-layout { display: grid; grid-template-columns: 360px minmax(0, 1fr); gap: 16px; align-items: start; }
/* min-width: 0 on both tracks, or a wide HTML email (fixed-width tables,
   long unbroken lines) expands the 1fr track past the container and shears
   the whole page, the grid item's default min-width: auto is min-content.
   Same trap as the signature boxes. */
.mail-list { padding: 0; overflow: hidden; min-width: 0; }
.mail-thread { min-width: 0; }
.mail-filter { padding: 10px; border-bottom: 1px solid var(--hairline); }

.mail-row {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 12px 14px; text-decoration: none; color: inherit;
  position: relative; transition: background .12s ease;
}
/* A hairline that stops short of the edges, so the list reads as one surface
   rather than a stack of boxes. */
.mail-row + .mail-row::before {
  content: ''; position: absolute; top: 0; left: 61px; right: 0;
  border-top: 1px solid var(--hairline);
}
.mail-row:hover { background: var(--surface-hover); }
.mail-row.open { background: var(--accent-soft); }
.mail-row.open::after {
  content: ''; position: absolute; left: 0; top: 6px; bottom: 6px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--brand);
}
.mail-avatar {
  flex: none; width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13.5px; font-weight: 650; letter-spacing: -0.01em;
  background: var(--surface-sunken); color: var(--ink-muted); user-select: none;
}
.mail-row.unread .mail-avatar { background: var(--brand); color: #fff; }
.mail-row-main { min-width: 0; flex: 1; display: block; }
.mail-row-top { display: flex; }
.mail-row-who, .mail-row-subject, .mail-row-snippet { display: block; }
.mail-row-top { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.mail-row-who {
  font-size: 14px; font-weight: 550; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mail-row.unread .mail-row-who { font-weight: 700; }
.mail-row.unread .mail-row-subject { font-weight: 600; color: var(--ink); }
.mail-row-when { font-size: 11.5px; color: var(--ink-faint); flex: none; }
.mail-row-subject { font-size: 13px; color: var(--ink-soft); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mail-row-snippet { font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; line-height: 1.35; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Unread is a dot as well as a weight: weight alone disappears on a phone in
   sunlight, and it is the one thing the list exists to tell you. */
.mail-unread-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--brand); margin-top: 6px; }

.mail-empty, .mail-none { padding: 48px 20px; text-align: center; color: var(--ink-faint); font-size: 13.5px; }
.mail-none { display: flex; flex-direction: column; gap: 8px; align-items: center; justify-content: center; min-height: 340px; }

.mail-thread { padding: 0; display: flex; flex-direction: column; min-height: 460px; }
.mail-thread-head {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--hairline);
  position: sticky; top: 0; z-index: 2; background: var(--surface);
}
.mail-thread-subject { font-size: 17px; font-weight: 650; letter-spacing: -0.02em; line-height: 1.3; }
.mail-thread-meta { font-size: 12.5px; color: var(--ink-muted); margin-top: 3px; }
.mail-back { display: none; }

.mail-msgs { flex: 1; overflow-y: auto; padding: 0; display: block; min-width: 0; }
.mail-msg { padding: 18px 20px; border-bottom: 1px solid var(--hairline); max-width: none; border-radius: 0; min-width: 0; overflow: hidden; }
.mail-msg:last-child { border-bottom: none; }
/* An HTML email is a stranger's markup: contain it so a 700px newsletter
   table or a run of "====" scrolls inside the message instead of stretching
   the mailbox. Images never exceed the column. */
.mail-msg-html { max-width: 100%; overflow-x: auto; }
.mail-msg-html img, .mail-msg-body img { max-width: 100%; height: auto; }
.mail-msg-body { overflow-wrap: anywhere; }
/* Outbound is tinted rather than moved: which side of the page a message sits
   on is not information, and reading a thread should not zig-zag. */
.mail-msg.out { background: color-mix(in srgb, var(--brand) 3%, transparent); }
.mail-msg.in { background: transparent; }
.mail-msg-meta {
  display: flex; gap: 10px; justify-content: space-between; align-items: baseline;
  font-size: 12.5px; color: var(--ink-muted); margin-bottom: 8px;
}
.mail-msg-meta strong { font-size: 13.5px; font-weight: 650; color: var(--ink); letter-spacing: -0.01em; }
.mail-msg-body { font-size: 14.5px; line-height: 1.6; word-break: break-word; color: var(--ink-soft); }
/* linkify() writes real anchors now, so they need to look like links. */
.mail-msg-body a, .mail-msg-html a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }
.mail-msg-body a:hover, .mail-msg-html a:hover { text-decoration-thickness: 2px; }

.mail-reply { display: flex; gap: 10px; align-items: flex-end; padding: 14px 20px; border-top: 1px solid var(--hairline); }
.mail-reply textarea { margin: 0; flex: 1; resize: vertical; border-radius: 12px; }

.mail-scrim { position: fixed; inset: 0; z-index: 70; background: rgba(0,0,0,.38); }
.mail-compose { position: fixed; top: 0; right: 0; bottom: 0; z-index: 71; width: min(480px, 100vw); border-radius: 0; overflow-y: auto; padding: 18px; box-shadow: -12px 0 40px rgba(0,0,0,.18); }
.mail-compose-head { display: flex; justify-content: space-between; align-items: center; font-weight: 650; margin-bottom: 12px; }
.mail-x { border: none; background: var(--border-soft); width: 30px; height: 30px; border-radius: 999px; font-size: 15px; cursor: pointer; color: var(--ink-muted); }

@media (max-width: 900px) {
  /* One pane at a time: the list, or the open thread with a Back button. */
  .mail-layout { grid-template-columns: 1fr; }
  .mail-layout.has-open .mail-list { display: none; }
  .mail-layout:not(.has-open) .mail-thread { display: none; }
  .mail-back { display: inline-flex; }
  .mail-msg { max-width: 100%; }
}

/* A preset names the work; its price is typed in beside it before it is added. */
.qt-preset-form { display: flex; align-items: stretch; gap: 4px; }
.qt-preset-cost { width: 74px; padding: 4px 6px; font-size: 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--ink); }
.qt-preset-cost::placeholder { color: var(--ink-muted); }

/* Customize: one tile per thing you can change, picture first so the page can
   be scanned rather than read. */
/* ── Customize ────────────────────────────────────────────────────────
   Six equal tiles for the things you change yourself, then two cards side by
   side for what Lumen proposed and what can be put back. Navy icon squares
   with the gold mark, the same language as the rest of the shell. */
.cz-header { align-items: center; }
.cz-eyebrow { font-size: 10.5px; font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase; color: var(--gold-ink); margin-bottom: 4px; }
.cz-ask { display: flex; gap: 12px; align-items: center; padding: 12px 16px 12px 12px; border-radius: 14px; background: var(--navy); color: #fff; max-width: 380px; }
.cz-ask-orb { width: 36px; height: 36px; border-radius: 50%; flex: none; display: grid; place-items: center; background: linear-gradient(135deg, #c9a227, #e0b93a); color: #0f1b2d; font-size: 16px; font-weight: 800; }
.cz-ask strong { display: block; font-size: 13px; font-weight: 750; }
.cz-ask span:last-child { display: block; font-size: 12px; color: rgba(255, 255, 255, .72); line-height: 1.4; margin-top: 1px; }
.cz-flash { margin-bottom: 16px; font-size: 13px; border-left: 3px solid var(--accent); }
.cz-section { margin-bottom: 22px; }
.cz-section-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; margin-bottom: 12px; }
.cz-section-head h2 { font-size: 15px; font-weight: 750; margin: 0; }
.cz-section-head p { margin: 2px 0 0; font-size: 12.5px; color: var(--ink-3); }
.cz-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.cz-tile {
  display: flex; flex-direction: column; gap: 10px; min-height: 156px; padding: 18px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: 14px;
  text-decoration: none; color: inherit; transition: border-color .12s, transform .12s, box-shadow .12s;
}
.cz-tile:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 8px 22px rgba(15, 27, 45, .08); }
.cz-tile-icon { width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center; background: var(--navy); color: #e0b93a; }
.cz-tile-name { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.cz-tile-what { font-size: 13px; color: var(--ink-3); line-height: 1.45; flex: 1; }
.cz-tile-go { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 650; color: var(--accent); }
.cz-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 20px; }
.cz-card { margin-bottom: 0; }
.cz-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line-2); }
.cz-card-head h2 { font-size: 14px; font-weight: 750; margin: 0; }
.cz-count { min-width: 22px; height: 22px; padding: 0 7px; border-radius: 999px; display: inline-grid; place-items: center; background: var(--navy); color: #fff; font-size: 11.5px; font-weight: 750; }
.cz-count.is-zero { background: var(--surface-sunken); color: var(--ink-3); }
.cz-hint { font-size: 12px; color: var(--ink-3); }
.cz-row { display: flex; gap: 14px; align-items: center; justify-content: space-between; padding: 13px 18px; }
.cz-row + .cz-row { border-top: 1px solid var(--line-2); }
.cz-row-body { min-width: 0; }
.cz-row-title { font-size: 14px; font-weight: 600; }
.cz-row-sub { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.cz-row-actions { display: flex; gap: 8px; flex: none; }
.cz-pill { flex: none; font-size: 12px; color: var(--ink-3); }
.cz-pill.is-applied { color: var(--accent); font-weight: 650; }
.cz-empty { padding: 30px 18px 32px; text-align: center; }
.cz-empty-icon { width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; margin: 0 auto 10px; background: var(--surface-sunken); color: var(--ink-3); }
.cz-empty-title { font-size: 13.5px; font-weight: 650; }
.cz-empty-sub { font-size: 12.5px; color: var(--ink-3); margin: 4px auto 0; max-width: 300px; line-height: 1.45; }
@media (max-width: 1100px) { .cz-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px) { .cz-cols { grid-template-columns: 1fr; } .cz-ask { max-width: none; } .cz-header { align-items: flex-start; } }
@media (max-width: 560px) { .cz-grid { grid-template-columns: 1fr; } .cz-tile { min-height: 0; } .cz-row { flex-wrap: wrap; } }

/* Something Lumen filled in, waiting on a person. Same on every screen, so it
   reads as one thing the app does rather than a feature of the bids page. */
.draft-waiting { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--line-2); border-left: 3px solid var(--accent); border-radius: var(--radius); }
.draft-waiting-text { flex: 1; min-width: 220px; }
.draft-waiting-title { display: block; font-size: 14px; }
.draft-waiting-meta { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 2px; }

/* Money boxes carry their own currency sign so nobody has to type one, and the
   digits sit right of it rather than shuffling under it. */
/* The sign is centred on the wrapper, so the wrapper has to be exactly the
   box: the input's own bottom margin moves out to the field so it cannot
   make the wrapper taller than what is drawn and drop the sign below the
   number's centre line. */
.money-field { display: block; margin-bottom: 16px; }
.money-field .money-input { margin-bottom: 0 !important; }
.cr-field .money-field, .qt-row .money-field, .qt-preset-form .money-field { margin-bottom: 0; }
/* The box's bottom margin lives on the wrap, not the input, so the sign is
   centred on the input's own height. Left on the input it made the wrap
   16px taller than the box and the sign sat low, most visibly on Discount. */
.money-wrap { position: relative; display: block; margin-bottom: 16px; }
.money-wrap > .money-input { margin-bottom: 0 !important; }
.money-field .money-wrap, .cr-field .money-wrap, .qt-row .money-wrap, .qt-preset-form .money-wrap,
.money-wrap:has(> .money-input[style*="margin"]) { margin-bottom: 0; }
/* The sign sits against the figure and in the same ink, so the box reads as
   one number, "$25,000.00", rather than a sign parked at the far left with
   the amount at the other end of the field. */
/* The sign is a prefix, not part of the number: same face and size as the
   digits, the same inset as every other field's text, muted until the box is
   in use so the eye reads "$ 1,200.00" and not a stray glyph. */
.money-sign { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); font: inherit; font-size: 16px; line-height: 1; color: var(--ink-muted); pointer-events: none; }
.money-input { padding-left: 24px !important; text-align: left; font-variant-numeric: tabular-nums; }
/* One tone for the whole thing. Empty, the sign and the 0.00 hint are the same
   grey; typed or focused, the sign takes the ink colour with the digits. Two
   tones side by side looked like a stray glyph. */
.money-input::placeholder { color: var(--ink-muted); opacity: 1; }
.money-wrap:focus-within .money-sign,
.money-wrap:has(> .money-input:not(:placeholder-shown)) .money-sign { color: var(--ink); }
.qt-preset-form .money-wrap { width: 84px; }
.qt-preset-form .money-input { padding: 4px 6px 4px 18px !important; font-size: 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--ink); }
.qt-preset-form .money-sign { left: 7px; font-size: 12px; }
.qt-preset-form .money-input { padding-left: 17px !important; }

/* Permits: what the job needs, and what has come back approved. */
.permit-list { display: grid; gap: 6px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.permit-row { display: flex; align-items: center; gap: 12px; padding: 7px 10px; border: 1px solid var(--line-2); border-radius: 10px; }
.permit-name { flex: 1; margin: 0; }
.permit-ok { margin: 0; font-size: 12px; color: var(--ink-3); white-space: nowrap; }

/* ── Phones ────────────────────────────────────────────────────────────────
   Two columns at 375px gives every field about 170px, which is not enough for
   a money box with a currency sign in it, let alone a job address. Below 640px
   the forms run one field per line and every control is full width, so nothing
   has to be pinched to be read or typed into. Applies to the trade job form,
   the property lead card and the bid line builder alike. */
@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
  /* These carry the span inline, and an inline style beats a stylesheet rule
     unless it is forced. Without this the wide fields punch out a phantom
     second column and sit at a different width to everything else. */
  .form-grid > [style*="grid-column:span 2"],
  .form-grid > [style*="grid-column: span 2"] { grid-column: span 1 !important; }

  .qt-row { gap: 8px; }
  .qt-row > .field,
  .qt-row > .money-field { width: 100% !important; flex: 1 1 100% !important; min-width: 0 !important; }
  .qt-row .btn { width: 100%; }

  /* A preset and its two price boxes stack rather than squeezing off the edge. */
  .qt-presets { flex-direction: column; }
  .qt-preset-form { width: 100%; }
  .qt-preset-form .money-wrap { flex: 1; width: auto; }
  .qt-preset-form .qt-preset { flex: 0 0 auto; }

  .permit-list { grid-template-columns: 1fr; }
  .draft-waiting { align-items: flex-start; }
  .draft-waiting .btn { width: 100%; }
}

/* ── A bid on a phone ──────────────────────────────────────────────────────
   The line table is five columns with 24px of padding a side. At 375px that
   is 513px of table in a 375px screen, so Rate and Amount ran off the right
   edge and the one thing a bid is for, what it costs, could not be seen.

   Below 640px each line becomes its own block: the description on top, then
   the figures as labelled rows. Nothing is hidden and nothing scrolls
   sideways. These cover the internal bid and the invoice; the customer's copy
   carries its own stylesheet and has the matching rules there. Every business
   type shares these views, so this is one change for all 54. */
@media (max-width: 640px) {
  .qt-lines, .qt-lines thead, .qt-lines tbody, .qt-lines tr, .qt-lines td { display: block; width: 100%; }
  .qt-lines thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .qt-lines tr { padding: 12px 14px; border-bottom: 1px solid var(--hairline); }
  .qt-lines tbody tr:last-child { border-bottom: none; }
  .qt-lines td { padding: 0; border: none; text-align: left; }
  .qt-lines td.qt-money { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 3px 0 0; }
  /* The column heading travels with the number, which is the only way a bare
     figure means anything once the header row is gone. */
  .qt-lines td.qt-money::before { content: attr(data-label); font-size: 11.5px; color: var(--ink-3); font-weight: 500; }
  .qt-lines td.qt-money:not([data-label]) { justify-content: flex-end; }
  .qt-lines .qt-line-desc { font-weight: 600; }
  .qt-lines .qt-empty { text-align: center; padding: 24px 14px !important; }

}

/* Editing a line in place, rather than deleting and retyping it. */
.qt-line-actions { display: flex; gap: 4px; justify-content: flex-end; align-items: center; }
.qt-line-edit > td { background: var(--border-soft); padding: 12px 24px !important; }
.qt-line-edit .qt-row { margin: 0; }
@media (max-width: 640px) {
  .qt-line-actions { justify-content: flex-start; padding-top: 6px !important; }
  .qt-line-actions::before { content: none !important; }
  .qt-line-edit > td { padding: 12px 14px !important; }
}

/* What a parts figure is made of. */
.qt-mat-toggle { display: inline-block; margin-top: 4px; padding: 0; border: 0; background: none; color: var(--accent); font-size: 11.5px; cursor: pointer; text-decoration: underline; }
.qt-mat-count { margin-left: 6px; padding: 1px 6px; border-radius: 999px; background: var(--border-soft); font-size: 10.5px; }
.qt-mats { width: 100%; border-collapse: collapse; font-size: 13px; }
.qt-mats td { padding: 7px 0; border-bottom: 1px solid var(--hairline); }
.qt-mats tr:last-child td { border-bottom: none; }
.qt-mats-total td { border-top: 1px solid var(--border); padding-top: 9px; }
@media (max-width: 640px) {
  .qt-mats, .qt-mats tbody, .qt-mats tr, .qt-mats td { display: block; width: 100%; }
  .qt-mats tr { padding: 9px 0; border-bottom: 1px solid var(--hairline); }
  .qt-mats td { padding: 0; border: none; }
  .qt-mats td.qt-money { display: flex; justify-content: space-between; gap: 12px; padding-top: 2px; }
  .qt-mats td.qt-money::before { content: attr(data-label); font-size: 11px; color: var(--ink-3); }
  .qt-mats td.qt-money:not([data-label]) { justify-content: flex-end; }
}

/* A material corrected where it is read. */
.qt-mats td { position: relative; }
.qt-mat-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; padding-right: 26px; }
.qt-mat-total { min-width: 84px; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }
.qt-mat-del { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
@media (max-width: 640px) {
  .qt-mat-row { padding-right: 0; }
  .qt-mat-row > * { flex: 1 1 auto; }
  .qt-mat-total { flex-basis: 100%; text-align: left; }
  .qt-mat-del { position: static; transform: none; }
}

/* The preset catalogue, out of the way until asked for. */
.qt-preset-link { margin-left: 8px; padding: 0; border: 0; background: none; color: var(--accent); font-size: 11px; font-weight: 500; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.qt-preset-link:hover { opacity: .8; }
.qt-preset-pop { position: fixed; z-index: 1200; padding: 8px; background: var(--surface-raised, var(--surface)); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 8px 28px rgba(0,0,0,.18); }
.qt-preset-search { margin-bottom: 6px !important; }
.qt-preset-list { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
.qt-preset-opt { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; width: 100%; padding: 7px 9px; border: 0; border-radius: 8px; background: none; color: inherit; font-size: 13px; text-align: left; cursor: pointer; }
.qt-preset-opt:hover { background: var(--border-soft); }
.qt-preset-cat { flex: none; font-size: 10.5px; color: var(--ink-3); }
.qt-preset-none { padding: 10px 9px; font-size: 12.5px; color: var(--ink-3); }
/* The field is the anchor, so the panel hangs off it. */
.qt-row .field { position: relative; }

/* Somebody is paying cash: the office needs to know before it arrives. */
.collect-cash { background: #fff4d6; color: #8a5a00; border: 1px solid #f0d089; }
.collect-note { margin-top: 10px; padding: 10px 12px; border-radius: 10px; background: #fff4d6; border: 1px solid #f0d089; color: #6b4600; }
.collect-note strong { display: block; font-size: 13.5px; }
.collect-note span { display: block; margin-top: 3px; font-size: 12px; line-height: 1.45; }

/* Billing a job in pieces. */
.bill-pick { display: flex; flex-direction: column; gap: 2px; }
.bill-line { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 9px; cursor: pointer; }
.bill-line:hover { background: var(--border-soft); }
.bill-desc { flex: 1; font-size: 13.5px; }
.bill-amt { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 13.5px; }
.bill-list { margin-top: 12px; border-top: 1px solid var(--hairline); padding-top: 10px; display: flex; flex-direction: column; gap: 2px; }
.bill-inv { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 9px; text-decoration: none; color: inherit; font-size: 13px; }
.bill-inv:hover { background: var(--border-soft); }
.bill-inv .bill-amt { margin-left: auto; }

/* One search box for however you remember somebody. */
.contact-search { display: flex; gap: 8px; align-items: center; margin-bottom: 16px; }
.cal-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 12px; }
.cal-toolbar-nav { display: flex; align-items: center; gap: 10px; }
.upload-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.upload-form input[type=file] { flex: 1 1 220px; min-width: 0; }
.msg-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  color: var(--ink-muted); font-size: 14px; text-align: center; padding: 32px 16px; }
.msg-empty-icon { font-size: 30px; opacity: .6; }
.contact-search .field-input { margin: 0; flex: 1; max-width: 460px; }
@media (max-width: 640px) { .contact-search { flex-wrap: wrap; } .contact-search .field-input { max-width: none; flex: 1 1 100%; } }

/* A provider that cannot work with a password should look like a warning. */
.provider-warn { color: #8a5a00 !important; background: #fff4d6; border: 1px solid #f0d089; border-radius: 8px; padding: 8px 10px; }

/* An email body is somebody else's markup: let it look like itself, but never
   let it break out of its box or drag the page sideways. */
.mail-msg-html { max-width: 100%; overflow-x: auto; font-size: 14.5px; line-height: 1.6; color: var(--ink-soft); }
.mail-msg-html img { max-width: 100%; height: auto; }
.mail-msg-html table { max-width: 100%; }
.mail-msg-html a { color: var(--accent); }
.mail-msg-html * { max-width: 100%; }

/* Closed work: kept on the record, folded out of the way of the day's board.
   A finished column sitting between two live ones is scrolled past every
   morning; a strip you open when you want it is not. */
.closed-strip { margin-top: 12px; }
.closed-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--ink-muted);
  font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.closed-toggle:hover { border-color: var(--brand); color: var(--ink); }
.closed-toggle .count {
  min-width: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--border); color: var(--ink-muted); font-size: 11px; text-align: center;
}
.closed-list { margin-top: 8px; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.closed-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 12px; border-bottom: 1px solid var(--border); background: var(--surface); font-size: 13.5px;
}
.closed-row:last-child { border-bottom: 0; }

/* The name underneath the name. Shown wherever something can be renamed, so
   it is obvious that the rename is skin and the tag is the thing anything
   automated actually holds on to. */
.tag-id {
  display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px; color: var(--ink-muted); background: var(--surface-2, var(--surface));
  border: 1px solid var(--border); border-radius: 5px; padding: 0 5px; letter-spacing: 0;
  text-transform: none; font-weight: 500;
}

/* ── Phones: a screen you hold, not a desk you sit at ─────────────────────
   Everything below fixes something seen on a 375px screenshot of the real
   app, not something imagined. Each rule says which. */
@media (max-width: 640px) {
  /* The create button floated over the last card's stage dropdown and over
     a contact's phone number. The page now ends above it. */
  body:has(.mob-fab) main.content { padding-bottom: calc(var(--mob-dock-h) + var(--safe-bottom) + 100px); }
  /* And the same action was also a button at the top of the page. One is
     enough; the floating one is the one your thumb reaches. */
  body:has(.mob-fab) .page-actions .btn-primary { display: none; }

  /* "General Contracting & Repair Dashboard" wrapped onto two lines of 21px
     type while the top bar already said the same thing. */
  .page-title { font-size: 18px; }
  .page-subtitle { font-size: 12.5px; }

  /* Small buttons wrapped mid-word ("Copy / link"). */
  .btn-sm { white-space: nowrap; }

  /* Board header: title, then the controls on their own line, instead of
     everything wrapping into a stair. */
  .workspace-header { flex-wrap: wrap; gap: 8px; }
  .workspace-title { flex: 1 1 100%; }
  .workspace-header > .row { width: 100%; justify-content: space-between; }

  /* A kanban with eleven 264px columns is unusable on a phone: one and a
     bit columns showed and the rest had to be dragged into view blind. On a
     phone you pick the stage, and see that stage, full width. */
  .kanban-stages { display: flex; gap: 6px; overflow-x: auto; padding: 2px 2px 10px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .kanban-stages::-webkit-scrollbar { display: none; }
  .kanban-stage-chip {
    flex: none; display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 11px; border-radius: 999px; border: 1px solid var(--border);
    background: var(--surface); color: var(--ink-muted); font-size: 12.5px; font-weight: 550;
    white-space: nowrap; cursor: pointer;
  }
  .kanban-stage-chip .n { font-size: 11px; background: var(--border-soft); border-radius: 999px; padding: 0 6px; }
  .kanban-stage-chip.active { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
  .kanban-stage-chip.active .n { background: color-mix(in srgb, #fff 22%, transparent); color: inherit; }
  .kanban-scroll { overflow-x: visible; padding-bottom: 0; }
  .kanban-col { width: 100%; }

  /* Eleven stage pills over five rows became a select. */
  .stage-pills { display: none !important; }
  .stage-mobile { display: block; }

  /* Tabs ran off the right edge with nothing to say so. */
  .tabbar { overflow-x: auto; white-space: nowrap; scrollbar-width: none; -webkit-overflow-scrolling: touch;
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
  .tabbar::-webkit-scrollbar { display: none; }
  .tab-btn { flex: none; }

  /* Rearranging dashboard widgets is a desk job. */
  .dash-toolbar { display: none !important; }

  /* The money strip: two compact tiles per row, the way a banking app does it. */
  .attn-item { flex: 1 1 calc(50% - 5px); min-width: 0; padding: 10px 14px; }
  .attn-value { font-size: 19px; }

  /* Bid and invoice lists stack each row into a card instead of scrolling sideways. */
  .qt-stack thead { display: none; }
  .qt-stack, .qt-stack tbody, .qt-stack tr, .qt-stack td { display: block; width: 100%; }
  .qt-stack tr { padding: 6px 0; border-bottom: 1px solid var(--hairline); }
  .qt-stack tbody tr:last-child { border-bottom: none; }
  .qt-stack td { display: flex; justify-content: space-between; align-items: center; gap: 12px;
    padding: 6px 14px; border-bottom: none; text-align: right; }
  .qt-stack td::before { content: attr(data-label); font-size: 11.5px; color: var(--ink-muted);
    text-transform: uppercase; letter-spacing: .04em; font-weight: 600; text-align: left; flex-shrink: 0; }
  .qt-stack td[data-label="Number"] { padding-top: 10px; }
  .qt-stack td[data-label="Number"]::before { content: none; }
  .qt-stack td[data-label="Number"] .qt-num { font-size: 15px; }

  /* The bid's seen/unseen pill drops beneath the link instead of crowding it. */
  .qt-link-row > div:last-child { flex: 1 1 100%; white-space: normal !important; }

  /* The bid's share link: input full width, copy button beneath. */
  .qt-link-row > div:first-child { flex-wrap: wrap; }
  .qt-link-row .field-input { min-width: 0; flex: 1 1 100%; }

  .cal-toolbar { flex-wrap: wrap; }
  .cal-toolbar .view-toggle { flex: 1 1 100%; }
  .cal-toolbar .view-toggle button { flex: 1; }
  .upload-form { flex-wrap: wrap; }
  .upload-form input[type=file] { flex: 1 1 100%; min-width: 0; }
  .upload-form .btn-primary { width: 100% !important; }
  .contact-search .btn { flex: 1; }
}
@media (min-width: 641px) {
  .kanban-stages { display: none; }
  .stage-mobile { display: none; }
}

/* ---------------------------------------------------------------------
   Signatures: preparing a document, and seeing how it will look.
   The customer-facing ceremony carries its own styles, because it has to
   render on a phone that has never loaded this stylesheet.
   --------------------------------------------------------------------- */
.sign-cols { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; align-items: start; }
@media (max-width: 1000px) { .sign-cols { grid-template-columns: minmax(0, 1fr); } }

.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.tpl-card {
  text-align: left; padding: 12px 14px; border: 1.5px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); cursor: pointer; font: inherit;
}
.tpl-card:hover { border-color: var(--brand); }
.tpl-card.selected { border-color: var(--accent); background: var(--accent-soft); }
.tpl-name { font-weight: 650; font-size: 14px; margin-bottom: 3px; }
.tpl-blurb { font-size: 12.5px; color: var(--ink-muted); line-height: 1.45; }

.signer-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.signer-row .field-input { margin: 0; flex: 1 1 160px; min-width: 0; }
@media (max-width: 640px) { .signer-row .field-input { flex: 1 1 100%; } .signer-row .btn { width: 100%; } }

.signer-item {
  display: flex; gap: 10px; justify-content: space-between; align-items: flex-start;
  padding: 10px 0; border-bottom: 1px solid var(--hairline);
}
.signer-item:last-of-type { border-bottom: none; }
.signer-name { font-weight: 600; font-size: 14px; }
.signer-mail { font-size: 12px; color: var(--ink-muted); word-break: break-all; }
.signer-state { font-size: 12px; margin-top: 2px; color: var(--ink-muted); }
.signer-state-signed { color: var(--accent-ink, #15803d); font-weight: 600; }
.signer-state-declined { color: var(--danger, #c0392b); }
.signer-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

.token-bar { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.token-group { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.token-who { font-size: 12px; font-weight: 650; color: var(--ink-muted); min-width: 90px; }

.doc-editor { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; line-height: 1.6; }

.doc-sheet {
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
  padding: 22px 20px 26px; font-size: 14.5px; line-height: 1.6;
}
.doc-sheet h2 { font-size: 15px; margin: 20px 0 8px; }
.doc-sheet h2:first-child { margin-top: 0; }
.doc-sheet p { margin: 0 0 11px; }
.doc-sheet ul { margin: 0 0 11px; padding-left: 20px; }
.doc-sheet .doc-blank { display: inline-block; min-width: 110px; border-bottom: 1px solid var(--ink-faint); }
.doc-sheet .doc-field.is-waiting,
.doc-sheet .doc-field.is-open {
  display: inline-flex; align-items: center; margin: 2px 3px; padding: 4px 9px;
  border: 1px dashed var(--accent); border-radius: 8px; background: var(--accent-soft);
  color: var(--accent-ink); font-size: 12px; font-weight: 650;
}
/* A box placed on a page is not a chip in a paragraph: no fill, no pill,
   just a thin line on the white paper underneath it. Same treatment as the
   live signing page, so the office's own preview does not show something the
   signer never sees (see sign.blade.php's identical rule). */
.doc-sheet .pdf-field .doc-field.is-waiting,
.doc-sheet .pdf-field .doc-field.is-open {
  display: flex; margin: 0; padding: 1px 2px 2px;
  border: none; border-bottom: 1.5px dashed rgba(13, 84, 36, .5); border-radius: 0;
  background: transparent; color: #5b616e; font-weight: 600;
}
.doc-sheet .doc-field.is-filled img.doc-sig-img { height: 40px; width: auto; vertical-align: middle; }
/* Ink on a dark workspace would be invisible; the stored image is unchanged. */
[data-theme="dark"] .doc-sheet img.doc-sig-img,
[data-theme="dark"] .doc-field-slot img { filter: invert(1) brightness(1.6); }
.doc-signblock { margin-top: 24px; border-top: 1px solid var(--hairline); padding-top: 16px; }
.doc-signblock-row { margin-bottom: 14px; }
.doc-signblock-who { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-muted); margin-bottom: 5px; }

.cert-row { padding: 8px 0; border-bottom: 1px solid var(--hairline); font-size: 13px; }
.cert-row:last-of-type { border-bottom: none; }
.cert-dim { color: var(--ink-muted); font-size: 12px; }

/* Uploaded documents: page images with boxes floating on them. ---------- */
/* Block layout, not flex: a flex column squashes a child that sizes itself
   from an aspect ratio, and a page has to keep the shape of the paper. */
.pdf-doc > .pdf-page + .pdf-page, .place-pages > .pdf-page + .pdf-page { margin-top: 14px; }
.pdf-page {
  position: relative; width: 100%; background: #fff; border: 1px solid var(--hairline);
  border-radius: 8px; overflow: hidden;
}
.pdf-page-img { display: block; width: 100%; height: 100%; object-fit: contain; user-select: none; }
.pdf-page-missing { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--ink-muted); font-size: 13px; }
.pdf-layer { position: absolute; inset: 0; }
/* Sits on its bottom edge, not centred: that edge is the line it was placed
   on, and a signature centred in a box taller than it needs to be would
   float above the line instead of landing on it. */
.pdf-field { position: absolute; display: flex; align-items: flex-end; }
/* min-*: 0 stops a flex item's default "never smaller than its
   content" rule from winning, which is what let a cropped signature
   image render at its own natural size instead of the box's, once it
   stopped being mostly empty transparent padding and the browser had
   an actual size worth defending. */
.pdf-field > * { width: 100%; height: 100%; min-width: 0; min-height: 0; }
.pdf-field .doc-field { display: flex; align-items: flex-end; justify-content: center; margin: 0; border-radius: 6px; overflow: hidden; }
.pdf-field .doc-field.is-check { align-items: center; }
.pdf-field .doc-field.is-check input { width: 100%; height: 100%; max-width: min(20px, 100%); max-height: min(20px, 100%); margin: 0; }
.pdf-field .doc-field-slot .stamp-text { font-size: inherit; }
.pdf-field img.doc-sig-img { width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; object-position: center bottom; }
/* Typed values scale with the box they were put in, and never wrap: a date
   that folds onto two lines stops looking like something written on the form. */
.pdf-field { container-type: size; }
/* Identical to the recipient's signing page (sign.blade.php), so the office
   "How it will look" preview is a true preview: same size, same baseline. */
.pdf-field .doc-field.is-filled,
.pdf-field .doc-field.is-waiting,
.pdf-field .doc-input { font-size: clamp(7px, min(52cqh, 6.2cqw), 18px); white-space: nowrap; line-height: 1.1; }
.pdf-field .doc-field.is-filled { overflow: visible; }
/* The page underneath is white paper: what is written on it stays dark ink. */
.pdf-page .doc-field.is-filled, .pdf-page .doc-field-slot .stamp-text { color: #14161a; }


/* Placing them. --------------------------------------------------------- */
.place-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.place-tools { display: flex; gap: 6px; flex-wrap: wrap; }
.place-tool {
  font: inherit; font-size: 12.5px; font-weight: 600; padding: 7px 11px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--ink);
}
.place-tool.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-ink); }
.place-save { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.place-status { font-size: 12px; color: var(--ink-muted); }
.place-legend { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; font-size: 12px; color: var(--ink-muted); }
.place-key { display: inline-flex; align-items: center; gap: 6px; }
.place-key i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.place-pages { max-height: 78vh; overflow-y: auto; padding-right: 4px; }
.place-page { cursor: crosshair; touch-action: manipulation; }
.place-pagenum {
  position: absolute; right: 8px; bottom: 8px; font-size: 11px; color: var(--ink-muted);
  background: rgba(255,255,255,.85); border-radius: 6px; padding: 2px 7px;
}
/* Bottom-aligned, not centred: a signature lands on the BOTTOM edge of its
   box (see sign.blade.php), so the label has to sit there too. Otherwise
   what you watch while dragging, the word sitting mid-box on the printed
   line, is not where the ink ends up once it's signed, it lands below where
   the label made it look like it would. */
/* A thin dashed outline, not a filled tile: it only has to mark the box's
   extent for dragging and resizing. Whose it is shows in the word itself
   (see the inline colour on .place-field-label), not in a coloured slab
   sitting over somebody's contract. */
.place-field {
  position: absolute; display: flex; align-items: flex-end; justify-content: center;
  border: 1.5px dashed var(--accent); border-radius: 4px; cursor: move; touch-action: none;
  font-weight: 650; overflow: visible;
  /* The label is sized by the SAME formula as the stamped value on the
     signing page (see sign.blade.php .pdf-field), and sits on the same
     bottom padding. A fixed 11.5px word bottom-aligned in the box put its
     baseline in a different place than a larger scaled stamp, so lining the
     word up on the printed line did not line the signature up on it. */
  container-type: size;
}
.place-field-label {
  padding: 1px 2px 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: clamp(7px, min(52cqh, 6.2cqw), 18px); line-height: 1.1;
}
/* A signature or initials is an image that FILLS its box (see the recipient's
   .doc-sig-img: height 100%, bottom-aligned). A word occupying only the bottom
   third of that box misrepresented how tall the ink would be, so a box whose
   word sat "on the line" put the signature's body well above it. The word now
   spans the box the way the ink will, so placing the box IS placing the
   signature. */
.place-field.is-signature .place-field-label,
.place-field.is-initials .place-field-label {
  /* Height-driven only: a width cap (cqw) throttled this to ~13px on a
     normal box and left the word at 40% of the height the ink will take.
     Overflowing width is fine here, the label is ellipsized. */
  font-size: clamp(9px, 88cqh, 44px); line-height: 1; padding: 0 2px 1px;
}
.place-del {
  position: absolute; top: -9px; right: -9px; width: 20px; height: 20px; line-height: 1;
  border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font-size: 13px; cursor: pointer; padding: 0;
}
.place-grip {
  position: absolute; right: -6px; bottom: -6px; width: 14px; height: 14px; border-radius: 4px;
  background: var(--surface); border: 1.5px solid var(--accent); cursor: nwse-resize;
}
@media (max-width: 640px) {
  .place-save { margin-left: 0; width: 100%; justify-content: space-between; }
  .place-pages { max-height: none; }
}

/* The customer's account: jobs with a running position. ------------------ */
.acct-job { padding: 12px 18px; border-bottom: 1px solid var(--hairline); }
.acct-job:last-child { border-bottom: none; }
.acct-job-head { display: flex; gap: 10px; align-items: baseline; justify-content: space-between; flex-wrap: wrap; }
.acct-job-title { font-weight: 600; font-size: 14px; color: inherit; text-decoration: none; }
.acct-job-title .muted { font-weight: 400; font-size: 12px; color: var(--ink-muted); margin-left: 6px; }
.acct-job-title:hover { text-decoration: underline; }
.acct-job-remaining { font-size: 13px; font-weight: 650; color: var(--danger, #c0392b); }
.acct-job-remaining.is-clear { color: var(--accent-ink, #15803d); }
.acct-figs { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 5px; font-size: 12px; color: var(--ink-muted); }
.acct-figs strong { color: var(--ink); font-weight: 600; }
.acct-invs { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.acct-inv {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  font-size: 12.5px; color: inherit; text-decoration: none;
}
.acct-inv:hover { text-decoration: underline; }
.acct-inv .bill-amt { margin-left: auto; font-variant-numeric: tabular-nums; }
.deposit-panel { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--hairline); }
.deposit-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
@media (max-width: 640px) {
  .deposit-row > * { flex: 1 1 100%; }
  .acct-inv .bill-amt { margin-left: 0; }
}
.acct-hint { font-size: 12px; color: var(--ink-muted); margin-left: 8px; }

/* Team and pay. ---------------------------------------------------------- */
.pay-log { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.pay-log .field-input { margin: 0; flex: 1 1 150px; min-width: 0; }
.pay-log .money-wrap { margin: 0; flex: 1 1 150px; min-width: 0; }
.pay-log .checkbox-row { margin: 0; }
@media (max-width: 700px) { .pay-log > * { flex: 1 1 100% !important; } }

.worker-row { display: flex; gap: 10px; justify-content: space-between; align-items: flex-start; padding: 11px 18px; border-bottom: 1px solid var(--hairline); }
.worker-row:last-child { border-bottom: none; }
.worker-name { font-weight: 600; font-size: 14px; color: inherit; text-decoration: none; }
.worker-name:hover { text-decoration: underline; }
.worker-meta { font-size: 12px; color: var(--ink-muted); margin-top: 2px; }
.worker-warn { font-size: 12px; color: var(--danger, #c0392b); margin-top: 2px; }

.payline { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; padding: 12px 18px; border-bottom: 1px solid var(--hairline); }
.payline:last-child { border-bottom: none; }
.payline-who { flex: 1 1 220px; min-width: 0; font-size: 14px; }
.payline-meta { font-size: 12px; color: var(--ink-muted); }
.payline-amount { font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; }
.payline-action { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-left: auto; }
.payline-form { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.payline-form .field-input { margin: 0; width: auto; max-width: 150px; }
.payline-form .money-wrap { margin: 0; }
.payline-total { background: var(--surface-hover, rgba(0,0,0,.02)); }
@media (max-width: 640px) {
  .payline-action { margin-left: 0; width: 100%; }
  .payline-form .field-input { flex: 1 1 100%; max-width: none; }
}

/* One person's own pay. ------------------------------------------------- */
.mypay-payment {
  display: flex; gap: 10px; align-items: center; justify-content: space-between; flex-wrap: wrap;
  padding: 10px 12px; border: 1px solid var(--hairline); border-radius: var(--radius-sm, 10px);
  font-size: 13.5px;
}
.mypay-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mypay-dispute { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; flex: 1 1 100%; }
.mypay-dispute .field-input { margin: 0; flex: 1; min-width: 160px; }
@media (max-width: 640px) { .mypay-actions, .mypay-actions form, .mypay-actions .btn { width: 100%; } }
.paylog { flex: 1 1 100%; display: flex; flex-direction: column; gap: 4px; margin-top: 8px; padding-left: 2px; }
.paylog-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; font-size: 12.5px; color: var(--ink-muted); }
.paylog-row strong { color: var(--ink); }
.paylog-state { margin-left: auto; }
.paylog-state.is-good { color: var(--accent-ink, #15803d); }
.paylog-state.is-bad { color: var(--danger, #c0392b); }

/* The document a message is about, one tap away. --------------------------- */
.mail-doc-btn {
  display: inline-block; margin: 0 0 10px; padding: 10px 16px; border-radius: 10px;
  background: var(--accent); color: #06280f; font-size: 13.5px; font-weight: 650; text-decoration: none;
}
.mail-doc-btn:hover { filter: brightness(0.96); }

/* Placing boxes: the bar that stays put, and the pages under it. ----------- */
.place-card { padding-bottom: 8px; }
.place-bar { position: sticky; top: 0; z-index: 5; background: var(--surface); padding-bottom: 10px; }
.place-bar-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.place-bar-foot { margin-top: 8px; }
.place-bar-top { display: flex; gap: 12px; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; }
.place-lbl {
  flex: none; width: 74px; font-size: 11.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-muted);
}
.place-people { display: flex; gap: 6px; flex-wrap: wrap; }
.place-who {
  display: inline-flex; align-items: center; gap: 7px; margin: 0; max-width: 230px; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; padding: 6px 11px;
  border: 1.5px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--ink);
}
.place-who i { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--who); }
.place-who span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.place-who em { font-style: normal; font-size: 11px; color: var(--ink-muted); }
.place-who.on { border-color: var(--who); background: var(--who-soft); }
.place-empty { margin: 10px 0 0; font-size: 13px; color: var(--ink-muted); }
.place-pages.is-dragging .place-page { outline: 2px dashed var(--accent); outline-offset: 2px; }
.place-field.picked { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px currentColor; }
.place-tool[draggable="true"] { cursor: grab; }
.place-tool { display: inline-flex; align-items: center; gap: 6px; }
.place-tool-icon { font-size: 13px; opacity: .7; }
.place-help { margin: 8px 0 0; }
.place-status { font-size: 12px; color: var(--ink-muted); min-width: 80px; }
.place-status.is-saved { color: var(--accent-ink, #15803d); }
.place-pagelabel {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 11.5px; color: var(--ink-muted); margin: 14px 0 4px; text-transform: uppercase; letter-spacing: .05em;
}
.doc-row {
  display: flex; gap: 10px; align-items: center; justify-content: space-between;
  padding: 8px 0; border-bottom: 1px solid var(--hairline); font-size: 13.5px;
}
.doc-row:last-child { border-bottom: none; }

/* Picking several rows, then doing one thing to them. --------------------- */
.pick-bar {
  position: sticky; top: 0; z-index: 6;
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 10px;
  background: var(--accent-soft); border: 1px solid var(--accent); border-radius: var(--radius);
  font-size: 13px;
}
.pick-count { font-weight: 650; color: var(--accent-ink, #15803d); }
.pick-hint { color: var(--ink-muted); }
.pick-bar form { margin-left: auto; }
.pick-cell { width: 34px; }
.pick-cell input { width: 17px; height: 17px; accent-color: var(--accent); }
@media (max-width: 640px) {
  .pick-bar form { margin-left: 0; width: 100%; }
  .pick-bar form .btn { width: 100%; }
  .qt-stack td.pick-cell { justify-content: flex-start; }
  .qt-stack td.pick-cell::before { content: 'Select'; }
}

/* Placer: the rubber band drawn across a page, and the right-click menu. */
.place-marquee { position: absolute; border: 1.5px dashed var(--accent); background: rgba(201, 162, 39, .12); pointer-events: none; z-index: 5; }
.place-history { display: inline-flex; gap: 4px; }
.place-history .btn { padding: 6px 9px; font-size: 14px; line-height: 1; }
.place-menu {
  position: fixed; z-index: 60; min-width: 190px; padding: 6px; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  display: flex; flex-direction: column;
}
.place-menu button {
  appearance: none; border: 0; background: transparent; color: var(--ink); text-align: left;
  font: inherit; font-size: 13px; padding: 8px 10px; border-radius: 7px; cursor: pointer;
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}
.place-menu button:hover:not(:disabled) { background: var(--accent-soft); }
.place-menu button:disabled { opacity: .45; cursor: default; }
.place-menu kbd { font: inherit; font-size: 11px; color: var(--ink-soft); }
.place-menu-group { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); margin: 4px 0; padding: 4px 0; display: flex; flex-direction: column; }
.place-menu-group > span { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); padding: 4px 10px; }

/* A glyph swapped for its SVG (see the glyph block in app.js): sized by the
   text around it, so it fits a button, a heading or a chip alike. */
.glyph-svg { display: inline-block; width: 1em; height: 1em; vertical-align: -0.15em; flex: none; }

/* ── Courses ──────────────────────────────────────────────────────────
   A program and the people working through it. Two columns on a desk,
   one on a phone, and a progress bar that reads at a glance. */
.cr-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; align-items: start; }
.cr-card { margin-bottom: 0; }
/* .pad only ever existed as .card.pad, so a bare "pad" div inside an
   unpadded card got nothing and its form sat hard against the card's edge.
   Pad it only there: a card that pads itself must not pad a child twice. */
.card:not(.pad) > .pad, .card:not(.pad) .pad { padding: 14px 18px; }
.cr-card .pad .field-hint { margin-bottom: 0; margin-top: 8px; }
.cr-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--line-2); }
.cr-card-head h2 { font-size: 14px; font-weight: 750; margin: 0; }
.cr-row { display: flex; gap: 12px; align-items: center; justify-content: space-between; padding: 12px 18px; text-decoration: none; color: inherit; }
.cr-row + .cr-row { border-top: 1px solid var(--line-2); }
.cr-row:hover { background: var(--surface-sunken); }
.cr-row-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.cr-row-title { font-size: 14px; font-weight: 650; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cr-row-sub { font-size: 12px; color: var(--ink-3); }
.cr-row-end { display: flex; gap: 6px; align-items: center; flex: none; }
.cr-bar { display: block; height: 4px; border-radius: 999px; background: var(--border-soft); overflow: hidden; margin-top: 4px; max-width: 320px; }
.cr-bar-fill { display: block; height: 100%; background: var(--accent); }
.cr-section { border-top: 1px solid var(--line-2); }
.cr-section-name { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--gold-ink); padding: 10px 18px 4px; }
.cr-lesson { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 8px 18px; font-size: 13.5px; }
.cr-lesson + .cr-lesson { border-top: 1px solid var(--line-2); }
.cr-lesson.is-done { color: var(--ink-3); }
.cr-lesson-end { display: flex; gap: 6px; align-items: center; flex: none; }
.cr-tick { color: var(--green-ink); font-weight: 800; }
.cr-tickform { display: flex; gap: 6px; align-items: center; }
.cr-score { width: 78px; margin: 0; font-size: 12px; padding: 4px 6px; }
.cr-form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.cr-form + .cr-form { margin-top: 10px; }
.cr-field { display: flex; flex-direction: column; min-width: 0; }
.cr-field .field-input { margin-bottom: 0; }
.cr-grow { flex: 1 1 180px; }
.cr-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
.cr-stat { background: var(--surface); border: 1px solid var(--line-2); border-radius: 12px; padding: 14px 16px; display: flex; flex-direction: column; gap: 2px; }
.cr-stat-label { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3); }
.cr-stat-value { font-size: 20px; font-weight: 750; letter-spacing: -.02em; }
.cr-stat-sub { font-size: 11.5px; color: var(--ink-3); }
@media (max-width: 900px) { .cr-cols { grid-template-columns: 1fr; } .cr-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Scope picker: which business types a thing belongs to.
   Used for the permission door on a person and for which businesses a mailbox
   answers for. It is a real field with a border and a label, not a row of
   loose checkboxes with a sentence floating under it: ticking one of these
   decides what a separate CRM can see, so it should look like a decision. */
.scope-field {
  border: 1px solid var(--line-2); border-radius: 12px; padding: 12px 14px 11px;
  background: var(--surface);
}
.scope-field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 9px; }
.scope-field-label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-muted);
}
.scope-options { display: flex; flex-wrap: wrap; gap: 7px; }
.scope-option {
  position: relative; display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px 7px 11px; border: 1px solid var(--line-2); border-radius: 999px;
  font-size: 13px; line-height: 1; cursor: pointer; user-select: none;
  background: var(--surface); transition: border-color .12s ease, background .12s ease;
}
.scope-option:hover { border-color: var(--ink-faint); }
/* The native box is kept for the keyboard and for screen readers, and hidden
   behind the drawn one, which is the part that has to look deliberate. */
.scope-option input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.scope-tick {
  width: 15px; height: 15px; flex: none; border-radius: 4px;
  border: 1.5px solid var(--line); display: grid; place-items: center;
  background: var(--surface);
}
.scope-tick::after {
  content: ''; width: 7px; height: 4px; border-left: 2px solid var(--on-accent-deep);
  border-bottom: 2px solid var(--on-accent-deep); transform: rotate(-45deg) translate(0.5px, -1px);
  opacity: 0;
}
.scope-option.on { border-color: var(--accent); background: var(--accent-soft); font-weight: 650; }
.scope-option.on .scope-tick { background: var(--accent-deep); border-color: var(--accent-deep); }
.scope-option.on .scope-tick::after { opacity: 1; }
.scope-option:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.scope-hint { margin: 9px 0 0; font-size: 12px; color: var(--ink-3); line-height: 1.4; }
.scope-empty { font-size: 12.5px; color: var(--ink-3); }

/* Users & Access: the same picker, one per person in the roster. */
.bt-access { padding: 0 16px 14px; border-bottom: 1px solid var(--line-2); }
.bt-access:last-child { border-bottom: none; }
.bt-access-form { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.bt-access-form .scope-field { flex: 1 1 420px; }
@media (max-width: 640px) { .bt-access-form { align-items: stretch; } }
.bt-access-suite { display: inline-block; }

/* A company's own CRM address: the three steps from typed-in to live. */
.dom-steps { padding: 4px 18px 12px; display: flex; flex-direction: column; gap: 2px; }
.dom-step { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 13.5px; color: var(--ink-3); }
.dom-step-dot {
  width: 18px; height: 18px; flex: none; border-radius: 50%; display: grid; place-items: center;
  border: 1.5px solid var(--border); font-size: 11px; font-weight: 800; color: var(--on-accent-deep);
}
.dom-step.is-done { color: var(--ink); font-weight: 600; }
.dom-step.is-done .dom-step-dot { background: var(--accent-deep); border-color: var(--accent-deep); }
.dom-dns th { text-align: left; width: 110px; color: var(--ink-3); font-weight: 600; font-size: 12.5px; }
.dom-dns td code { font-size: 12.5px; }

/* Courses: the mentorship panels on a person's page */
.mt-thread { display: flex; flex-direction: column; gap: 8px; max-height: 420px; overflow-y: auto; }
.mt-bubble { max-width: 85%; padding: 9px 12px; border-radius: 14px; font-size: 13.5px; line-height: 1.45; }
.mt-bubble.is-member { align-self: flex-start; background: var(--surface-sunken); border: 1px solid var(--line-2); border-bottom-left-radius: 4px; }
.mt-bubble.is-coach { align-self: flex-end; background: var(--ink); color: #fff; border-bottom-right-radius: 4px; }
.mt-body { white-space: pre-wrap; word-wrap: break-word; }
.mt-meta { font-size: 11px; opacity: .65; margin-top: 3px; }
.mt-session { border-top: 1px solid var(--line-2); padding: 0 18px; }
.mt-session:first-of-type { border-top: 0; }
.mt-session > summary { list-style: none; cursor: pointer; padding: 12px 0; display: flex; flex-direction: column; gap: 3px; }
.mt-session > summary::-webkit-details-marker { display: none; }
.mt-new > summary .cr-row-title { color: var(--gold-ink); }
.mt-form { display: flex; flex-direction: column; gap: 8px; padding-bottom: 10px; }
.mt-form .field-label { display: flex; flex-direction: column; gap: 4px; }
.mt-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mt-acts { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.mt-acts li.is-done { color: var(--green-ink); }
.mt-actions { display: flex; justify-content: flex-end; }
.mt-del { display: flex; justify-content: flex-end; padding-bottom: 12px; }
@media (max-width: 900px) { .mt-grid { grid-template-columns: 1fr; } }

/* ── Quantum Shifting skin ──────────────────────────────────────────────
   A course run on the Quantum Shifting app wears that app's look: a
   midnight banner with the gold orb, Cormorant for names and numbers,
   hairlines instead of boxes. Uses the CRM's own tokens below the banner,
   so it follows whichever theme the business has chosen. */
.qs-skin { --qs-serif: "Cormorant Garamond", Georgia, serif; --qs-sans: "Outfit", -apple-system, BlinkMacSystemFont, sans-serif; --qs-gold: #C9A24A; --qs-goldT: #8A6A1F; }
.qs-hero { position: relative; overflow: hidden; display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap;
  padding: 40px 36px 34px; margin-bottom: 18px; border-radius: 18px; color: #F6F2E9;
  background: radial-gradient(120% 140% at 85% 10%, #1B2244 0%, #0E1330 45%, #060814 100%); }
.qs-hero-orb { position: absolute; right: -70px; top: -90px; width: 320px; height: 320px; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle at 50% 50%, rgba(232,196,110,.55) 0%, rgba(201,162,74,.22) 34%, rgba(201,162,74,0) 68%);
  animation: qs-breathe 9s ease-in-out infinite; }
@keyframes qs-breathe { 0%, 100% { transform: scale(.92); opacity: .75; } 33% { transform: scale(1.06); opacity: 1; } 66% { transform: scale(1.02); opacity: .9; } }
@media (prefers-reduced-motion: reduce) { .qs-hero-orb { animation: none; } }
.qs-hero-text { position: relative; min-width: 0; }
.qs-eyebrow { font: 500 11px/1.4 var(--qs-sans); letter-spacing: .28em; text-transform: uppercase; color: #D9B866; }
.qs-title { font: 300 clamp(32px, 4.4vw, 50px)/1.05 var(--qs-serif); letter-spacing: -.01em; margin: 12px 0 0; color: #F6F2E9; }
.qs-sub { font: 400 14.5px/1.5 var(--qs-sans); color: rgba(246,242,233,.72); margin-top: 10px; }
.qs-hero-actions { position: relative; display: flex; gap: 10px; flex-wrap: wrap; }
.qs-hero-actions form { margin: 0; }
.qs-btn { display: inline-flex; align-items: center; min-height: 38px; padding: 0 16px; border-radius: 999px; cursor: pointer;
  font: 500 12px var(--qs-sans); letter-spacing: .12em; text-transform: uppercase; color: #F6F2E9;
  background: rgba(246,242,233,.06); border: 1px solid rgba(246,242,233,.28); transition: background .15s, border-color .15s; }
.qs-btn:hover { background: rgba(246,242,233,.12); border-color: rgba(246,242,233,.5); }
.qs-btn.is-gold { background: var(--qs-gold); border-color: var(--qs-gold); color: #0A0D1B; }
.qs-btn.is-gold:hover { background: #D9B866; }
.qs-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; background: var(--line-2); border: 1px solid var(--line-2); border-radius: 16px; overflow: hidden; margin-bottom: 18px; }
.qs-tile { background: var(--surface); padding: 20px 22px; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.qs-k { font: 500 10.5px/1.3 var(--qs-sans); letter-spacing: .2em; text-transform: uppercase; color: var(--ink-muted); }
.qs-n { font: 400 34px/1.05 var(--qs-serif); font-variant-numeric: lining-nums; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qs-n.is-warn { color: #A4582F; }
.qs-s { font: 400 12.5px/1.4 var(--qs-sans); color: var(--ink-muted); }
.qs-path { background: var(--surface); border: 1px solid var(--line-2); border-radius: 16px; padding: 20px 22px 16px; margin-bottom: 18px; }
.qs-path-row { display: grid; grid-template-columns: repeat(15, minmax(0, 1fr)); gap: 5px; }
.qs-path-year { grid-template-columns: repeat(9, minmax(0, 1fr)); margin-top: 16px; }
.qs-seg { position: relative; display: block; height: 8px; border-radius: 4px; background: var(--surface-sunken); border: 1px solid var(--line-2); overflow: hidden; }
.qs-seg > span { display: block; height: 100%; background: linear-gradient(90deg, #B8892B, #D9B866); }
.qs-seg.is-living > span { background: linear-gradient(90deg, #8A6A1F, #C9A24A); }
.qs-seg.is-month > span { background: linear-gradient(90deg, #1C2A52, #3A4F8A); }
.qs-seg.is-now { box-shadow: 0 0 0 2px rgba(201,162,74,.45); border-color: var(--qs-gold); }
.qs-path-legend { display: flex; justify-content: space-between; gap: 12px; margin-top: 9px; font: 400 11.5px var(--qs-sans); color: var(--ink-muted); letter-spacing: .04em; }
.qs-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line-2); margin-bottom: 18px; overflow-x: auto; scrollbar-width: none; }
.qs-tabs::-webkit-scrollbar { display: none; }
.qs-tabs button { background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; padding: 12px 16px; cursor: pointer; white-space: nowrap;
  font: 400 14px var(--qs-sans); color: var(--ink-muted); display: inline-flex; align-items: center; gap: 7px; }
.qs-tabs button:hover { color: var(--ink); }
.qs-tabs button.on { color: var(--ink); border-bottom-color: var(--qs-gold); }
.qs-dot { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--qs-gold); color: #0A0D1B; font: 500 11px/18px var(--qs-sans); text-align: center; }
.qs-stack { display: flex; flex-direction: column; gap: 16px; margin-bottom: 16px; }
.qs-skin .card { border-radius: 16px; }
.qs-skin .cr-card-head { padding: 16px 20px; }
.qs-skin .cr-card-head h2, .qs-skin .section-title { font: 400 22px/1.2 var(--qs-serif); letter-spacing: 0; text-transform: none; }
.qs-skin .cr-row-title { font-family: var(--qs-sans); font-weight: 500; font-size: 15px; }
.qs-skin .cr-row-sub, .qs-skin .field-hint, .qs-skin .cz-hint { font-family: var(--qs-sans); }
.qs-skin .cr-bar { height: 3px; }
.qs-skin .cr-bar-fill { background: linear-gradient(90deg, #B8892B, #D9B866); }
.qs-skin .cr-section-name { font-family: var(--qs-sans); font-weight: 500; letter-spacing: .2em; }
.qs-skin .mt-bubble { font-family: var(--qs-sans); font-size: 14px; border-radius: 16px; }
.qs-skin .mt-bubble.is-coach { background: #0E1330; color: #F6F2E9; }
.qs-skin .mt-thread { max-height: 540px; }
.qs-skin .btn-primary { background: var(--qs-gold); border-color: var(--qs-gold); color: #0A0D1B; }
.qs-skin .btn-primary:hover { background: #D9B866; border-color: #D9B866; }
.qs-skin em { font-family: var(--qs-serif); font-size: 18px; }
@media (max-width: 900px) {
  .qs-hero { padding: 28px 20px 24px; border-radius: 14px; }
  .qs-hero-orb { width: 220px; height: 220px; right: -60px; top: -70px; }
  .qs-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qs-n { font-size: 28px; }
  .qs-path-row { gap: 3px; }
  .qs-path-legend { flex-direction: column; gap: 2px; }
}
