/* SIR — platform shell and boilerplate areas.

   Loaded after styles.css and reuses its :root tokens; defines no colours of its
   own. Covers the sidebar/topbar shell, the auth screens, and the Home,
   Dashboard, Reports, Activity, Search, Notifications and Settings pages.
   See DESIGN.md — restrained palette, thin rules, no hero metrics. */

/* ------------------------------------------------------------------ shell */

.app-shell {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  min-height: 100vh;
  transition: grid-template-columns var(--t-med) var(--ease-out);
}

body.nav-collapsed .app-shell { grid-template-columns: 62px minmax(0, 1fr); }

.shell-main { min-width: 0; display: flex; flex-direction: column; }

body.auth-mode .app-shell { grid-template-columns: minmax(0, 1fr); }
body.auth-mode .sidebar,
body.auth-mode .topbar { display: none; }
body.auth-mode main { max-width: none; padding: 0; }

/* ----------------------------------------------------------------- sidebar */

.sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.7rem 0.6rem 0.8rem;
  background: var(--surface);
  border-right: 1px solid var(--line);
  overflow: hidden;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 0.5rem 0.75rem;
  text-decoration: none;
  color: var(--ink);
}

.sidebar-brand .wordmark-glyph { width: 20px; height: 20px; flex: none; }

.wordmark-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }

.sidebar .wordmark-sub { white-space: nowrap; }

.nav { display: flex; flex-direction: column; gap: 1rem; flex: 1; min-height: 0; overflow-y: auto; }

.nav-group { display: flex; flex-direction: column; gap: 1px; }

.nav-group-label {
  margin: 0 0 0.3rem 0.55rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--faint);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.42rem 0.55rem;
  border-radius: var(--radius-s);
  text-decoration: none;
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}

.nav-item:hover { background: var(--surface-2); color: var(--ink); }

.nav-item.active { background: var(--primary-tint); color: var(--primary); font-weight: 600; }

.nav-item:focus-visible { outline: 2px solid var(--client); outline-offset: -2px; }

.nav-icon { width: 17px; height: 17px; flex: none; }

.nav-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }

.nav-badge {
  flex: none;
  min-width: 1.15rem;
  padding: 0 0.28rem;
  border-radius: 999px;
  background: var(--attn-tint);
  color: var(--attn-strong);
  border: 1px solid color-mix(in oklab, var(--attn) 28%, white);
  font-size: 0.6875rem;
  font-weight: 600;
  text-align: center;
  font-feature-settings: "tnum";
}

.sidebar-foot {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line);
}

.org-card {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1;
  min-width: 0;
  padding: 0.35rem 0.4rem;
  border-radius: var(--radius-s);
}

.org-badge {
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-s);
  background: var(--ink);
  color: var(--bg);
  font-size: 0.75rem;
  font-weight: 700;
}

.org-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.org-text b { font-size: 0.78125rem; }
.org-text span { font-size: 0.6875rem; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.nav-collapse {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}

.nav-collapse:hover { background: var(--surface-2); color: var(--ink); }

body.nav-collapsed .nav-collapse svg { transform: rotate(180deg); }

/* collapsed rail: icons only */
body.nav-collapsed .wordmark-text,
body.nav-collapsed .nav-group-label,
body.nav-collapsed .nav-label,
body.nav-collapsed .nav-badge,
body.nav-collapsed .org-text { display: none; }

body.nav-collapsed .sidebar { padding-inline: 0.5rem; }
body.nav-collapsed .nav-item { justify-content: center; padding-inline: 0; }
body.nav-collapsed .sidebar-brand { justify-content: center; padding-inline: 0; }
body.nav-collapsed .sidebar-foot { flex-direction: column; }
body.nav-collapsed .org-card { justify-content: center; padding-inline: 0; }

/* ------------------------------------------------------------------ topbar */

.topbar { padding: 0.55rem 1.25rem; gap: 0.75rem; }

.nav-toggle {
  display: none;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--bg);
  color: var(--muted);
  cursor: pointer;
}

.topbar-title {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
  font-size: 0.8125rem;
}

.crumb-root { color: var(--muted); white-space: nowrap; }
.crumb-sep { color: var(--faint); }
.crumb-leaf { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.topbar-search {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex: 1;
  max-width: 420px;
  margin-inline: auto;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--faint);
}

.topbar-search:focus-within { border-color: var(--line-strong); background: var(--bg); }

.topbar-search .nav-icon { width: 15px; height: 15px; }

.topbar-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink);
  outline: none;
}

.topbar-search input::placeholder { color: var(--faint); }
.topbar-search input::-webkit-search-cancel-button { display: none; }

.topbar-search kbd {
  flex: none;
  padding: 0.05rem 0.3rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
  font-family: inherit;
  font-size: 0.6875rem;
  color: var(--faint);
}

/* Holds the search box's column when the viewer has no mail to search. The
   topbar is a fixed grid, so removing the form outright would slide the
   actions leftwards into the title. */
.topbar-spacer { flex: 1; }

.topbar-actions { gap: 0.4rem; }

.icon-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn:focus-visible { outline: 2px solid var(--client); outline-offset: 1px; }

.icon-btn.has-unread::after {
  content: "";
  position: absolute;
  top: 5px;
  right: 5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--attn);
  border: 1.5px solid var(--bg);
}

.user-btn {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.22rem 0.4rem 0.22rem 0.22rem;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}

.user-btn:hover { background: var(--surface-2); }
.user-btn:focus-visible { outline: 2px solid var(--client); outline-offset: 1px; }

.user-btn-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.25; }
.user-btn-text b { font-size: 0.78125rem; color: var(--ink); }
.user-btn-text span { font-size: 0.6875rem; color: var(--faint); }

/* ---------------------------------------------------------------- popovers */

.popover-host { position: relative; }

.popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: var(--z-tooltip);
  min-width: 240px;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-m);
  box-shadow: 0 12px 28px oklch(0.21 0.015 343 / 0.12);
  overflow: hidden;
  animation: pop-in var(--t-fast) var(--ease-out);
}

@keyframes pop-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

.popover-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.55rem 0.5rem 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line);
}

.popover-foot {
  display: block;
  padding: 0.55rem 0.75rem;
  border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: 0.75rem;
  text-decoration: none;
}

.popover-foot:hover { background: var(--surface-2); }

.user-menu { min-width: 250px; }

.user-menu-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.75rem;
  border-bottom: 1px solid var(--line);
}

.user-menu-head > span { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }

.menu-item {
  display: block;
  width: 100%;
  padding: 0.45rem 0.75rem;
  border: 0;
  background: transparent;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  font-size: 0.8125rem;
  cursor: pointer;
}

.menu-item:hover { background: var(--surface-2); }
.menu-item.danger { color: var(--primary); }
.menu-sep { height: 1px; margin: 0.3rem 0; background: var(--line); }

.mono-sub {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.6875rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ----------------------------------------------------------- notifications */

.notif-popover { width: 340px; }

.notif-list { list-style: none; margin: 0; padding: 0; max-height: 380px; overflow-y: auto; }

.notif-row + .notif-row { border-top: 1px solid var(--line); }

.notif-row a {
  display: flex;
  gap: 0.6rem;
  padding: 0.6rem 0.75rem;
  text-decoration: none;
  color: inherit;
}

.notif-row a:hover { background: var(--surface); }

.notif-row.unread { background: var(--primary-tint); }
.notif-row.unread a:hover { background: color-mix(in oklab, var(--primary-tint) 70%, var(--surface-2)); }

.notif-glyph {
  flex: none;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-s);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 0.75rem;
}

.notif-glyph.kind-attention { background: var(--attn-tint); color: var(--attn-strong); }
.notif-glyph.kind-import { background: var(--third-tint); color: oklch(0.45 0.11 168); }
.notif-glyph.kind-mention { background: var(--client-tint); color: var(--client); }
.notif-glyph.kind-report { background: var(--primary-tint); color: var(--primary); }

.notif-text { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.notif-text b { font-size: 0.78125rem; }
.notif-text > span { font-size: 0.75rem; color: var(--muted); }
.notif-when { font-size: 0.6875rem !important; color: var(--faint) !important; }

.notif-list.page {
  max-height: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  overflow: hidden;
}

/* -------------------------------------------------------------- auth screens */

.auth-screen {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: 100vh;
}

.auth-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 25rem;
  width: 100%;
  margin-inline: auto;
  padding: 3rem 2rem;
}

.auth-wordmark {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 2rem;
  text-decoration: none;
  color: var(--ink);
}

.auth-title { font-size: 1.5625rem; letter-spacing: -0.015em; margin: 0; }
.auth-sub { color: var(--muted); margin: 0.3rem 0 1.4rem; }

.auth-sso { width: 100%; justify-content: center; padding-block: 0.55rem; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0;
  color: var(--faint);
  font-size: 0.75rem;
}

.auth-divider::before,
.auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.auth-form { margin: 0; }

.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: 0.75rem;
}

.auth-check { flex-direction: row !important; align-items: center; gap: 0.4rem !important; font-weight: 500 !important; color: var(--muted); }
.auth-check input { width: auto; }

.auth-submit { width: 100%; justify-content: center; padding-block: 0.55rem; }

/* Sign-in feedback. The palette has no dedicated danger colour, so this reuses
   the attention orange rather than introducing one (see DESIGN.md). */
.auth-error {
  margin: 0 0 0.9rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--attn);
  border-radius: var(--radius-s);
  background: var(--attn-tint);
  color: var(--attn-strong);
  font-size: 0.8125rem;
}

.auth-error.is-info {
  border-color: var(--line-strong);
  background: var(--surface-2);
  color: var(--muted);
}

.auth-note {
  margin-top: 1.1rem;
  padding: 0.5rem 0.65rem;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-s);
  color: var(--muted);
  font-size: 0.75rem;
}

.auth-alt { margin-top: 1rem; font-size: 0.8125rem; color: var(--muted); }

.auth-aside {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.5rem;
  padding: 3rem;
  background: var(--surface);
  border-left: 1px solid var(--line);
}

.auth-quote {
  max-width: 26ch;
  margin: 0;
  font-size: 1.5625rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink);
}

.auth-points { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.9rem; max-width: 44ch; }
.auth-points li { padding-left: 0.9rem; border-left: 2px solid var(--line-strong); color: var(--muted); font-size: 0.8125rem; }
.auth-points b { color: var(--ink); }

.auth-aside-foot { display: flex; align-items: center; gap: 0.6rem; color: var(--faint); font-size: 0.75rem; }

