/* CRM Builder — app styles. Inter + professional blue/slate palette,
   light/dark via prefers-color-scheme, desktop-first density. */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/inter-var-latin.woff2') format('woff2-variations');
}

:root {
  --accent: #1570ef;
  --accent-hover: #175cd3;
  --accent-soft: #1570ef14;
  --on-accent: #ffffff;
  --bg: #f7f8fa;
  --surface: #ffffff;
  --surface-2: #f2f4f7;
  --border: #e4e7ec;
  --border-strong: #d0d5dd;
  --text: #101828;
  --text-soft: #475467;
  --text-faint: #98a2b3;
  --danger: #d92d20;
  --ok: #099250;
  --warn: #dc6803;
  --shadow: 0 1px 2px rgb(16 24 40 / 6%), 0 1px 3px rgb(16 24 40 / 10%);
  --shadow-lg: 0 12px 16px -4px rgb(16 24 40 / 10%), 0 4px 6px -2px rgb(16 24 40 / 5%);
  --radius: 10px;
  --sidebar-w: 250px;
}
/*
 * DARK IN TWO BLOCKS, AND BOTH ARE LOAD-BEARING.
 *
 * The media query guarded with :not([data-theme="light"]) follows the OS and is
 * what everybody has today. The explicit [data-theme="dark"] block is what lets
 * a manual choice win in the OTHER direction — dark on a light OS. legal.css
 * shipped with only the first half, so its pages could be forced light by an
 * attribute nothing set and could never be forced dark (§57). Half a toggle.
 *
 * THE TWO LISTS MUST MOVE TOGETHER. CSS has no mixin and a media query cannot
 * be folded into a selector, so the duplication is unavoidable. The tempting
 * alternative — resolve `system` in JS and keep one attribute-keyed block —
 * was rejected: it makes the palette depend on a script, so the boot read
 * failing (it is wrapped in try/catch, §3) would render light on a dark OS,
 * turning a currently invisible failure into a visible one. It would also give
 * the app and legal.css two different mechanisms to keep in step, and
 * legal.css cannot use JS at all (§19).
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --accent: #4692f5;
    --accent-hover: #6ba6f2;
    --accent-soft: #4692f51f;
    /* Ink FOR the accent, not the accent itself. In light the accent is dark
       enough for white (4.57:1); in dark it is a LIGHTER blue, so white on it
       measured 3.14:1 on `.btn-primary` — under AA, on the first button a new
       user presses, live for every dark-OS visitor. Measured in the running
       app, not computed. Three sites used a literal #fff over var(--accent);
       they all take this token now. */
    --on-accent: #06101f;
    --bg: #0c111d;
    --surface: #131722;
    --surface-2: #1c2130;
    --border: #283041;
    --border-strong: #333e54;
    --text: #ecf0f6;
    --text-soft: #98a5bd;
    --text-faint: #5f6c85;
    /* Raised so they clear AA on a dark ground; the light values are too
       saturated to read on #0c111d and the dark block never redefined them. */
    --danger: #ff8a80;
    --ok: #3ddc97;
    --warn: #f5b678;
    --shadow: 0 1px 2px rgb(0 0 0 / 30%);
    --shadow-lg: 0 12px 24px rgb(0 0 0 / 40%);
  }
}
:root[data-theme="dark"] {
  --accent: #4692f5;
  --accent-hover: #6ba6f2;
  --accent-soft: #4692f51f;
  /* Ink FOR the accent, not the accent itself. In light the accent is dark
     enough for white (4.57:1); in dark it is a LIGHTER blue, so white on it
     measured 3.14:1 on `.btn-primary` — under AA, on the first button a new
     user presses, live for every dark-OS visitor. Measured in the running
     app, not computed. Three sites used a literal #fff over var(--accent);
     they all take this token now. */
  --on-accent: #06101f;
  --bg: #0c111d;
  --surface: #131722;
  --surface-2: #1c2130;
  --border: #283041;
  --border-strong: #333e54;
  --text: #ecf0f6;
  --text-soft: #98a5bd;
  --text-faint: #5f6c85;
  /* Raised so they clear AA on a dark ground; the light values are too
     saturated to read on #0c111d and the dark block never redefined them. */
  --danger: #ff8a80;
  --ok: #3ddc97;
  --warn: #f5b678;
  --shadow: 0 1px 2px rgb(0 0 0 / 30%);
  --shadow-lg: 0 12px 24px rgb(0 0 0 / 40%);
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  font-feature-settings: 'cv05' 1, 'cv11' 1;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.nav-link:hover, .recent-item:hover, .stat-card:hover { text-decoration: none; }
.hidden { display: none !important; }
.muted { color: var(--text-soft); }
.num { font-variant-numeric: tabular-nums; }
svg.lucide { vertical-align: -2px; flex-shrink: 0; }
.emoji-icon { font-size: 14px; }

/* ------------------------------------------------------------ layout */
#app { display: flex; min-height: 100dvh; }
#topbar {
  display: none;
  position: sticky; top: 0; z-index: 30;
  align-items: center; gap: 10px;
  padding: 10px 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.brand-mini { font-weight: 700; letter-spacing: -0.01em; }

#sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  display: flex; flex-direction: column;
  padding: 16px 12px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  position: sticky; top: 0; height: 100dvh; overflow-y: auto;
}
#main { flex: 1; min-width: 0; }
#scrim { display: none; }

.brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px 14px; }
.brand-name {
  font-weight: 700; font-size: 15px; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.brand-logo {
  width: 28px; height: 28px; border-radius: 8px; position: relative; flex-shrink: 0;
  background: linear-gradient(160deg, #1570ef, #175cd3);
}
.brand-logo::after {
  content: ""; position: absolute; inset: 6px;
  background: conic-gradient(from 0deg at 50% 50%, transparent 0 25%, #fff 25% 50%, transparent 50% 75%, #fff 75%);
  border-radius: 3px;
}
.brand-logo.big { width: 60px; height: 60px; border-radius: 14px; margin: 0 auto 18px; display: block; }
.brand-logo.big::after { inset: 13px; border-radius: 6px; }

.nav-link {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px; margin: 1px 0;
  border-radius: 8px;
  color: var(--text-soft); font-weight: 500; font-size: 13.5px;
  overflow: hidden; white-space: nowrap;
}
.nav-link:hover { background: var(--surface-2); color: var(--text); }
.nav-link.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.nav-icon {
  width: 26px; height: 26px; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; color: inherit;
}
#nav-main .nav-icon, .sidebar-footer .nav-icon { width: 20px; height: 20px; }
.nav-label { overflow: hidden; text-overflow: ellipsis; }
.nav-empty { color: var(--text-faint); font-size: 12.5px; padding: 4px 8px; }
.nav-section {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 8px 6px;
}
.nav-section-title {
  font-size: 11px; font-weight: 600; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-faint);
}
.sidebar-footer { margin-top: auto; padding-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.offline-badge {
  font-size: 12px; color: var(--warn); background: #dc680318;
  padding: 6px 10px; border-radius: 8px; text-align: center;
}

.user-chip { display: flex; align-items: center; gap: 9px; padding: 6px 8px; border-radius: 8px; background: var(--surface-2); }
.avatar { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.avatar-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--on-accent); font-weight: 600; font-size: 13px;
}
.user-chip-text { min-width: 0; display: flex; flex-direction: column; }
.user-chip-name { font-weight: 600; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sync-status { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--text-soft); }
.sync-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.sync-status[data-status="syncing"] .sync-dot { background: var(--warn); }
.sync-status[data-status="error"] .sync-dot,
.sync-status[data-status="offline"] .sync-dot { background: var(--danger); }
.sync-status[data-status="connecting"] .sync-dot { background: var(--text-faint); animation: pulse-dot 1.4s ease-in-out infinite; }
.boot-chip { padding: 8px 10px; border-radius: 8px; background: var(--surface-2); }
@keyframes pulse-dot { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .sync-status .sync-dot { animation: none; } }

/* ------------------------------------------------------------ pages */
.page { padding: 26px 36px 60px; max-width: 1180px; margin: 0 auto; }
.page-full { max-width: 1440px; }
.page-head { margin-bottom: 18px; }
.page-head h1 {
  margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -0.02em;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.subtitle { margin: 4px 0 0; color: var(--text-soft); font-size: 13.5px; }
.module-head { display: flex; justify-content: space-between; align-items: center; gap: 14px; flex-wrap: wrap; }
.module-title-icon {
  width: 38px; height: 38px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
}
.count-badge {
  font-size: 12.5px; font-weight: 600; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 99px; padding: 1px 9px;
}
.module-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.search-wrap {
  position: relative; display: inline-flex; align-items: center; color: var(--text-faint);
}
.search-wrap svg { position: absolute; left: 10px; pointer-events: none; }
/* Specificity note: .input below declares the `padding` shorthand and would
   otherwise win on source order, dropping the text onto the search icon. */
.search-wrap .search-input { width: 230px; padding-left: 34px; }
/* Specificity note, and it is the same one as the line above — §4's standing
   trap. `.input` declares `width: 100%` and sits LATER in this file, so a bare
   `.due-filter { width: auto }` loses on source order: the select stretched to
   the full flex row (measured at 777px), wrapped .module-actions into six rows,
   and pushed the page head to 130px tall. That in turn left the guided tour no
   room at step 2 and broke a test three files away. Keep the parent in the
   selector.
   `auto` so the control sizes to its longest option, which carries the module's
   own field label ("Expected close: next 90 days"). */
.module-actions .due-filter { width: auto; max-width: 260px; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px 20px;
  margin-bottom: 16px;
}
.card-head h2 { margin: 0 0 12px; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.empty-hint { color: var(--text-soft); margin: 8px 0; }
.settings-hint { color: var(--text-soft); margin: 0 0 14px; max-width: 62ch; font-size: 13px; }
/* Said once, where identity lives, rather than as a banner on every screen —
   a banner nags and reads as an error state (§36). Quiet on purpose: this is a
   fact about the account, not a warning about it. */
.role-pill {
  margin-left: 6px; padding: 1px 6px; border-radius: 99px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.02em;
  color: var(--text-soft); background: var(--surface);
  border: 1px solid var(--border-strong); vertical-align: 1px;
}

/* A record shown to somebody who cannot edit it.
   Values on a page, not a form with its inputs turned off — a greyed-out form
   still reads as one that failed. The label is quiet and the value carries the
   weight, which is the opposite emphasis to the edit form and is what makes
   this legible as a different thing rather than a broken version of it. */
.record-read .read-row { padding: 10px 0; border-bottom: 1px solid var(--border); }
.record-read .read-row:last-of-type { border-bottom: 0; }
.read-label { font-size: 12px; color: var(--text-soft); margin-bottom: 3px; }
.read-value { font-size: 14px; color: var(--text); line-height: 1.45; }
/* The whole note, wrapped as written. fmtValue truncates for a table cell; a
   detail view is where somebody goes to read all of it. */
.read-note { margin: 0; white-space: pre-wrap; }

/* A qualifier under a table figure — "50% reclaimable" beneath a byte count.
   Its own class rather than an inline style: the CSP keeps 'unsafe-inline' on
   style-src only for genuinely dynamic values like a meter width (§30). */
.cell-sub { color: var(--text-soft); font-size: 11px; font-weight: 400; margin-top: 2px; white-space: nowrap; }
.danger-zone { border-color: #d92d2040; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; }
.settings-grid { display: grid; grid-template-columns: 1fr 220px; gap: 16px; max-width: 640px; }

/* ------------------------------------------------------------ dashboard */
.stat-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  margin-bottom: 18px;
}
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
  display: flex; flex-direction: column; gap: 3px;
  color: var(--text);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
a.stat-card:hover, .stat-card-add:hover { border-color: var(--border-strong); box-shadow: var(--shadow-lg); }
.stat-icon {
  width: 32px; height: 32px; border-radius: 8px; margin-bottom: 6px;
  display: inline-flex; align-items: center; justify-content: center;
}
.accent-chip { color: var(--accent); background: var(--accent-soft); }
.stat-count { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat-label { color: var(--text-soft); font-weight: 500; font-size: 12.5px; }
.stat-card-add {
  cursor: pointer; align-items: center; justify-content: center;
  border-style: dashed; color: var(--text-soft);
  font: inherit; background: transparent; box-shadow: none;
}
.stat-card-add:hover { color: var(--accent); border-color: var(--accent); }
.stat-card-add .stat-icon { color: inherit; background: transparent; margin: 0; }

.dash-grid { display: grid; grid-template-columns: 1fr 320px; gap: 16px; align-items: start; }
.dash-side { display: flex; flex-direction: column; gap: 0; }
.stat-tile { display: flex; flex-direction: column; gap: 2px; }
.stat-tile-label { font-size: 12.5px; font-weight: 500; color: var(--text-soft); }
.stat-tile-value { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat-tile-sub { font-size: 12px; color: var(--text-faint); }
.quick-add { display: flex; flex-direction: column; gap: 8px; }

.recent-list { list-style: none; margin: 0; padding: 0; }
.recent-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 6px; border-radius: 8px; color: var(--text);
}
.recent-item:hover { background: var(--surface-2); }
.recent-name { font-weight: 600; font-size: 13.5px; }
.recent-meta { margin-left: auto; color: var(--text-faint); font-size: 12.5px; }

/* ------------------------------------------------------------ onboarding */
.onboarding { max-width: 880px; }
.onboard-hero { text-align: center; margin: 24px 0 26px; }
.onboard-hero h1 { font-size: 30px; margin: 0 0 8px; letter-spacing: -0.03em; }
.onboard-hero .subtitle { max-width: 54ch; margin: 0 auto; }
.onboard-biz { max-width: 620px; margin: 0 auto 22px; }
.onboard-biz-grid { display: grid; grid-template-columns: 1fr 160px; gap: 14px; }
.onboard-biz .form-row { margin-bottom: 0; }
.template-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.template-card {
  position: relative; cursor: pointer;
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex; flex-direction: column; gap: 6px;
  transition: border-color 0.12s ease;
}
.template-card:hover { border-color: var(--border-strong); }
.template-card:has(input:checked) { border-color: var(--accent); }
.template-card:has(input:checked)::after {
  content: "✓"; position: absolute; top: 10px; right: 12px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center; font-size: 12px;
}
.template-card input { position: absolute; opacity: 0; pointer-events: none; }
.template-icon {
  width: 36px; height: 36px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
}
.template-name { font-weight: 600; font-size: 14px; }
.template-desc { color: var(--text-soft); font-size: 12.5px; }
.onboard-actions { margin-top: 24px; text-align: center; display: flex; flex-direction: column; gap: 14px; align-items: center; }
.onboard-buttons { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
/* Set apart rather than made louder: the anonymous path above stays the
   primary action, this only has to stop reading as somebody else's door. */
.onboard-account {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  max-width: 460px; padding: 14px 18px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-2);
}
.onboard-account .settings-hint { margin: 0; }
.mode-switch { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }
.team-role { padding: 4px 8px; font-size: 12.5px; width: auto; }
/* The daily digest block inside the Notifications card. Defined here rather
   than reached for: §27 records that inventing a class name produces an
   unstyled box that still looks plausible in a screenshot, which is how
   `.note.warn` shipped looking fine and meaning nothing. */
.hr { border: 0; border-top: 1px solid var(--border); margin: 20px 0 16px; }
.settings-sub { margin: 0 0 6px; font-size: 14px; font-weight: 650; letter-spacing: -0.01em; }
/* The exact string that would be sent, so it is shown as text rather than
   prose — pre-wrap keeps the line breaks the message really has, and the
   overflow guard stops a long module name pushing the card sideways. */
.digest-preview {
  margin: 0 0 14px; padding: 10px 12px; max-width: 62ch;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.55; white-space: pre-wrap;
  overflow-wrap: anywhere; overflow-x: auto;
}
.settings-hint code {
  padding: 1px 5px; border-radius: 5px; background: var(--surface-2);
  border: 1px solid var(--border); font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
/* "Your webhook did not survive a recovery." Warm rather than red: nothing is
   broken and nobody did anything wrong — a credential we deliberately do not
   keep could not come back, and one paste fixes it. An error colour here would
   read as a fault in the product. Defined, not assumed (§27). */
/* The translucent-tint idiom this file already uses for --warn (see the sync
   chip): an alpha-suffixed hex over whatever surface is behind it, so it reads
   correctly in both themes. A solid light background here would have been
   hardcoded into a themed app. */
.note-restore {
  margin: 0 0 14px; padding: 11px 13px; max-width: 62ch;
  border: 1px solid #dc680355; border-radius: 8px;
  background: #dc680318; color: var(--text);
  font-size: 13px; line-height: 1.6;
}
.note-restore code {
  padding: 1px 5px; border-radius: 5px; background: var(--surface);
  border: 1px solid var(--border); font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
/* Telegram setup. Collapsed by default because it is one provider of three,
   and an open block of instructions reads as work everybody has to do. */
.tg-setup { margin-top: 16px; border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2); padding: 0 14px; }
.tg-setup[open] { padding-bottom: 14px; }
.tg-summary { cursor: pointer; padding: 11px 0; font-size: 13px; font-weight: 600; list-style-position: inside; }
.tg-steps { margin: 0 0 14px; padding-left: 20px; max-width: 62ch; color: var(--text-soft); font-size: 13px; line-height: 1.7; }
/* The destination choice inside step 2. Defined rather than assumed — an
   undefined class renders as an unstyled box that still looks plausible
   (§27). */
.tg-choice { margin: 6px 0; padding-left: 18px; }
.tg-choice li { margin: 2px 0; }
.tg-steps code {
  padding: 1px 5px; border-radius: 5px; background: var(--surface);
  border: 1px solid var(--border); font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
/* The chats a lookup found. Buttons rather than a select: each row carries a
   kind ("group", "private") that a picker's single line would have to drop,
   and that is what tells two similarly named chats apart. */
.tg-chat-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 0; }
.tg-chat {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); cursor: pointer;
  font: inherit; font-size: 13px; text-align: left;
}
.tg-chat:hover { border-color: var(--accent); }
.tg-chat[disabled] { opacity: 0.6; cursor: default; }
.tg-chat-kind { color: var(--text-soft); font-size: 12px; }
/* Subject-access search. Defined here rather than assumed: an undefined class
   renders as an unstyled box that still looks plausible in a screenshot, which
   is how `.note.warn` shipped meaning nothing (§27). */
.dsar-search { display: flex; gap: 8px; align-items: center; margin: 12px 0; max-width: 62ch; }
/* The input carries `.input`, which is `width: 100%` and sits later in this
   file — so the button would be squeezed to nothing without a flex basis of
   its own here. §4's cascade trap, in the shape it usually takes. */
.dsar-search .input { flex: 1 1 auto; min-width: 0; }
.dsar-search .btn { flex: 0 0 auto; }
/*
 * The retention row carries up to five controls (§50) where Data requests has
 * two, so it wraps and drops the 62ch cap that stops a single search field
 * running the width of the screen. Without this the selects are squeezed to
 * about ten characters each and read as broken chrome rather than as a
 * question - §4's layout trap, which renders perfectly plausibly.
 *
 * MUST sit after `.dsar-search .input` above: same specificity (0,2,0), so
 * order is what decides, and `width: auto` is what releases `.input`'s 100%.
 */
.stale-search { flex-wrap: wrap; max-width: none; }
.stale-search .input { flex: 0 1 auto; width: auto; min-width: 11ch; }
.dsar-list { list-style: none; margin: 12px 0; padding: 0; display: flex; flex-direction: column; gap: 6px; max-width: 62ch; }
.dsar-hit {
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
}
.dsar-where { font-size: 13px; font-weight: 600; }
.dsar-fields { font-size: 12px; color: var(--text-soft); }
.dsar-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 99px;
  background: var(--border); color: var(--text-soft); font-size: 11px; font-weight: 600;
}
/* A flat tint rather than `color-mix`, which appears nowhere else in this file
   and would be the one rule that silently does nothing on an older browser —
   leaving the "removed field" tag looking identical to the neutral one, which
   is the distinction it exists to draw. */
.dsar-tag-warn { background: #fdf1e3; color: #b54708; }
/* Both branches, for the same reason as the token blocks above — and this
   comment used to say the opposite: "there is no `data-theme` attribute and no
   `.dark` class to hang a rule on". That was true when it was written and went
   false the moment the toggle landed. A component-level dark override is
   exactly the kind of rule that gets left behind by a theme change, because
   nothing points at it from the token block. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .dsar-tag-warn { background: #3a2408; color: #f5b678; }
}
:root[data-theme="dark"] .dsar-tag-warn { background: #3a2408; color: #f5b678; }
.meter-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; margin: 12px 0; }
.meter { border: 1px solid var(--border); border-radius: 9px; padding: 12px 14px; background: var(--surface-2); }
.meter-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; font-weight: 600; }
.meter-bar { height: 6px; border-radius: 99px; background: var(--border); margin: 8px 0 6px; overflow: hidden; }
.meter-bar span { display: block; height: 100%; background: var(--ok); border-radius: 99px; }
.meter[data-level="warn"] .meter-bar span { background: var(--warn); }
.meter[data-level="critical"] .meter-bar span { background: var(--danger); }
.meter p { margin: 0; font-size: 12px; }
.meter-note { margin-top: 4px !important; font-size: 11.5px; opacity: 0.85; }
.mode-switch .btn[disabled] { opacity: 1; cursor: default; }
.restore-choice p { margin: 0 0 10px; color: var(--text-soft); font-size: 13.5px; }
.restore-choice p:last-child { margin-bottom: 0; }
.restore-choice strong { color: var(--text); }
/* A nested question sits above the modal it was asked from. */
.modal-backdrop + .modal-backdrop { z-index: 60; }

.template-list { display: flex; flex-direction: column; gap: 8px; }
.template-line {
  display: flex; align-items: center; gap: 14px; text-align: left;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 9px; padding: 12px 14px; cursor: pointer;
  color: var(--text); font: inherit;
}
.template-line:hover { border-color: var(--accent); }

/* ------------------------------------------------------------ table */
.table-wrap { padding: 0; overflow-x: auto; }
.table-wrap .card-head { padding: 16px 16px 0; }
.records-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.records-table th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.05em;
  font-weight: 600; color: var(--text-faint);
  padding: 11px 16px; border-bottom: 1px solid var(--border);
  background: var(--surface); position: sticky; top: 0; z-index: 1;
}
.records-table td { padding: 10px 16px; border-bottom: 1px solid var(--border); }
.th-num, .td-num { text-align: right; }
.th-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.th-sortable:hover { color: var(--text); }
.th-sortable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.th-sorted { color: var(--accent); }
.sort-hint, .sort-on { display: inline-flex; margin-left: 4px; vertical-align: middle; }
.sort-hint { opacity: 0; transition: opacity 0.1s ease; }
.th-sortable:hover .sort-hint { opacity: 0.45; }
.sort-on.desc { transform: rotate(180deg); }
.th-num .sort-hint, .th-num .sort-on { margin-left: 2px; }

/* CSV import column mapping */
.map-table { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.map-row {
  display: grid; grid-template-columns: 1fr 1fr 1.2fr;
  gap: 10px; align-items: center;
  padding: 6px 8px; border-radius: 8px; background: var(--surface-2);
}
.map-head {
  background: transparent; padding-bottom: 0;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-faint); font-weight: 600;
}
.map-col, .map-sample { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.import-mode { margin-top: 4px; }
/* The date-format control on the CSV import screen (§45). Bordered rather
   than plain, because it is a question that must be answered rather than a
   line to skim past. */
.csv-format {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; margin-bottom: 14px; background: var(--surface-2);
}
.csv-format-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; }
.csv-format-row .input { width: auto; min-width: 190px; }
.csv-format .settings-hint { margin: 6px 0 0; }
.csv-format .settings-hint:empty { display: none; }
/* A required-but-unanswered question disables the import. Without this the
   button looks pressable and simply does nothing, which reads as a bug. */
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
@media (max-width: 760px) {
  .map-row { grid-template-columns: 1fr; gap: 4px; }
  .map-head { display: none; }
}
.records-table tbody tr { cursor: pointer; }
.records-table tbody tr:hover { background: var(--surface-2); }
.records-table tbody tr:last-child td { border-bottom: none; }
.pill {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 99px; padding: 1.5px 9px;
  font-size: 12px; font-weight: 500; white-space: nowrap;
}
.pill-accent { color: var(--accent); background: var(--accent-soft); border-color: transparent; }
.pill-ok { color: var(--ok); background: #09925014; border-color: transparent; }
.pill-danger { color: var(--danger); background: #d92d2014; border-color: transparent; }
.check-yes { color: var(--ok); }

/* ------------------------------------------------------------ kanban */
.kanban {
  display: flex; gap: 12px; align-items: flex-start;
  overflow-x: auto; padding-bottom: 14px;
}
.kanban-col {
  background: var(--surface-2);
  border-radius: var(--radius);
  padding: 8px;
  width: 264px; flex-shrink: 0;
  border: 1.5px solid transparent;
}
.kanban-col.drop-target { border-color: var(--accent); }
.kanban-col-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-weight: 600; font-size: 12.5px; padding: 4px 8px 10px;
}
.kanban-col-meta { color: var(--text-faint); font-weight: 500; font-size: 11.5px; font-variant-numeric: tabular-nums; }
.kanban-cards { display: flex; flex-direction: column; gap: 8px; min-height: 30px; }
.kanban-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 10px 12px;
  cursor: grab;
  display: flex; flex-direction: column; gap: 2px;
}
.kanban-card:hover { border-color: var(--border-strong); }
.kanban-card.dragging { opacity: 0.5; }
.kanban-card-title { font-weight: 600; font-size: 13px; }
.kanban-card-value { color: var(--text-soft); font-size: 12.5px; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------ forms & buttons */
.input {
  width: 100%; padding: 8px 12px;
  border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--surface); color: var(--text);
  font: inherit;
}
.input::placeholder { color: var(--text-faint); }
.input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
.form-row { margin-bottom: 14px; }
.form-row > label { display: block; font-weight: 500; font-size: 13px; margin-bottom: 5px; color: var(--text-soft); }
.req { color: var(--danger); }
.checkbox-line { display: inline-flex; align-items: center; gap: 8px; font-weight: 500; cursor: pointer; }
input[type="checkbox"] { accent-color: var(--accent); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 14px; border-radius: 8px;
  border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--text);
  font: inherit; font-weight: 600; font-size: 13.5px; cursor: pointer;
  transition: background 0.1s ease, border-color 0.1s ease;
}
.btn:hover { background: var(--surface-2); }
.btn-lg { padding: 10px 20px; font-size: 14.5px; }
.btn-block { width: 100%; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-outline { background: transparent; }
.btn-outline:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--accent); }
.btn-ghost:hover { background: var(--accent-soft); }
.btn-danger-ghost { background: transparent; border-color: #d92d2055; color: var(--danger); }
.btn-danger-ghost:hover { background: #d92d200f; }
.btn-google { background: var(--surface); }
.or-line {
  display: flex; align-items: center; gap: 12px; margin: 16px 0;
  color: var(--text-faint); font-size: 12px;
}
.or-line::before, .or-line::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.icon-btn {
  width: 33px; height: 33px; border-radius: 8px;
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text-soft); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; padding: 0;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn-sm { width: 26px; height: 26px; border-radius: 7px; }
.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: 8px; overflow: hidden; }
.seg-btn {
  border: none; background: var(--surface); color: var(--text-soft);
  padding: 7px 12px; cursor: pointer; display: inline-flex; align-items: center;
}
.seg-btn.on { background: var(--accent-soft); color: var(--accent); }

/* ------------------------------------------------------------ modal */
#modal-root .modal-backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: rgb(9 12 20 / 55%);
  display: flex; align-items: center; justify-content: center;
  padding: 18px;
}
.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  width: 100%; max-width: 500px;
  max-height: calc(100dvh - 36px);
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg);
}
.modal-wide { max-width: 820px; }
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px 12px;
}
.modal-head h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.modal-body { padding: 6px 22px; overflow-y: auto; }
.modal-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 22px 18px; gap: 10px;
  border-top: 1px solid var(--border); margin-top: 8px;
}
.modal-foot-right { display: flex; gap: 10px; }
body.modal-open { overflow: hidden; }

