:root {
  --bg: oklch(99% 0.002 200);          /* page + sidebar background (seamless) */
  --bg-elev: #ffffff;                  /* cards */
  --bg-elev-2: oklch(97% 0.003 200);   /* hover tint (nav, ghost/icon buttons, code chip) */
  --border: oklch(91% 0.005 200);      /* card borders, table header rule */
  --border-soft: oklch(94% 0.003 200); /* row dividers */
  --text: oklch(20% 0.01 240);         /* primary text / headings */
  --text-dim: oklch(48% 0.01 240);     /* subtitles, dates */
  --text-muted: oklch(58% 0.01 240);   /* section labels, hints */

  /* Primary — teal. Every first-party interactive/brand element. */
  --accent: oklch(50% 0.09 195);
  --accent-tint: oklch(93% 0.03 195);
  --accent-tint-strong: oklch(96% 0.025 195);
  --accent-border: oklch(85% 0.06 195);
  --accent-wash: oklch(97% 0.015 195);

  /* Purple. In salewise this was reserved for the external CrowdVoice link;
     here the app IS CrowdVoice, so purple is a regular secondary tint
     (used for the proposal_delivered stage pill). */
  --purple: oklch(48% 0.1 300);
  --purple-tint: oklch(93% 0.04 300);
  --purple-wash: oklch(97% 0.014 300);
  --purple-border: oklch(87% 0.045 300);

  /* Semantic — status bands */
  --good: oklch(40% 0.14 150);
  --good-bg: oklch(93% 0.06 150);
  --good-border: oklch(80% 0.08 150);
  --mid: oklch(46% 0.13 70);
  --mid-bg: oklch(94% 0.06 75);
  --mid-border: oklch(82% 0.08 75);
  --low: oklch(48% 0.16 25);
  --low-bg: oklch(93% 0.06 25);
  --low-border: oklch(85% 0.07 25);

  --radius: 14px;
  --shadow: 0 1px 2px rgba(20,30,40,.04);
  --font: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  display: flex;
  min-height: 100vh;
}

/* ---------- Sidebar ---------- */
.sidebar {
  width: 236px;
  flex-shrink: 0;
  background: var(--bg);
  border-right: 1px solid var(--border);
  padding: 22px 14px;
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
}
.sidebar nav { flex: 1; }
.brand { display: flex; align-items: center; gap: 8px; padding: 4px 8px 22px; }
.brand-logo { display: block; height: 22px; width: auto; }
.bd-tag {
  font-size: 10px; font-weight: 800; letter-spacing: .5px;
  color: var(--accent); background: var(--accent-tint);
  padding: 2px 6px; border-radius: 6px;
}
.nav-section {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px;
  color: var(--text-muted); margin: 6px 10px 8px;
}
/* :not(:first-child), not the "+" adjacent-sibling combinator — each section's
   nav-link <a> items sit between the .nav-section divs, so they're never
   direct siblings of each other. */
.nav-section:not(:first-child) { margin-top: 22px; padding-top: 22px; position: relative; }
.nav-section:not(:first-child)::before {
  content: ""; position: absolute; top: 0; left: 10px; right: 10px;
  height: 1px; background: var(--border-soft);
}
.nav-link {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: 8px;
  color: var(--text-dim); text-decoration: none;
  font-weight: 500; font-size: 13.5px; transition: background .12s, color .12s;
}
.nav-link:hover { background: var(--bg-elev-2); color: var(--text); }
.nav-link.active { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.nav-icon { width: 17px; height: 17px; flex-shrink: 0; }
/* Two-word workspace labels ("Milestones & Deliver.") are abbreviated to hold one line in
   236px — the page's own h1 spells them out in full. nowrap keeps an abbreviation from
   breaking after the "&", which reads worse than the abbreviation itself; there's ~8px of
   slack at the longest label, enough for a two-digit count badge beside it. */
.nav-label { white-space: nowrap; }
.nav-cnt { margin-left: auto; font-size: 11px; color: var(--text-muted); font-weight: 500; }

.org-foot {
  border-top: 1px solid var(--border-soft);
  padding: 12px 8px;
  position: relative;
}
.user-pill {
  width: 100%; display: flex; align-items: center; gap: 10px; padding: 8px 6px;
  background: transparent; border: none; border-radius: 8px;
  cursor: pointer; color: inherit; font: inherit; text-align: left;
}
.user-pill:hover {
  background: var(--bg-elev-2);
}
.user-avatar {
  flex-shrink: 0; width: 17px; height: 17px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-tint-strong); border-radius: 50%; color: var(--accent);
}
.user-info { min-width: 0; flex: 1; }
/* Shared two-line caption style: bold/dark primary line, smaller/muted
   secondary line — used for the sidebar footer (org name, then user name)
   and the Lead workspace section's active-lead subtitle. */
.caption-primary { font-weight: 600; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.caption-secondary { font-weight: 500; font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-section-sub {
  margin: -6px 10px 10px; text-transform: none; letter-spacing: normal;
  color: oklch(71% 0.01 240);
}
.user-menu {
  display: none; position: absolute; bottom: 100%; left: 8px; right: 8px; margin-bottom: 8px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 8px; box-shadow: var(--shadow); z-index: 1000;
}
.user-menu[data-open] {
  display: block;
}
.menu-item {
  width: 100%; display: flex; align-items: center; gap: 8px; padding: 10px 12px; border: none;
  background: transparent; color: var(--text); cursor: pointer; font: inherit; text-align: left;
  font-size: 13px; text-decoration: none;
}
.menu-item:hover {
  background: var(--bg-elev-2);
}
.menu-item.logout { color: var(--low); }
.menu-item.logout:hover { background: var(--low-bg); }
.menu-item.current { color: var(--accent); font-weight: 600; }
.menu-icon { flex-shrink: 0; opacity: .8; }
.menu-item .menu-check { margin-left: auto; color: var(--accent); font-weight: 700; }
.menu-section {
  font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px;
  color: var(--text-muted); padding: 9px 12px 4px;
}
.menu-divider { height: 1px; background: var(--border-soft); margin: 4px 0; }

/* ---------- Content ---------- */
/* min-width: 0 — .content is a flex item (body is the flex container), and a flex
   item's default min-width:auto lets any long unwrappable descendant (e.g. a nowrap
   .note-preview-text holding a long note/FAQ answer) blow the whole page out to the
   right. min-width:0 breaks that chain so inner ellipsis rules can actually apply. */
.content { flex: 1; padding: 40px 32px; max-width: 1180px; min-width: 0; }
.content.wide { max-width: none; }
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 26px;
}
h1 { font-size: 26px; margin: 0 0 6px; letter-spacing: -.4px; font-weight: 700; color: var(--text); }
.subtitle { color: var(--text-dim); margin: 0; max-width: 900px; font-size: 14px; }
.content.wide .page-head .subtitle { max-width: none; }
/* For a header with nothing under it but the page's own content — Invoices & payments, whose
   h1 carries only a currency chip beside it. (It used to pair with a full-width .subtitle
   below the header; those sentences now live on the h1's ⓘ instead.) */
.page-head.head-tight { margin-bottom: 8px; }
/* An ⓘ beside an h1: the shared 12px dot reads as a speck against 26px type. */
.page-head h1 .info-dot { font-size: .55em; }
.card-title { font-size: 15px; margin: 0 0 10px; font-weight: 700; color: var(--text); }
h2 { font-size: 18px; margin: 0 0 12px; }

/* ---------- Cards / layout ---------- */
.card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px 24px;
  margin-bottom: 20px;
}
.resource-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.resource-grid > .card { margin-bottom: 0; }
.pricing-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.pricing-grid > .card { margin-bottom: 0; }
@media (max-width: 980px) { .resource-grid, .pricing-grid { grid-template-columns: 1fr; } }
.pricing-body { color: var(--text-dim); margin: 12px 0 0; white-space: pre-line; }
.doc-tile {
  width: 42px; height: 42px; border-radius: 10px; flex-shrink: 0;
  background: var(--accent-tint-strong); border: 1px solid var(--accent-border);
  color: var(--accent); display: flex; align-items: center; justify-content: center;
}

/* ---------- Tables ---------- */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th {
  position: relative;
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--text-muted); font-weight: 600; padding: 0 12px 12px; border-bottom: 1px solid var(--border);
}
.data-table td { padding: 16px 12px; border-bottom: 1px solid var(--border-soft); font-size: 13.5px; vertical-align: middle; }
.data-table tr:last-child td { border-bottom: none; }
.data-table .right, th.right { text-align: right; }
.strong { font-weight: 600; font-size: 14px; color: var(--text); }
.muted { color: var(--text-dim); }
.small { font-size: 12px; }
.nowrap { white-space: nowrap; }
.empty { text-align: center; color: var(--text-muted); padding: 28px 12px; }
/* An empty state that isn't standing in for table rows doesn't need to reserve their height —
   used by a layer card with no invoices, where it's the card's whole body. */
.empty-tight { padding: 4px 12px 2px; max-width: 620px; margin: 0 auto; }
.next-step, .action-items-cell { max-width: 220px; }
.row-archived { opacity: .55; }
.truncate-1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
.clamp-3 {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; max-width: 220px;
}
.clamp-4 {
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden; max-width: 220px;
}
.col-client { max-width: 190px; }
.stage-col { width: 96px; }

/* Hover/focus label for anything carrying title="…" — icon buttons, field labels, column
   headers (main.js moves the title onto one shared node). Same light bubble as Crowd Voice BD:
   elevated surface, hairline border, roomy padding at reading size, so a bubble that has to
   hold a sentence of explanation is comfortable rather than cramped.

   Positioned `fixed` — BD gets this look from a pure-CSS `[data-tooltip]::after`, but an
   absolutely positioned bubble is clipped by .table-scroll's own overflow, which is exactly
   where the column-header tooltips live. Hence the shared node placed by JS. */
.tip {
  position: fixed; z-index: 200; pointer-events: none;
  max-width: 320px; width: max-content; padding: 10px 13px;
  background: var(--bg-elev); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(20,30,40,.16);
  font-size: 13.5px; font-weight: 400; line-height: 1.5; white-space: pre-line;
  /* A titled element can be an uppercase field label (.fin-label) or a table header — the
     bubble's own text must not inherit its casing, tracking or alignment. */
  text-transform: none; letter-spacing: normal; text-align: left;
  opacity: 0; transition: opacity .12s ease;
}
.tip.show { opacity: 1; }

/* A column header (or any label) whose meaning is worth spelling out: the copy lives in
   FIELD_TIPS (app/templating.py) and reaches the bubble as a plain title="". No underline or
   glyph marking it — the cursor is the only tell, which keeps a ten-column financial table
   clean. On the span rather than the <th> so the cursor tracks the label, not the whole cell. */
.th-tip { cursor: help; }
/* A card title with an explanation behind it. Same bubble as .th-tip, but the ⓘ is what makes
   it findable — a heading gives no other clue that hovering does anything. The title="" lives
   on this wrapper, never on the glyph, so the two are one hover target and not two. */
.card-tip { cursor: help; display: inline-flex; align-items: baseline; gap: 5px; }
.info-dot { font-size: 12px; font-weight: 400; color: var(--text-muted); }
.card-tip:hover .info-dot { color: var(--accent); }
/* The currency the whole page is denominated in, beside the h1. Every figure below depends on
   it and it is set nowhere else on the page, so it's a badge rather than a whisper — but a
   rectangular one, because the round .pill shape means *status* everywhere else in this app.
   Sentence case: "ALL AMOUNTS IN SAR" in caps reads as shouting. */
.currency-note {
  align-self: center; flex-shrink: 0;
  padding: 5px 12px; border-radius: 8px;
  border: 1px solid var(--accent-border); background: var(--accent-tint);
  color: var(--accent); font-size: 12.5px; font-weight: 700; white-space: nowrap;
  letter-spacing: .01em;
}

/* ---------- Table header: sort + filter ---------- */
/* Column label and filter icon stack instead of sitting side by side, so
   filterable columns don't need extra horizontal room for the icon. */