/* --------------------------------------------------------------- panels */

.panel {
  margin-bottom: 1.5rem;
  padding: 1rem 1.1rem 1.1rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.85rem;
}

.panel-head h2 {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0;
  font-size: 0.9375rem;
  letter-spacing: -0.005em;
}

.panel-sub { color: var(--faint); font-size: 0.75rem; font-weight: 400; margin: 0; }

.panel-note { margin: 0.7rem 0 0; color: var(--muted); font-size: 0.75rem; }

/* The base .empty-state is a bare text block; inside a panel it also carries a
   call to action and needs to sit tighter. */
.panel .empty-state { padding: 1.75rem 1rem; }
.empty-state p { margin: 0; max-width: 52ch; margin-inline: auto; }
.empty-state .btn { margin-top: 0.9rem; }

/* ----------------------------------------------------------------- home */

.eyebrow { margin: 0 0 0.15rem; color: var(--muted); font-size: 0.8125rem; }
.home-head h1 { margin: 0; }

.tile-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.tile {
  padding: 0.85rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg);
}

.tile-label { margin: 0; font-size: 0.71875rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.tile-value { margin: 0.25rem 0 0.1rem; font-size: 1.5625rem; font-weight: 600; letter-spacing: -0.02em; font-feature-settings: "tnum"; }
.tile-sub { margin: 0; font-size: 0.75rem; color: var(--muted); }

.tile.tone-attn { border-color: color-mix(in oklab, var(--attn) 30%, var(--line)); background: var(--attn-tint); }
.tile.tone-attn .tile-value { color: var(--attn-strong); }
.tile.tone-ok .tile-value { color: oklch(0.45 0.11 168); }

.queue { list-style: none; margin: 0; padding: 0; }
.queue-row + .queue-row { border-top: 1px solid var(--line); }

.queue-row a {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.6rem 0.2rem;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-s);
}

.queue-row a:hover { background: var(--surface); }

.queue-mark { color: var(--attn); font-size: 0.75rem; }
.queue-text { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.35; }
.queue-text b { font-size: 0.8125rem; }
.queue-text span { font-size: 0.75rem; color: var(--muted); }

.home-split { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 1.25rem; align-items: start; }

.project-list.compact { border-top: 0; }
.project-list.compact .project-row:last-child { border-bottom: 0; }

/* The full-width project row is a four-column grid that needs ~700px. In a
   half-width panel it has to fold onto two lines instead. */
.project-list.stacked .project-row {
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.4rem 0.75rem;
  padding: 0.7rem 0.5rem;
}

.project-list.stacked .project-meta { justify-content: flex-start; gap: 0.9rem; font-size: 0.75rem; }
.project-list.stacked .mini-activity { display: none; }
.project-list.stacked .attn-pill { justify-self: end; }

.quick-actions { display: grid; gap: 0.5rem; }

.quick-action {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  text-align: left;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.quick-action:hover:not(:disabled) { background: var(--surface); border-color: var(--line-strong); }
.quick-action:disabled { opacity: 0.55; cursor: default; }
.quick-action b { font-size: 0.8125rem; }
.quick-action span { font-size: 0.75rem; color: var(--muted); }

.changelog { margin-top: 1.1rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
.changelog ul { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
.changelog li { font-size: 0.75rem; color: var(--muted); }
.changelog .ver {
  display: inline-block;
  min-width: 2rem;
  margin-right: 0.4rem;
  padding: 0 0.3rem;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--faint);
  font-size: 0.6875rem;
  font-weight: 600;
  text-align: center;
}

/* -------------------------------------------------------------- dashboard */

.dash-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.chip-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.dash-count { color: var(--faint); font-size: 0.75rem; font-feature-settings: "tnum"; }

.dash-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.25rem;
  align-items: start;
}

.dash-grid .panel { margin-bottom: 0; }
.dash-grid .span-2 { grid-column: span 2; }

.dash-activity { height: 120px; align-items: flex-end; }

.chart-empty { padding: 1.4rem 0; color: var(--faint); font-size: 0.8125rem; text-align: center; }

.chart-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 0.5rem;
  color: var(--faint);
  font-size: 0.6875rem;
  font-feature-settings: "tnum";
}

.split-bar {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-2);
}

.seg { display: block; height: 100%; }
.seg-team { background: var(--primary); }
.seg-client { background: var(--client); }
.seg-third { background: var(--third); }
.seg-unknown { background: var(--unknown); }

.legend { list-style: none; display: flex; flex-wrap: wrap; gap: 0.9rem; margin: 0.7rem 0 0; padding: 0; font-size: 0.75rem; color: var(--muted); }
.legend b { color: var(--ink); font-feature-settings: "tnum"; margin-left: 0.2rem; }

.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.35rem; }
.dot-team { background: var(--primary); }
.dot-client { background: var(--client); }
.dot-third { background: var(--third); }
.dot-unknown { background: var(--unknown); }

.bar-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }

.bar-list a {
  display: grid;
  grid-template-columns: minmax(6rem, 8rem) minmax(0, 1fr) 2.2rem;
  align-items: center;
  gap: 0.6rem;
  padding: 0.22rem 0.2rem;
  border-radius: var(--radius-s);
  text-decoration: none;
  color: inherit;
}

.bar-list a:hover { background: var(--surface); }

.bar-label { font-size: 0.78125rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { display: block; height: 7px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 999px; background: var(--primary); }
.bar-value { font-size: 0.75rem; color: var(--muted); text-align: right; font-feature-settings: "tnum"; }

.stage-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.stage-list li { display: flex; align-items: center; gap: 0.6rem; }
.stage-count { font-size: 0.75rem; color: var(--muted); }

.big-stat { margin: 0 0 0.8rem; font-size: 1.5625rem; font-weight: 600; letter-spacing: -0.02em; font-feature-settings: "tnum"; }
.big-stat span { display: block; font-size: 0.75rem; font-weight: 400; letter-spacing: 0; color: var(--muted); }
.big-stat.tone-attn { color: var(--attn-strong); }

/* ------------------------------------------------------------ data tables */

.data-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }

.data-table th {
  padding: 0.35rem 0.5rem 0.35rem 0;
  border-bottom: 1px solid var(--line-strong);
  text-align: left;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--faint);
}

.data-table td { padding: 0.5rem 0.5rem 0.5rem 0; border-bottom: 1px solid var(--line); vertical-align: middle; }
.data-table tr:last-child td { border-bottom: 0; }
.data-table .num { text-align: right; font-feature-settings: "tnum"; }
.data-table th.num { text-align: right; }
.data-table .center, .data-table th.center { text-align: center; }
.data-table .right { text-align: right; }
.data-table .share { width: 28%; }

.data-table .who { display: flex; align-items: center; gap: 0.55rem; }
.data-table .who > span { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.data-table td .mono-sub { display: block; }

.aff-tag {
  display: inline-block;
  padding: 0.08rem 0.4rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
}

.aff-tag.aff-team { background: var(--primary-tint); color: var(--primary); border-color: color-mix(in oklab, var(--primary) 22%, white); }
.aff-tag.aff-client { background: var(--client-tint); color: var(--client); border-color: color-mix(in oklab, var(--client) 25%, white); }
.aff-tag.aff-third-party { background: var(--third-tint); color: oklch(0.45 0.11 168); border-color: color-mix(in oklab, var(--third) 30%, white); }
.aff-tag.aff-unknown { background: var(--unknown-tint); color: var(--muted); border-style: dashed; border-color: var(--line-strong); }

/* ---------------------------------------------------------------- cards */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg);
}

.card.muted { background: var(--surface); }
.card h3 { margin: 0; font-size: 0.875rem; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.card-desc { margin: 0; color: var(--muted); font-size: 0.78125rem; }
.card-actions { display: flex; gap: 0.4rem; margin-top: auto; padding-top: 0.3rem; }

.soon-tag {
  padding: 0.08rem 0.4rem;
  border: 1px dashed var(--line-strong);
  border-radius: 999px;
  color: var(--faint);
  font-size: 0.6875rem;
  white-space: nowrap;
}

/* --------------------------------------------------------------- activity */

.audit { list-style: none; margin: 1.25rem 0 0; padding: 0 0 0 0.35rem; border-left: 1px solid var(--line); }

.audit-row { position: relative; display: flex; gap: 0.8rem; padding: 0.6rem 0 0.6rem 1rem; }

.audit-node {
  position: absolute;
  left: -0.42rem;
  top: 0.95rem;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--unknown);
  box-shadow: 0 0 0 3px var(--bg);
}

.audit-node.aff-team { background: var(--primary); }
.audit-node.aff-client { background: var(--client); }
.audit-node.aff-third-party { background: var(--third); }

.audit-body { min-width: 0; }
.audit-line { margin: 0; font-size: 0.8125rem; }
.audit-obj { color: var(--muted); }
.audit-meta { display: flex; gap: 0.6rem; margin: 0.15rem 0 0; font-size: 0.71875rem; color: var(--faint); }
.audit-proj { color: var(--muted); }
.audit-foot { margin-top: 1rem; color: var(--faint); font-size: 0.71875rem; }

/* ----------------------------------------------------------------- search */

.search-form { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; }

.search-form input {
  flex: 1;
  min-width: 0;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--bg);
  font: inherit;
  font-size: 0.875rem;
  color: var(--ink);
}

.search-form input:focus-visible { outline: 2px solid var(--client); outline-offset: 1px; }

.people-list, .result-list { list-style: none; margin: 0; padding: 0; }
.people-list li + li, .result-list li + li { border-top: 1px solid var(--line); }

.people-list a {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.2rem;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-s);
}

.people-list a:hover, .result-list a:hover { background: var(--surface); }

.people-text { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.3; }
.people-text b { font-size: 0.8125rem; }
.people-proj { font-size: 0.75rem; color: var(--faint); white-space: nowrap; }

.result-list a {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.55rem 0.2rem;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-s);
}

.result-list b { font-size: 0.8125rem; }
.result-meta { font-size: 0.75rem; color: var(--muted); }

/* --------------------------------------------------------------- settings */

.settings-tabs { margin-bottom: 1.5rem; overflow-x: auto; }
.settings-tabs .source-tab { text-decoration: none; }

.settings-body .panel:last-child { margin-bottom: 0; }

.profile-id {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.1rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--line);
}

.profile-id b { font-size: 0.9375rem; }
.profile-id p { margin: 0.1rem 0 0.5rem; }