/* ------------------------------------------------------------ builder */
.builder-meta { display: flex; gap: 24px; flex-wrap: wrap; }
.swatch-row { display: flex; gap: 6px; flex-wrap: wrap; max-width: 460px; }
.swatch {
  width: 32px; height: 32px; border-radius: 8px;
  border: 1.5px solid var(--border); background: var(--surface-2);
  cursor: pointer; color: var(--text-soft);
  display: inline-flex; align-items: center; justify-content: center;
}
.swatch:hover { border-color: var(--border-strong); }
.swatch.on { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.swatch.color { border-color: transparent; }
.swatch.color.on { outline: 2px solid var(--text); outline-offset: 2px; }
#builder-fields { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
/* Flex, not grid: the options/relation input needs a line of its own, and with
   grid the trailing controls were auto-placed into columns sized for the first
   line — squashing "Req" to 16px so its label painted over "List". Flex lets
   the extra input claim a full line without disturbing anything before it. */
.builder-field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  background: var(--surface-2);
  border-radius: 9px; padding: 8px;
}
.builder-field .drag-dots { flex: 0 0 auto; }
.builder-field .bf-label { flex: 1 1 150px; min-width: 0; }
.builder-field .bf-type { flex: 0 0 150px; } /* fits "Link to module" untruncated */
.bf-actions { flex: 0 0 auto; margin-left: auto; display: flex; align-items: center; gap: 8px; }
/* flex-basis:100% puts these on their own line, below everything above. */
.builder-field .bf-options, .builder-field .bf-related { flex: 1 1 100%; margin-left: 24px; }
.drag-dots { color: var(--text-faint); display: inline-flex; }
.bf-flag {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--text-soft); cursor: pointer; white-space: nowrap;
}