.th-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.th-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  font-weight: 600; color: var(--text-muted); cursor: pointer;
}
.th-btn:hover { color: var(--text); }
.th-btn.active { color: var(--accent); }
.sort-arrow { font-size: 8px; width: 8px; display: inline-block; }
.th-filter-btn {
  background: none; border: none; padding: 2px; margin: 0; border-radius: 4px;
  color: var(--text-muted); cursor: pointer; font-size: 10px; line-height: 1;
}
.th-filter-btn:hover { color: var(--text); background: var(--bg-elev-2); }
.th-filter-btn.active { color: var(--accent); background: var(--accent-tint-strong); }
.th-filter-panel {
  position: absolute; top: 100%; left: 0; margin-top: 6px; z-index: 30;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(20,30,40,.14); padding: 10px 12px; min-width: 150px;
  text-transform: none; letter-spacing: normal;
}
.th-filter-panel-wide { min-width: 190px; }
.th-filter-panel label {
  display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 500;
  color: var(--text-dim); margin-bottom: 7px; cursor: pointer; white-space: nowrap;
}
.th-filter-panel label:last-of-type { margin-bottom: 0; }
.th-filter-panel input { width: auto; margin: 0; }
.filter-panel-actions {
  display: flex; gap: 10px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-soft);
}
.filter-panel-actions button {
  background: none; border: none; padding: 0; font-size: 11.5px; font-weight: 600;
  color: var(--accent); cursor: pointer;
}
.filter-panel-actions button:hover { text-decoration: underline; }

/* ---------- Drag-to-reorder handle ---------- */
.drag-col { width: 24px; padding: 0 0 0 8px !important; }
.drag-handle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; color: var(--text-muted); cursor: grab;
}
.drag-handle:hover { color: var(--text-dim); }
tr.dragging { opacity: .4; }
.leads-table.drag-disabled .drag-handle { opacity: .25; cursor: not-allowed; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-family: inherit; font-size: 13px; font-weight: 600;
  padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-elev); color: var(--text); cursor: pointer;
  text-decoration: none; transition: filter .12s, background .12s, border-color .12s;
}
.btn:hover { background: var(--bg-elev-2); }
/* A button that reads as a link in running text — for an action that belongs inside a
   sentence (e.g. "pick a contract and open its Milestones", which opens the breadcrumb's
   picker). Inherits the surrounding type so the sentence stays one sentence. */
.link-btn {
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: var(--accent); cursor: pointer; text-align: left;
}
.link-btn:hover { text-decoration: underline; }
/* Without this, a disabled button (e.g. the slides-popup Generate button while the
   logo/images are still loading) is functionally inert but LOOKS identical to an
   enabled one — no dimming, still a pointer cursor — reading as clickable when it isn't. */
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover { background: var(--bg-elev); filter: none; }
.btn.primary {
  background: var(--accent); border: none; color: #fff;
  padding: 11px 18px; border-radius: 9px; font-weight: 700; font-size: 13.5px;
}
.btn.primary:hover { filter: brightness(1.08); }
.btn.primary:disabled:hover { filter: none; }
.btn.ghost { background: transparent; padding: 7px 12px; font-size: 12.5px; }
.btn.ghost:hover { background: var(--bg-elev-2); }
.btn.danger { color: var(--low); border-color: var(--low-border); background: transparent; }
.btn.danger:hover { background: var(--low-bg); }
/* Solid destructive button — the confirm dialog's primary action. */
.btn.danger-solid {
  background: var(--low); border: none; color: #fff;
  padding: 11px 18px; border-radius: 9px; font-weight: 700; font-size: 13.5px;
}
.btn.danger-solid:hover { filter: brightness(1.08); }
.btn.tiny { padding: 5px 10px; font-size: 12px; }
.btn.copied { color: var(--good); border-color: var(--good-border); }
.wide-btn { width: 100%; margin-top: 6px; }
.inline { display: inline; }
form.inline { margin: 0; }

.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; padding: 7px 0; border-radius: 8px; border: 1px solid var(--border); background: var(--bg-elev); color: oklch(30% 0.01 240); cursor: pointer; text-decoration: none; }
.icon-btn:hover { background: var(--bg-elev-2); }
.icon-btn.danger { border-color: var(--low-border); color: var(--low); }
/* An icon button whose job is to fix something (retry the Drive archive) — flagged, not fatal. */
.icon-btn.warn { border-color: var(--mid-border); color: var(--mid); }
.icon-btn.danger:hover { background: var(--low-bg); }
.actions-row { display: inline-flex; gap: 6px; }

/* ---------- Pills / chips ---------- */
.pill {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
/* Lead stage pills */
.pill.stage-neutral   { background: oklch(93% 0.005 240); color: oklch(50% 0.01 240); }
.pill.stage-teal-soft { background: var(--accent-tint-strong); color: var(--accent); }
.pill.stage-teal      { background: var(--accent-tint); color: var(--accent); }
.pill.stage-purple    { background: var(--purple-tint); color: var(--purple); }
.pill.stage-good      { background: var(--good-bg); color: var(--good); }
.pill.stage-mid       { background: var(--mid-bg); color: var(--mid); }
.pill.stage-muted     { background: oklch(95% 0.003 240); color: var(--text-muted); }

.weight-chip {
  font-size: 11px; font-weight: 600; color: var(--accent);
  background: var(--accent-tint-strong); padding: 2px 8px; border-radius: 999px;
}
/* ---------- Stars ---------- */
.stars { letter-spacing: 1px; font-size: 15px; white-space: nowrap; }
.stars .star { color: var(--border); }
.stars .star.on { color: var(--accent); }
.star-input { display: flex; gap: 2px; margin-top: 6px; }
.star-input button {
  background: none; border: none; cursor: pointer; padding: 0 2px;
  font-size: 22px; line-height: 1; color: var(--border); transition: color .1s;
}
.star-input button.on { color: var(--accent); }
.star-input button:hover { color: var(--accent-border); }
.star-input button.on:hover { color: var(--accent); }


/* ---------- Banners ---------- */
/* Inline, in-flow notices that are page *content*, not a receipt for something the user
   just did: a login form's error, a static contextual warning, the extraction's confidence
   notes. Anything transient is a .toast instead (below) — it must not push the page down. */
.banner {
  position: relative; padding: 10px 14px;
  border-radius: 8px; margin-bottom: 18px; font-weight: 500;
}
.banner.warn { background: var(--mid-bg); color: var(--mid); border: 1px solid var(--mid-border); }
.banner.ok { background: var(--good-bg); color: var(--good); border: 1px solid var(--good); }
.hint { color: var(--text-muted); font-size: 12.5px; margin: 16px 0 0; }
.hint strong { color: oklch(40% 0.01 240); }

/* ---------- Toasts ---------- */
/* Every "you just did a thing" message — the ?ok=/?warn= flash a redirect carries, and the
   AJAX-failure notice toast() raises. Overlaid bottom-right rather than in the flow, because
   "Invoice deleted." is a receipt: it should not shove the page it refers to downward, and it
   should see itself out (main.js: 4.5s for ok, 9s for warn, paused while hovered or focused).
   The region ignores pointer events so it can never swallow a click meant for the page under
   it; each toast takes them back for its × and its optional follow-up link. */
.toast-region {
  position: fixed; bottom: 20px; right: 20px; z-index: 150;
  display: flex; flex-direction: column; gap: 10px; align-items: flex-end;
  max-width: min(380px, calc(100vw - 40px)); pointer-events: none;
}
.toast {
  position: relative; pointer-events: auto;
  width: 100%; padding: 10px 38px 10px 14px;
  border-radius: 8px; font-size: 13.5px; font-weight: 500; line-height: 1.45;
  box-shadow: 0 8px 24px rgba(20,30,40,.16);
  opacity: 0; transform: translateX(16px);
  transition: opacity .18s ease, transform .18s ease;
}
.toast.show { opacity: 1; transform: translateX(0); }
.toast.ok { background: var(--good-bg); color: var(--good); border: 1px solid var(--good); }
.toast.warn { background: var(--mid-bg); color: var(--mid); border: 1px solid var(--mid-border); }
/* A follow-up action inside a toast (e.g. "Marked paid. Draft the payment email →"). */
.toast a { color: inherit; text-decoration: underline; margin-left: 6px; font-weight: 600; }
/* The dismiss ×. Absolutely positioned rather than a flex item so it can't disturb how the
   message and its optional inline link wrap. */
.toast-x {
  position: absolute; top: 4px; right: 6px;
  background: none; border: none; padding: 2px 6px;
  font-family: inherit; font-size: 19px; line-height: 1;
  color: inherit; opacity: .55; cursor: pointer;
}
.toast-x:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .toast { transform: none; transition: opacity .18s ease; }
  .toast.show { transform: none; }
}

/* ---------- Forms ---------- */
label { display: block; margin-bottom: 14px; font-size: 13px; font-weight: 500; color: var(--text-dim); }
/* …and that `display` (author origin) beats the native [hidden] rule (UA origin), so a
   label toggled through the hidden attribute needs this to actually disappear — same trap
   as .chip-check and .uc-card-body below. Without it the Direct-costs edit modal showed its
   exchange-rate field on every line, including same-currency ones. */
label[hidden] { display: none; }
input, select, textarea {
  width: 100%; margin-top: 6px; padding: 9px 11px;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font-family: inherit; font-size: 14px;
}
textarea { resize: vertical; }
/* The width:100% above is for text-like fields; checkboxes and radios must never take
   the full input width or they stretch and shove their labels off to the side. Without
   this global reset every checkbox/radio row needs its own width:auto override — the
   Generate-slides popup (logo radios, use-case rows, modules toggle) regressed for
   exactly that reason. */
input[type=checkbox], input[type=radio] { width: auto; margin: 0; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
input[type=file] { padding: 7px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

/* ---------- Modal ---------- */
.modal {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev); color: var(--text); padding: 24px;
  width: 440px; max-width: 92vw; box-shadow: 0 8px 30px rgba(20,30,40,.14);
}
.modal.modal-lg { width: 760px; }
/* The contract-data editor: a long form, so the dialog itself scrolls and its actions ride
   along at the bottom. Wide enough for the 2- and 3-column field grids inside it. */
.modal.modal-xl { width: 1020px; max-height: 88vh; overflow-y: auto; }
/* Sections inside a dialog, where nested .cards would read as boxes on a box. */
.modal-section + .modal-section {
  margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--border);
}
.modal-actions.sticky {
  position: sticky; bottom: -24px; margin-top: 20px; padding: 14px 0;
  background: var(--bg-elev); border-top: 1px solid var(--border);
}
/* An anchor for .dropdown-menu's absolute positioning (the "Edit contract data ▾" menu). */
.menu-wrap { position: relative; display: inline-flex; }
.modal::backdrop { background: rgba(15,20,28,.5); }
.modal h2 { margin-top: 0; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 8px; }

/* ---------- Confirmation dialog ---------- */
.confirm-modal { width: 420px; }
.confirm-head { display: flex; align-items: center; gap: 12px; }
.confirm-head h2 { margin: 0; font-size: 17px; }
.confirm-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%; flex: none;
}
.confirm-icon.danger { background: var(--low-bg); color: var(--low); }
.confirm-icon.neutral { background: var(--accent-tint-strong); color: var(--accent); }
.confirm-message { color: var(--text-dim); font-size: 13.5px; line-height: 1.5; margin: 12px 0 22px; }

/* ---------- Lead workspace pages ---------- */
.crumb {
  font-size: 13.5px; font-weight: 600; margin-bottom: 10px;
  color: var(--text-dim);
}
.crumb a { color: var(--accent); text-decoration: none; }
.crumb a:hover { text-decoration: underline; }
.crumb .crumb-sep { color: var(--text-muted); font-weight: 500; margin: 0 4px; }
.hpill { vertical-align: 4px; margin-left: 10px; }
.leads-table tbody tr[data-id] { cursor: pointer; }
.leads-table tbody tr[data-id]:hover td { background: var(--bg-elev-2); }
/* Edit/delete stacked instead of side by side — narrows the actions column. */
.leads-table .actions-row { flex-direction: column; gap: 4px; }

/* Breadcrumb lead switcher — click the caret to search/jump to another lead
   without leaving the current subpage. Anchored dropdown, not a modal. */