.form-actions { display: flex; gap: 0.5rem; margin-top: 0.3rem; }

/* Which developments an invited client may see. Only rendered for the
   client_viewer role — every other role's access follows from the role, and
   empty checkboxes beside "Sales" would imply otherwise. */
.invite-grants { margin: 0.75rem 0 0; padding: 0.7rem 0.85rem; border: 1px solid var(--line); border-radius: var(--radius-m); }
.invite-grants legend { padding: 0 0.35rem; font-size: 0.71875rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.grant-check { display: flex; align-items: center; gap: 0.45rem; padding: 0.25rem 0; font-size: 0.8125rem; }

.toggle-list { list-style: none; margin: 0; padding: 0; }
.toggle-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.55rem 0;
  font-size: 0.8125rem;
}
.toggle-list li + li { border-top: 1px solid var(--line); }

.switch { display: inline-flex; align-items: center; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch-track {
  display: block;
  width: 32px;
  height: 18px;
  padding: 2px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.switch-thumb {
  display: block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--bg);
  box-shadow: 0 1px 2px oklch(0.21 0.015 343 / 0.25);
  transition: transform var(--t-fast) var(--ease-out);
}

.switch input:checked + .switch-track { background: var(--primary); border-color: var(--primary); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(14px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--client); outline-offset: 2px; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.role-select {
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--bg);
  font: inherit;
  font-size: 0.75rem;
  color: var(--ink);
}

.danger-zone { border-color: color-mix(in oklab, var(--primary) 22%, var(--line)); }
.danger-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.danger-row b { font-size: 0.8125rem; }
.btn.danger { color: var(--primary); border-color: color-mix(in oklab, var(--primary) 35%, white); }

.plan-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--surface);
  margin-bottom: 1.1rem;
}

.plan-name { margin: 0; font-size: 0.9375rem; font-weight: 600; }
.plan-price { margin: 0; font-size: 1.25rem; font-weight: 600; font-feature-settings: "tnum"; }
.plan-price span { font-size: 0.75rem; font-weight: 400; color: var(--muted); margin-left: 0.25rem; }

.usage-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 1rem; }
.usage-label { margin: 0; font-size: 0.71875rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.usage-value { margin: 0.2rem 0 0.4rem; font-size: 0.9375rem; font-weight: 600; font-feature-settings: "tnum"; }
.usage-value span { font-size: 0.75rem; font-weight: 400; color: var(--muted); }

/* ------------------------------------------------------------- responsive */

@media (max-width: 1080px) {
  .home-split { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .app-shell,
  body.nav-collapsed .app-shell { grid-template-columns: 62px minmax(0, 1fr); }
  .wordmark-text, .nav-group-label, .nav-label, .nav-badge, .org-text { display: none; }
  .sidebar { padding-inline: 0.5rem; }
  .nav-item, .sidebar-brand, .org-card { justify-content: center; padding-inline: 0; }
  .sidebar-foot { flex-direction: column; }
  .tile-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-grid { grid-template-columns: minmax(0, 1fr); }
  .dash-grid .span-2 { grid-column: span 1; }
  .auth-screen { grid-template-columns: minmax(0, 1fr); }
  .auth-aside { display: none; }
  .user-btn-text { display: none; }
}

@media (max-width: 720px) {
  .app-shell,
  body.nav-collapsed .app-shell { grid-template-columns: minmax(0, 1fr); }
  .sidebar { display: none; }
  body.nav-collapsed .sidebar {
    display: flex;
    position: fixed;
    inset: 0 auto 0 0;
    width: 232px;
    z-index: calc(var(--z-tooltip) - 1);
    box-shadow: 0 0 40px oklch(0.21 0.015 343 / 0.18);
    padding-inline: 0.6rem;
  }
  body.nav-collapsed .wordmark-text,
  body.nav-collapsed .nav-group-label,
  body.nav-collapsed .nav-label,
  body.nav-collapsed .nav-badge,
  body.nav-collapsed .org-text { display: block; }
  body.nav-collapsed .nav-item { justify-content: flex-start; padding-inline: 0.55rem; }
  body.nav-collapsed .sidebar-brand { justify-content: flex-start; }
  body.nav-collapsed .sidebar-foot { flex-direction: row; }
  body.nav-collapsed .nav-collapse svg { transform: none; }

  .nav-toggle { display: grid; }
  .topbar { padding-inline: 1rem; flex-wrap: wrap; }
  .topbar-search { order: 5; max-width: none; flex-basis: 100%; margin-inline: 0; }
  .topbar-search kbd { display: none; }
  .topbar-import { display: none; }
  .tile-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card-grid { grid-template-columns: minmax(0, 1fr); }
  .notif-popover { width: min(340px, calc(100vw - 2rem)); }
  .popover { right: -0.5rem; }
  .data-table { display: block; overflow-x: auto; }
  .plan-card { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell, .popover, .switch-track, .switch-thumb, .quick-action, .nav-item {
    transition: none;
    animation: none;
  }
}

/* ------------------------------------------------------- pipeline (funnel)

   Uses styles.css tokens only — no colours are defined here. Per DESIGN.md the
   palette stays restrained: the rose ramp carries weighted value, the attention
   orange is reserved for deals needing action, and nothing else is saturated.
   There is exactly one hero metric on the page, and it is the only figure
   allowed to be large. */

.pipe-headline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.1rem 1.25rem;
  margin-bottom: 0.9rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
}

.pipe-headline-main { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }

.pipe-headline-label {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.pipe-headline-value {
  font-size: 2.35rem;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.pipe-headline-gross { font-size: 0.8125rem; color: var(--muted); font-variant-numeric: tabular-nums; }

.pipe-headline-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: var(--muted);
}

/* Coverage sits next to the headline, not in a footnote: with ~85% of deals
   carrying no amount it is the number that decides whether the big one means
   anything. */
.pipe-coverage { display: flex; align-items: center; gap: 0.5rem; font-variant-numeric: tabular-nums; }
.pipe-coverage b { color: var(--ink); }

.pipe-coverage-bar {
  width: 78px; height: 5px;
  background: var(--surface-2);
  border-radius: 999px;
  overflow: hidden;
}
.pipe-coverage-bar > span { display: block; height: 100%; background: var(--primary); }

.pipe-synced { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }

/* "next in 3h" — quieter than the last-synced time it sits beside, because it
   answers a follow-up question rather than the first one. */
.pipe-next { color: var(--faint); }

/* A failed scheduled sync has to be visible without being alarming: the numbers
   on the page are still real, they are just older than they should be. */
.pipe-sync-failed {
  color: var(--attn-strong);
  font-weight: 600;
  cursor: help;
}

.btn-xs { font-size: 0.7rem; padding: 0.15rem 0.5rem; }

/* ------------------------------------------------------------- filter rail */

.pipe-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem;
  padding: 0.7rem 0.85rem;
  margin-bottom: 1rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
}

.pipe-filter { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.pipe-filter > span { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--faint); }

.pipe-filter select,
.pipe-filter input {
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.28rem 0.45rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  max-width: 200px;
}
.pipe-filter-search input { min-width: 190px; }
.pipe-clear { align-self: flex-end; margin-left: auto; }

/* ------------------------------------------------------- filter dropdown */

/* The filter bar is sticky, so an absolutely-positioned popover inside it
   would be clipped by nothing and painted under the funnel below. It gets a
   stacking context of its own, above the bar it hangs from. */
.ms {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}
.ms.is-open { z-index: calc(var(--z-sticky) + 1); }

.ms-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--faint);
}

/* Sized to be hit without aiming — the old native selects were 0.28rem tall
   and sat in a row of nine, which made the bar a target-practice exercise. */
.ms-button {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 150px;
  max-width: 230px;
  padding: 0.46rem 0.6rem;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.ms-button:hover { border-color: var(--muted); background: var(--surface); }
.ms-button:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

/* An active filter has to be legible at a glance. A bar of nine identical
   controls, one of which is quietly excluding most of the pipeline, is how
   somebody screenshots a funnel and misreads their own business. */
.ms.is-active .ms-button {
  border-color: var(--primary);
  background: var(--primary-tint);
  color: var(--primary-strong);
  font-weight: 600;
}

.ms-value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ms-badge {
  flex: none;
  min-width: 1.15rem;
  padding: 0 0.3rem;
  border-radius: 999px;
  background: var(--primary);
  color: white;
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1.15rem;
  text-align: center;
}

.ms-caret {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  opacity: 0.5;
  transition: transform 0.15s ease;
}
.ms.is-open .ms-caret { transform: translateY(1px) rotate(-135deg); }

.ms-pop {
  position: absolute;
  top: calc(100% + 0.3rem);
  left: 0;
  min-width: 260px;
  max-width: 340px;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-m);
  box-shadow: 0 12px 28px -12px oklch(0.21 0.015 343 / 0.28), 0 2px 6px -2px oklch(0.21 0.015 343 / 0.12);
  overflow: hidden;
  animation: ms-in 0.12s ease-out;
}

/* Edge collisions are handled by settleMultiselect(), which measures. A CSS
   guess cannot: the bar wraps, so whether a control is near an edge depends on
   the viewport, not on its position in the source. */

@keyframes ms-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ms-pop { animation: none; }
  .ms-caret { transition: none; }
}