/* ------------------------------------------------------------ admin & charts */
.admin-stats { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.chart { position: relative; }
.chart-svg { width: 100%; height: 130px; display: block; }
.chart .bar { fill: var(--accent); }
.chart .bar-zero { fill: var(--border); }
.chart .bar-g:hover .bar { fill: var(--accent-hover); }
.chart-baseline { stroke: var(--border); stroke-width: 1; }
.chart-x {
  display: flex; justify-content: space-between;
  font-size: 11.5px; color: var(--text-faint); margin-top: 4px;
  font-variant-numeric: tabular-nums;
}
.chart-max { position: absolute; top: 0; left: 0; font-size: 11.5px; }
.table-card { padding: 0; }
.table-card .admin-users-head { padding: 16px 16px 12px; }
.table-scroll { overflow-x: auto; }
.chart-tip {
  position: fixed; z-index: 95; pointer-events: none;
  background: var(--text); color: var(--bg);
  font-size: 12px; font-weight: 500;
  padding: 5px 10px; border-radius: 7px;
  box-shadow: var(--shadow-lg);
  white-space: nowrap;
}
.admin-users-head {
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 10px;
}
.admin-users-head h2 { margin: 0; }
.admin-actions { white-space: nowrap; }
.admin-actions .icon-btn { width: 29px; height: 29px; margin-right: 4px; }
.records-table tr.admin-row { cursor: default; }

/* ------------------------------------------------------------ toast */
#toast-root {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  z-index: 90; display: flex; flex-direction: column; gap: 8px; align-items: center;
  pointer-events: none;
}
.toast {
  background: var(--text); color: var(--bg);
  padding: 9px 18px; border-radius: 99px;
  font-weight: 500; font-size: 13px;
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  box-shadow: var(--shadow-lg);
}
.toast.show { opacity: 1; transform: translateY(0); }