.crumb-lead { position: relative; display: inline-block; }
.crumb-lead-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; font-weight: 600; font-size: 13.5px; color: var(--accent); cursor: pointer;
}
.crumb-lead-btn:hover { text-decoration: underline; }
.crumb-lead-btn.crumb-lead-empty { color: var(--text-dim); font-style: italic; }
.crumb-caret { font-size: 10px; color: var(--text-muted); }
.crumb-dropdown {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 30;
  width: 300px; background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(20,30,40,.14); padding: 12px;
}
.crumb-dropdown-search { margin: 0 0 8px; }
.crumb-dropdown-list { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.crumb-dropdown-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px; border-radius: 7px; text-decoration: none;
  color: var(--text); font-weight: 500; font-size: 13px;
}
.crumb-dropdown-row:hover { background: var(--bg-elev-2); }
.crumb-dropdown-client { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crumb-dropdown-row .pill { flex-shrink: 0; }
.crumb-dropdown-all {
  display: block; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-soft);
  color: var(--accent); font-weight: 600; font-size: 12.5px; text-decoration: none;
}
.crumb-dropdown-all:hover { text-decoration: underline; }

/* Overview page: structured fields (BD leader, stage, stars) in one compact
   row, open-text fields (contact / next step / BD goal / comments) in a
   second row of equal columns — both rows read left-to-right, top-aligned
   since the text column heights vary. */
.field-strip { display: grid; grid-template-columns: repeat(6, 1fr); }
.field-item { padding: 0 18px; }
.field-item:first-child { padding-left: 0; }
.field-item:last-child { padding-right: 0; }
.field-item + .field-item { border-left: 1px solid var(--border-soft); }
.field-item .l, .text-field .l {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); margin-bottom: 6px;
}
.field-item .v { font-size: 14px; font-weight: 600; color: var(--text); }
@media (max-width: 900px) {
  .field-strip { grid-template-columns: repeat(3, 1fr); row-gap: 16px; }
  .field-item { border-left: none !important; padding: 0 !important; }
}

/* No align-items override: grid's default (stretch) makes every column's box
   fill the row's full height, so the border-left dividers below span edge to
   edge instead of stopping wherever that column's own text ends. */
.text-fields { display: grid; grid-template-columns: repeat(4, 1fr); }
.text-field { padding: 0 20px; }
.text-field:first-child { padding-left: 0; }
.text-field:last-child { padding-right: 0; }
.text-field + .text-field { border-left: 1px solid var(--border-soft); }
.text-field .v { font-size: 13.5px; color: var(--text-dim); line-height: 1.55; }
@media (max-width: 900px) {
  .text-fields { grid-template-columns: 1fr 1fr; row-gap: 18px; }
  .text-field { border-left: none !important; padding: 0 !important; }
}

.tcard { padding: 16px 18px; border-radius: 12px; }
.tcard h4 {
  margin: 0 0 6px; font-size: 13.5px; color: var(--text);
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.tcard-stat { margin: 0; font-size: 12.5px; color: var(--text-dim); line-height: 1.55; }
.tcard-go {
  display: inline-block; margin-top: 10px; font-size: 12.5px;
  color: var(--accent); font-weight: 600; text-decoration: none;
}
.tcard-go:hover { text-decoration: underline; }

/* Overview page: Action items (left) next to a stack of the four tool cards
   (right, each at roughly half height so all four fit alongside it). Grid's
   default row-stretch makes the Action items card match the stack's total
   height; its body scrolls internally instead of growing the card further.
   minmax(0, 1fr) (not bare 1fr) keeps both columns exactly 50/50 — otherwise
   the tool-stack cards' intrinsic content width can push past their share
   and squeeze Action items narrower. */
.overview-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; margin-bottom: 20px; }
/* min-width: 0 on both grid items too — minmax(0, 1fr) alone only stops the
   *track* from growing to fit content; without this the cards' own nowrap/
   ellipsis text still forces the card wider than its track ("grid blowout"),
   pushing the tool-stack past the page's right edge. */
.overview-split > * { min-width: 0; }
/* overflow: hidden here isn't primarily about clipping — per the CSS Grid
   spec, a grid item's automatic minimum size is content-based (auto) unless
   its own overflow is non-visible, in which case it drops to 0. That's what
   lets this card be capped at the tool-stack's shorter natural height instead
   of forcing the grid row to grow to fit long action-item text; the excess
   then scrolls inside .action-items-body instead. Because of this, the
   "Action items" label's tooltip can't safely pop *upward* here (it would be
   clipped by this same overflow) — see the mdfield() call site, which omits
   tooltip-top so it opens downward, inside the card's own bounds, instead. */
.action-items-card { display: flex; flex-direction: column; margin-bottom: 0; overflow: hidden; }
.action-items-card .l {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); margin-bottom: 6px; flex-shrink: 0;
}
.action-items-card .v { font-size: 13.5px; color: var(--text-dim); line-height: 1.55; }
.action-items-body { flex: 1 1 auto; overflow-y: auto; }
.tool-stack { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, 1fr); gap: 10px; min-width: 0; }
.tool-stack > .card { margin-bottom: 0; min-width: 0; overflow: hidden; }
.tcard-compact { padding: 12px 16px 10px; border-radius: 10px; display: flex; flex-direction: column; justify-content: center; }
.tcard-compact h4 { margin: 0 0 4px; font-size: 12.5px; line-height: 1.2; }
.tcard-compact h4 .pill { line-height: 1.2; }
/* Second line: subtitle (left, truncates) + go-link (right, under the pill) on one row */
.tcard-compact .tcard-row { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; min-width: 0; }
.tcard-compact .tcard-stat {
  margin: 0; font-size: 11.5px; line-height: 1.4;
  flex: 0 1 auto; min-width: 0; max-width: 75%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tcard-compact .tcard-go { margin-top: 0; font-size: 11.5px; flex-shrink: 0; }
@media (max-width: 900px) { .overview-split { grid-template-columns: 1fr; } }

/* Pitch page: two columns — Templates (org-wide) | Client pitches (per-lead) —
   each holding any number of matching cards, or the add-mechanism tiles when empty. */
.pitch-page { max-width: 1160px; }
.pitch-columns {
  display: grid; grid-template-columns: 1fr 1fr; gap: 28px; align-items: start;
  margin-top: 20px;
}
.pitch-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.pitch-col-head { display: flex; align-items: baseline; gap: 10px; }
.pitch-col-head h2 { margin: 0; font-size: 17px; }
.pitch-col-add { margin-left: auto; padding: 6px 12px; font-size: 12.5px; }
@media (max-width: 980px) { .pitch-columns { grid-template-columns: 1fr; } }

/* ---------- Notes: collapsed-by-default, expandable, inline-editable rows ---------- */
.note-card { padding: 14px 20px; }
.note-row { display: flex; align-items: center; gap: 8px; }
.note-row-toggle {
  display: flex; align-items: center; gap: 14px; cursor: pointer; flex: 1; min-width: 0;
  margin: -14px 0 -14px -20px; padding: 14px 10px 14px 20px;
  border-radius: var(--radius) 0 0 var(--radius);
}
.note-row-toggle:hover { background: var(--bg-elev-2); }
.note-row-meta { display: flex; align-items: baseline; gap: 10px; flex-shrink: 0; }
/* Own ellipsis rule instead of the shared .truncate-1 (built for the leads
   table's narrow column, capped at 200px) — this row is much wider and
   should use all the space its flex siblings leave it. */
.note-preview-text {
  flex: 1; min-width: 0; color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.note-chevron { flex-shrink: 0; color: var(--text-muted); transition: transform .15s ease; }
.note-row-toggle[aria-expanded="true"] .note-chevron { transform: rotate(180deg); }
.note-row-actions { flex-shrink: 0; }
.note-view, .note-edit-form {
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-soft);
}
.note-view { color: var(--text-dim); }
.note-edit-form .modal-actions { margin-top: 12px; }

/* Split "+ Add note" / "▾" button pair reading as one connected control,
   plus the small coming-soon menu the ▾ opens (see main.js data-dropdown-toggle). */
.note-add-split { position: relative; display: inline-flex; }
.note-add-split .btn.primary:first-child { border-radius: 9px 0 0 9px; }
.note-add-split .btn.primary.icon-only {
  border-radius: 0 9px 9px 0; border-left: 1px solid rgba(255,255,255,.35); padding: 11px 12px;
}
.dropdown-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
  width: 260px; background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(20,30,40,.14); padding: 6px;
}
.dropdown-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 10px; border-radius: 7px; font-size: 13px; font-weight: 500;
}
.dropdown-item.disabled { color: var(--text-muted); cursor: default; }
/* A dropdown item that's a real link (e.g. the use case generator's Export menu) —
   reset the anchor's default blue/underline so it reads like .dropdown-item-btn. */
a.dropdown-item { color: var(--text); text-decoration: none; }
a.dropdown-item:hover { background: var(--bg-elev-2); }
/* A dropdown item that's a real action (e.g. the FAQ page's "✨ Add from Q&A"),
   not an inert "Soon" entry — reset button chrome so it reads like .dropdown-item. */
.dropdown-menu form { margin: 0; }
.dropdown-item-btn {
  display: flex; width: 100%; align-items: center; gap: 8px;
  padding: 9px 10px; border-radius: 7px; font: inherit; font-size: 13px; font-weight: 500;
  background: none; border: none; color: var(--text); text-align: left; cursor: pointer;
}
.dropdown-item-btn:hover { background: var(--bg-elev-2); }
.dropdown-item-btn:disabled { color: var(--text-muted); cursor: default; }
.dropdown-item-btn:disabled:hover { background: none; }

/* ---------- Inline markdown editor (richtext.js) — one contenteditable
   surface that shows styled markdown as you type (no raw-source pane). ------ */