.ms-search { padding: 0.5rem; border-bottom: 1px solid var(--line); }
.ms-search input {
  width: 100%;
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.4rem 0.5rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
.ms-search input:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

/* Roughly eight rows before it scrolls: enough that most lists show whole,
   short enough that Owner (which grows with the sales team) cannot cover the
   funnel it is filtering. */
.ms-list {
  list-style: none;
  margin: 0;
  padding: 0.25rem;
  max-height: 19rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* A headed section inside the list — see the grouping note in
   public/shared/multiselect.js for why this is a real `role="group"` rather
   than a styled row between the options.

   The heading is STICKY. These lists scroll at 19rem, and a group heading that
   scrolls away leaves the reader looking at three names with no idea whose
   team they are on — which is the entire question the grouping answers. It
   sits above the options rather than indenting them: an indent would cost
   horizontal room in a 15rem popover and buy less. */
/* A team heading that selects the whole team. Same position and weight as
   the plain heading, so the list reads the same; the action appears on the
   right, and the heading takes the ink colour when every member is on. */
.ms-group-toggle {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  width: 100%;
  border: 0;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.ms-group-toggle:hover, .ms-group-toggle:focus-visible { color: var(--ink); }
.ms-group-toggle.is-on { color: var(--ink); }
.ms-group-action { color: var(--client); font-weight: 500; text-transform: none; letter-spacing: 0; }
.ms-group { display: block; }
.ms-group + .ms-group { border-top: 1px solid var(--line); margin-top: 0.25rem; padding-top: 0.25rem; }
.ms-group-list { list-style: none; margin: 0; padding: 0; }
.ms-group-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: block;
  padding: 0.3rem 0.5rem 0.2rem;
  background: var(--bg);
  color: var(--faint);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: lowercase;
}

.ms-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.42rem 0.5rem;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: var(--radius-s);
  cursor: pointer;
  text-align: left;
}
.ms-option:hover { background: var(--surface); }
.ms-option:focus-visible {
  outline: none;
  background: var(--surface);
  box-shadow: inset 0 0 0 2px var(--primary-tint);
}
.ms-option.is-on { color: var(--primary-strong); font-weight: 600; }

.ms-check {
  flex: none;
  width: 1rem;
  height: 1rem;
  border: 1.5px solid var(--line-strong);
  border-radius: 4px;
  background: var(--bg);
  position: relative;
}
.ms-option.is-on .ms-check { background: var(--primary); border-color: var(--primary); }
/* The tick is drawn rather than typed: a ✓ glyph shifts baseline between
   fonts and lands off-centre in the box on Windows. */
.ms-option.is-on .ms-check::after {
  content: "";
  position: absolute;
  left: 0.28rem;
  top: 0.1rem;
  width: 0.22rem;
  height: 0.45rem;
  border-right: 2px solid white;
  border-bottom: 2px solid white;
  transform: rotate(42deg);
}

.ms-option-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tabular figures so the counts form a column instead of jittering. */
.ms-count {
  flex: none;
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
  color: var(--faint);
}
.ms-option.is-on .ms-count { color: var(--primary); }

.ms-empty { padding: 0.8rem 0.6rem; font-size: 0.78rem; color: var(--faint); text-align: center; }

.ms-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.4rem 0.55rem;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.ms-foot-count { font-size: 0.7rem; color: var(--faint); }
.ms-clear {
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.2rem 0.45rem;
  color: var(--primary);
  background: none;
  border: 0;
  border-radius: var(--radius-s);
  cursor: pointer;
}
.ms-clear:hover:not(:disabled) { background: var(--primary-tint); }
.ms-clear:disabled { color: var(--faint); cursor: default; }

/* On a narrow screen the bar wraps to one control per row; a popover pinned
   to its button would then hang off the side. */
@media (max-width: 640px) {
  .ms-button { max-width: none; width: 100%; }
  .ms-pop { left: 0; right: 0; min-width: 0; max-width: none; }
}

/* ----------------------------------------------------------------- funnel */

.funnel-strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0.35rem 0 0;
}

.funnel-col { min-width: 0; }

.funnel-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  width: 100%;
  padding: 0.6rem 0.65rem 0.7rem;
  font: inherit;
  text-align: left;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}
.funnel-btn:hover { border-color: var(--line-strong); background: var(--surface); }
.funnel-col.active .funnel-btn { border-color: var(--primary); background: var(--primary-tint); }

.funnel-label { font-weight: 600; font-size: 0.8125rem; }
.funnel-prob { font-size: 0.7rem; color: var(--faint); font-variant-numeric: tabular-nums; }

/* Two bars of EQUAL width: solid rose is weighted value, the muted one beside
   it is deal count. Both are scaled against the largest phase on their own
   measure, so a phase that is busy but worth little reads as a tall grey column
   next to a short rose one — which is the single most useful thing this strip
   can tell you, and the reason both are drawn rather than just the money.

   They were briefly a wide fill with a narrow outline "ghost" behind it. At
   155px against 8px the ghost was invisible and the fill read as a solid
   block rather than a measurement, so the comparison the chart exists to make
   could not be made. Equal widths are what make two bars comparable. */
.funnel-bar {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  width: 100%;
  height: 52px;
  margin: 0.4rem 0 0.35rem;
  border-bottom: 1px solid var(--line);
}
.funnel-fill,
.funnel-ghost {
  flex: 1;
  min-height: 2px;
  border-radius: 2px 2px 0 0;
}
.funnel-fill { background: var(--primary); }
.funnel-ghost { background: var(--surface-2); border: 1px solid var(--line-strong); border-bottom: 0; }

/* Legend, so the two bars do not need a caption per column. */
.funnel-legend { display: inline-flex; align-items: center; gap: 0.7rem; font-size: 0.7rem; color: var(--faint); }
.funnel-legend span { display: inline-flex; align-items: center; gap: 0.3rem; }
.funnel-legend i {
  width: 9px; height: 9px; border-radius: 2px; font-style: normal;
}
.funnel-legend .swatch-value { background: var(--primary); }
.funnel-legend .swatch-count { background: var(--surface-2); border: 1px solid var(--line-strong); }