/* ------------------------------------------------------------ mobile */
@media (max-width: 980px) {
  .dash-grid, .chart-grid { grid-template-columns: 1fr; }
  .settings-grid { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  #topbar { display: flex; }
  #app { flex-direction: column; }
  #sidebar {
    position: fixed; z-index: 40; left: 0; top: 0; bottom: 0;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: var(--shadow-lg);
  }
  body.sidebar-open #sidebar { transform: translateX(0); }
  body.sidebar-open #scrim {
    display: block; position: fixed; inset: 0; z-index: 35;
    background: rgb(9 12 20 / 45%);
  }
  .page { padding: 18px 16px 50px; }
  .search-wrap .search-input { width: 100%; }
  .search-wrap { width: 100%; }
  .module-actions { width: 100%; }
  .onboard-biz-grid { grid-template-columns: 1fr; }
  .builder-field .bf-label { flex: 1 1 100%; }
  .builder-field .bf-type { flex: 1 1 auto; }
  .bf-actions { margin-left: 0; }
  .records-table thead { display: none; }
  .records-table tbody tr { display: block; padding: 10px 14px; border-bottom: 1px solid var(--border); }
  .records-table td { display: flex; gap: 10px; padding: 3px 0; border: none; }
  .records-table td::before {
    content: attr(data-label); flex-shrink: 0; width: 40%;
    color: var(--text-faint); font-size: 11.5px; text-transform: uppercase;
    letter-spacing: 0.05em; padding-top: 2px;
  }
  .th-num, .td-num { text-align: left; }
}