.richtext { margin-bottom: 20px; }
.richtext-toolbar { display: flex; gap: 6px; margin-bottom: 8px; }
.richtext-surface {
  border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px;
  background: var(--bg-elev); min-height: 88px; overflow-y: auto;
  cursor: text;
}
.richtext-surface:focus {
  outline: none; border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.richtext-surface:empty::before {
  content: attr(data-placeholder); color: var(--text-muted); pointer-events: none;
}
/* First/last children flush so the caret sits where the border padding expects. */
.richtext-surface > :first-child { margin-top: 0; }
.richtext-surface > :last-child { margin-bottom: 0; }

/* ---------- Markdown-rendered fields (lead Next step / Comments) ---------- */
.md-content p { margin: 0 0 6px; }
.md-content p:last-child { margin-bottom: 0; }
.md-content ul, .md-content ol { margin: 4px 0; padding-left: 20px; }
.md-content li { margin-bottom: 2px; }
.md-content code { background: var(--bg-elev-2); padding: 1px 5px; border-radius: 4px; font-size: .9em; }
.md-content pre { background: var(--bg-elev-2); padding: 8px 10px; border-radius: 8px; overflow-x: auto; margin: 4px 0; }
.md-content pre code { background: none; padding: 0; }
.md-content a { color: var(--accent); }
.md-content blockquote {
  border-left: 3px solid var(--border); margin: 4px 0; padding-left: 10px; color: var(--text-muted);
}
/* GFM task-list checkboxes ("- [ ] "/"- [x] " — see app/md.py). Targeted
   structurally (:has(), not a class) — the checkbox is inserted as the <li>'s
   first child both server-side (md.py) and client-side (richtext.js), and
   nh3's sanitizer allow-list doesn't need widening for a class attribute this
   way. Read-only checkboxes (server-rendered, or the .field-view display) stay
   disabled; only the .richtext-surface editor's are click-to-toggle (its
   hydrate() re-enables them — see richtext.js). Plain inline flow, not flex —
   the item's text is a bare text node (no wrapping <span>), and turning it
   into an anonymous flex item shrank/pushed it outside the card entirely. */
.md-content ul:has(> li > input[type="checkbox"]) { list-style: none; padding-left: 2px; }
.md-content li:has(> input[type="checkbox"]) { list-style: none; }
.md-content li:has(> input[type="checkbox"]) input[type="checkbox"] {
  /* Reset the generic `input, select, textarea` form-field rule (width:100%,
     padding, background, border...) — meant for text inputs, but it also
     matches this bare checkbox and was stretching it full-width. */
  all: revert; accent-color: var(--accent); vertical-align: middle;
  margin: 0 7px 0 0; position: relative; bottom: 1px; /* optical baseline nudge */
}
.md-content li:has(> input[type="checkbox"]:not(:disabled)) input[type="checkbox"] { cursor: pointer; }

/* ---------- Documents ---------- */
.doc-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
.doc-tile-thumb {
  width: 96px; height: 64px; flex-shrink: 0; padding: 0; overflow: hidden;
  background: #fff; border-radius: 8px;
}
.doc-thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.doc-fallback-icon { display: inline-flex; align-items: center; justify-content: center; }

/* The contract documents table (same shape as .costs-table). The thumbnail is a row-sized
   version of the doc tile — the two-class selectors win over .doc-tile/.doc-tile-thumb's
   own dimensions wherever else those are used. */
.docs-table { min-width: 740px; }
.docs-table th:nth-child(2), .docs-table td:nth-child(2) { min-width: 260px; }
/* File: wide enough for the download + Drive buttons side by side. */
.docs-table th:nth-child(6), .docs-table td:nth-child(6) { min-width: 96px; }
.doc-thumb-cell { width: 62px; }
.doc-thumb-cell .doc-tile { width: 48px; height: 34px; border-radius: 8px; }
.doc-thumb-cell .doc-tile-thumb { border-radius: 6px; }
/* The contract-data bar: .extract-cta's layout, sitting above the table rather than
   below it, so it reads as a strip and not as a second card of content. */
.extract-cta.slim { padding: 16px 20px; }

/* Context docs: full-width rows (like Notes) but keeping the doc-tile icon/thumbnail. */
.resource-list { display: flex; flex-direction: column; gap: 14px; }
.doc-row-card { padding: 16px 20px; margin-bottom: 0; }
.doc-row { display: flex; align-items: center; gap: 16px; }
.doc-row-body { flex: 1; min-width: 0; }
.doc-row-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

/* ---------- Media thumb (shared: pitch card, demo cards) — full-bleed
   image/fallback strip at the top of a card, clipped by the card's own
   border-radius via overflow:hidden on the parent. ---------- */
.media-thumb {
  width: 100%; background: #fff; border-bottom: 1px solid var(--border-soft);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.media-thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-fallback { display: flex; align-items: center; justify-content: center; color: var(--accent); }

/* ---------- Pitch document card (templates + client pitches, same look):
   16:9 slide-ratio thumbnail strip on top, then title / meta / actions. ---------- */
.pitch-doc-card {
  overflow: hidden;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.pitch-doc-card .media-thumb { width: 100%; aspect-ratio: 16 / 9; }
.pitch-doc-body {
  padding: 14px 16px; display: flex; flex-direction: column; gap: 6px;
}
.pitch-doc-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 8px;
}
/* Inline checkbox row (e.g. the disabled "Add use case slides" option in the
   create-from-template modal). The `all: revert` undoes the generic form-field
   rule (width:100%, padding…) that otherwise stretches a bare checkbox. */
.checkline { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.checkline input[type="checkbox"] { all: revert; accent-color: var(--accent); margin: 0; }

/* A list of choices in a dialog, each with its own explanation (the re-extract scope).
   Whole row is the label, so the description is clickable too. Same `all: revert` trick
   as .checkline for the box itself. */
.check-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 4px; }
.check-row {
  display: flex; align-items: flex-start; gap: 11px; margin: 0; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg-elev-2);
  cursor: pointer; font-weight: 400;
}
.check-row:hover { border-color: var(--accent-border); }
.check-row input[type="checkbox"] { all: revert; accent-color: var(--accent); margin: 2px 0 0; flex-shrink: 0; }
.check-row > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* ---------- Busy overlay (slow Drive POSTs) — a top-layer <dialog> with a spinner ---------- */
dialog.busy-overlay {
  border: none; background: transparent; padding: 0;
  max-width: none; max-height: none; overflow: visible;
}
dialog.busy-overlay::backdrop { background: rgba(15,20,28,.5); }
.busy-box {
  background: var(--bg-elev); border-radius: 14px; padding: 26px 30px;
  box-shadow: 0 14px 44px rgba(20,30,40,.28);
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  max-width: 340px; text-align: center;
}
.busy-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--accent-tint); border-top-color: var(--accent);
  animation: busy-spin .8s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }
.busy-msg { font-weight: 700; font-size: 15px; color: var(--text); }
.busy-sub { font-size: 12.5px; color: var(--text-muted); line-height: 1.4; }

/* ---------- Add-mechanism option tiles (templates: 2, client decks: 3) — shown
   stacked inside an empty column, and in a row inside the "+ Add" chooser modals. ---------- */
.deck-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.pitch-col .deck-options { grid-template-columns: 1fr; }
.deck-option {
  display: flex; flex-direction: column; align-items: flex-start; gap: 18px;
  min-height: 200px; width: 100%; text-align: left; cursor: pointer; font-family: inherit;
  padding: 22px; border-radius: 16px; border: 1px solid var(--border);
  background: var(--bg-elev); color: var(--text); box-shadow: var(--shadow);
  transition: border-color .14s, background .14s, box-shadow .14s, transform .14s;
}
.deck-option:hover {
  border-color: var(--accent-border); background: var(--accent-wash);
  box-shadow: 0 10px 26px rgba(20,30,40,.10); transform: translateY(-2px);
}
.deck-option-icon {
  width: 54px; height: 54px; border-radius: 14px; font-size: 25px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  /* Lighter tint than the original accent-tint — the teal icon inside was getting
     lost against a too-green square (see the copy/upload/link glyphs). Kept a shade
     above accent-wash so the square stays visible on the accent-wash hover bg. */
  background: var(--accent-tint-strong); color: var(--accent);
}
.deck-option-text { display: flex; flex-direction: column; gap: 5px; }
.deck-option-title { font-weight: 700; font-size: 15.5px; color: var(--text); }
.deck-option-desc { font-size: 12.8px; color: var(--text-muted); line-height: 1.45; }
.modal .deck-options { margin: 8px 0 4px; }

/* ---------- Demo cards (full-bleed thumb on top) ---------- */
.demo-card { padding: 0; overflow: hidden; }
.demo-card .media-thumb { height: 160px; }
.demo-card-body { padding: 18px 20px 20px; }

/* ---------- Login (standalone) ---------- */
body.standalone { display: block; background: var(--bg); }
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.login-card { width: 380px; max-width: 94vw; padding: 32px 30px; }
.login-brand { padding: 0 0 18px; }
.login-brand .brand-logo { height: 30px; }
.login-card h1 { font-size: 20px; margin-bottom: 18px; }
.password-field { position: relative; margin-top: 6px; }
.password-field input { margin-top: 0; padding-right: 40px; }
.password-toggle {
  position: absolute; top: 0; right: 0; height: 100%; width: 38px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0; cursor: pointer; color: var(--text-muted);
}
.password-toggle:hover { color: var(--text-dim); }
.password-toggle .eye-off-icon { display: none; }

/* ---------- Overview inline editing (overview.js) ---------- */

/* Live star widget in the compact field strip — smaller than the modal's 22px. */
.field-item .live-stars { margin-top: 2px; }
.field-item .live-stars button { font-size: 18px; }

/* BD leader: click-to-edit short text */
.editable-text {
  display: inline-flex; align-items: center; max-width: 100%;
  margin: -2px -6px; padding: 2px 6px; border-radius: 6px; cursor: text;
}
.editable-text:hover { background: var(--bg-elev-2); }
.editable-text:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-wash); }
.editable-text .editable-input {
  width: 100%; margin: 0; padding: 3px 6px; font-size: 14px; font-weight: 600;
}

/* Stage: pill trigger + a small menu of stage pills */
.stage-editor { position: relative; display: inline-block; }
button.stage-trigger { border: none; cursor: pointer; font: inherit; font-size: 11px; font-weight: 700; }
button.stage-trigger::after { content: " \25be"; font-size: 9px; opacity: .55; }
.dropdown-menu.stage-menu {
  width: auto; min-width: 160px; left: 0; right: auto;
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
}
.dropdown-menu.stage-menu[hidden] { display: none; } /* beat the UA [hidden] rule our display:flex overrides */
button.stage-option { border: none; cursor: pointer; font: inherit; font-size: 11px; font-weight: 700; }
button.stage-option:hover { box-shadow: inset 0 0 0 1px var(--border); }

/* Long-text cards: hover-revealed edit affordance + inline editor */
.field-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.editable-field .inline-edit-toggle {
  width: 24px; padding: 3px 0; flex-shrink: 0; opacity: 0; transition: opacity .1s;
}
.editable-field:hover .inline-edit-toggle { opacity: 1; }
.editable-field .inline-edit-toggle:focus-visible { opacity: 1; }
/* A secondary action that lives in a data cell — revealed on row hover so the column reads as
   its data first (the Received column is money, not a button). Same trick as the inline-edit
   pencil above, and it honours :focus-visible so tabbing still finds it. */
.row-action {
  display: inline-block; margin-top: 4px;
  background: none; border: none; padding: 0;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent);
  cursor: pointer; opacity: 0; transition: opacity .12s ease;
}
.row-action:hover { text-decoration: underline; }
tr:hover .row-action, .row-action:focus-visible { opacity: 1; }
.inline-edit-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.field-edit .richtext-surface { min-height: 70px; }

/* ---------- Q&A page family (/qa, /qa/faq, /qa/docs) ---------- */
.qa-tabs {
  display: flex; gap: 6px; margin: -6px 0 20px;
  border-bottom: 1px solid var(--border); padding-bottom: 0;
}
.qa-tab {
  padding: 8px 14px 10px; font-size: 13.5px; font-weight: 600;
  color: var(--text-muted); text-decoration: none;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.qa-tab:hover { color: var(--text); }
.qa-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

/* Ask tab. The exchange list and the FAQ list both reuse the Notes page's
   expandable-row pattern (.note-card/.note-row/.note-view + data-note-toggle in
   main.js) so all three surfaces read the same. */
.qa-ask-card { margin-bottom: 20px; }
.qa-ask-label { display: block; font-weight: 600; font-size: 13px; }
.qa-ask-card textarea { width: 100%; margin-top: 6px; resize: vertical; }
.qa-ask-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; }
.qa-exchange-question {
  font-weight: 600; font-size: 14px; margin-bottom: 10px;
}
.qa-answer { font-size: 13.5px; max-width: 900px; }
.qa-answer h1, .qa-answer h2, .qa-answer h3 { font-size: 14px; margin: 14px 0 6px; }
.qa-answer h4 { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); margin: 16px 0 4px; }
.qa-answer table { border-collapse: collapse; margin: 8px 0; }
.qa-answer th, .qa-answer td { border: 1px solid var(--border); padding: 5px 10px; text-align: left; }
.qa-answer th { background: var(--bg-elev-2); }
/* The trailing References list (after the #### References heading) reads as a footnote. */
.qa-answer h4 + ol, .qa-answer h4 + ul { font-size: 12px; color: var(--text-muted); }
.qa-section-title { font-size: 14px; font-weight: 700; margin: 26px 0 10px; }

/* Row-1 tweaks on top of the shared note-row pattern: the question is the row's
   subject — bold, first, and given the flexible space (it reuses .note-preview-text's
   flex/ellipsis). The Ask tab's byline (who asked, when) sits after it, de-emphasized. */
.qa-row-question { color: var(--text); font-weight: 600; }
.qa-row-byline { color: var(--text-muted); font-size: 12px; gap: 8px; }
.qa-byline-name { max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.faq-block { margin-bottom: 12px; }
.faq-block-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); margin-bottom: 4px;
}
.faq-list { margin: 0; padding-left: 18px; font-size: 13px; }
.faq-list li { margin-bottom: 3px; }
.faq-actions { justify-content: flex-end; border-top: 1px solid var(--border); padding-top: 10px; }
.radio-row { display: flex; align-items: center; gap: 8px; font-size: 13px; margin: 8px 0; }
.radio-row input { width: auto; margin: 0; }
/* "Add from Q&A" suggestion picker rows */
.suggest-row {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 8px;
}
.suggest-row:hover { background: var(--bg-elev-2); }
.suggest-row input { width: auto; margin: 3px 0 0; flex-shrink: 0; }
.suggest-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* ============================================================ Admin site + accounts */