.funnel-value { font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.funnel-count { font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.funnel-sub { font-size: 0.68rem; color: var(--faint); font-variant-numeric: tabular-nums; }

.funnel-alert {
  margin-top: 0.3rem;
  padding: 0.05rem 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  color: #fff;
  background: var(--attn);
  border-radius: 999px;
}

/* -------------------------------------------------------- closed / reconcile */

.pipe-closed {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.65rem 0 1.1rem;
  font-size: 0.75rem;
  color: var(--muted);
}
.pipe-closed-label { text-transform: uppercase; letter-spacing: 0.04em; color: var(--faint); }

.pipe-closed-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.2rem 0.55rem;
  font: inherit;
  font-size: 0.75rem;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.pipe-closed-chip:hover { border-color: var(--line-strong); }
.pipe-closed-chip.active { border-color: var(--primary); color: var(--ink); background: var(--primary-tint); }
.pipe-closed-chip b { color: var(--ink); }
.pipe-closed-chip span { color: var(--faint); }

.pipe-closed-recon { margin-left: auto; color: var(--faint); font-variant-numeric: tabular-nums; }
.pipe-closed-recon.bad { color: var(--attn-strong); font-weight: 600; }

/* ------------------------------------------------------------- deal lists */

.pipe-phases { display: flex; flex-direction: column; gap: 0.9rem; margin-bottom: 1rem; }

.deal-list { list-style: none; margin: 0; padding: 0; }

.deal-row + .deal-row { border-top: 1px solid var(--line); }

/* The row is a flex track holding the navigation link and, when the deal
   needs action, the HubSpot button. They are SIBLINGS: the button cannot be
   inside the link, because an <a> inside an <a> is invalid and browsers
   unnest it, which tears the row apart. */
.deal-row { display: flex; align-items: center; gap: 0.4rem; }
/* Every row body fills the track, whichever kind it is: the funnel's deal link,
   the presales panel's project link, and the static span an unlinked tracked
   development renders instead of a dead link. Naming only the first left the
   other two shrink-wrapped to their text. */
.deal-row > a,
.deal-row > .deal-row-static { flex: 1; min-width: 0; }
.deal-row > .btn-hubspot { flex: none; margin-right: 0.2rem; }

.deal-row > a {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto 150px;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0.2rem;
  text-decoration: none;
  color: inherit;
}
.deal-row > a:hover { background: var(--surface); }

/* A tracked development with no project linked to it yet has nowhere to click
   through to. It keeps the row's grid so it stays aligned with its neighbours,
   and drops the hover so it does not advertise an interaction it cannot honour. */
.deal-row > .deal-row-static {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto 150px;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0.2rem;
  color: var(--muted);
}

/* The exclamation the brief asks for. It occupies a column of its own so
   alerted and clear rows stay aligned — a marker that shifts the text is a
   marker people stop seeing. */
.deal-flag {
  justify-self: center;
  font-weight: 700;
  color: var(--attn);
  font-size: 0.95rem;
}
.deal-row.high .deal-flag { color: var(--attn-strong); }

.deal-main { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.deal-name { font-weight: 600; font-size: 0.8125rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Which development this deal belongs to. Phase columns interleave deals
   from every tracked development by default — that was the actual gap: a
   row had a name, a unit, an origin, a stage, but nothing said which
   pipeline it was on. Lives INSIDE .deal-name's nowrap/ellipsis span
   (rather than as a flex sibling) so a long buyer name still truncates on
   its own rather than the tag being squeezed or the row needing a second
   line. One flat colour for every development rather than one colour per
   development: the tag's job is "this is a category label", not to also
   double as a legend — that would stop scaling the moment a fourth
   development is added to config/pipelines.json. */
.deal-development {
  display: inline-block;
  margin-right: 0.35rem;
  padding: 0.04rem 0.32rem;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--client);
  background: var(--client-tint);
  border-radius: var(--radius-s);
  vertical-align: 1px;
}
.deal-sub { font-size: 0.72rem; color: var(--muted); }
.deal-override { color: var(--primary-strong); font-weight: 600; }

/* Lead age. Inherits .deal-sub's size and colour — it is one more fact on that
   line, not a callout — but carries a dotted underline to advertise that the
   exact timestamp is in the tooltip, since "4d ago" otherwise looks like all
   the precision there is. */
.deal-created {
  border-bottom: 1px dotted var(--line-strong);
  cursor: help;
}

.deal-reasons { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.15rem; }
.deal-reason {
  padding: 0.05rem 0.4rem;
  font-size: 0.68rem;
  border-radius: var(--radius-s);
  color: var(--muted);
  background: var(--surface-2);
}
.deal-reason.sev-high { color: var(--attn-strong); background: var(--attn-tint); }

.deal-value { text-align: right; font-variant-numeric: tabular-nums; }
.deal-value b { display: block; font-size: 0.875rem; }
.deal-basis { font-size: 0.68rem; color: var(--faint); }

.deal-stage { font-size: 0.72rem; color: var(--muted); text-align: right; }

.attn-text { color: var(--attn-strong); }
.faint { color: var(--faint); }

.pipe-more { margin-top: 0.5rem; }

/* A phase column mid-expansion: rows already fetched stay on screen while
   the next page loads, rather than the whole panel flashing back to a
   spinner — the same "keep the last good render" instinct loadPipeline()
   already applies to the funnel as a whole.  .pipe-phase-loading is only
   for the FIRST page of an expansion, when there is nothing to keep on
   screen yet. */
.pipe-phase-loading { margin: 0.4rem 0 0; font-size: 0.78rem; color: var(--faint); }

.pipe-phase-foot { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.pipe-phase-foot button:disabled { opacity: 0.6; cursor: default; }

/* ------------------------------------------------------------- breakdowns */

.pipe-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 0.9rem;
  margin-bottom: 1rem;
}

/* ------------------------------------------------------------ donut

   The part-to-whole panels (donutPanelHtml in core.js). Five slice hues,
   validated for colour-blind separation including the wrap from the last
   slice back to the first, plus one neutral for "Other" and one lighter
   neutral for the unstated bucket. None is the deal board's red, orange or
   green, so no slice reads as a status. Identity never rests on colour: the
   legend beside the ring names every slice with its count and share. */
.donut-panel {
  --donut-s1: #2a78d6;
  --donut-s2: #1baf7a;
  --donut-s3: #eda100;
  --donut-s4: #e87ba4;
  --donut-s5: #4a3aa7;
  --donut-other: oklch(0.62 0.01 343);
  --donut-unstated: oklch(0.86 0.005 343);
}
.donut-body {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  flex-wrap: wrap;
}
.donut { width: 132px; height: 132px; flex-shrink: 0; overflow: visible; }
.donut-arc {
  fill: none;
  stroke-width: 16;
  transition: stroke-width var(--t-fast) var(--ease-out);
  cursor: default;
}
/* The hovered slice thickens outward a little — the tooltip says the rest. */
.donut-arc:hover { stroke-width: 20; }
.donut-total {
  font-size: 22px;
  font-weight: 600;
  fill: var(--ink);
  font-feature-settings: "tnum";
  letter-spacing: -0.02em;
}
.donut-noun { font-size: 10px; fill: var(--faint); }

.donut-panel .tone-s1 { stroke: var(--donut-s1); background: var(--donut-s1); }
.donut-panel .tone-s2 { stroke: var(--donut-s2); background: var(--donut-s2); }
.donut-panel .tone-s3 { stroke: var(--donut-s3); background: var(--donut-s3); }
.donut-panel .tone-s4 { stroke: var(--donut-s4); background: var(--donut-s4); }
.donut-panel .tone-s5 { stroke: var(--donut-s5); background: var(--donut-s5); }
.donut-panel .tone-other { stroke: var(--donut-other); background: var(--donut-other); }
.donut-panel .tone-unstated { stroke: var(--donut-unstated); background: var(--donut-unstated); }

.donut-legend {
  flex: 1;
  min-width: 180px;
  border-collapse: collapse;
  font-size: 0.78125rem;
}
.donut-legend th,
.donut-legend td { padding: 0.28rem 0; border-bottom: 1px solid var(--line); vertical-align: baseline; }
.donut-legend tr:last-child th,
.donut-legend tr:last-child td { border-bottom: 0; }
.donut-legend th { font-weight: 400; text-align: left; color: var(--ink); }
.donut-legend td { text-align: right; font-feature-settings: "tnum"; white-space: nowrap; }
.donut-n { color: var(--ink); padding-left: 0.75rem !important; }
.donut-pct { color: var(--faint); padding-left: 0.6rem !important; width: 3.2em; }
.donut-key {
  display: inline-block;
  width: 0.6rem;
  height: 0.6rem;
  margin-right: 0.45rem;
  border-radius: 2px;
  vertical-align: 0.02em;
}

.bar-list { list-style: none; margin: 0; padding: 0; }

.bar-row {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) 62px 42px;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.3rem 0.15rem;
  font: inherit;
  font-size: 0.75rem;
  text-align: left;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: var(--radius-s);
  cursor: pointer;
}

/* ------------------------------------------------------------ deal board

   The HubSpot-style Kanban (public/shared/deal-board.js). One column per
   stage, one card per deal, most urgent first.

   The traffic light is STATE, which is what the restrained palette reserves
   saturated colour for. Orange is --attn, this app's attention colour
   everywhere else; red and green are added here and used nowhere else. The
   colour is never alone: every card also says "Overdue 12d", "Act now" or
   "On track" in words.

   The board scrolls SIDEWAYS INSIDE THE PANEL, never the page: a pipeline has
   as many stages as the CRM says, and a page that scrolled horizontally to
   fit them would drag the filter bar and every chart along with it. */
.deal-board-panel {
  --urg-red: oklch(0.55 0.2 27);
  --urg-red-tint: oklch(0.965 0.02 27);
  --urg-orange: var(--attn);
  --urg-orange-tint: var(--attn-tint);
  --urg-green: oklch(0.58 0.13 155);
  --urg-green-tint: oklch(0.97 0.02 155);
  --urg-lost: var(--unknown);
}

.deal-board-head { flex-wrap: wrap; justify-content: flex-start; }
.deal-board-head .panel-sub { flex: 1; }
.deal-tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.deal-tab-count { opacity: 0.7; margin-left: 0.2rem; font-feature-settings: "tnum"; }

.deal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.1rem;
  margin: -0.35rem 0 0.75rem;
  color: var(--muted);
  font-size: 0.75rem;
  font-feature-settings: "tnum";
}
.deal-legend > span { display: inline-flex; align-items: center; gap: 0.35rem; }

.deal-board {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(248px, 272px);
  gap: 0.6rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  /* Without this the board's full width (eleven columns is ~3000px) becomes
     its min-content size and pushes <main> wider than the viewport. Contained,
     it takes the panel's width and scrolls inside it. */
  contain: inline-size;
  padding-bottom: 0.5rem;
  scroll-snap-type: x proximity;
}
.deal-board:focus-visible { outline: 2px solid var(--client); outline-offset: 2px; }

.deal-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 0.5rem;
  border-radius: var(--radius-m);
  background: var(--surface);
  scroll-snap-align: start;
  /* Clears the sticky topbar when a collapsed column is scrolled back into
     view (settleBoardToggle). */
  scroll-margin-top: 4.5rem;
}
.deal-col.is-empty { background: transparent; border: 1px dashed var(--line-strong); }

.deal-col-head {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.15rem 0.25rem 0.5rem;
}
.deal-col-head h3 {
  flex: 1;
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--muted);
}
.deal-col-count { font-size: 0.8125rem; font-weight: 600; font-feature-settings: "tnum"; }
.deal-col-empty { margin: 0.25rem; color: var(--faint); font-size: 0.75rem; }
/* The stage's weighted total, pinned to the foot of the column. */
.deal-col-value {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 0.5rem;
  align-items: baseline;
  margin-top: auto;
  padding: 0.55rem 0.25rem 0.1rem;
  border-top: 1px solid var(--line-strong);
  cursor: help;
}
.deal-col-value-label { color: var(--faint); font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.deal-col-value-total { font-size: 0.9375rem; font-feature-settings: "tnum"; }
.deal-col-value-sub { grid-column: 1 / -1; color: var(--muted); font-size: 0.6875rem; font-feature-settings: "tnum"; }

.deal-pill {
  min-width: 1.35rem;
  padding: 0.05rem 0.35rem;
  border-radius: 999px;
  color: var(--bg);
  font-size: 0.6875rem;
  font-weight: 600;
  text-align: center;
  font-feature-settings: "tnum";
}
.deal-pill.urg-red { background: var(--urg-red); }
.deal-pill.urg-orange { background: var(--urg-orange-strong, var(--attn-strong)); }

.deal-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; align-content: start; }

/* An expanded column scrolls INSIDE itself, at a height that leaves the
   board on one screen. The scroll is contained so reaching the end of a
   column does not start scrolling the page, and the small right padding
   keeps the scrollbar off the cards' borders. */
.deal-cards.is-scroll {
  max-height: min(34rem, 62vh);
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  margin-right: -0.3rem;
  /* The bottom padding is the fade's height, so the last card scrolls clear
     of it and is never left half-transparent at the end of the list. */
  padding: 0.05rem 0.3rem 1.25rem 0;
  scroll-padding-block: 0.4rem;
  /* A soft fade at the foot says "there is more below" before anybody
     notices the scrollbar. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.25rem), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.25rem), transparent);
}

.deal-card {
  display: grid;
  /* minmax(0, …) so a long unit name ellipsises instead of widening the card. */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.2rem;
  width: 100%;
  padding: 0.55rem 0.65rem 0.6rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--urg-card, var(--line-strong));
  border-radius: var(--radius-s);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}
.deal-card:hover { border-color: var(--line-strong); border-left-color: var(--urg-card, var(--line-strong)); box-shadow: 0 1px 3px oklch(0.21 0.015 343 / 0.08); }
.deal-card:focus-visible { outline: 2px solid var(--client); outline-offset: 1px; }
/* Amount and weighted amount, one line under the unit. */
.deal-value { display: block; text-align: left; color: var(--muted); font-size: 0.75rem; font-feature-settings: "tnum"; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.deal-value strong { color: var(--ink); font-weight: 600; }
.deal-card.urg-red { --urg-card: var(--urg-red); }
.deal-card.urg-orange { --urg-card: var(--urg-orange); }
.deal-card.urg-green { --urg-card: var(--urg-green); }
.deal-card.urg-lost { --urg-card: var(--urg-lost); background: var(--surface); }

.deal-card-top { display: flex; align-items: flex-start; gap: 0.5rem; }
.deal-name {
  flex: 1;
  min-width: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.deal-state {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
  color: var(--muted);
  font-size: 0.6875rem;
  font-weight: 500;
  white-space: nowrap;
}
.deal-card.urg-red .deal-state { color: var(--urg-red); }
.deal-card.urg-orange .deal-state { color: var(--attn-strong); }

.deal-dot {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--urg-card, var(--line-strong));
}
.deal-dot.urg-red { background: var(--urg-red); }
.deal-dot.urg-orange { background: var(--urg-orange); }
.deal-dot.urg-green { background: var(--urg-green); }

.deal-unit { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deal-unit.is-empty { color: var(--faint); font-style: italic; }
.deal-meta { color: var(--faint); }
.deal-owner { color: var(--muted); }
.deal-reason {
  padding: 0.25rem 0.4rem;
  border-radius: var(--radius-s);
  background: var(--surface-2);
  color: var(--ink);
}
.deal-card.urg-lost .deal-reason { background: var(--bg); }
.deal-next {
  margin-top: 0.15rem;
  padding-top: 0.3rem;
  border-top: 1px solid var(--line);
  font-weight: 600;
  line-height: 1.3;
}
/* Two things on one line, the second pushed right: the contact date and the
   owner, or the due date and the "suggested" tag. */
.deal-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  min-width: 0;
}
.deal-row > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deal-row > :last-child:not(:first-child) { flex-shrink: 0; }
.deal-tag {
  padding: 0 0.3rem;
  border: 1px dashed var(--line-strong);
  border-radius: 999px;
  color: var(--faint);
  font-size: 0.625rem;
  letter-spacing: 0.02em;
}
.deal-tag.is-manual { border-style: solid; border-color: var(--client); color: var(--client); }
/* A note's timing, offered. Indigo like a link rather than a status colour:
   it is something to look at, not a state the deal is in. */
.deal-hint {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--client);
  font-size: 0.6875rem;
}
.deal-due { color: var(--muted); font-feature-settings: "tnum"; }
.deal-card.urg-red .deal-due { color: var(--urg-red); font-weight: 500; }
.deal-card.urg-orange .deal-due { color: var(--attn-strong); font-weight: 500; }