/* ------------------------------------------------------------ guided tour */
body.tour-open { overflow: hidden; }
.tour-root { position: fixed; inset: 0; z-index: 80; }
.tour-scrim { position: absolute; inset: 0; background: rgb(9 12 20 / 55%); }
/* The "hole": a ring drawn around the target rather than a clipped overlay,
   so the highlighted control stays fully legible and correctly coloured. */
.tour-ring {
  position: absolute;
  border-radius: 10px;
  box-shadow: 0 0 0 3px var(--accent), 0 0 0 9999px rgb(9 12 20 / 55%);
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
  opacity: 0;
}
.tour-pop {
  position: absolute;
  width: min(360px, calc(100vw - 24px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 18px 20px 16px;
  transition: left 0.25s ease, top 0.25s ease, opacity 0.15s ease;
}
.tour-pop.is-loading { opacity: 0.35; }
.tour-count {
  margin: 0 0 6px; font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--accent);
}
.tour-pop h3 { margin: 0 0 7px; font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
.tour-body { margin: 0 0 16px; font-size: 13.5px; color: var(--text-soft); }
.tour-actions { display: flex; align-items: center; gap: 8px; }
.tour-spacer { flex: 1; }
.tour-actions .btn { padding: 7px 14px; font-size: 13px; }
.tour-skip { color: var(--text-faint); }

@media (prefers-reduced-motion: reduce) {
  .tour-ring, .tour-pop { transition: none; }
}
@media (max-width: 760px) {
  /* Anchoring to a specific control is unreliable once the sidebar is a
     drawer; pin the card to the bottom instead. */
  .tour-pop { left: 12px !important; right: 12px; top: auto !important; bottom: 14px; width: auto; }
}

/* Sign-in claim and consent prompts: several full-width choices stacked, with
   the recommended one first. Flex rather than grid for the same reason the
   builder rows are — auto-placement sizes later items to the first row. */
.claim-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}
.claim-actions .btn { width: 100%; justify-content: center; }

/* Team member list in Settings. */
.team-list { list-style: none; margin: 0 0 14px; padding: 0; }
.team-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
/* The name takes the slack so the role pill and the actions stay put down the
   column rather than drifting with the length of each person's name. */
.team-who { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.team-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; min-width: 62px; justify-content: flex-end; }
.team-list li:last-child { border-bottom: 0; }

/* A member opening the module builder sees the schema, not an invitation to
   change it. Pointer-events rather than the disabled attribute: the fields are
   still readable and selectable, they just cannot be edited, and the server
   would refuse the write anyway. */
.builder-readonly .builder-field input,
.builder-readonly .builder-field select,
.builder-readonly .swatch-row { pointer-events: none; opacity: 0.75; }
.builder-readonly .bf-actions { display: none; }

.record-author { margin: 14px 0 0; padding-top: 10px; border-top: 1px solid var(--border); }

/* Problem reports on the admin dashboard. */
.report-list { list-style: none; margin: 0; padding: 0; }
.report-list li { padding: 12px 0; border-bottom: 1px solid var(--border); }
.report-list li:last-child { border-bottom: 0; }
.report-list li.is-resolved { opacity: 0.55; }
.report-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 14px; }
.report-head .btn { margin-left: auto; }
.report-message { margin: 8px 0; white-space: pre-wrap; }
.report-errors {
  margin: 8px 0 0;
  padding: 10px;
  background: var(--bg-subtle, rgba(127, 127, 127, 0.08));
  border-radius: 6px;
  font-size: 12px;
  overflow-x: auto;
}
.report-meta { font-size: 12px; margin: 6px 0 0; }

.beta-points { margin: 0 0 14px; padding-left: 20px; }
.beta-points li { margin-bottom: 8px; color: var(--muted, inherit); font-size: 14px; line-height: 1.55; }