/* Admin tag on the sidebar brand + admin avatar — purple, to set the admin site apart
   from the teal org site at a glance. */
.bd-tag.admin-tag {
  color: var(--purple); background: var(--purple-tint);
}
.user-avatar.admin-avatar {
  background: var(--purple-tint); color: var(--purple);
}
.admin-sidebar .nav-link.active { background: var(--purple-tint); color: var(--purple); }

/* ---------- Destination chooser (choose.html) ---------- */
.chooser-card-wrap { width: 460px; max-width: 94vw; }
.chooser-card-wrap .subtitle { margin: -8px 0 4px; }
.chooser-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 18px 0 6px;
}
.chooser-form { margin: 0; display: block; }
.chooser-card {
  width: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; text-decoration: none; cursor: pointer;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; color: var(--text); font: inherit; transition: border-color .12s, background .12s, transform .06s;
}
.chooser-card:hover { border-color: var(--accent-border); background: var(--accent-wash); }
.chooser-card:active { transform: translateY(1px); }
.chooser-card.admin:hover { border-color: var(--purple-border); background: var(--purple-wash); }
.chooser-icon {
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 10px; margin-bottom: 6px;
  background: var(--accent-tint); color: var(--accent);
}
.chooser-card.admin .chooser-icon { background: var(--purple-tint); color: var(--purple); }
.chooser-label { font-weight: 700; font-size: 15px; }
.chooser-sub { font-size: 12px; color: var(--text-muted); }
.chooser-logout { margin: 14px 0 0; text-align: center; }

/* ---------- Org selector (admin submitted-questions) ---------- */
.org-select-form { margin: 0; }
.org-select-label {
  display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--text-dim);
}
.org-select-label select { width: auto; min-width: 180px; }

/* ---------- Admin sections (orgs & users) ---------- */
.admin-section { margin-top: 26px; }
.admin-section:first-of-type { margin-top: 8px; }
.admin-section-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px;
}
.admin-section-head h2 { font-size: 16px; margin: 0; }

.admin-org-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.admin-org-card {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 16px;
}
.admin-org-body { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.admin-org-name { font-weight: 700; font-size: 14px; }

/* Users table — a table scans and scales to many rows far better than the stacked
   per-user cards this replaced (name/email/orgs/role align in columns; edit opens a
   modal instead of inline forms eating vertical space). */
.admin-users-table-wrap { padding: 4px 24px; }
.admin-users-table td { padding: 11px 12px; }
.admin-users-table .role-badge { margin-left: 0; }
.row-actions-inline { justify-content: flex-end; }
.admin-field-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; color: var(--text-muted);
}

.checkbox-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-check {
  display: inline-flex; align-items: center; gap: 6px; margin: 0;
  padding: 5px 10px; border: 1px solid var(--border); border-radius: 999px;
  font-size: 12.5px; font-weight: 500; cursor: pointer; background: var(--bg-elev);
}
.chip-check:hover { border-color: var(--accent-border); }
.chip-check input { width: auto; margin: 0; flex-shrink: 0; }
.chip-check.toggle { border-radius: 8px; }
/* .chip-check's own `display` otherwise beats the `[hidden]` UA rule (equal-origin
   author CSS wins over the default stylesheet) — toggling `hidden` on the admin-access
   row (see admin_orgs.html) would silently no-op without this. */
.chip-check[hidden] { display: none; }

.role-badge {
  display: inline-block; flex-shrink: 0; font-size: 10.5px; font-weight: 700; letter-spacing: .3px;
  padding: 2px 8px; border-radius: 999px; vertical-align: middle; margin-left: 4px;
  background: var(--accent-tint); color: var(--accent);
}
.role-badge.admin { background: var(--purple-tint); color: var(--purple); }
.role-badge.super { background: var(--good-bg); color: var(--good); }

.btn.small { padding: 6px 12px; font-size: 12.5px; }

/* ---------------------------------------------------------- Use case generator */
/* Basics: 6-col grid so the two free-text fields (client name / website) get half-width
   each, and the three selects share the second row a third each. */
.uc-basics-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px 18px; align-items: start; }
.uc-basics-grid > label { margin: 0; }
.uc-basics-grid .uc-col-3 { grid-column: span 3; }
.uc-basics-grid .uc-col-2 { grid-column: span 2; }
.uc-basics-grid .uc-col-full { grid-column: 1 / -1; }
.uc-basics-grid textarea { min-height: 68px; }
.uc-field-hint { display: block; margin-top: 5px; font-weight: 400; }
@media (max-width: 820px) {
  .uc-basics-grid { grid-template-columns: 1fr 1fr; }
  .uc-basics-grid .uc-col-3, .uc-basics-grid .uc-col-2 { grid-column: span 1; }
}
/* Number-of-use-cases slider + live value bubble. */
.uc-slider-row { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.uc-slider-row input[type=range] {
  flex: 1; width: auto; min-width: 0; margin: 0; padding: 0; height: 6px;
  accent-color: var(--accent); cursor: pointer;
}
.uc-slider-value {
  flex-shrink: 0; min-width: 1.75em; text-align: center; font-weight: 700; font-size: 15px;
  color: var(--accent);
}
.uc-export-btn { display: inline-flex; align-items: center; gap: 6px; }
.uc-docs { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border-soft); }
.uc-docs-head { font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.uc-doc-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.uc-doc-item {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg-elev-2);
  font-size: 13px; cursor: pointer;
}
.uc-doc-item input { width: auto; margin: 0; flex-shrink: 0; }
.uc-doc-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uc-gen-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 16px; flex-wrap: wrap;
}
.uc-sources-actions { margin-top: 10px; }
.uc-section-title { margin: 26px 0 12px; font-size: 17px; }
.uc-section-title-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: 26px 0 12px;
}
.uc-section-title-row .uc-section-title { margin: 0; }

.uc-list { display: flex; flex-direction: column; gap: 16px; }
.uc-card { padding: 0; overflow: hidden; }
.uc-card.uc-selected { border-color: var(--accent-border); box-shadow: 0 0 0 1px var(--accent-border); }
.uc-card-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 18px; background: var(--accent-wash); border-bottom: 1px solid var(--border);
}
/* Use-case cards reuse the Notes/Q&A collapsible pattern verbatim (main.js's
   toggleNoteRow + .note-card/.note-chevron/.note-view, collapsed by default, no JS
   changes) for independent per-card expand/collapse. The toggle row here sits inside
   this card's own head padding (not note-card's edge-to-edge padding), so reset
   note-row-toggle's negative margins/radius back to neutral. */
.uc-card-head .note-row-toggle { margin: 0; padding: 0; border-radius: 0; }
.uc-selected-pill { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.uc-card-title { margin: 2px 0 8px; font-size: 18px; }
.uc-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.uc-card-actions { flex-shrink: 0; }

.uc-card-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 16px; }
/* .uc-card-body doubles as .note-view (collapsible body) — its own `display: flex`
   ties in specificity with the native [hidden] rule and (author origin beats UA origin)
   wins, silently ignoring `hidden`. Force it back off explicitly when hidden. */
.uc-card-body[hidden] { display: none; }
.uc-section h4 { font-size: 13px; margin: 0 0 6px; color: var(--text); }
.uc-section ul, .uc-section ol { margin: 0; padding-left: 18px; }
.uc-section li { font-size: 13.5px; line-height: 1.5; margin-bottom: 3px; }

.uc-modules { display: flex; flex-direction: column; gap: 12px; }
.uc-module { border: 1px solid var(--border); border-radius: 10px; padding: 12px; }
.uc-module-name { font-weight: 600; font-size: 13.5px; margin: 0 0 6px; }
/* The KPI line is the title only on pre-name rows; under a module name it reads as a
   normal metadata line. */
.uc-module-name + .uc-kpi { font-weight: 400; font-size: 13px; color: var(--text-dim); }
.uc-kpi { font-weight: 600; font-size: 13.5px; margin: 0 0 10px; display: flex; align-items: baseline; gap: 8px; }
.uc-kpi-label {
  flex: none; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  color: var(--accent); background: var(--accent-tint); border-radius: 5px; padding: 2px 6px;
}
.uc-questions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.uc-q { display: flex; align-items: baseline; gap: 8px; font-size: 13px; line-height: 1.5; }
.uc-q-followup { margin-left: 22px; }
.uc-q-chain { flex: none; color: var(--text-muted); font-weight: 600; }
.uc-q-type {
  flex: none; font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  border-radius: 5px; padding: 2px 7px; white-space: nowrap;
  background: var(--bg-elev-2); color: var(--text-dim);
}
.uc-q-positiveFactors { background: var(--good-bg); color: var(--good); }
.uc-q-negativeFactors { background: var(--low-bg); color: var(--low); }
.uc-q-barriers { background: var(--mid-bg); color: var(--mid); }
.uc-q-ideasForImprovement { background: var(--accent-tint); color: var(--accent); }
.uc-q-behavioral, .uc-q-knowledge, .uc-q-experience, .uc-q-imaginative {
  background: var(--purple-tint); color: var(--purple);
}
.uc-support-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.uc-support-item { font-size: 13px; line-height: 1.5; }
.uc-support-note { font-size: 12.5px; margin-top: 1px; }

/* Header actions: title + info-icon (left) and Export / Generate (right, always
   visible regardless of active step — the Generate button uses form="uc-wizard-form"
   to submit the wizard form from outside its DOM position, see lead_usecases.html). */
.uc-header-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.uc-export-wrap { position: relative; display: inline-block; }

/* 4-step wizard header (Basics / Documents / Web Sources / Use Cases). Each step is a
   clickable button (main.js's showStep) — free, non-linear navigation, since every
   step's data is already loaded in the page. */