/* The column's foot: "+35 more deals" when collapsed, "Collapse" when open.
   Always in the same place, full width and with a divider above it, so it
   reads as the end of the column rather than one more card. */
.deal-more {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.15rem 0.45rem;
  margin-top: 0.45rem;
  padding: 0.45rem 0.4rem 0.2rem;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--client);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: color var(--t-fast) var(--ease-out);
}
.deal-more:hover { color: var(--ink); }
.deal-more:focus-visible { outline: 2px solid var(--client); outline-offset: 2px; border-radius: var(--radius-s); }
.deal-more-count { color: var(--faint); font-weight: 400; }
/* An open column is lifted slightly, so it is clear which one is scrolling. */
.deal-col.is-open { box-shadow: inset 0 0 0 1px var(--line-strong); }

@media (max-width: 480px) {
  .deal-board { grid-auto-columns: minmax(232px, 84vw); }
}

/* The next action at the top of the lead record — the reason the card sat
   where it did on the board. */
.next-action {
  display: grid;
  gap: 0.15rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-s);
  border-left: 3px solid var(--line-strong);
  background: var(--surface);
}
.next-action.urg-red { border-left-color: oklch(0.55 0.2 27); background: oklch(0.965 0.02 27); }
.next-action.urg-orange { border-left-color: var(--attn); background: var(--attn-tint); }
.next-action.urg-green { border-left-color: oklch(0.58 0.13 155); }
.next-action-label { font-weight: 600; font-size: 0.8125rem; }
.next-action-due { font-size: 0.75rem; font-feature-settings: "tnum"; }
.next-action-src { color: var(--muted); font-size: 0.75rem; }

/* The follow-up form in the lead drawer. */
.follow-hint {
  display: grid;
  gap: 0.35rem;
  margin-bottom: 0.6rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-s);
  border-left: 3px solid var(--client);
  background: var(--client-tint);
  font-size: 0.78125rem;
}
.follow-hint q { color: var(--ink); font-style: italic; }
.follow-hint .btn { justify-self: start; }
.follow-form { display: grid; grid-template-columns: max-content 1fr; gap: 0.45rem 0.6rem; align-items: center; font-size: 0.78125rem; }
.follow-form label { color: var(--faint); }
.follow-form input {
  font: inherit;
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--bg);
  color: var(--ink);
  min-width: 0;
}
.follow-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.follow-meta { color: var(--faint); font-size: 0.75rem; }

.bar-row.static { cursor: default; }
.bar-row.muted { color: var(--faint); }
.bar-row:not(.static):not(:disabled):hover { background: var(--surface); }
.bar-row:disabled { cursor: default; color: var(--faint); }

.bar-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 6px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.bar-fill { display: block; height: 100%; background: var(--primary); }
.bar-fill.empty { background: var(--line-strong); }
.bar-value, .bar-count { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }

.panel-note { margin: 0.6rem 0 0; font-size: 0.72rem; color: var(--faint); line-height: 1.45; }

/* -------------------------------------------------------------- drill-down */

.deal-head .back-link {
  display: inline-block;
  margin-bottom: 0.35rem;
  font-size: 0.75rem;
  color: var(--muted);
  text-decoration: none;
}
.deal-head .back-link:hover { color: var(--ink); }

/* Alerts on one side, the way to act on them on the other. The button is
   pinned to the section rather than repeated per alert: three alerts on one
   deal still lead to one record. */
.deal-alerts {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.9rem;
}
.deal-alerts-list { display: flex; flex-direction: column; gap: 0.4rem; flex: 1; min-width: 0; }
.deal-alerts > .btn-hubspot { flex: none; margin-top: 0.1rem; }

@media (max-width: 640px) {
  .deal-alerts { flex-direction: column; align-items: stretch; }
}

.deal-alert {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.5rem 0.7rem;
  font-size: 0.78rem;
  border-radius: var(--radius-m);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
}
.deal-alert.sev-high {
  border-color: var(--attn);
  background: var(--attn-tint);
  color: var(--attn-strong);
}
.deal-alert b { color: inherit; }

.deal-layout {
  display: grid;
  grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
  gap: 0.9rem;
  margin-bottom: 1rem;
}

.fact-list { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.3rem 0.7rem; margin: 0; font-size: 0.78rem; }
.fact-list dt { color: var(--faint); white-space: nowrap; }
.fact-list dd { margin: 0; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }

.deal-override-form { margin-top: 0.9rem; padding-top: 0.7rem; border-top: 1px solid var(--line); }
.deal-override-form label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.72rem; color: var(--faint); }
.deal-override-form select {
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.28rem 0.45rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
}

.deal-timeline { list-style: none; margin: 0; padding: 0; }

.tl-entry { border-left: 2px solid var(--line); padding-left: 0.7rem; }
.tl-entry.dir-received { border-left-color: var(--client); }
.tl-entry.dir-sent { border-left-color: var(--primary); }

.tl-head {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.45rem 0.2rem;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
.tl-head:hover { background: var(--surface); }

.tl-dir { color: var(--muted); }
.tl-body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.tl-subject { font-size: 0.8125rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-meta { font-size: 0.7rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-when { font-size: 0.7rem; color: var(--faint); white-space: nowrap; font-variant-numeric: tabular-nums; }
.tl-channel {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--faint);
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--line);
  border-radius: 999px;
}

.tl-detail { padding: 0 0.2rem 0.6rem; }
.tl-detail pre {
  margin: 0;
  padding: 0.6rem 0.7rem;
  max-height: 340px;
  overflow: auto;
  font-size: 0.75rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--muted);
  background: var(--surface);
  border-radius: var(--radius-m);
}

.empty-state.small { padding: 1.2rem; font-size: 0.8125rem; }

@media (max-width: 900px) {
  .funnel-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .deal-layout { grid-template-columns: minmax(0, 1fr); }
  .deal-row > a { grid-template-columns: 18px minmax(0, 1fr) auto; }
  .deal-stage { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .funnel-btn { transition: none; }
}

/* ------------------------------------------------------ open in HubSpot */

/* Shown only where a deal is stalled or waiting on somebody. Every alert on
   this dashboard is a prompt to do something, and all of that happens in the
   CRM — so the alert and the way to act on it belong together. */
.btn-hubspot {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.7rem;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  color: var(--ink);
  background: var(--hubspot);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  transition: background 0.12s ease, transform 0.06s ease;
}
.btn-hubspot:hover { background: var(--hubspot-strong); }
.btn-hubspot:active { transform: translateY(1px); }
.btn-hubspot:focus-visible {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px var(--hubspot-tint);
}

.btn-hubspot-arrow { font-size: 0.85em; opacity: 0.75; }

/* In a phase column the row is already dense, so the button keeps the brand
   colour and loses everything else it can. */
.btn-hubspot.is-compact { padding: 0.3rem 0.5rem; font-size: 0.7rem; }

/* Under about 900px the phase lists are tight enough that a 130px button
   squeezes the deal name to nothing. The label goes, the colour and the
   arrow stay, and the accessible name is on aria-label either way. */
@media (max-width: 900px) {
  .btn-hubspot.is-compact .btn-hubspot-text { 
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .btn-hubspot.is-compact { padding: 0.3rem 0.42rem; }
}

@media (prefers-reduced-motion: reduce) {
  .btn-hubspot { transition: none; }
  .btn-hubspot:active { transform: none; }
}
/* ------------------------------------------------------------------ shares

   Three surfaces, and they do not share a context:

     .share-dialog*   the staff-facing publish/revoke dialog, inside the app.
     .share-gate*     the password prompt a visitor meets, outside it.
     .share-head/foot the frame around a published report.

   The last two are served by public/share.html, which loads this stylesheet
   and styles.css and no other CSS. So anything the report itself needs is
   already defined above — panels, funnel, bar lists, tiles — and only the
   frame is new. That is deliberate: a share link renders the same markup as
   the signed-in page (see public/share.js), and a second set of panel styles
   would let the two drift visually even while the numbers agreed. */

/* --- the page head's action row, shared by both pages that can publish --- */

.page-head-line { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.proj-head-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* A funnel column or closed chip on a read-only report. Same box, none of the
   affordances — no pointer, no hover lift, no focus ring, because there is
   nothing behind it to reach. */
.funnel-btn.static,
.pipe-closed-chip.static { cursor: default; }
.funnel-btn.static:hover,
.pipe-closed-chip.static:hover { background: inherit; border-color: var(--line); }

/* ------------------------------------------------------------ share dialog */

.share-dialog {
  width: min(44rem, calc(100vw - 2rem));
  max-height: min(85vh, 54rem);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-l);
  background: var(--bg);
  color: var(--ink);
  overflow: hidden;
}

.share-dialog::backdrop { background: oklch(0.21 0.015 343 / 0.4); }

.share-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem 1.3rem 0.9rem;
  border-bottom: 1px solid var(--line);
}

.share-dialog-head h2 { font-size: 1.0625rem; margin: 0; letter-spacing: -0.01em; }
.share-dialog-head .sub { color: var(--muted); font-size: 0.8125rem; margin: 0.2rem 0 0; }

.share-dialog-body {
  padding: 1.1rem 1.3rem 1.3rem;
  overflow-y: auto;
  max-height: calc(85vh - 4.5rem);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.share-dialog-body .attention { border-top: 0; border-radius: var(--radius-m); padding: 0.9rem 1rem 1rem; }

.share-dialog-body section > .panel-note { margin: 0 0 0.9rem; }

.share-subhead {
  font-size: 0.71875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--faint);
  margin: 0 0 0.7rem;
}

.share-list { list-style: none; margin: 0 0 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

.share-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--surface);
}

/* A revoked or expired link is kept in the list — it is the record that the
   link existed, which is the question somebody asks after it has been passed
   on — but it must not read as one you could still send. */