.uc-stepper {
  display: flex; align-items: flex-start; gap: 0; margin: 18px 0 22px; overflow-x: auto;
}
.uc-step {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  background: none; border: none; padding: 0; cursor: pointer; font: inherit; text-align: left;
}
.uc-step-num {
  flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; font-size: 13px; font-weight: 700;
  background: var(--bg-elev-2); color: var(--text-muted); border: 2px solid var(--border);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.uc-step-label {
  font-size: 13px; font-weight: 600; color: var(--text-muted); white-space: nowrap;
  transition: color .15s ease;
}
.uc-step.active .uc-step-num { background: var(--accent); border-color: var(--accent); color: #fff; }
.uc-step.active .uc-step-label { color: var(--text); }
.uc-step-connector { flex: 1; height: 2px; background: var(--border); margin: 0 10px; align-self: center; min-width: 20px; }

/* Step panels — shown/hidden purely via the [hidden] attribute (main.js's showStep). */
.uc-step-panel-foot {
  display: flex; justify-content: space-between; margin-top: 18px;
  padding-top: 16px; border-top: 1px solid var(--border-soft);
}

/* Documents step: context docs (with usability status) + extra uploads, side by side
   on wide screens. */
.uc-doc-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
@media (max-width: 860px) { .uc-doc-columns { grid-template-columns: 1fr; } }
.uc-doc-item.uc-doc-disabled { opacity: .55; cursor: not-allowed; }
.uc-doc-item.uc-doc-disabled input { cursor: not-allowed; }
/* A non-selectable doc row (extra uploads: always used, so no checkbox — just name +
   pill + remove). Default cursor since there's nothing to toggle. */
.uc-doc-item.uc-doc-static { cursor: default; }
.uc-doc-remove { flex-shrink: 0; margin: 0; }
.uc-doc-remove .icon-btn { padding: 4px; }
/* "＋ Add document": the real file input is hidden and triggered by this styled label,
   which auto-submits the upload on change (main.js data-auto-upload). */
.uc-upload { margin-top: 12px; }
.uc-upload input[type=file] { display: none; }
.uc-add-doc-btn { cursor: pointer; }
.uc-cap-note { margin-top: 8px; }

/* External web sources: two rows per entry (title, then a clickable URL), a distinct
   tinted type pill (not grey, which read as disabled). */
.uc-source-item {
  display: flex; align-items: flex-start; gap: 11px; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg-elev-2);
}
.uc-source-item > input[type=checkbox] { width: auto; margin: 3px 0 0; flex-shrink: 0; cursor: pointer; }
.uc-source-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.uc-source-title { font-size: 13px; font-weight: 500; color: var(--text); cursor: pointer; }
.uc-source-url {
  font-size: 12px; color: var(--accent); text-decoration: none; word-break: break-all;
}
.uc-source-url:hover { text-decoration: underline; }
.uc-type-pill { flex-shrink: 0; background: var(--accent-tint); color: var(--accent); }
/* The researched "condensed" content per source (what actually grounds generation) —
   collapsed by default so the list stays scannable, expandable to review quality. */
.uc-source-condensed { margin-top: 4px; }
.uc-source-condensed > summary {
  cursor: pointer; font-size: 12px; font-weight: 600; color: var(--accent);
  list-style: none; width: max-content;
}
.uc-source-condensed > summary::before { content: "▸ "; font-size: 10px; }
.uc-source-condensed[open] > summary::before { content: "▾ "; }
.uc-source-condensed-body {
  margin-top: 6px; font-size: 12.5px; line-height: 1.55; color: var(--text-dim);
  white-space: pre-wrap; border-left: 2px solid var(--border); padding-left: 10px;
}
.uc-source-reason { margin: 2px 0 0; }

/* ---- Admin: Use case generator KB (/admin/usecases) ---- */
.kb-view { margin-top: 6px; }
.kb-view > summary {
  cursor: pointer; font-weight: 600; color: var(--accent);
  list-style: none; width: max-content;
}
.kb-view > summary::before { content: "▸ "; font-size: 10px; }
.kb-view[open] > summary::before { content: "▾ "; }
.kb-pre {
  margin: 8px 0 0; padding: 12px 14px; max-height: 420px; overflow: auto;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
  font-size: 12px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}

/* ---- Use case generator: v2 polish ---- */
/* The hidden attribute must beat .pill's display rule (same UA-vs-author gotcha as the
   admin-access row) — without this, "✓ Selected" pills show on unselected cards. */
.pill[hidden] { display: none; }
/* Unified empty-state CTA for steps 3/4/5 (Web Sources / Use Cases / Slides): a centered
   prompt + one big green generate/search button. Once content exists the action moves to
   the top-right (.uc-section-title-row > .uc-header-actions) and this card is replaced by
   the content — same pattern across all three steps. */
.uc-generate-cta { text-align: center; padding: 40px 24px 44px; }
.uc-generate-cta p { max-width: 560px; margin: 0 auto 6px; }
.uc-cta-btn { margin: 14px 0 10px; padding: 12px 26px; font-size: 14.5px; }
/* Required-field marker (Basics: client website). */
.uc-req { color: var(--low); font-weight: 700; }
/* Step 5: the generated-deck card sits alone; cap its width for balance. */
.uc-slides-card { max-width: 720px; }
.uc-slides-form { margin-top: 10px; }

/* Generate-slides popup: logo choices as radio rows with a small preview. */
.uc-logo-options { display: flex; flex-direction: column; gap: 6px; margin: 6px 0 10px; }
.uc-logo-option { display: flex; align-items: center; gap: 10px; padding: 6px 8px;
  border: 1px solid var(--border); border-radius: 8px; cursor: pointer; }
.uc-logo-option:has(input:checked) { border-color: var(--accent); background: var(--accent-soft, rgba(2,128,144,.06)); }
.uc-logo-option img { height: 30px; max-width: 120px; object-fit: contain;
  background: #fff; border: 1px solid var(--border); border-radius: 4px; padding: 2px; }
.uc-logo-upload { display: block; margin-bottom: 4px; }

/* Generate-slides popup: use-case review rows, spinners, modules toggle. */
.uc-slides-cases { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 12px; }
.uc-slides-case { display: flex; align-items: center; gap: 10px; padding: 8px;
  border: 1px solid var(--border); border-radius: 8px; }
.uc-slides-case > input[type="checkbox"] { flex: none; }
.uc-slides-thumb { flex: none; width: 96px; height: 54px; border-radius: 5px; overflow: hidden;
  background: var(--surface-2, #f2f4f5); display: flex; align-items: center; justify-content: center; }
.uc-slides-thumb img { width: 100%; height: 100%; object-fit: cover; }
.uc-slides-thumb-empty { font-size: 11px; color: var(--muted); }
.uc-slides-case-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.uc-slides-case-upload { font-size: 12px; margin-top: 2px; }
.uc-toggle-row { display: flex; align-items: center; gap: 8px; margin: 4px 0 10px; }
.uc-toggle-disabled { opacity: .55; cursor: not-allowed; }
/* Prominent loader while the popup fetches logo/images — a large centered spinner over a
   dashed placeholder block, so it's obvious work is in progress (the old tiny inline
   spinner was easy to miss). */
.uc-prep-loading {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; padding: 30px 16px; text-align: center; font-size: 13px; color: var(--text-muted);
  border: 1px dashed var(--border); border-radius: 9px; background: var(--bg-elev-2);
}
.uc-prep-loading .uc-spinner { width: 30px; height: 30px; border-width: 3px; }
.uc-spinner { width: 13px; height: 13px; border: 2px solid var(--border);
  border-top-color: var(--accent); border-radius: 50%; display: inline-block;
  animation: uc-spin .7s linear infinite; }
@keyframes uc-spin { to { transform: rotate(360deg); } }

/* ============================================================ Delivery additions */

/* Draft-contract wizard steps strip (contract_docs.html) */
.wizard-steps { display: flex; gap: 24px; align-items: center; padding: 14px 20px; flex-wrap: wrap; }
.wizard-step { display: inline-flex; align-items: center; gap: 8px; color: var(--text-muted); font-size: 13.5px; font-weight: 500; }
.wizard-step .wizard-num { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--border); font-size: 12px; font-weight: 700; }
.wizard-step.current { color: var(--text); }
.wizard-step.current .wizard-num { border-color: var(--accent); color: var(--accent); }
.wizard-step.done { color: var(--accent); }
.wizard-step.done .wizard-num { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Contract document cards: kind/layer badges */
.doc-badges { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }

/* "Contract data" call-to-action card at the bottom of the Documents page */
.extract-cta { display: flex; justify-content: space-between; align-items: center; gap: 20px; flex-wrap: wrap; }
.extract-cta .actions-row { flex-wrap: wrap; }

/* Review page: milestone editor rows */
.ms-row { border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; margin-bottom: 14px; background: var(--bg-elev); }
.ms-row.locked { opacity: .75; background: var(--bg-elev-2); }
.ms-row-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
/* A milestone row is a collapsible card (.note-card + data-note-toggle). Two adjustments on
   top of the shared pattern: the toggle's negative margins are cut for .note-card's 14/20
   padding and this row uses 16/18, and the header only needs a gap under it when the body
   below is actually open. */
.ms-row .note-row-toggle { margin: -16px 0 -16px -18px; padding: 16px 10px 16px 18px; }
.ms-row .note-row-toggle[aria-expanded="true"] { margin-bottom: -6px; }
.ms-row .note-view { margin-top: 14px; }
.ms-row .note-view > label:last-child, .ms-row .note-view .ms-last { margin-bottom: 0; }
/* The title, echoed into the header so a collapsed row still identifies itself. */
.ms-preview { font-weight: 500; color: var(--text-dim); }
.ms-row.locked .note-row-toggle { cursor: pointer; }
/* The controls at the right of a milestone's header: move ↑↓, and remove where it's allowed. */
.ms-row-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* Reorder pair. Butted together as one control so they read as a single up/down affordance
   rather than two unrelated buttons; the disabled end of the range is dimmed, not hidden, so
   the pair never changes width as a row moves. */
.ms-move { display: inline-flex; }
.ms-move .icon-btn:first-child { border-radius: 7px 0 0 7px; }
.ms-move .icon-btn:last-child { border-radius: 0 7px 7px 0; margin-left: -1px; }
.ms-move .icon-btn:disabled { opacity: .3; cursor: default; background: none; }
.ms-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 16px; }
@media (max-width: 900px) { .ms-grid { grid-template-columns: 1fr; } }

/* Overview page */
.ov-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 980px) { .ov-grid { grid-template-columns: 1fr; } }
/* minmax(0,1fr), not 1fr: the clamped summary below is a paragraph whose min-content width
   would otherwise stretch the track — and the whole card with it. */
/* margin: 0 — a <dl>'s 1em default is invisible on one list and becomes a ragged gap between
   the two columns of .ov-facts-split, whose spacing is the grid's job. */
.ov-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 18px; font-size: 13.5px; margin: 0; }
.ov-facts dt { color: var(--text-muted); font-weight: 500; }
.ov-facts dd { margin: 0; }
/* Basic info's six short facts, three per column. Two independent <dl>s rather than one
   four-track grid: each column then sizes its own label track, so "Structure" doesn't set the
   indent for "Client". align-items: start keeps both columns' first rows on the same line. */
.ov-facts-split { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 26px; align-items: start; margin-top: 2px; }
/* container-type: a container query, not a media query, sizes the columns — this card is half of
   .ov-grid above 980px and the whole width below it, so its own width doesn't follow the
   viewport's ("Three-party (client + partner)" has more room on a phone than on a 1300px
   laptop). padding-bottom: the clamped paragraph's last line carries its own line-box slack
   underneath, so the standard 22px reads as ~28 and leaves the card looking bottom-heavy. */
.ov-basic { container-type: inline-size; padding-bottom: 16px; }
@container (max-width: 520px) { .ov-facts-split { grid-template-columns: 1fr; } }
/* The seventh field, across the full width: the scope summary is a paragraph, not a fact. Its
   rule is a separator, not a section break — kept tight so the card stays as near the height of
   Contacts beside it as its own content allows. */
.ov-facts-summary { margin-top: 8px; padding-top: 9px; border-top: 1px solid var(--border-soft); }
/* Collapsible scope summary inside Basic info: three lines closed, full text open. Pure CSS
   (<details>) — the whole paragraph lives in the <summary>, so nothing is duplicated in the
   DOM. Native marker replaced by a caret matching .crumb-caret. */
.ov-summary > summary {
  display: flex; align-items: flex-start; gap: 6px;
  cursor: pointer; color: var(--text-dim); list-style: none;
}
.ov-summary > summary::marker,
.ov-summary > summary::-webkit-details-marker { display: none; content: ""; }
/* Three lines, then an ellipsis. -webkit-box is the only line-clamp that ships everywhere;
   min-width: 0 lets it shrink inside the flex row. */
.ov-summary > summary > span {
  min-width: 0; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
}
/* line-height, not baseline alignment: the clamped box's baseline is its *last* line, which
   would drop the caret three lines down. This puts it on the first one. */
.ov-summary > summary::after {
  content: "▾"; flex-shrink: 0; font-size: 10px; line-height: 2; color: var(--text-muted);
}
.ov-summary[open] > summary > span { display: block; }
.ov-summary[open] > summary::after { content: "▴"; }
.contact-block { border: 1px solid var(--border-soft); border-radius: 10px; padding: 10px 14px; }
.contact-block .contact-role { font-size: 12px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; margin-bottom: 4px; }
/* An unassigned slot is a placeholder, not a value: dashed and dimmed, so the filled
   ones read first. */
.contact-block.empty-slot { border-style: dashed; background: var(--bg-elev-2); }
.contact-block.empty-slot > div:not(.contact-role) { color: var(--text-muted); }
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }

/* Row-click navigation (tr[data-href] — see main.js) */
tr[data-href] { cursor: pointer; }
tr[data-href]:hover td { background: var(--bg-elev-2); }
/* A whole card that stands for a page: the Overview's Milestones and Invoices & payments cards.
   The pointer and a lifted border are the entire affordance — a button in the corner would say
   what clicking the card already says. Inside a table card the rows keep tr[data-href]'s
   background highlight, which is the stronger cue wherever there are rows. */