.share-row.is-dead { opacity: 0.6; background: var(--surface-2); }

.share-row-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 0.3rem; }
.share-row-title { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; font-size: 0.875rem; font-weight: 600; }
.share-row-sub { font-size: 0.75rem; color: var(--muted); }
.share-row-actions { display: flex; gap: 0.4rem; flex: none; }

/* The link, in a readonly input rather than as text: it is long, it is meant
   to be selected whole, and an input is the one element that does that on a
   single click in every browser. */
.share-row input {
  width: 100%;
  font: inherit;
  font-size: 0.75rem;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius-s);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--muted);
}

.refchip.tone-attn {
  color: var(--attn-strong);
  background: var(--attn-tint);
  border-color: color-mix(in oklab, var(--attn) 35%, white);
}

/* --- the publish form --- */

.share-form { display: flex; flex-direction: column; gap: 0.9rem; margin: 0; }

.field { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.71875rem; font-weight: 600; color: var(--muted); }
.field em { font-style: normal; font-weight: 400; color: var(--faint); }

.field input,
.field select {
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--ink);
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius-s);
  border: 1px solid var(--line-strong);
  background: var(--bg);
  width: 100%;
}

.share-password { display: flex; gap: 0.5rem; align-items: stretch; }
.share-password input { flex: 1; min-width: 0; font-feature-settings: "tnum"; }
.share-password .btn { flex: none; }

/* The disclosure toggle. Given more room than a checkbox normally gets,
   because the sentence beside it is the decision — a tick this size with a
   three-word label is how buyer names end up in a forwarded link. */
.share-check {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--surface);
  font-size: 0.8125rem;
  cursor: pointer;
}

.share-check input { margin-top: 0.15rem; flex: none; }
.share-check b { display: block; font-size: 0.8125rem; }
.share-check em { display: block; font-style: normal; color: var(--muted); font-size: 0.75rem; margin-top: 0.15rem; }

/* -------------------------------------------------------- the shared report */

.share-body { background: var(--bg); }

.share-main { max-width: 1060px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }

/* The password prompt. Centred and alone — there is nothing else on this page
   to look at, and no nav to anchor it against. */
.share-gate { display: flex; align-items: center; justify-content: center; min-height: 70vh; }

.share-gate-card {
  width: min(24rem, 100%);
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.6rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
}

.share-gate-card h1 { font-size: 1.1875rem; margin: 0; letter-spacing: -0.01em; }
.share-gate-card .sub { color: var(--muted); font-size: 0.8125rem; margin: -0.4rem 0 0; }
.share-gate-card .btn { justify-content: center; }

/* Reuses the sign-in screen's reasoning: the palette has no danger colour, so
   a failed unlock borrows the attention orange rather than inventing one. */
.share-gate-error {
  margin: 0;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-s);
  background: var(--attn-tint);
  color: var(--attn-strong);
  font-size: 0.8125rem;
}

.share-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding-bottom: 1.1rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--line);
}

.share-eyebrow {
  margin: 0 0 0.3rem;
  font-size: 0.71875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--faint);
}

.share-head h1 { font-size: 1.5625rem; margin: 0; letter-spacing: -0.015em; }

/* The development report carries its own heading, so the banner above it
   shrinks to a single line rather than repeating the name. */
.share-head.is-slim { align-items: center; padding-bottom: 0.7rem; margin-bottom: 1.2rem; }
.share-head.is-slim .share-eyebrow { margin: 0; }
.share-fresh { margin: 0; font-size: 0.8125rem; color: var(--muted); }

/* What this link left out, said before the panels rather than inside them. A
   reader who knows exactly which section is missing can trust the rest. */
.share-note {
  margin: 0 0 1.5rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--muted);
  font-size: 0.8125rem;
}

.share-foot {
  margin-top: 2.5rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 0.75rem;
}

.share-foot p { margin: 0 0 0.35rem; }

@media (max-width: 640px) {
  .share-row { flex-direction: column; }
  .share-row-actions { width: 100%; }
  .share-main { padding: 1.25rem 1rem 3rem; }
}

/* ------------------------------------------- development filter bar (range) */

/* The controls for the per-development report.

   Built from the same pieces as the funnel's filter rail — `.chip-btn`, the
   multiselect popover, the one-pixel `--line` border — rather than a new
   visual language. PRODUCT.md names "SaaS analytics dashboards (hero metrics,
   gradient cards, KPI confetti)" as the thing this tool must not become, and a
   date picker is exactly where that drift starts: a full-width tinted bar with
   its own typography would be the first thing on the page and would announce a
   different product. This sits in the page's existing rhythm and takes up the
   height of two rows of chips.

   STICKY, pinned under the top bar (public/shared/sticky.js). The report is
   long — a board, a funnel, three charts — and the filters narrow all of it,
   so a reader adjusting them halfway down should not have to scroll back up
   to do it. Pinned, the bar compacts: the note goes, the controls tighten,
   and it takes one or two rows instead of four. */
.filter-bar {
  --filter-gap: 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.75rem 0.9rem;
  margin-bottom: var(--filter-gap);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  position: sticky;
  top: var(--sticky-top, 0px);
  /* Under the top bar's own menus, over every panel. */
  z-index: calc(var(--z-sticky) - 1);
  transition: box-shadow var(--t-fast) var(--ease-out);
}

/* Pinned: one tight band. The period chips and the dropdowns share rows, the
   explanatory note is hidden (it is still there at the top of the page), and
   a soft shadow separates the bar from the report scrolling beneath it. */
.filter-bar.is-stuck {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.45rem 0.9rem;
  padding: 0.45rem 0.7rem;
  border-color: var(--line-strong);
  box-shadow: 0 6px 16px -10px oklch(0.21 0.015 343 / 0.28);
}
.filter-bar.is-stuck .filter-note { display: none; }
.filter-bar.is-stuck .range-bar { gap: 0.35rem 0.5rem; }
.filter-bar.is-stuck .range-presets { gap: 0.25rem; }
.filter-bar.is-stuck .chip-btn { padding: 0.22rem 0.6rem; }
.filter-bar.is-stuck .range-note { margin-left: 0; }
.filter-bar.is-stuck .filter-controls { gap: 0.45rem; }
.filter-bar.is-stuck .ms { gap: 0.1rem; }
.filter-bar.is-stuck .ms-label { font-size: 0.625rem; }
.filter-bar.is-stuck .ms-button { min-width: 124px; padding: 0.3rem 0.5rem; font-size: 0.75rem; }

@media (max-width: 640px) {
  /* On a phone even two rows is a lot of screen: the resolved-dates line goes
     too, and the chips scroll sideways instead of wrapping. */
  .filter-bar.is-stuck .range-note { display: none; }
  /* Both rows are contained: their full content width would otherwise count
     as the page's minimum width and push it sideways. */
  .filter-bar.is-stuck .range-bar { width: 100%; }
  .filter-bar.is-stuck .range-presets {
    flex-wrap: nowrap;
    width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
    contain: inline-size;
  }
  .filter-bar.is-stuck .range-presets .chip-btn { flex-shrink: 0; white-space: nowrap; }
  /* The dropdowns share ONE row, each taking an equal slice, rather than
     wrapping two to a row. Not a scrolling row: an overflow container would
     clip the popovers that open beneath them. Long captions ellipsise; the
     full label is back the moment the bar unpins. */
  .filter-bar.is-stuck .filter-controls { flex-wrap: nowrap; width: 100%; contain: inline-size; }
  .filter-bar.is-stuck .ms { flex: 1 1 0; }
  .filter-bar.is-stuck .ms-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .filter-bar.is-stuck .ms-button { min-width: 0; width: 100%; padding: 0.3rem 0.4rem; }
  .filter-bar.is-stuck .ms-badge { display: none; }
}

.range-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
.range-presets { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0; }

.range-custom { display: flex; align-items: center; gap: 0.5rem; }
.range-custom.hidden { display: none; }
.range-custom label {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  color: var(--faint);
}
.range-custom input {
  font: inherit;
  font-size: 0.8125rem;
  color: var(--ink);
  padding: 0.24rem 0.4rem;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
}

/* The resolved period, spelled out. Every preset is a rolling window, so the
   label alone does not say which days — see the header of daterange.js. */
.range-note {
  margin: 0;
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--faint);
  font-feature-settings: "tnum";
}

.filter-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.6rem; }

.filter-note {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--faint);
  max-width: 68ch;
}

/* The period-over-period line on a tile.

   Deliberately the same muted grey as every other `.tile-sub`, with no arrow
   glyph and no green/red. A rise is not good news — a rise in losses is a rise
   — and colouring it would make that judgement for the reader on a page whose
   whole discipline is that it does not. */
.tile-delta {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.6875rem;
  line-height: 1.4;
  color: var(--faint);
  font-feature-settings: "tnum";
}

@media (max-width: 640px) {
  .range-note { margin-left: 0; width: 100%; }
  .range-custom { width: 100%; }
  .range-custom input { flex: 1; min-width: 0; }
}

/* ------------------------------------------------------- activity chart */

/* The daily line chart. See lineChartHtml in public/core.js for why it is
   hand-rolled SVG and what it refuses to draw.

   The SVG scales with its container and keeps its stroke widths, which is what
   `vector-effect="non-scaling-stroke"` in the markup is for — without it a
   1.6px line becomes 3px on a narrow pane and hairline on a wide one. */
.line-chart {
  display: block;
  width: 100%;
  height: auto;
  margin: 0.2rem 0 0.4rem;
  overflow: visible;
}

.chart-tick {
  fill: var(--faint);
  font-size: 9px;
  font-family: var(--font);
  font-feature-settings: "tnum";
}

/* The legend carries each series' own total, so the chart is readable without
   hovering anything — and so a printed or screenshotted copy still says what
   the lines add up to. */
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1rem;
  margin: 0.1rem 0 0.35rem;
  font-size: 0.75rem;
  color: var(--muted);
}

.chart-key { display: inline-flex; align-items: center; gap: 0.4rem; }
.chart-key b { color: var(--ink); font-feature-settings: "tnum"; font-weight: 600; }
.chart-swatch { width: 22px; height: 10px; flex: none; }

/* A channel the CRM connection cannot read. Said in words rather than drawn as
   a flat zero line — see the comment on this state in development.js. */
.chart-legend-note { color: var(--faint); font-style: italic; }

/* A tighter tile row for the activity counts, so three channel totals do not
   read as another bank of headline figures directly under the eight that are. */