.card.is-linked { cursor: pointer; transition: border-color .12s, background-color .12s; }
.card.is-linked:hover { border-color: var(--accent-border); }
/* No rows to highlight on the Summary card, so the whole surface takes the wash — the same
   hover the invoice picker's source cards use. On a card that *has* rows this would fight
   tr[data-href]'s own highlight, which is why it isn't on .card.is-linked itself. */
.sum-matrix-card.is-linked:hover { background: var(--accent-wash); }

/* Red pill for failed states (uses the same --low tokens as icon-btn.danger) */
.pill.stage-low { background: var(--low-bg); color: var(--low); }

/* Milestone status menu: a <details> whose summary is the status pill */
.status-menu { position: relative; display: inline-block; }
.status-menu > summary { list-style: none; cursor: pointer; user-select: none; }
.status-menu > summary::-webkit-details-marker { display: none; }
.status-menu-panel {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow); padding: 6px; min-width: 220px;
}
.status-menu-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 7px 10px; border: 0; background: transparent;
  border-radius: 8px; cursor: pointer; text-align: left;
}
.status-menu-item:hover { background: var(--bg-elev-2); }
.status-menu-item.current { background: var(--bg-elev-2); }
.status-menu .menu-divider { margin: 6px 4px; }
.status-menu-item .menu-check { color: var(--accent); font-weight: 700; }
/* The panel hangs right by default (menus live in right-hand action rows). In a
   left-aligned field — Basic info's Status — it has to hang left instead. */
.status-menu.align-left .status-menu-panel { left: 0; right: auto; }

/* Milestone/deliverable sub-rows. Written for the Milestones page, which moved to a
   data-table in Stage 4.6 — these now serve the Invoicing page's per-milestone blocks
   (contract_invoicing.html), the only remaining user. */
.deliv-row { border: 1px solid var(--border-soft); border-radius: 10px; padding: 10px 14px; margin-bottom: 8px; }
.deliv-row-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.dldoc-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 0 0 14px; margin-top: 7px; border-top: 1px dashed var(--border-soft);
  font-size: 13px;
}
.dldoc-actions { margin-left: auto; }

/* ---------------------------------------------------------------------------
   Milestones & deliverables (Stage 4.6). One collapsible card per milestone —
   the header is the whole summary (name, money, progress, derived stage) and the
   body is a plain data-table of deliverables, so the page skims as a list and
   only opens where you're working.
   Collapse reuses the Notes pattern verbatim (main.js toggleNoteRow +
   .note-card/.note-view/.note-chevron); the rules below only re-seat the toggle
   inside this card's own padding.
   --------------------------------------------------------------------------- */
.ms-card { padding: 0; }
.ms-head { display: flex; align-items: center; gap: 14px; padding: 16px 22px; }
/* The toggle spans the left of the head and stretches into the card's padding so
   the whole strip is clickable — same trick as .note-row-toggle, re-cut for this
   card's 16px/22px padding instead of note-card's 14px/20px. */
.ms-toggle {
  margin: -16px 0 -16px -22px; padding: 16px 12px 16px 22px;
  border-radius: var(--radius) 0 0 var(--radius);
}
/* flex: 1 — the headline has to take the space the chevron leaves rather than its content's
   width, or the description's line-clamp has nothing to clamp against. */
.ms-headline { min-width: 0; flex: 1; }
.ms-name { font-size: 15px; font-weight: 700; color: var(--text); display: flex; gap: 8px; }
/* The ordinal is scaffolding, not content: same size, dimmed, fixed width so the
   titles of milestones 1..9 all start on the same x. */
.ms-num { color: var(--text-muted); font-weight: 600; min-width: 14px; }
.ms-meta { margin-top: 2px; }
.ms-head-right { display: flex; align-items: center; gap: 14px; margin-left: auto; flex-shrink: 0; }
/* The due date is a column, not a phrase: right-aligned in a fixed box so the dates stack in
   line down the page, and an em-dash holds the column when a milestone has no date. */
.ms-due {
  font-size: 12px; color: var(--text-dim); white-space: nowrap; text-align: right;
  min-width: 104px; font-variant-numeric: tabular-nums;
}
/* Progress reads as text, not more chips — the stage pill next to it is the one thing in
   this row that should carry colour. Two tight lines rather than one long one: the counts
   share a shape, so stacking them makes the pair readable at a glance and costs a third
   of the horizontal space. */