.tile-row-tight { margin-top: -0.35rem; }
.tile-row-tight .tile { padding-top: 0.6rem; padding-bottom: 0.6rem; }
.tile-row-tight .tile-value { font-size: 1.35rem; }

/* The table behind every chart, for a screen reader and for nobody else.

   `clip-path` plus a 1px box rather than `display:none`, which would take it
   out of the accessibility tree entirely and defeat the point.

   PUT IT ON A WRAPPER, NEVER ON THE <table> ITSELF. A table treats `height` as
   a minimum and ignores `overflow`, so a "1px" table grows to all of its rows
   anyway — 365 of them at `range=365d`, about 9,400px. `clip-path` hides that
   visually, but it still counts toward the page's scrollable area, which
   showed up as thousands of pixels of blank page below the report. A div
   respects both properties, so the rows are clipped inside it. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------- drawer */

/* The right-hand panel. A native <dialog> in the top layer — see the header of
   public/shared/drawer.js for why that rather than an <aside>.

   `margin: 0 0 0 auto` is what pins it right: a modal dialog is centred by
   `margin: auto`, and overriding only the horizontal half slides it to the
   edge while keeping the top-layer behaviour. */
.drawer {
  margin: 0 0 0 auto;
  height: 100dvh;
  max-height: none;
  max-width: none;
  /* `100%` rather than `100vw`: for a top-layer dialog both resolve against
     the viewport, and this one does not disagree with it when a browser is
     emulating a narrower device than the window it is drawn in. */
  width: min(30rem, 100%);
  padding: 0;
  border: 0;
  border-left: 1px solid var(--line);
  border-radius: 0;
  background: var(--bg);
  color: var(--ink);
  box-shadow: -18px 0 40px oklch(0.21 0.015 343 / 0.10);
  flex-direction: column;
  overflow: hidden;
}

/* ONLY WHEN OPEN, and the `[open]` is load-bearing rather than tidy.

   A <dialog> is `display: none` until it is shown; that UA rule is the only
   thing keeping a closed one out of the layout. This block used to set
   `display: flex` unconditionally, which overrode it — so the drawer element,
   which is created once and reused for every open (see public/shared/drawer.js),
   stayed laid out for the rest of the session after its first close.

   Off-screen and invisible, as long as the reader has animations on: the
   `translate: 100%` that parks it lives inside a
   `prefers-reduced-motion: no-preference` block. WITH REDUCED MOTION THERE IS
   NO TRANSLATE, so a closed drawer sat at `inset: 0` with `margin-left: auto`
   — a full-height panel covering the right 480px of the viewport, over the
   page, swallowing clicks and wheel events in that strip for anyone who had
   ever opened a drawer. The accessibility preference turned a hidden bug into
   a broken page, which is the wrong way round.

   `display` is already in the transition list with `allow-discrete`, so the
   slide-out still runs to completion before the switch back to `none`. */
.drawer[open] { display: flex; }

.drawer::backdrop { background: oklch(0.21 0.015 343 / 0.28); }

/* Slides in from the edge it is anchored to. `allow-discrete` is what lets a
   display:none → block transition animate at all; without it the panel appears
   instantly and only the backdrop fades, which reads as a rendering glitch. */
@media (prefers-reduced-motion: no-preference) {
  .drawer {
    transition: translate var(--t-med) var(--ease-out), overlay var(--t-med) allow-discrete,
                display var(--t-med) allow-discrete;
    translate: 0 0;
  }
  .drawer:not([open]) { translate: 100% 0; }
  @starting-style { .drawer[open] { translate: 100% 0; } }
  .drawer::backdrop { transition: opacity var(--t-med) var(--ease-out), display var(--t-med) allow-discrete; }
}

.drawer-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1rem 1.1rem 0.8rem;
  border-bottom: 1px solid var(--line);
}
.drawer-title { min-width: 0; flex: 1; }
.drawer-head h2 { margin: 0; font-size: 1.0625rem; letter-spacing: -0.01em; }
/* The heading takes focus when the drawer opens — see openDrawer. It is a
   programmatic target rather than a tab stop, so it must not draw a focus ring
   nobody asked for; every real control inside keeps its own. */
.drawer-head h2:focus { outline: none; }
.drawer-sub { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.8125rem; }

.drawer-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.drawer-close:hover { background: var(--surface-2); color: var(--ink); }

.drawer-body { flex: 1; overflow-y: auto; padding: 0.4rem 1.1rem 1.4rem; }
.drawer-foot { padding: 0.8rem 1.1rem; border-top: 1px solid var(--line); }

.drawer-section { padding: 0.9rem 0; border-bottom: 1px solid var(--line); }
.drawer-section:last-child { border-bottom: 0; }
.drawer-section h3 {
  margin: 0 0 0.5rem;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--faint);
  font-weight: 600;
}
.drawer-quiet { margin: 0; color: var(--muted); font-size: 0.8125rem; }
.drawer-lede { margin: 0.6rem 0 0.9rem; color: var(--muted); font-size: 0.8125rem; line-height: 1.5; }
.drawer-value { color: var(--muted); font-size: 0.75rem; }

/* The action items. Orange for the ones that are somebody waiting on us —
   `--attn` is this app's attention colour everywhere else and must not become
   a second thing here. */
.action-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.action {
  display: grid;
  gap: 0.15rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-s);
  background: var(--surface);
  border-left: 3px solid var(--line-strong);
}
.action-high { background: var(--attn-tint); border-left-color: var(--attn); }
.action-label { font-weight: 600; font-size: 0.8125rem; }
.action-why { color: var(--muted); font-size: 0.75rem; }

.drawer-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 0.9rem;
  margin: 0;
  font-size: 0.8125rem;
}
.drawer-facts dt { color: var(--faint); }
.drawer-facts dd { margin: 0; font-feature-settings: "tnum"; }

/* The contact timeline. Channel-coloured to match the activity graph's legend,
   so a line on the chart and a row in here are recognisably the same thing. */
.touch-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
.touch {
  display: grid;
  grid-template-columns: 1fr max-content;
  gap: 0.1rem 0.6rem;
  align-items: baseline;
  padding: 0.4rem 0 0.4rem 0.6rem;
  border-left: 2px solid var(--line-strong);
  font-size: 0.8125rem;
}
.touch-email { border-left-color: var(--client); }
.touch-call { border-left-color: var(--attn); }
.touch-whatsapp { border-left-color: var(--third); }
.touch-dir { color: var(--faint); font-weight: 400; }
.touch-what { font-weight: 500; }
.touch-subject { grid-column: 1 / -1; color: var(--muted); font-size: 0.75rem; }
.touch-when { color: var(--faint); font-size: 0.75rem; white-space: nowrap; }
.touch-note { border-left-color: var(--primary); }
.touch-task { border-left-color: var(--unknown); }
/* A deal's written summary. Label and text side by side like the facts
   above it; the citations are quiet chips so the sentence still reads as a
   sentence, and the hover tip carries the HubSpot id. */
.summary-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.45rem 0.9rem;
  margin: 0 0 0.6rem;
  font-size: 0.8125rem;
  line-height: 1.5;
}
.summary-list dt { color: var(--faint); }
.summary-list dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .summary-list { grid-template-columns: 1fr; gap: 0.1rem; }
  .summary-list dd { margin-bottom: 0.4rem; }
}
.cite {
  display: inline-block;
  margin: 0 0.1rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--line);
  color: var(--muted);
  font-size: 0.6875rem;
  line-height: 1.5;
  white-space: nowrap;
  cursor: help;
}

/* A note's text, as written. Plain text (stripped at sync), wrapped rather
   than truncated — the drawer is where somebody reads it. */
.touch-body {
  grid-column: 1 / -1;
  color: var(--ink);
  font-size: 0.75rem;
  line-height: 1.45;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.drawer-contact { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; }

/* The rows behind a metric. */
.lead-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.lead-row {
  display: grid;
  gap: 0.1rem;
  width: 100%;
  text-align: left;
  padding: 0.5rem 0.6rem;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.lead-row:hover { background: var(--surface); border-color: var(--line); }
.lead-name { font-weight: 600; font-size: 0.8125rem; }
.lead-meta { color: var(--muted); font-size: 0.75rem; }
.lead-flag { color: var(--attn-strong); font-size: 0.6875rem; font-weight: 600; }

/* A tile that opens something. Still a tile — the only difference is the
   cursor and a hover border, because a tile that looked like a button would be
   the "KPI card" PRODUCT.md rules out. */
.tile-open {
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.tile-open:hover { border-color: var(--line-strong); background: var(--surface); }

/* The pipeline value report. Three totals over the stage table; the open
   subtotal is set off so it reads as a sum of the rows above it, and Closed
   Won sits under it, apart, because it is not part of that sum. */
.value-totals { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 1rem; }
@media (max-width: 560px) { .value-totals { grid-template-columns: 1fr; } }
.value-table .value-subtotal td,
.data-table .value-subtotal td { font-weight: 600; border-top: 1px solid var(--line-strong); }
.value-extra { margin-left: 0.3rem; color: var(--faint); font-size: 0.6875rem; cursor: help; }
.value-flag {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--attn-tint);
  color: var(--attn);
  font-size: 0.6875rem;
  white-space: nowrap;
  cursor: help;
}
.value-meta { display: block; color: var(--faint); font-size: 0.6875rem; }
/* The "From Unit Reserved / All stages" switch: radio buttons dressed as the
   page's chips, and :has() showing the chosen view. No script, so it works the
   same on the share page. */
.value-scopes { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-left: auto; }
.value-scopes input { position: absolute; opacity: 0; pointer-events: none; }
.value-scopes label { cursor: pointer; }
.value-scopes label:has(input:checked) { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.value-scopes label:has(input:focus-visible) { outline: 2px solid var(--client); outline-offset: 2px; }
.pipeline-value .scope-all { display: none; }
.pipeline-value:has(input[value="all"]:checked) .scope-all { display: block; }
.pipeline-value:has(input[value="all"]:checked) .scope-reserved { display: none; }
.pipeline-value .panel-head { flex-wrap: wrap; }
/* Nine stages and five columns can outgrow a phone; the table scrolls inside
   its panel rather than pushing the page sideways. */
.value-table-wrap { overflow-x: auto; }
.drawer-muted { color: var(--faint); font-size: 0.75rem; }

tr.row-open { cursor: pointer; }
tr.row-open:hover { background: var(--surface); }

@media (max-width: 560px) {
  .drawer { width: 100%; border-left: 0; }
}