.ms-count {
  display: flex; flex-direction: column; line-height: 1.3;
  font-size: 12px; color: var(--text-dim);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* The sidebar is a fixed 235px, so the content column gets narrow well before the
   viewport does. Below this the counts + pill + actions starve the title and it wraps one
   word per line — stack the header instead. The toggle keeps its negative top margin (it
   still reaches into the card's padding) but gives up the bottom one, so the row-gap can
   do the spacing. */
@media (max-width: 1150px) {
  .ms-head { flex-wrap: wrap; row-gap: 10px; }
  .ms-toggle { flex: 1 1 100%; margin-bottom: 0; padding-bottom: 0; }
  .ms-head-right { margin-left: 30px; }
}
.ms-body { padding: 0 22px 18px; }
/* .ms-body sets its own `padding` but not `display`; [hidden] still works. Kept
   explicit anyway — the .uc-card-body trap (author `display` silently beating the
   UA [hidden] rule) is one edit away otherwise. */
.ms-body[hidden] { display: none; }

/* Clamped text + a "more"/"less" toggle (main.js syncClampToggles reveals the toggle only
   for text that actually overflows).
   A flex row in BOTH states, baseline-aligned to the first line: expanding must not move
   the toggle, or you click "more" and then have to go hunting for "less". That rules out
   letting the text reflow inline around it — the toggle stays parked at the end of line
   one. It also has to be a flex row rather than an inline sibling, because
   -webkit-line-clamp needs `display: -webkit-box`, a block, which would push the toggle
   onto a second line. */
.clamp-wrap { display: flex; align-items: baseline; gap: 4px; min-width: 0; }
.clamp-text {
  flex: 1; min-width: 0;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;
  overflow: hidden;
}
.clamp-wrap.is-expanded .clamp-text { -webkit-line-clamp: unset; overflow: visible; }
/* Deliberately quiet: it's an affordance on secondary text, not an action worth competing
   with the row's real controls. Colour and underline only on hover. */
.clamp-toggle {
  flex: none; background: none; border: 0; padding: 0; margin: 0 0 0 4px;
  font: inherit; font-weight: 400; color: var(--text-muted); cursor: pointer;
  /* "less" is narrower than "more", and the text beside it is flex:1 — without a fixed
     box the toggle would slide a few px sideways on every click. em so it tracks the
     font size, which differs between the milestone description and the table. */
  min-width: 2.6em; text-align: left;
}
.clamp-toggle:hover { color: var(--accent); text-decoration: underline; }
.clamp-toggle[hidden] { display: none; }
.ms-body-actions { margin-top: 14px; }
.deliv-table { min-width: 720px; }
/* The deliverable is the content; the other three columns are controls. Give the first
   column all the slack and let the rest size to their widgets, otherwise the browser
   splits the width evenly and the titles wrap into a narrow ribbon beside empty space. */
.deliv-table th:first-child, .deliv-table td:first-child { padding-left: 0; width: 62%; }
.deliv-table th:not(:first-child), .deliv-table td:not(:first-child) { width: 1%; white-space: nowrap; }
.deliv-table td { padding-top: 12px; padding-bottom: 12px; }
/* One chip + its remove ×, always on one line — wrapping the × underneath makes that
   row taller than every other and reads like a layout bug. */
.deliv-table .doc-chips { align-items: center; flex-wrap: nowrap; }
.doc-chip-name { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-chip-link { border-color: var(--purple-border); background: var(--purple-wash); color: var(--purple); }
.doc-chip-warn { border-color: var(--mid-border); background: var(--mid-bg); color: var(--mid); cursor: pointer; font-family: inherit; }
.doc-chip-low { border-color: var(--low-border); background: var(--low-bg); color: var(--low); cursor: pointer; font-family: inherit; }
/* The remove × rides with the chip rather than sitting in the actions column —
   it acts on the document, not the deliverable. */
.doc-chip-x { width: 24px; padding: 3px 0; border-color: transparent; }
.doc-chip-x:hover { border-color: var(--low-border); }

/* A stage the document gate rules out. Same row height as a live item (the costs page's
   two-line .status-menu-blocked would make four ragged rows here) — the reason is stated
   once in .status-menu-note at the foot of the panel instead. */
.status-menu-item.is-blocked { cursor: not-allowed; }
.status-menu-item.is-blocked:hover { background: transparent; }
.status-menu-note { margin: 0; padding: 2px 10px 4px; font-size: 12px; color: var(--text-muted); }

/* Financials strip — written for the Overview (Stage 3), now the Direct costs page's, which is
   its only user: the Overview shows the Summary matrix below instead. Equal-width tiles: sized by content they came out
   ragged, because the labels differ wildly in length. Flex rather than an auto-fit grid —
   a grid either wraps five tiles onto two rows (capped track) or balloons two tiles across
   the page (1fr track); `flex: 1 1 190px` + a max-width does both jobs. */
.fin-strip { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 20px; }
.fin-tile {
  flex: 1 1 190px; max-width: 260px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 16px;
}
.fin-label { font-size: 11.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
.fin-value { font-size: 16px; font-weight: 700; margin-top: 2px; }
/* An empty tile's em-dash is a placeholder, not a number — don't let it read as loud as
   a real figure sitting next to it. */
.fin-value.is-empty { color: var(--text-muted); font-weight: 600; }

/* The Summary matrix (templates/_sum_matrix.html) — layers down the side, the four figures
   across, the same order as the per-milestone tracking table. It heads Invoices & payments and
   closes the Overview; on both it replaced a strip of one card per figure, which cost two
   wrapped rows of height to say the same thing. Everything here fights for vertical space:
   .data-table's 16px cell padding is cut right down, the card's own padding with it, and the
   whole thing should read as a header rather than a section. */
.sum-matrix-card {
  display: flex; align-items: flex-start; gap: 28px;
  padding: 12px 16px; margin-bottom: 20px;
}
/* Nudged down by the height difference between the title and the table's small caps header, so
   it sits on the header row's line rather than floating above it. */
.sum-matrix-title { margin: 0; flex-shrink: 0; padding-top: 1px; }
.sum-matrix { width: auto; min-width: min(560px, 100%); }
.sum-matrix th, .sum-matrix td { padding: 3px 10px; }
.sum-matrix thead th { padding-bottom: 5px; font-size: 10.5px; }
/* Row labels: the layer name, left-aligned and readable, not another uppercase column head. */
.sum-matrix tbody th {
  text-align: left; text-transform: none; letter-spacing: 0;
  font-size: 13px; font-weight: 600; color: var(--text);
  border-bottom: none; padding-right: 22px;
}
.sum-matrix tbody td {
  border-bottom: none; font-size: 13.5px;
  font-variant-numeric: tabular-nums;   /* so the columns line up digit for digit */
}

/* Overview danger zone: a muted footer row, not a card. Destructive, rarely used. */
.danger-zone {
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  flex-wrap: wrap; margin-top: 24px; padding: 14px 18px;
  border: 1px dashed var(--border); border-radius: var(--radius);
}
.danger-zone-title { font-size: 13px; font-weight: 700; color: var(--text-dim); }

/* ---------------------------------------------------------------------------
   Direct costs (Stage 4.5). Document-first: a cost line is created from an
   uploaded invoice/PO, so the page needs a source chooser, a strip for uploads
   still awaiting review, and per-row document chips. */

/* Source chooser: upload an invoice / upload a PO / (soon) generate a PO. */
/* auto-fit rather than a fixed 3, so the grid follows how many choices there actually are:
   Direct costs offers three ways to add a cost, the invoice picker two (create / upload), and a
   hard `repeat(3, 1fr)` left the two-card version with a third of the dialog empty. */
.source-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px; margin: 18px 0 4px;
}
.source-card {
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  border: 1px solid var(--border); border-radius: var(--radius); padding: 16px;
  background: var(--bg-elev);
}
.source-card .btn { margin-top: auto; align-self: stretch; text-align: center; }
.source-card-soon { background: var(--bg-elev-2); border-style: dashed; }
.source-card-soon .source-icon, .source-card-soon .source-title { opacity: .65; }
.source-icon {
  width: 38px; height: 38px; border-radius: 10px; display: inline-flex;
  align-items: center; justify-content: center;
  background: var(--accent-tint-strong); color: var(--accent);
}
.source-title { font-size: 14px; font-weight: 700; margin: 0; }
.source-card .source-file { font-size: 12.5px; width: 100%; }
.source-card .hint { margin-top: 4px; }

/* Per-row document chips (invoice / PO / receipt) + the "attach the invoice" affordance. */
.doc-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.doc-chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px;
  border: 1px solid var(--accent-border); border-radius: 999px;
  background: var(--accent-tint-strong); color: var(--accent);
  font-size: 11.5px; font-weight: 700; text-decoration: none; white-space: nowrap;
}
.doc-chip:hover { background: var(--accent-tint); }
.doc-chip-receipt { border-color: var(--good-border); background: var(--good-bg); color: var(--good); }
.doc-chip-add {
  border-style: dashed; border-color: var(--mid-border); background: transparent;
  color: var(--mid); cursor: pointer; font-family: inherit;
}
.doc-chip-add:hover { background: var(--mid-bg); }
/* A document chip that owns its own actions: the chip IS the menu trigger, so a cell holds
   one control instead of a link plus an "open in Drive" button plus a delete ×. Built on
   details.status-menu deliberately — that's the menu the Status column in these same tables
   uses, so it already escapes .table-scroll's clipping (see the :has() rule further down) and
   already closes on an outside click, with no new JS. The panel hangs left-aligned: these
   chips sit at the left of a mid-table column, so opening rightward tracks the click. */
.doc-menu > summary { display: inline-flex; }          /* summary is the chip; don't let it fill the cell */
.doc-menu .doc-caret { font-size: 9px; opacity: .75; }
.doc-menu[open] .doc-caret { transform: rotate(180deg); }
.doc-menu .status-menu-panel { left: 0; right: auto; min-width: 210px; }
/* Menu rows are a mix of <a>, <button> and form-wrapped <button>; the anchors need the
   button's reset. Label left, hint right — .status-menu-item is already that flex row. */
.doc-menu .status-menu-item { color: var(--text); text-decoration: none; font-size: 13px; }
.doc-menu .status-menu-item.danger { color: var(--low); }
.doc-menu form { margin: 0; }
/* A copy-state flag riding on the chip: the warning has to survive the actions moving into
   the panel, so it stays on the trigger where it can't be missed. Inherits the chip's own
   warn/low colour, just lighter and lower-case so the filename still reads first. */
.doc-chip-flag {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  opacity: .8; padding-left: 2px;
}
.costs-table td { vertical-align: middle; }
.costs-table { min-width: 900px; }   /* below this the columns squeeze illegibly */
/* Description is the one free-text column — give it room so a normal sentence doesn't
   wrap into a seven-line row. */
.costs-table td:nth-child(3), .costs-table th:nth-child(3) { min-width: 240px; }
.nowrap { white-space: nowrap; }

/* A wide table inside a card: scroll it instead of clipping columns at the card edge. */
.table-scroll { overflow-x: auto; margin: -4px; padding: 4px; }

/* A status the user can't pick yet, with the reason under it (the reason is a sentence,
   so stacking beats the side-by-side layout the pickable items use). */
.status-menu-blocked {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 8px 10px; border-radius: 8px; cursor: not-allowed; max-width: 200px;
}
.status-menu-blocked .small { line-height: 1.35; }

/* Page-head actions must never wrap mid-label ("+ Add direct / cost"). */
.page-head .btn { white-space: nowrap; }

/* Fixed, non-editable field in a form grid — reads as a value, not an input. */
/* Amount + its currency as one control: the number takes the room, the 3-letter code sits
   next to it (both are `width:100%` inputs by default, hence the flex reset). */
.amount-ccy { display: flex; gap: 8px; align-items: flex-start; }
.amount-ccy input { flex: 1; min-width: 0; }
.amount-ccy select { width: auto; flex-shrink: 0; }
.field-static { display: flex; flex-direction: column; gap: 6px; }
.field-static-value { display: flex; align-items: center; gap: 8px; padding: 2px 0; }

/* A file input following an explanatory hint needs air above it. */
.modal .hint + label { margin-top: 14px; }

/* Under the totals strip: what was converted into the contract currency, and what
   couldn't be. */
.fin-note { font-size: 12.5px; color: var(--text-muted); margin: -8px 0 20px; }
.fin-note-warn { color: var(--mid); display: inline-block; }
.fin-flag {
  font-size: 11px; font-weight: 700; color: var(--mid); background: var(--mid-bg);
  border-radius: 999px; padding: 2px 8px; margin-left: 6px; vertical-align: middle;
}

/* Review-the-extraction modal. */
.review-head { display: flex; align-items: center; gap: 10px; justify-content: space-between; }
.review-head h2 { margin: 0; }
.match-note {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; margin: 14px 0 4px;
}
.match-note.ok { border-color: var(--good-border); background: oklch(98% 0.02 150); }
.match-note.warn { border-color: var(--mid-border); background: oklch(99% 0.02 75); }
.match-note .btn { margin-left: auto; }
.extraction-notes { border-top: 1px dashed var(--border-soft); padding-top: 10px; }

/* Payment-email compose page. */
.compose-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 20px; align-items: start; max-width: 1040px; }
.compose-wrap .banner { grid-column: 1 / -1; margin-bottom: 0; }
.compose-card { margin-bottom: 0; }
.compose-card textarea { font-family: var(--font); line-height: 1.5; }
.compose-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
.compose-facts { margin-bottom: 0; }
.attach-box { border: 1px solid var(--border-soft); border-radius: 10px; padding: 12px 14px; margin-top: 14px; }
.attach-head { font-size: 11.5px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em; }
.attach-list { list-style: none; padding: 0; margin: 8px 0 0; display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.attach-list li { display: flex; align-items: center; gap: 8px; }
.attach-box .hint { margin-top: 10px; }

/* Admin › Suppliers: the org whose registry is shown. */
.org-switch-card { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; padding: 14px 20px; }
.org-switch-label { display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 600; margin: 0; }
.org-switch-label select { width: auto; min-width: 180px; }
.org-switch-card .muted { margin: 0; }

/* ---------------------------------------------------------------- Invoices & payments */

/* The per-milestone rollup: two four-column groups (client / partner) that have to read as
   groups, or it's ten anonymous numbers. A single left border per group does it — cheaper
   visually than boxes or alternating fills. */
.rollup-table { min-width: 880px; }
/* Milestone titles are full sentences ("Work Order 1 — Diriyah dining experience
   diagnostic research"); without a floor this column loses to eight numeric ones and wraps
   one or two words per line. The numeric columns give the padding back so the whole table
   still fits a 1280px window without scrolling. */
.rollup-table td:first-child, .rollup-table th:first-child { min-width: 210px; }
.rollup-table td, .rollup-table th { padding-left: 9px; padding-right: 9px; }
.rollup-group th {
  padding-bottom: 6px; border-bottom: none; color: var(--text-dim);
  font-size: 11px; letter-spacing: .04em;
}
.rollup-table .grp-start { border-left: 1px solid var(--border); }
/* The layer label belongs over the middle of the four columns it heads, not left-aligned at
   their edge where it reads as a label for the first one. */
.rollup-group th.grp-head { text-align: center; }
.rollup-table tfoot th {
  text-transform: none; font-size: 13px; color: var(--text); letter-spacing: 0;
  padding: 14px 12px 0; border-bottom: none; border-top: 1px solid var(--border);
}
/* A row is clickable through to Milestones; the "Invoice it →" button must still read as
   the primary thing in its cell. */
.rollup-table tr[data-href] { cursor: pointer; }
.rollup-table tr[data-href]:hover { background: var(--bg-elev-2); }

/* Eight columns since Received folded into Receipt — below this they squeeze illegibly and
   .table-scroll takes over. */
.inv-table { min-width: 920px; }
.inv-table td { vertical-align: top; }
/* A status menu inside .table-scroll is clipped by it — overflow-x:auto makes the container
   a clipping context in both directions — which bites exactly when the table is short and
   the open panel reaches past its last row. Make room only while a menu is open, so the
   card doesn't carry dead space the rest of the time. */
.table-scroll:has(details.status-menu[open]) { padding-bottom: 160px; }
/* A document menu is the tallest of them — up to five actions plus a divider, where a status
   menu offers two or three — so 160px leaves its last item clipped. Its own rule rather than
   raising the number above, which would hang 60px of dead space under every status menu too.
   Written out in full (details.status-menu.doc-menu) to outrank the rule above: a doc menu
   matches both, and the shorter `:has(.doc-menu[open])` loses the specificity tie. */
.table-scroll:has(details.status-menu.doc-menu[open]) { padding-bottom: 240px; }
.inv-table .doc-chips { align-items: center; }
/* Past due and still owed — the one red thing on the page. */
.date-overdue { color: var(--low); font-weight: 700; }
.inv-table .ms-cell span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; max-width: 190px;
}
/* Payments hang under the invoice they settle: same table, indented continuation row. */
.pay-chips { margin-left: auto; }
.pay-date { margin-top: 4px; }
/* Notes: narrow on purpose. The cell clips to a couple of dozen characters and the whole note
   is the hover label, so one long note can't widen every other column. */
.notes-col { max-width: 160px; }
.inv-table .notes-col { font-size: 13px; color: var(--text-dim); }
/* A checkbox on its own line, reading as a sentence rather than a labelled field. */
.check-line {
  display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap;
  font-weight: 500; color: var(--text);
}
.check-line input { width: auto; margin: 0; }

/* ---- the composer ---- */
/* Line items as a grid rather than a nested table: the header row and every line share one
   template, so the columns line up without a table's row/cell markup in a form. */
.line-head, .line-row {
  display: grid; grid-template-columns: minmax(0, 1fr) 80px 140px 130px 34px;
  gap: 10px; align-items: center;
}
.line-head {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); padding: 0 4px 6px;
}
.line-row { margin-bottom: 8px; }
.line-row input { margin: 0; }
.line-row input.right { text-align: right; }
.line-amount { font-size: 13.5px; font-weight: 600; padding-right: 4px; }

/* The composer's first section follows the dialog title directly (no .modal-section sibling
   above it to inherit a top border from), so it has to make its own room. */
.modal .review-head + .modal-section { margin-top: 16px; }

.inv-totals {
  display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap;
  border-top: 1px solid var(--border-soft); margin-top: 14px; padding-top: 14px;
}
.inv-totals .tax-toggle { margin: 0; }
.inv-totals .tax-rate { width: 110px; margin: 0; }
.totals-list {
  display: grid; grid-template-columns: auto auto; gap: 4px 18px;
  margin: 0 0 0 auto; font-size: 13.5px; text-align: right;
}
.totals-list dt { color: var(--text-dim); }
.totals-list dd { margin: 0; font-variant-numeric: tabular-nums; }

/* Admin › Invoicing: the template's placeholder vocabulary, shown as code chips so whoever
   is designing the .docx can copy them straight out. */
.code-chip {
  display: inline-block; padding: 2px 7px; border-radius: 6px;
  background: var(--bg-elev-2); border: 1px solid var(--border-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
  color: var(--text);
}
.tpl-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; margin: 4px 0 14px; }
.tpl-group { display: flex; flex-direction: column; gap: 6px; }
.tpl-actions { margin-left: auto; gap: 8px; }

/* A source card that is itself the button (the invoice sources have nothing to fill in
   before you pick one, unlike the cost uploads with their file inputs). */
button.source-card.as-button {
  cursor: pointer; font: inherit; text-align: left; width: 100%;
}
button.source-card.as-button:hover { border-color: var(--accent-border); background: var(--accent-wash); }

@media (max-width: 1100px) {
  .source-grid { grid-template-columns: 1fr; }
  .compose-wrap { grid-template-columns: minmax(0, 1fr); }
  .line-head, .line-row { grid-template-columns: minmax(0, 1fr) 64px 110px 100px 30px; }
}
