/* ============================================================
   EveryChat — components.css
   ------------------------------------------------------------
   Reusable UI components. Page-specific patterns and layout
   wrappers (app shell, sidebars-as-layout, page chrome) live
   in layout.css.

   All values reference tokens from tokens.css. Any value that
   should be a token but isn't yet is listed in the
   "Missing tokens" block at the bottom of this file.

   Token-mapping notes (decisions doc names → tokens.css names):
     --color-accent          → --color-blue
     --color-accent-dark     → --color-blue-dark
     --color-accent-bg       → --color-blue-glow
     --color-fg1 / fg2 / fg3 → --color-text / -secondary / -dim
     --color-backdrop        → --color-overlay-modal
     --radius-md             → --radius-sm (8px)
     --font-size-xs/sm/md/lg → --font-size-caption / small / body / body-lg
     --transition-fast/base  → --duration-fast / --duration-base
     --focus-ring (boxshadow)→ 0 0 0 3px var(--color-focus-ring)

   Cross-cutting rules:
     - BEM: .block, .block__element, .block--modifier
     - State classes use the is- prefix (.is-active, .is-loading…)
     - Modifiers compose: color × shape × size are independent
     - .composer__send / .email-composer__send provide positioning
       only; visual treatment comes from .btn modifiers
   ============================================================ */


/* ===========================================================
   1. BUTTONS — .btn
   Visual reference: Profile Settings pages
   =========================================================== */

/* **`display: inline-flex` BELOW BEATS THE `hidden` ATTRIBUTE, AND THAT IS
   A TRAP.** The browser hides `[hidden]` with a `display: none` of the
   lowest possible weight, so any class that sets `display` wins and the
   element stays on screen. `.empty-state` and `.notice` already carry this
   guard for the same reason (see the note further down).

   Found on the New estimate box, 2026-10-22: its Back button is hidden with
   `back.hidden = !picker.trail.length` and had never once gone away. Every
   script in the product that reaches for `button.hidden` was in the same
   position. */
.btn[hidden] { display: none; }

.btn {
  /* Base — shared by every variant. Color/size modifiers layer on. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4); /* 8px */
  padding: 9px var(--space-8); /* 9px / 16px — Profile Settings rhythm */
  height: 36px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm); /* 8px */
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px — ps-btn baseline */
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition:
    background-color var(--duration-base) var(--easing-default),
    border-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default),
    transform var(--duration-base) var(--easing-default);
}
.btn:hover {
  color: white; /* block a:hover { color } from base.css overriding btn variant colors */
}

.btn:focus { outline: 0; }
.btn:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.btn:disabled,
.btn.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
  pointer-events: none;
}

/* Icons inside buttons inherit currentColor */
.btn > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: currentColor;
}

/* ----- Color variants ----- */

/* Primary — solid blue, the dominant CTA */
.btn--primary {
  background: var(--color-blue);
  color: var(--color-text-on-action);
  border-color: var(--color-blue);
  box-shadow: var(--shadow-button-action-rest);
}
.btn--primary:hover {
  background: var(--color-blue-dark);
  border-color: var(--color-blue-dark);
  box-shadow: var(--shadow-button-action-hover);
  transform: translateY(-1px);
}
.btn--primary:active {
  transform: translateY(0);
  box-shadow: var(--shadow-button-action-active);
}
.btn--primary:focus-visible {
  box-shadow: var(--shadow-button-action-rest), 0 0 0 3px var(--color-focus-ring);
}

/* Secondary — white background, neutral border, dark text */
.btn--secondary {
  background: var(--color-bg);
  color: var(--color-text);
  border-color: var(--color-border);
}
.btn--secondary:hover {
  background: var(--color-bg-off);
  border-color: var(--color-blue);
  color: var(--color-blue);
}
.btn--secondary:hover > svg { color: inherit; }
.btn--secondary:active {
  background: var(--color-bg-hover);
}

/* Ghost — transparent, secondary text color, hover bg */
.btn--ghost {
  background: transparent;
  color: var(--color-text-secondary);
  border-color: transparent;
}
.btn--ghost:hover {
  background: var(--color-bg-off);
  color: var(--color-text);
}
.btn--ghost:active {
  background: var(--color-bg-hover);
}

/* Danger — solid red, destructive confirms */
.btn--danger {
  background: var(--color-red);
  color: var(--color-text-on-action);
  border-color: var(--color-red);
  box-shadow: var(--shadow-button-danger-rest);
}
.btn--danger:hover {
  background: var(--color-red-dark);
  border-color: var(--color-red-dark);
  box-shadow: var(--shadow-button-danger-hover);
  transform: translateY(-1px);
}
.btn--danger:active {
  transform: translateY(0);
  box-shadow: var(--shadow-button-danger-active);
}
.btn--danger:focus-visible {
  box-shadow: var(--shadow-button-danger-rest), 0 0 0 3px var(--color-focus-ring-danger);
}

/* Danger ghost — transparent, no border, red text. Inline destructive. */
.btn--danger-ghost {
  background: transparent;
  color: var(--color-red);
  border-color: transparent;
}
.btn--danger-ghost:hover {
  background: var(--color-red-glow);
  color: var(--color-red);
}
.btn--danger-ghost:active {
  background: var(--color-red-pale);
}
.btn--danger-ghost:focus-visible {
  box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
}

/* Danger outline — transparent, red border, red text. Scoped destructive. */
.btn--danger-outline {
  background: transparent;
  color: var(--color-red);
  border-color: var(--color-red);
}
.btn--danger-outline:hover {
  background: var(--color-red-glow);
  color: var(--color-red);
}
.btn--danger-outline:active {
  background: var(--color-red-pale);
}
.btn--danger-outline:focus-visible {
  box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
}

/* Blue ghost — transparent, no border, blue text. Inline primary action. */
.btn--blue-ghost {
  background: transparent;
  color: var(--color-blue);
  border-color: transparent;
}
.btn--blue-ghost:hover {
  background: var(--color-blue-glow);
  color: var(--color-blue);
}
.btn--blue-ghost:active {
  background: var(--color-blue-glow);
}
.btn--blue-ghost:focus-visible {
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* ----- Shape variants ----- */

/* Icon — square, icon-only */
.btn--icon {
  width: 36px;
  padding: 0;
  gap: 0;
}
.btn--icon > svg {
  width: 18px;
  height: 18px;
}

/* Block — full-width */
.btn--block {
  width: 100%;
  display: flex;
}

/* ----- Size variants (compose with color and shape) ----- */

.btn--sm {
  height: 30px;
  padding: 6px var(--space-6); /* 6px / 12px */
  font-size: var(--font-size-caption); /* 12px */
  gap: var(--space-3); /* 6px */
}
.btn--sm.btn--icon {
  width: 30px;
  padding: 0;
}
.btn--sm > svg {
  width: 14px;
  height: 14px;
}

.btn--lg {
  height: 44px;
  padding: 12px var(--space-10); /* 12px / 24px */
  font-size: var(--font-size-body); /* 14px */
  gap: var(--space-4); /* 8px */
}
.btn--lg.btn--icon {
  width: 44px;
  padding: 0;
}
.btn--lg > svg {
  width: 20px;
  height: 20px;
}

/* ----- Surface modifier: --on-rail ----- */
/* Re-skins .btn--secondary and .btn--ghost for dark-rail
   surfaces (Form Builder sidebar, similar contexts). Color
   variants like --primary / --danger don't need this — their
   solid fills already work on any surface. */
.btn--on-rail.btn--secondary {
  background: transparent;
  border-color: var(--color-rail-border);
  color: var(--color-rail-fg);
}
.btn--on-rail.btn--secondary:hover {
  background: var(--color-rail-bg-hover);
  border-color: var(--color-rail-border-hover);
  color: var(--color-text-on-action);
}
.btn--on-rail.btn--secondary:hover > svg { color: var(--color-text-on-action); }
.btn--on-rail.btn--secondary:active {
  background: var(--color-rail-bg-hover);
}

.btn--on-rail.btn--ghost {
  background: transparent;
  color: var(--color-rail-fg);
  border-color: transparent;
}
.btn--on-rail.btn--ghost:hover {
  background: var(--color-rail-bg-hover);
  color: var(--color-text-on-action);
}
.btn--on-rail.btn--ghost:active {
  background: var(--color-rail-bg-hover);
}

/* Danger-ghost on rail: keep red text (it carries on dark),
   tint the hover background to a translucent red rather than
   the bright red-glow which clashes with the rail surface. */
.btn--on-rail.btn--danger-ghost:hover {
  background: rgba(239, 68, 68, 0.12);
}

/* ----- Loading state ----- */
/* Replaces the button label with a spinner; preserves footprint. */
.btn.is-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.btn.is-loading > * { visibility: hidden; }
.btn.is-loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  opacity: 0.9;
  animation: ec-spin 0.8s linear infinite;
  /* Bring the spinner color back even though the label is transparent */
  color: var(--color-text-on-action);
}
.btn--secondary.is-loading::after,
.btn--ghost.is-loading::after,
.btn--danger-ghost.is-loading::after {
  color: var(--color-text-secondary);
}


/* ===========================================================
   2. MODAL — .modal
   Visual reference: Profile Settings destructive-confirm modals
   for default; thread-attached note dialogs for .modal--note
   =========================================================== */

.modal__backdrop {
  position: fixed;
  inset: 0;
  background: var(--color-overlay-modal);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-9); /* 20px */
}

.modal {
  position: relative;
  background: var(--color-bg);
  border-radius: var(--radius-lg); /* 16px */
  box-shadow: var(--shadow-xl);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow: auto;
  z-index: calc(var(--z-modal) + 1);
  display: flex;
  flex-direction: column;
}

.modal__head {
  position: relative;
  padding: var(--space-10) var(--space-12) var(--space-3); /* 24 32 6 */
}

.modal__title {
  font-family: var(--font-display);
  font-size: var(--font-size-h3); /* 22px */
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-h3);
  letter-spacing: var(--letter-spacing-h3);
  color: var(--color-text);
  margin: 0;
}

.modal__head p {
  margin-top: var(--space-3);
}

.modal__meta {
  margin-top: var(--space-2); /* 4px */
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-small);
  color: var(--color-text-secondary);
}

.modal__close {
  position: absolute;
  top: var(--space-8); /* 16px */
  right: var(--space-8);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default);
}
.modal__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.modal__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.modal__close > svg { width: 18px; height: 18px; }

.modal__body {
  padding: var(--space-7) var(--space-12) var(--space-4); /* 14 32 8 */
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  color: var(--color-text);
}
/* When body follows head directly with no extra body intro, give it some air */
.modal__head + .modal__body { padding-top: var(--space-6); }

.modal__foot {
  padding: var(--space-8) var(--space-12) var(--space-10); /* 16 32 24 */
  display: flex;
  gap: var(--space-5); /* 10px */
  justify-content: flex-end;
  align-items: center;
}

/* ----- Size modifiers ----- */
.modal--sm  { max-width: 420px; }
.modal--lg  { max-width: 720px; }
.modal--full {
  max-width: none;
  width: calc(100vw - 48px);
  height: calc(100vh - 48px);
  max-height: calc(100vh - 48px);
}

/* ----- Overflow override: lets a dropdown/select escape the modal bounds -----
   The base .modal uses overflow:auto so tall content gets its own scrollbar,
   but that same rule clips any dropdown that pops out past the modal's edge.
   Opt in per modal (disables that scrollbar for overflowing content). */
.modal--overflow-visible {
  overflow: visible;
}

/* ----- Alert modifier: headless/footless dialog (empty-state content) ----- */
.modal--alert .modal__body {
  padding: var(--space-14) var(--space-12); /* 40 32 — generous vertical room without head/foot */
}

/* ----- Drawer modifier: slide in from right edge -----
   General slide-in panel. For filter UIs specifically, use
   .filter-drawer (Component #5). */
.modal--drawer {
  margin-left: auto;
  margin-right: 0;
  max-width: 480px;
  width: 100%;
  height: 100vh;
  max-height: 100vh;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  align-self: stretch;
  transform: translateX(100%);
  transition: transform 0.3s ease;
}
.modal__backdrop:has(.modal--drawer) {
  align-items: stretch;
  justify-content: flex-end;
  padding: 0;
}
.modal__backdrop.open .modal--drawer {
  transform: translateX(0);
}

/* ----- Note variant: cream surface for thread-attached notes ----- */
.modal--note {
  background: var(--gradient-note-surface);
  border: 1px solid var(--color-cream-border);
  color: var(--color-cream-text);
}
.modal--note .modal__title {
  color: var(--color-cream-text-strong);
}
.modal--note .modal__meta {
  color: var(--color-cream-text-dim);
}
.modal--note .modal__body {
  color: var(--color-cream-text);
}
.modal--note .modal__head,
.modal--note .modal__foot {
  /* Re-tint head/foot dividers to match the cream surface */
  border-color: var(--color-cream-border);
}
.modal--note .modal__head {
  border-bottom: 1px solid var(--color-cream-border);
}
.modal--note .modal__foot {
  border-top: 1px solid var(--color-cream-border);
}
.modal--note .modal__close {
  color: var(--color-cream-text-dim);
}
.modal--note .modal__close:hover {
  background: rgba(180, 83, 9, 0.12);
  color: var(--color-cream-text-strong);
}
/* Foot actions on a note modal use a cream-tinted secondary button
   so they don't look starkly out of place on the warm surface. */
.modal--note .modal__foot .btn--secondary {
  background: var(--color-yellow-glow);
  border-color: var(--color-cream-border);
  color: var(--color-cream-text-strong);
}
.modal--note .modal__foot .btn--secondary:hover {
  background: var(--color-yellow-pale);
  border-color: var(--color-yellow);
  color: var(--color-cream-text-strong);
}

/* Share modal — status toggle row used in Manage & Share modals */
.modal-status {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  background: var(--color-bg-off);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-8);
}

.modal-status p {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  margin: 0;
}

/* Pushes the left-side action (e.g. Scheduling Settings) to the far left
   while the remaining modal footer buttons stay right-aligned */
.modal-foot-start {
  margin-right: auto;
}

/* Sub-panel headings for the text / email share panels */
.appt-share-panel__title {
  font-size: var(--font-size-body-lg);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  margin: 0 0 var(--space-3);
}

.appt-share-panel__desc {
  font-size: var(--font-size-body);
  color: var(--color-text-secondary);
  margin: 0 0 var(--space-8);
}


/* ===========================================================
   3. SIDEBARS — .inbox-rail, .profile-sidebar,
                 .scheduling-sidebar, .form-builder-sidebar
   Visual reference: Profile Settings sidebar (.profile-sidebar);
   Email Inbox rail (.inbox-rail)
   Note: Layout positioning (width, grid placement) lives in
   layout.css. This file provides the chrome and item styling.
   =========================================================== */

/* ---- Inbox rail (icon-only thin sidebar, dark navy) ----
   Dark surface shared with the Settings sidebars (Profile,
   Scheduling). All four rail tokens live in tokens.css §1c. */
.inbox-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2); /* 4px */
  padding: var(--space-6) 0; /* 12px 0 */
  background: var(--color-rail-bg);
  color: var(--color-rail-fg);
  overflow-y: auto;
}

.inbox-rail__section {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
}
.inbox-rail__section + .inbox-rail__section {
  margin-top: var(--space-3); /* 6px */
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-rail-bg-hover);
}

.inbox-rail__item {
  position: relative;
  width: 50px;
  height: 50px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--color-rail-fg-dim);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default);
}
.inbox-rail__item:hover {
  background: var(--color-rail-bg-hover);
  color: var(--color-text-on-action);
}
.inbox-rail__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.inbox-rail__item.is-active {
  background: var(--color-blue);
  color: var(--color-text-on-action);
}

.inbox-rail__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  margin-bottom: 2px;
}
.inbox-rail__icon > svg { width: 20px; height: 20px; }

.inbox-rail__label {
  font-size: 9px;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
}

.inbox-rail__count {
  position: absolute;
  top: 3px;
  right: 3px;
  min-width: 15px;
  height: 15px;
  padding: 0 var(--space-2);
  background: var(--color-red);
  color: var(--color-text-on-action);
  border-radius: var(--radius-pill);
  font-size: 9px;
  font-weight: var(--font-weight-bold);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--color-rail-bg);
}

/* ---- Profile Settings sidebar (dark navy surface) ----
   Used by all Settings pages. Dark surface lets the user know
   they've left the main app context and are now in a different
   mode. Uses the same rail palette as .inbox-rail. */
.profile-sidebar {
  padding: var(--space-9) var(--space-7) var(--space-10); /* 20 14 24 */
  background: var(--color-rail-bg);
  border-right: 1px solid rgba(255, 255, 255, 0.06);
  overflow-y: auto;
  color: var(--color-rail-fg);
}

.profile-sidebar-format {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* Header — eyebrow + display title sitting above the items.
   Source: ps-sb-head pattern in the existing Settings designs. */
.profile-sidebar__header {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 6px between eyebrow and title */
  padding: var(--space-1) var(--space-6) var(--space-7); /* 2 12 14 */
  margin-bottom: var(--space-3);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.profile-sidebar__eyebrow {
  font-family: var(--font-body);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  letter-spacing: var(--letter-spacing-uppercase);
  text-transform: uppercase;
  color: var(--color-rail-fg-dim);
}

.profile-sidebar__title {
  font-family: var(--font-display);
  font-size: 20px; /* between h4 (18px) and h3 (22px) — sized for sidebar context */
  font-weight: var(--font-weight-bold);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--color-text-on-action);
  margin: 0;
}

.profile-sidebar__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.profile-sidebar__section + .profile-sidebar__section {
  margin-top: var(--space-7);
  padding-top: var(--space-7);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

/* ----- Collapsible groups (folder-style categories) ----- */

.profile-sidebar__group + .profile-sidebar__group {
  margin-top: var(--space-2);
}

.profile-sidebar__group-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) var(--space-7);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-rail-fg-dim);
  cursor: pointer;
  text-align: left;
  transition: color var(--duration-base) var(--easing-default),
              background-color var(--duration-base) var(--easing-default);
}
.profile-sidebar__group-toggle:hover {
  color: var(--color-text-on-action);
  background: var(--color-rail-bg-hover);
}
.profile-sidebar__group-toggle:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.profile-sidebar__group-label {
  flex: 1;
  min-width: 0;
}

.profile-sidebar__group-chevron {
  flex-shrink: 0;
  transition: transform var(--duration-base) var(--easing-default);
}
.profile-sidebar__group.is-open > .profile-sidebar__group-toggle > .profile-sidebar__group-chevron {
  transform: rotate(90deg);
}

.profile-sidebar__group-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease;
}
.profile-sidebar__group.is-open > .profile-sidebar__group-body {
  max-height: 600px;
}

.profile-sidebar__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-6); /* 12px */
  padding: var(--space-5) var(--space-7); /* 10px 14px */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  line-height: 1.1;
  color: var(--color-rail-fg);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default);
}
.profile-sidebar__item:hover:not(.is-active) {
  background: var(--color-rail-bg-hover);
  color: var(--color-text-on-action);
}
/* Visible and clickable, but the feature behind it is not on this account's subscription (OpenAI without Automations). */
.profile-sidebar__item.is-disabled:not(.is-active) { opacity: 0.55; }
.profile-sidebar__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.profile-sidebar__item.is-active {
  background: var(--color-blue);
  color: var(--color-text-on-action);
  font-weight: var(--font-weight-semibold);
  box-shadow: var(--shadow-sm);
}
.profile-sidebar__item.is-active .profile-sidebar__icon { color: var(--color-text-on-action); }

.profile-sidebar__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--color-rail-fg-dim);
  transition: color var(--duration-base) var(--easing-default);
}
.profile-sidebar__icon > svg { width: 18px; height: 18px; }
.profile-sidebar__item:hover:not(.is-active) .profile-sidebar__icon {
  color: var(--color-text-on-action);
}

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

.profile-sidebar__count {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  padding: 1px var(--space-3); /* 1px 6px */
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  color: var(--color-rail-fg);
  flex-shrink: 0;
}
.profile-sidebar__item.is-active .profile-sidebar__count {
  background: rgba(255, 255, 255, 0.2);
  color: var(--color-text-on-action);
}
.profile-sidebar__item:hover:not(.is-active) .profile-sidebar__count {
  background: rgba(255, 255, 255, 0.12);
  color: var(--color-text-on-action);
}

/* ----- Mobile drawer extras ----- */

/* Close button: tucked in the top-right of the sidebar header.
   Hidden on desktop; surfaced at ≤1000px. */
.profile-sidebar__header {
  position: relative;
}

.profile-sidebar__close {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  color: var(--color-rail-fg-dim);
}

/* Backdrop: full-screen overlay behind the drawer. */
.profile-sidebar__backdrop {
  display: none;
}

/* Trigger strip: appears at the top of the content area on mobile. */
.profile-sidebar__mobile-trigger {
  display: none;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-5) var(--space-9);
  background: var(--color-bg);
  border: 0;
  border-bottom: 1px solid var(--color-border-light);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  cursor: pointer;
  text-align: left;
  transition: background-color var(--duration-fast) var(--easing-default);
}

.profile-sidebar__mobile-trigger > svg:last-child {
  margin-left: auto;
  flex-shrink: 0;
}

.profile-sidebar__mobile-trigger:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

@media (max-width: 1055px) {
  /* Lift the sidebar out of the flow and position it as a left-side
     drawer. It stays rendered (not display:none) so the CSS transition
     on transform can run smoothly. The layout.css display:none is
     overridden here — transform handles visibility instead. */
  .profile-sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: var(--topbar-height);
    left: 0;
    height: calc(100dvh - var(--topbar-height));
    width: 280px;
    z-index: var(--z-modal);
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    overflow-y: auto;
  }

  .profile-sidebar.is-open {
    transform: translateX(0);
    box-shadow: var(--shadow-lg);
  }

  .profile-sidebar__close {
    display: inline-flex;
  }

  .profile-sidebar__mobile-trigger {
    display: flex;
    flex-shrink: 0;
  }

  /* The trigger is a flex item inside app__main (a flex row).
     Switch app__main to column on profile pages so the trigger
     stacks above the page content instead of sitting beside it. */
  .app__main:has(.profile-sidebar__mobile-trigger) {
    flex-direction: column;
  }
}

/* ---- Scheduling Settings sidebar (white surface) ----
   Used by editor-style flows (the scheduling editor, and any
   other "you're inside a focused editor" page). Different from
   .profile-sidebar in three ways: white surface, an __exit
   button at the top, and a soft blue-tint active state instead
   of the solid blue fill. */
.scheduling-sidebar {
  padding: var(--space-7) var(--space-7) var(--space-10); /* 14 14 24 */
  background: var(--color-bg);
  border-right: 1px solid var(--color-border);
  overflow-y: auto;
  color: var(--color-text);
}

.scheduling-sidebar-format {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* Exit slot — sizing only; spacing owned by __header. */

.scheduling-sidebar__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.scheduling-sidebar__section + .scheduling-sidebar__section {
  margin-top: var(--space-7);
  padding-top: var(--space-7);
  border-top: 1px solid var(--color-border-light);
}
.scheduling-sidebar__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-5) var(--space-7);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  line-height: 1.1;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default);
}
.scheduling-sidebar__item:hover:not(.is-active) {
  background: var(--color-bg-off);
  color: var(--color-text);
}
.scheduling-sidebar__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.scheduling-sidebar__item.is-active {
  background: var(--color-blue-glow);
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
}
.scheduling-sidebar__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--color-text-dim);
  transition: color var(--duration-base) var(--easing-default);
}
.scheduling-sidebar__icon > svg { width: 18px; height: 18px; }
.scheduling-sidebar__item:hover:not(.is-active) .scheduling-sidebar__icon {
  color: var(--color-text-secondary);
}
.scheduling-sidebar__item.is-active .scheduling-sidebar__icon { color: var(--color-blue); }
.scheduling-sidebar__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scheduling-sidebar__count {
  font-size: var(--font-size-rail-label);
  font-weight: var(--font-weight-semibold);
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-blue-glow);
  color: var(--color-blue);
  flex-shrink: 0;
}
.scheduling-sidebar__item.is-active .scheduling-sidebar__count {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}

/* ----- Scheduling sidebar mobile drawer extras ----- */

/* Header row: exit button + close button side by side. */
.scheduling-sidebar__header {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.scheduling-sidebar__exit {
  flex: 1;
  min-width: 0;
}

/* Close button: in normal flow inside the header row; hidden on desktop. */
.scheduling-sidebar__close {
  display: none;
}

/* Backdrop: permanently hidden (no overlay for this sidebar). */
.scheduling-sidebar__backdrop {
  display: none;
}

/* Trigger strip: appears at the top of the content area on mobile. */
.scheduling-sidebar__mobile-trigger {
  display: none;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-5) var(--space-9);
  background: var(--color-bg);
  border: 0;
  border-bottom: 1px solid var(--color-border-light);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  cursor: pointer;
  text-align: left;
  transition: background-color var(--duration-fast) var(--easing-default);
}

.scheduling-sidebar__mobile-trigger > svg:last-child {
  margin-left: auto;
  flex-shrink: 0;
}

.scheduling-sidebar__mobile-trigger:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

@media (max-width:1055px) {
  .scheduling-sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: var(--topbar-height);
    left: 0;
    height: calc(100dvh - var(--topbar-height));
    width: 280px;
    z-index: var(--z-modal);
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    overflow-y: auto;
  }

  .scheduling-sidebar.is-open {
    transform: translateX(0);
    box-shadow: var(--shadow-lg);
  }

  .scheduling-sidebar__close {
    display: inline-flex;
  }

  .scheduling-sidebar__mobile-trigger {
    display: flex;
    flex-shrink: 0;
  }

  .app__main:has(.scheduling-sidebar__mobile-trigger) {
    flex-direction: column;
  }
}

/* ---- Form Builder sidebar (dark navy settings panel) ----
   Different in kind from the other three sidebars: this one is
   a settings panel (live form controls), not a navigation list.
   Four regions stacked vertically:
     __top    — home button + form switcher
     __title  — section heading + optional status chip
     __body   — scrollable middle, holds __section-label + __row groups
     __foot   — bottom action bar (Save + delete)
   Form controls inside __body use the --on-rail variants of
   .field, .input, .toggle, .checkbox, .radio, .btn so they
   render correctly on the dark surface. */
.form-builder-sidebar {
  display: flex;
  flex-direction: column;
  background: var(--color-rail-bg);
  color: var(--color-rail-fg);
  overflow: hidden; /* foot is sticky-bottom; body scrolls */
  font-family: var(--font-body);
}

/* ----- __top: navigation cluster (home + selector) ----- */
.form-builder-sidebar__top {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-7); /* 14px */
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  flex-shrink: 0;
}
/* Positioning slot for the home button. The button itself uses
   .btn .btn--secondary .btn--icon .btn--on-rail — visual treatment
   comes from those modifiers, not from this class. */
.form-builder-sidebar__home {
  flex-shrink: 0;
}
/* Positioning slot for the form-switcher. The control itself
   is a select.input.input--on-rail that fills the remaining
   horizontal space. */
.form-builder-sidebar__selector {
  flex: 1;
  min-width: 0;
}
.form-builder-sidebar__selector .input {
  width: 100%;
}

/* ----- __title: heading + optional status chip ----- */
.form-builder-sidebar__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-9) var(--space-8) var(--space-7); /* 20 16 14 */
  flex-shrink: 0;
}
.form-builder-sidebar__title > h2,
.form-builder-sidebar__title > h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: var(--font-weight-bold);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--color-text-on-action);
  flex: 1;
  min-width: 0;
}

/* ----- __body: scrollable settings region ----- */
.form-builder-sidebar__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--space-8) var(--space-8); /* 0 16 16 */
  display: flex;
  flex-direction: column;
  gap: var(--space-7); /* 14px between groups */
}

/* Optional dividers between groups inside __body. */
.form-builder-sidebar__divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.08);
  margin: var(--space-3) 0;
  border: 0;
}

/* Section eyebrow ("FORM NAME", "FORM DESIGN", "INTERNAL NOTIFICATIONS"). */
.form-builder-sidebar__section-label {
  font-family: var(--font-body);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  letter-spacing: var(--letter-spacing-uppercase);
  text-transform: uppercase;
  color: var(--color-rail-fg-dim);
  margin: var(--space-3) 0 var(--space-4); /* 6 0 8 */
}

/* A single setting row — label on the left, control on the
   right. Used for the toggle rows and color-swatch rows. */
.form-builder-sidebar__row {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-5) 0; /* 10px vertical, no horizontal */
  min-height: 36px;
}
.form-builder-sidebar__row + .form-builder-sidebar__row {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.form-builder-sidebar__row-label {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  font-family: var(--font-body);
  font-size: var(--font-size-body); /* 14px */
  font-weight: var(--font-weight-medium);
  color: var(--color-text-on-action);
}
.form-builder-sidebar__row-label > svg {
  width: 18px;
  height: 18px;
  color: var(--color-rail-fg-dim);
  flex-shrink: 0;
}

.form-builder-sidebar__row-control {
  display: flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  flex-shrink: 0;
}

/* ----- __foot: bottom action bar ----- */
.form-builder-sidebar__foot {
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  padding: var(--space-7) var(--space-8); /* 14 16 */
  background: var(--color-rail-bg);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  flex-shrink: 0;
}
/* Primary save action stretches to fill remaining space */
.form-builder-sidebar__foot .btn--primary {
  flex: 1;
}


/* ===========================================================
   4. TABS — .tabs
   Visual reference: SMS Inbox tabs (canonical for both
   --underline and --pills variants)
   =========================================================== */

.tabs {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  position: relative;
}

.tabs__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-7); /* 10 14 */
  background: transparent;
  border: 0;
  border-radius: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.tabs__item:hover:not(.is-active) { color: var(--color-text); }
.tabs__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.tabs__item:disabled,
.tabs__item.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.tabs__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
}
.tabs__item.is-active .tabs__count {
  background: var(--color-blue-glow);
  color: var(--color-blue);
}

/* ----- Underline (default) ----- */
.tabs--underline {
  border-bottom: 1px solid var(--color-border);
  gap: 0;
}
.tabs--underline .tabs__item {
  padding-bottom: var(--space-6); /* 12px */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px; /* overlap container border */
}
.tabs--underline .tabs__item:hover:not(.is-active) {
  border-bottom-color: var(--color-border-hover);
}
.tabs--underline .tabs__item.is-active {
  color: var(--color-blue);
  border-bottom-color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
}

/* ----- Pills ----- */
.tabs--pills {
  /* background: var(--color-bg-off);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); */
  padding: var(--space-1); /* 2px */
  gap: var(--space-1);
}
.tabs--pills .tabs__item {
  padding: var(--space-4) var(--space-7); /* 8 14 */
  border-radius: 6px;
}
.tabs--pills .tabs__item:hover:not(.is-active) {
  color: var(--color-text);
}
.tabs--pills .tabs__item.is-active {
  background: var(--color-bg);
  color: var(--color-text);
  font-weight: var(--font-weight-semibold);
  box-shadow: var(--shadow-sm);
}

/* ----- Vertical ----- */
.tabs--vertical {
  flex-direction: column;
  align-items: stretch;
  border-bottom: 0;
  border-right: 1px solid var(--color-border);
  gap: 0;
}
.tabs--vertical .tabs__item {
  justify-content: flex-start;
  padding: var(--space-5) var(--space-7);
  border-bottom: 0;
  border-right: 2px solid transparent;
  margin-bottom: 0;
  margin-right: -1px;
}
.tabs--vertical .tabs__item.is-active {
  color: var(--color-blue);
  border-right-color: var(--color-blue);
  background: var(--color-blue-glow);
}


/* ===========================================================
   5. FILTER DRAWER — .filter-drawer
   Visual reference: Contact Detail / Property Detail / Task
   Detail V2 filter drawer (the cdv-tw implementation)
   =========================================================== */

.filter-drawer {
  position: fixed;
  bottom: var(--space-9); /* 20px */
  right: var(--space-9);
  width: 300px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius); /* 12px */
  box-shadow: var(--shadow-xl);
  padding: var(--space-8); /* 16px */
  z-index: var(--z-popover);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  color: var(--color-text);
  display: flex;
  flex-direction: column;
  gap: var(--space-7); /* 14px */
}

.filter-drawer__row {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 6px */
}

.filter-drawer__label {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-micro);
  color: var(--color-text-dim);
}

.filter-drawer__field {
  /* Container slot for inputs / segments / selects per row.
     Provides nothing visual on its own — just a hook. */
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}


/* ===========================================================
   6. CHIP — .chip
   Visual reference: Contact List filter chips (.chip--outline);
   Email inbox row category chips (solid semantic chips)
   =========================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  gap: var(--space-2); /* 4px */
  padding: 2px var(--space-4); /* 2px 8px */
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  line-height: 1.4;
  white-space: nowrap;
  border: 1px solid transparent;
  /* Intentionally no default background — see decisions doc:
     bare .chip is a base layer for runtime color override via
     inline style (custom task statuses). */
}

.chip__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.chip__label {
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.chip__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-left: var(--space-1);
  margin-right: -2px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: currentColor;
  opacity: 0.6;
  cursor: pointer;
  transition: opacity var(--duration-fast) var(--easing-default), background-color var(--duration-fast) var(--easing-default);
}
.chip__clear:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.06);
}
.chip__clear:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-focus-ring);
  opacity: 1;
}
.chip__clear > svg { width: 10px; height: 10px; }

/* ----- Outline modifier (filter chips with __clear) ----- */
.chip--outline {
  background: var(--color-bg);
  border-color: var(--color-border);
  color: var(--color-text-secondary);
  padding: var(--space-2) var(--space-5); /* 4 10 */
}
.chip--outline:hover {
  border-color: var(--color-border-hover);
  color: var(--color-text);
}

/* ----- Semantic color modifiers ----- */
/* Each modifier defines its own background + text combination.
   tokens.css does not split chip background and chip text into
   separate aliases; the pale-color tokens already supply the
   bg, and we choose a darker matching token for the text. */

/* Contact lifecycle */
.chip--lead {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}
.chip--customer {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}
.chip--vip {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}
.chip--prospect {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.chip--former-customer {
  background: var(--color-red-pale);
  color: var(--color-red-dark);
}
.chip--lost-lead {
  background: var(--color-task-pale);
  color: var(--color-task-text);
}
.chip--other {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}
.chip--not-a-fit {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}
/* A contact's CATEGORY — vendor, contractor, team member — shown on the hero
   chip only when there is no lifecycle to show. Quiet grey on purpose: it is
   who somebody is to the company, not where they are in a sale, so it must
   not read like a status. Same pair as .chip--other. */
.chip--category {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}

/* Linked Google/Facebook review (contact detail hero) — neutral background;
   the platform icon inside the chip carries the distinction. */
/* ESTIMATE STAGES — the same four words the estimate list uses, as a chip, so
   the task screen can say what the estimate it came from is up to without a
   fourth private copy of the status pill. Added 2026-10-04 with the link back
   to the estimate in a task's header. Drafts and anything unrecognised fall
   through to the plain `.chip`, which is the quiet one. */
.chip--stage-sent {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}
.chip--stage-won {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}
.chip--stage-lost {
  background: var(--color-red-pale);
  color: var(--color-red-dark);
}

.chip--review {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}
.ctd-hero__review-icon {
  width: 12px;
  height: 12px;
  object-fit: contain;
  flex-shrink: 0;
}

/* Contact tier — pale backgrounds derived from the solid tier tokens
   via color-mix() since tokens.css only defines one hex per tier. */
.chip--tier-1 {
  background: color-mix(in srgb, var(--color-tier-1) 20%, white);
  color: var(--color-tier-1);
}
.chip--tier-2 {
  background: color-mix(in srgb, var(--color-tier-2) 20%, white);
  color: var(--color-tier-2);
}
.chip--tier-3 {
  background: color-mix(in srgb, var(--color-tier-3) 20%, white);
  color: var(--color-tier-3);
}
.chip--tier-4 {
  background: color-mix(in srgb, var(--color-tier-4) 20%, white);
  color: var(--color-tier-4);
}
.chip--tier-5 {
  background: color-mix(in srgb, var(--color-tier-5) 20%, white);
  color: var(--color-tier-5);
}

/* Task status — 7 system statuses */
.chip--new-unassigned {
  background: var(--color-green-glow);
  color: var(--color-green-dark);
}
.chip--need-to-respond-sms {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.chip--need-to-respond-email {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.chip--internal-update {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}
.chip--waiting-on-customer {
  background: var(--color-blue-glow);
  color: var(--color-blue-dark);
}
.chip--snoozed {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}
.chip--complete {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}

/* Content type */
.chip--note {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.chip--attachment {
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
}
.chip--mention {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}
.chip--team {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}

/* Appointment status — used on attendee-facing scheduling pages */
.chip--confirmed {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}
.chip--cancelled {
  background: var(--color-red-pale);
  color: var(--color-red-dark);
}
.chip--awaiting {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}

/* General status */
.chip--scheduled {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}
.chip--neutral {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
}


/* ===========================================================
   7. FORM FIELD — .field, .input
   Visual reference: Profile Settings form fields
   (e.g., Settings - Basic Information)
   =========================================================== */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 6px label→input */
  margin-bottom: var(--space-8); /* 16px between consecutive fields */
}

.field:last-child {
  margin-bottom: 0;
}

.field__label {
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
}

.field__hint {
  font-family: var(--font-body);
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text-dim);
  margin-top: var(--space-1);
}
/* **A HINT CAN SHOW THE THING IT IS DESCRIBING.** The link-out field on the
   contact and property screens says "Shows as [icon] next to the name", with
   the real icon in the sentence — which answers "what will this look like?"
   without a longer label (JP, 2026-10-19: *"How can we communicate to people
   that when you're using this, this is how that link shows? I want to keep
   this simple and not make it a big, long label."*).

   Three declarations and all three are needed, because base.css sets
   `svg { display: block; max-width: 100% }` on every icon in the product: a
   block icon would break the sentence onto its own line, and the cap collapses
   an icon to nothing inside a box that has not been given a width. */
.field__hint svg {
  display: inline-block;
  vertical-align: -2px;
  max-width: none;
}

.field__error {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  color: var(--color-red);
  margin-top: var(--space-1);
}
.field__error > svg { width: 14px; height: 14px; flex-shrink: 0; }

.field__success {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  color: var(--color-green-dark);
  margin-top: var(--space-1);
}
.field__success > svg { width: 14px; height: 14px; flex-shrink: 0; }

/* ---- Input / textarea / select ---- */
.input {
  width: 100%;
  padding: var(--space-5) var(--space-7); /* 10px 14px */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); /* 8px */
  font-family: var(--font-body);
  font-size: var(--font-size-body); /* 14px */
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text);
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default),
    background-color var(--duration-base) var(--easing-default);
  -webkit-appearance: none;
  appearance: none;
}
.input::placeholder {
  color: var(--color-text-dim);
}
.input:hover {
  border-color: var(--color-border-hover);
}
.input:focus,
.input:focus-visible {
  outline: 0;
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.input:disabled {
  background: var(--color-bg-off);
  color: var(--color-text-dim);
  cursor: not-allowed;
}

/* Textarea-specific tuning */
textarea.input {
  resize: vertical;
  min-height: 88px;
  line-height: var(--line-height-body);
}

/* Select chevron */
select.input {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239CA3AF' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
  cursor: pointer;
}

/* ---- Error / success states ---- */
.field--error .field__label { /* label keeps its color but sits with red error message below */ }
.field--error .input,
.input.is-invalid {
  border-color: var(--color-red);
}
.field--error .input:focus,
.input.is-invalid:focus {
  border-color: var(--color-red);
  box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
}

.field--success .input {
  border-color: var(--color-green);
}
.field--success .input:focus {
  border-color: var(--color-green);
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
  /* TODO: tokens.css does not define a green focus ring rgba —
     using inline rgba to match input contract. Flagged. */
}

/* ----- Surface modifier: --on-rail ----- */
/* Re-skins .field, its label/hint, and child .input for dark
   surfaces. Apply to BOTH the wrapper and the input:
     <label class="field field--on-rail">
       <span class="field__label">…</span>
       <input class="input input--on-rail">
     </label>
   The wrapper modifier handles label/hint colors. The input
   modifier handles the input's own surface treatment.        */
.field--on-rail .field__label {
  color: var(--color-text-on-action);
}
.field--on-rail .field__hint {
  color: var(--color-rail-fg-dim);
}
.field--on-rail .field__error {
  /* Red still reads on dark; keep the token. Lighten via
     pale-red if you find it too saturated in practice. */
  color: var(--color-red);
}
.field--on-rail .field__success {
  color: var(--color-green);
}

.input--on-rail {
  background: var(--color-rail-input-bg);
  border-color: var(--color-rail-border);
  color: var(--color-text-on-action);
}
.input--on-rail::placeholder {
  color: var(--color-rail-fg-dim);
}
.input--on-rail:hover {
  border-color: var(--color-rail-border-hover);
}
.input--on-rail:focus,
.input--on-rail:focus-visible {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
  /* Focus ring stays brand-blue — it pops on the dark surface. */
}
.input--on-rail:disabled {
  background: var(--color-rail-input-bg-disabled);
  color: var(--color-rail-fg-dim);
}
/* Select chevron — light slate version of the data URL */
select.input--on-rail {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394A3B8' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
}
/* Error / invalid state on rail — keep the red border but
   account for the rail-input-bg fill. */
.field--on-rail.field--error .input,
.input--on-rail.is-invalid {
  border-color: var(--color-red);
}
.field--on-rail.field--error .input:focus,
.input--on-rail.is-invalid:focus {
  border-color: var(--color-red);
  box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
}


/* ===========================================================
   8. AVATAR — .avatar
   Visual reference: tb-avatar (the topbar avatar)
   =========================================================== */

.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: var(--radius-pill); /* circular */
  background: var(--gradient-message-incoming);
  color: var(--color-text-on-action);
  font-family: var(--font-body);
  font-weight: var(--font-weight-semibold);
  overflow: hidden;
  /* Default size = md */
  width: 32px;
  height: 32px;
  font-size: var(--font-size-caption); /* 12px */
}

.avatar__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.avatar__initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  user-select: none;
}

.avatar__status {
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-text-dim); /* offline default */
  border: 2px solid var(--color-bg);
  box-sizing: content-box;
}
.avatar__status.is-online { background: var(--color-green); }
.avatar__status.is-offline { background: var(--color-text-dim); }

/* ----- Size modifiers ----- */
.avatar--xs {
  width: 20px;
  height: 20px;
  font-size: 9px;
}
.avatar--xs .avatar__status { width: 6px; height: 6px; right: -1px; bottom: -1px; border-width: 1.5px; }

.avatar--sm {
  width: 26px;
  height: 26px;
  font-size: var(--font-size-micro); /* 11px */
}
.avatar--sm .avatar__status { width: 8px; height: 8px; }

.avatar--md {
  width: 32px;
  height: 32px;
  font-size: var(--font-size-caption);
}

.avatar--lg {
  width: 44px;
  height: 44px;
  font-size: var(--font-size-h5); /* 15px */
}
.avatar--lg .avatar__status { width: 12px; height: 12px; }

.avatar--xl {
  width: 80px;
  height: 80px;
  font-size: 26px;
}
.avatar--xl .avatar__status {
  width: 16px;
  height: 16px;
  right: 2px;
  bottom: 2px;
}

/* ----- Square (rare — company / property avatars) ----- */
.avatar--square {
  border-radius: var(--radius-sm);
}


/* ===========================================================
   9. TOOLTIP — .tooltip
   Visual reference: Property Detail attachment filename tooltip
   Show on parent's :hover and :focus-within. CSS-only, with
   a 250ms transition-delay on show to prevent flicker.
   =========================================================== */

.tooltip {
  position: absolute;
  z-index: var(--z-popover);
  background: var(--color-text); /* dark bg */
  color: var(--color-text-on-action);
  padding: var(--space-3) var(--space-5); /* 6 10 */
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  white-space: nowrap;
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  box-shadow: var(--shadow-md);
  opacity: 0;
  transform: translateY(2px);
  transition:
    opacity var(--duration-base) var(--easing-default),
    transform var(--duration-base) var(--easing-default);
  transition-delay: 0ms;
}

/* Parent must have position: relative for these to anchor */
*:hover > .tooltip,
*:focus-within > .tooltip {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 250ms; /* prevents flicker */
}

/* ----- `.tooltip--rich` — THE ONE THAT HOLDS A SENTENCE OR TWO -----------
   The base tooltip is one short line: `nowrap`, capped at 280px, anything
   longer cut off with an ellipsis. That is right for a filename and wrong for
   an explanation.

   JP, 2026-10-20, on Snapshot: *"Imagine you're talking to Mary in the office
   who answers the phone and doesn't like technology... What does this number
   mean, and how is it calculated? Is it accurate?"* The answer to that is two
   short paragraphs, not six words, so it has to be allowed to wrap.

   `__lead` is the little heading above each half — what it means, then how
   it is worked out. */
.tooltip--rich {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  width: 260px;
  max-width: 260px;
  padding: var(--space-5) var(--space-6);
  font-weight: var(--font-weight-regular);
  line-height: 1.5;
  text-align: left;
}
.tooltip--rich .tooltip__lead {
  display: block;
  margin-bottom: 2px;
  font-size: 10px;
  font-weight: var(--font-weight-semibold);
  letter-spacing: .06em;
  text-transform: uppercase;
  opacity: .62;
}
.tooltip--rich .tooltip__body { display: block; }
.tooltip--rich .tooltip__lead + .tooltip__body + .tooltip__lead { margin-top: var(--space-4); }

.tooltip__arrow {
  position: absolute;
  width: 8px;
  height: 8px;
  background: var(--color-text);
  transform: rotate(45deg);
}

/* ----- Position modifiers ----- */
.tooltip--top {
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translate(-50%, 2px);
}
*:hover > .tooltip--top,
*:focus-within > .tooltip--top {
  transform: translate(-50%, 0);
}
.tooltip--top .tooltip__arrow {
  bottom: -3px;
  left: 50%;
  margin-left: -4px;
}

.tooltip--bottom {
  top: calc(100% + 8px);
  left: 50%;
  transform: translate(-50%, -2px);
}
*:hover > .tooltip--bottom,
*:focus-within > .tooltip--bottom {
  transform: translate(-50%, 0);
}
.tooltip--bottom .tooltip__arrow {
  top: -3px;
  left: 50%;
  margin-left: -4px;
}

.tooltip--left {
  right: calc(100% + 8px);
  top: 50%;
  transform: translate(2px, -50%);
}
*:hover > .tooltip--left,
*:focus-within > .tooltip--left {
  transform: translate(0, -50%);
}
.tooltip--left .tooltip__arrow {
  right: -3px;
  top: 50%;
  margin-top: -4px;
}

.tooltip--right {
  left: calc(100% + 8px);
  top: 50%;
  transform: translate(-2px, -50%);
}
*:hover > .tooltip--right,
*:focus-within > .tooltip--right {
  transform: translate(0, -50%);
}
.tooltip--right .tooltip__arrow {
  left: -3px;
  top: 50%;
  margin-top: -4px;
}

/* ----- Mono modifier (for filenames / code) ----- */
.tooltip--mono {
  font-family: var(--font-mono);
  font-size: var(--font-size-caption);
}


/* ===========================================================
   10. SPINNER + LOADING OVERLAY — .spinner, .loading-overlay
   New components — fill a system gap (no existing reference).
   =========================================================== */

@keyframes ec-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.spinner {
  display: inline-block;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2.5px solid var(--color-border);
  border-top-color: var(--color-blue);
  animation: ec-spin 0.8s linear infinite;
  vertical-align: middle;
  flex-shrink: 0;
}

.spinner--sm {
  width: 16px;
  height: 16px;
  border-width: 2px;
}
.spinner--lg {
  width: 40px;
  height: 40px;
  border-width: 3px;
}
.spinner--xl {
  width: 64px;
  height: 64px;
  border-width: 4px;
}

/* ----- Loading overlay ----- */
.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6); /* 12px */
  background: var(--color-overlay-modal);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: var(--z-popover);
}

.loading-overlay--solid {
  background: var(--color-bg);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.loading-overlay__spinner {
  /* Compose with a .spinner--lg element inside; this slot just
     gives spacing reliability if needed. */
}

.loading-overlay__label {
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
}
.loading-overlay--solid .loading-overlay__label {
  color: var(--color-text);
}


/* ===========================================================
   11. LOAD MORE — .load-more
   Replaces traditional pagination.
   =========================================================== */

.load-more {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-6); /* 12px */
  padding: var(--space-9) var(--space-8) var(--space-12); /* 20 16 32 */
}

.load-more__btn {
  /* Markup uses .btn .btn--secondary on this element — this
     class adds nothing visual. Reserved for hooks (e.g. tests). */
}

.load-more__count {
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-dim);
}

.load-more__spinner {
  /* Markup uses a .spinner.spinner--sm inside; this class is a
     positioning hook. */
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}


/* ===========================================================
   11b. PAGINATION — .contacts-pagination + .page-btn
   Numbered pager shared by the Contacts, Properties, and Campaign
   recipient lists. Buttons (.page-btn, .active, .disabled) are
   generated by each page's list script.
   =========================================================== */

.contacts-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-5) var(--space-8);
  border-top: 1px solid var(--color-border);
  flex-shrink: 0;
  background: var(--color-bg);
}

.contacts-pagination__info {
  font-size: var(--font-size-small);
  color: var(--color-text-dim);
}

.contacts-pagination__buttons {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.page-btn {
  min-width: 32px;
  height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  font-size: var(--font-size-small);
  font-family: var(--font-body);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
  transition: background var(--duration-fast) var(--easing-default),
              color var(--duration-fast) var(--easing-default);
}
.page-btn:not(.disabled):not(.active):hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.page-btn.active {
  background: var(--color-blue);
  color: #fff;
  font-weight: var(--font-weight-semibold);
  cursor: default;
}
.page-btn.disabled {
  opacity: 0.35;
  cursor: default;
}


/* ===========================================================
   12. SEARCH INPUT — .search-input
   Visual reference: Contact List search (cl-search)
   =========================================================== */

.search-input {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  max-width: 360px;
}

.search-input__icon {
  position: absolute;
  left: var(--space-6); /* 12px */
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-dim);
  pointer-events: none;
  display: inline-flex;
}
.search-input__icon > svg {
  width: 16px;
  height: 16px;
}

.search-input__field {
  width: 100%;
  height: 36px;
  padding: 0 var(--space-7) 0 38px; /* leave room for icon */
  background: var(--color-bg-off);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text);
  outline: 0;
  transition:
    background-color var(--duration-base) var(--easing-default),
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}
.search-input__field::placeholder {
  color: var(--color-text-dim);
}
.search-input__field:hover {
  border-color: var(--color-border-hover);
}
.search-input__field:focus,
.search-input__field:focus-visible {
  background: var(--color-bg);
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.search-input:focus-within .search-input__icon {
  color: var(--color-blue);
}

.search-input__field:disabled {
  background: var(--color-bg-off);
  color: var(--color-text-dim);
  cursor: not-allowed;
}

.search-input__clear {
  position: absolute;
  right: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--color-text-dim);
  border-radius: 50%;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.search-input__clear:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.search-input__clear:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-focus-ring);
}
.search-input__clear > svg { width: 12px; height: 12px; }

/* ----- Size modifiers ----- */
.search-input--sm .search-input__field {
  height: 30px;
  font-size: var(--font-size-caption);
  padding-left: 32px;
}
.search-input--sm .search-input__icon { left: var(--space-5); }
.search-input--sm .search-input__icon > svg { width: 14px; height: 14px; }

.search-input--lg .search-input__field {
  height: 44px;
  font-size: var(--font-size-body);
  padding-left: 44px;
}
.search-input--lg .search-input__icon { left: var(--space-7); }
.search-input--lg .search-input__icon > svg { width: 18px; height: 18px; }


/* ===========================================================
   13. EMPTY STATE — .empty-state
       NOTICE      — .notice  (alert / informational popups)
   Both share the same centered-column visual pattern.
   .empty-state = no-data / zero-content states.
   .notice      = alert dialogs, prerequisite warnings, confirmations.
   Visual reference: Contact List empty (--page);
   Contact Detail empty states (--inline); Twilio prerequisites modal.
   =========================================================== */

.empty-state,
.notice {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-7); /* 14px */
  font-family: var(--font-body);
  color: var(--color-text);
}

/* **`display: flex` ABOVE BEATS THE `hidden` ATTRIBUTE, AND THAT IS A TRAP.**
   The browser hides `[hidden]` with a `display: none` of the lowest possible
   weight, so any class that sets `display` wins and the element stays on
   screen. The Estimates section hid its empty state this way and the word
   "Loading…" sat under the table for ever, including on a reload (JP,
   2026-10-04). Nothing is wrong with the markup or the script in that case —
   the CSS simply never let go. */
.empty-state[hidden],
.notice[hidden] { display: none; }

.empty-state__icon,
.notice__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius);
  background: var(--color-bg-off);
  color: var(--color-text-dim);
  flex-shrink: 0;
}
.empty-state__icon > svg,
.notice__icon > svg { width: 28px; height: 28px; }

.empty-state__icon > i,
.notice__icon > i { font-size: 22px; }

/* Decorative tone modifiers on the icon — designer's choice */
.empty-state__icon--tone-blue,
.notice__icon--tone-blue {
  background: var(--color-blue-glow);
  color: var(--color-blue);
}
.empty-state__icon--tone-yellow,
.notice__icon--tone-yellow {
  background: var(--color-yellow-glow);
  color: var(--color-yellow);
}
.empty-state__icon--tone-purple,
.notice__icon--tone-purple {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}

/* **GREEN, FOR AN EMPTY STATE THAT IS GOOD NEWS.** Added 2026-10-13 for the
   estimates list's *"Nothing needs you right now"*, which was drawn in green
   before it used this component and would have lost the one thing its colour
   was saying. Blue, yellow and purple were already here; this follows them
   exactly rather than being set on one screen. */
.empty-state__icon--tone-green,
.notice__icon--tone-green {
  background: var(--color-green-glow);
  color: var(--color-green);
}

.empty-state__title,
.notice__title {
  font-family: var(--font-display);
  font-size: var(--font-size-h4); /* 18px */
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-h4);
  letter-spacing: var(--letter-spacing-display);
  color: var(--color-text);
  margin: 0;
}

.empty-state__body,
.notice__body {
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-small);
  color: var(--color-text-secondary);
  max-width: 480px;
  margin: 0;
}

.empty-state__actions,
.notice__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-6);
}

/* ----- Card grid (for secondary action cards) ----- */
.empty-state__cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
  width: 100%;
  margin-top: var(--space-4);
}

.empty-state__card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-7);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default),
    transform var(--duration-base) var(--easing-default);
  position: relative;
}
.empty-state__card:hover {
  border-color: var(--color-border-hover);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}
.empty-state__card:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.empty-state__card-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  background: var(--color-blue-glow);
  color: var(--color-blue);
  flex-shrink: 0;
}
.empty-state__card-icon > svg { width: 18px; height: 18px; }

.empty-state__card-title {
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  margin: 0;
}

.empty-state__card-description {
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-small);
  color: var(--color-text-secondary);
  margin: 0;
}

.empty-state__card-arrow {
  position: absolute;
  top: var(--space-7);
  right: var(--space-7);
  color: var(--color-text-dim);
  transition: color var(--duration-base) var(--easing-default), transform var(--duration-base) var(--easing-default);
}
.empty-state__card:hover .empty-state__card-arrow {
  color: var(--color-blue);
  transform: translateX(2px);
}
.empty-state__card-arrow > svg { width: 14px; height: 14px; }

/* ----- Bottom tip strip ----- */
.empty-state__tip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-7);
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  margin-top: var(--space-3);
}
.empty-state__tip > svg {
  width: 14px;
  height: 14px;
  color: var(--color-yellow);
  flex-shrink: 0;
}

/* ----- Page modifier (full-page empty) ----- */
.empty-state--page {
  padding: var(--space-16) var(--space-12); /* 48 32 */
  gap: var(--space-9);
}
.empty-state--page .empty-state__icon {
  width: 80px;
  height: 80px;
  border-radius: var(--radius-lg);
}
.empty-state--page .empty-state__icon > svg { width: 40px; height: 40px; }
.empty-state--page .empty-state__title {
  font-size: var(--font-size-h2); /* 30px */
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-h2);
  letter-spacing: var(--letter-spacing-h2);
}
.empty-state--page .empty-state__body {
  font-size: var(--font-size-body-lg);
  line-height: var(--line-height-body-lg);
}

/* ----- Inline modifier (section-level, dashed + striped) -----
   tokens.css does not currently provide a stripe-pattern color
   variable — using the same border color as a soft fallback.
   Flagged in Missing Tokens. */
.empty-state--inline {
  padding: var(--space-16) var(--space-12);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius);
  background-color: var(--color-bg-off);
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0,
    transparent 6px,
    rgba(229, 231, 235, 0.3) 6px,
    rgba(229, 231, 235, 0.3) 7px
  );
}
.empty-state--inline__white {
  background-color: white;
}
.empty-state--inline .empty-state__cards {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.empty-state--inline .empty-state__icon {
  width: 48px;
  height: 48px;
  background: var(--color-action-bg-subtle);
  color: var(--color-action);
}
.empty-state--inline .empty-state__icon > svg { width: 24px; height: 24px; }
.empty-state--inline .empty-state__body {
  margin-top: calc(var(--space-2) - var(--space-7)); /* tightens gap after title */
}

/* ----- Page-level is-empty: applied to the page wrapper -----
   Behavior implemented at the wrapper level (analogous to
   legacy .cl-zero): fades toolbar, hides column headers. The
   wrapper class names live in page CSS; here we provide the
   generic targets. */
.is-empty .toolbar { opacity: 0.5; pointer-events: none; }
.is-empty .list-head { display: none; }


/* ===========================================================
   14. ONBOARDING BANNER — .onboarding-banner
   Visual reference: Contact Detail empty state design
   (where the pattern originated)
   =========================================================== */

.onboarding-banner {
  display: flex;
  align-items: center;
  gap: var(--space-7); /* 14px */
  padding: var(--space-7) var(--space-8); /* 14 16 */
  background: var(--color-blue-glow);
  border: 1px solid var(--color-blue-pale);
  border-radius: var(--radius);
  font-family: var(--font-body);
  color: var(--color-text);
}

.onboarding-banner__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-blue);
  flex-shrink: 0;
  border: 1px solid var(--color-blue-pale);
}
.onboarding-banner__icon > svg { width: 18px; height: 18px; }

.onboarding-banner__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.onboarding-banner__label {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-micro);
  text-transform: uppercase;
  color: var(--color-blue);
}

.onboarding-banner__text {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  color: var(--color-text);
}

.onboarding-banner__progress {
  display: flex;
  align-items: center;
  gap: var(--space-2); /* 4px */
  flex-shrink: 0;
}

.onboarding-banner__progress-segment {
  width: 24px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  border: 1px solid var(--color-blue-pale);
  transition: background-color var(--duration-base) var(--easing-default);
}
.onboarding-banner__progress-segment.is-done {
  background: var(--color-blue);
  border-color: var(--color-blue);
}

.onboarding-banner__meta {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-dim);
  flex-shrink: 0;
}


/* ===========================================================
   15. COMPOSER — .composer
   Inline reply composer.
   Visual reference: SMS Inbox composer
   =========================================================== */

.composer {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6); /* 12px */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}
.composer:focus-within {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.composer__textarea {
  width: 100%;
  min-height: 64px;
  padding: var(--space-3) var(--space-4);
  background: transparent;
  border: 0;
  outline: 0;
  resize: none;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  color: var(--color-text);
}
.composer__textarea::placeholder {
  color: var(--color-text-dim);
}

.composer__tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-light);
}

/* Send button is a positioning class only.
   Markup: <button class="btn btn--primary btn--icon composer__send"> */
.composer__send {
  margin-left: auto;
}

/* ----- Sm modifier (TeamChat thread reply) ----- */
.composer--sm {
  padding: var(--space-4);
  gap: var(--space-2);
}
.composer--sm .composer__textarea {
  min-height: 40px;
  font-size: var(--font-size-small);
}
.composer--sm .composer__tools {
  padding-top: var(--space-2);
}

/* ----- Inline modifier (compact, no card chrome) ----- */
.composer--inline {
  padding: 0;
  border: 0;
  background: transparent;
}
.composer--inline:focus-within {
  border: 0;
  box-shadow: none;
}
.composer--inline .composer__textarea {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-5) var(--space-7);
  background: var(--color-bg);
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}
.composer--inline .composer__textarea:focus {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}


/* ===========================================================
   16. EMAIL COMPOSER — REMOVED
   The original .email-composer was an awkward middle state —
   it had body anatomy (recipients, subject, formatting toolbar,
   body, attachments) but no popup chrome (no head with title +
   close, no foot with Send/Schedule cluster). It didn't fit
   the popup use case OR the inline use case cleanly.

   It has been split into two purpose-built components:
     - .email-composer-popup (Component #35) — full standalone
       popup, like .sms-composer. Self-contained, mounts inside
       .modal__backdrop if dimming is needed.
     - .email-composer-inline (TBD) — inline variant that lives
       in a thread/page, no popup chrome. Will be built when
       designs are confirmed.

   Slot #16 is intentionally left empty. The component count
   skips this number.
   =========================================================== */


/* ===========================================================
   17. STATUS DOT — .status-dot
   Visual reference: Task List status dot (default);
   WebChat Settings status dot (color variants)
   =========================================================== */

.status-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-text-dim); /* neutral default */
  flex-shrink: 0;
  vertical-align: middle;
}

.status-dot--sm { width: 6px; height: 6px; }
.status-dot--lg { width: 10px; height: 10px; }

.status-dot--success { background: var(--color-green); }
.status-dot--warning { background: var(--color-yellow); }
.status-dot--danger  { background: var(--color-red); }
.status-dot--info    { background: var(--color-blue); }
.status-dot--neutral { background: var(--color-text-dim); }


/* ===========================================================
   18. TOGGLE — .toggle
   Visual reference: Settings - Linked Phone Numbers toggle
   (the ps-switch implementation)
   =========================================================== */

.toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  font-family: var(--font-body);
  cursor: pointer;
  user-select: none;
}

.toggle__track {
  position: relative;
  width: 40px;
  height: 22px;
  background: var(--color-border-hover);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--duration-slow) var(--easing-default);
}

.toggle__thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-bg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-slow) var(--easing-default);
}

.toggle__label {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

/* On state */
.toggle.is-on .toggle__track {
  background: var(--color-blue);
}
.toggle.is-on .toggle__thumb {
  transform: translateX(18px);
}

/* Focus */
.toggle__track:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* Disabled */
.toggle.is-disabled,
.toggle:has(input:disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}
.toggle.is-disabled .toggle__track {
  cursor: not-allowed;
}

/* ----- Size modifiers ----- */
.toggle--sm .toggle__track {
  width: 32px;
  height: 18px;
}
.toggle--sm .toggle__thumb {
  width: 14px;
  height: 14px;
  top: 2px;
  left: 2px;
}
.toggle--sm.is-on .toggle__thumb {
  transform: translateX(14px);
}

.toggle--lg .toggle__track {
  width: 48px;
  height: 28px;
}
.toggle--lg .toggle__thumb {
  width: 22px;
  height: 22px;
  top: 3px;
  left: 3px;
}
.toggle--lg.is-on .toggle__thumb {
  transform: translateX(20px);
}

/* ----- Surface modifier: --on-rail ----- */
.toggle--on-rail .toggle__track {
  background: var(--color-rail-track);
}
.toggle--on-rail .toggle__label {
  color: var(--color-text-on-action);
}
/* On-state stays brand-blue. The white thumb still reads on
   either track color, so no thumb override is needed. */


/* ===========================================================
   19. CHECKBOX — .checkbox
   Visual reference: Contact List row checkbox
   =========================================================== */

.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  font-family: var(--font-body);
  cursor: pointer;
  user-select: none;
  position: relative;
}

.checkbox__box {
  position: relative;
  width: 18px;
  height: 18px;
  background: var(--color-bg);
  border: 1.5px solid var(--color-border-hover);
  border-radius: var(--radius-xs);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-on-action);
  flex-shrink: 0;
  transition:
    background-color var(--duration-base) var(--easing-default),
    border-color var(--duration-base) var(--easing-default);
}
.checkbox:hover .checkbox__box {
  border-color: var(--color-blue);
}

.checkbox__box::after {
  /* Checkmark, hidden until is-checked */
  content: '';
  width: 10px;
  height: 10px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='3' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M5 13l4 4L19 7'/></svg>");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  display: none;
}

.checkbox__label {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

/* Checked */
.checkbox.is-checked .checkbox__box {
  background: var(--color-blue);
  border-color: var(--color-blue);
}
.checkbox.is-checked .checkbox__box::after {
  display: block;
}

/* Indeterminate */
.checkbox.is-indeterminate .checkbox__box {
  background: var(--color-blue);
  border-color: var(--color-blue);
}
.checkbox.is-indeterminate .checkbox__box::after {
  content: '';
  width: 8px;
  height: 2px;
  background-image: none;
  background-color: var(--color-text-on-action);
  border-radius: 1px;
  display: block;
}

/* Disabled */
.checkbox.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.checkbox.is-disabled:hover .checkbox__box {
  border-color: var(--color-border-hover);
}

/* Focus */
.checkbox__box:focus-visible,
.checkbox:focus-within .checkbox__box {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* ----- Color modifier: --danger (checked = blocked/denied) ----- */
.checkbox--danger.is-checked .checkbox__box {
  background: var(--color-red);
  border-color: var(--color-red);
}
.checkbox--danger.is-checked .checkbox__box::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='3' viewBox='0 0 24 24'><path stroke-linecap='round' stroke-linejoin='round' d='M6 6l12 12M18 6L6 18'/></svg>");
}

/* ----- Surface modifier: --on-rail ----- */
.checkbox--on-rail .checkbox__box {
  background: var(--color-rail-input-bg);
  border-color: var(--color-rail-border);
}
.checkbox--on-rail:hover .checkbox__box {
  border-color: var(--color-rail-border-hover);
}
.checkbox--on-rail .checkbox__label {
  color: var(--color-text-on-action);
}
/* Checked / indeterminate stay solid blue — no override needed. */
.checkbox--on-rail.is-checked .checkbox__box,
.checkbox--on-rail.is-indeterminate .checkbox__box {
  background: var(--color-blue);
  border-color: var(--color-blue);
}


/* ===========================================================
   20. RADIO — .radio
   Visual reference: Settings - Linked Phone Numbers radio group
   =========================================================== */

.radio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-body);
  cursor: pointer;
  user-select: none;
  position: relative;
}

.radio__circle {
  position: relative;
  width: 18px;
  height: 18px;
  background: var(--color-bg);
  border: 1.5px solid var(--color-border-hover);
  border-radius: 50%;
  flex-shrink: 0;
  transition:
    border-color var(--duration-base) var(--easing-default),
    background-color var(--duration-base) var(--easing-default);
}
.radio:hover .radio__circle {
  border-color: var(--color-blue);
}

.radio__circle::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--color-bg);
  transform: scale(0);
  transition: transform var(--duration-base) var(--easing-default);
}

.radio__label {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

/* Checked */
.radio.is-checked .radio__circle {
  background: var(--color-blue);
  border-color: var(--color-blue);
}
.radio.is-checked .radio__circle::after {
  transform: scale(1);
}

/* Disabled */
.radio.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.radio.is-disabled:hover .radio__circle {
  border-color: var(--color-border-hover);
}

/* Focus */
.radio__circle:focus-visible,
.radio:focus-within .radio__circle {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* ----- Surface modifier: --on-rail ----- */
.radio--on-rail .radio__circle {
  background: var(--color-rail-input-bg);
  border-color: var(--color-rail-border);
}
.radio--on-rail:hover .radio__circle {
  border-color: var(--color-rail-border-hover);
}
.radio--on-rail .radio__label {
  color: var(--color-text-on-action);
}
.radio--on-rail.is-checked .radio__circle {
  background: var(--color-blue);
  border-color: var(--color-blue);
}


/* ===========================================================
   21. CARD — .card
   Visual reference: Profile Settings card (ps-card)
   =========================================================== */

.card {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); /* 16px */
  padding: var(--space-10) var(--space-12); /* 24 32 — Profile Settings rhythm */
  box-shadow: var(--shadow-sm);
  font-family: var(--font-body);
  color: var(--color-text);
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default),
    transform var(--duration-base) var(--easing-default);
  margin-bottom: var(--space-8); /* 16px between consecutive cards */
}

.card:last-child {
  margin-bottom: 0;
}

.card__head {
  margin-bottom: var(--space-9); /* 20px */
  padding-bottom: var(--space-8); /* 16px — slightly tighter than margin */
  border-bottom: 1px solid var(--color-border-light);
}
.card__head:has(+ .card__body) { /* keeps natural rhythm */ }

.card__title {
  font-family: var(--font-display);
  font-size: var(--font-size-body-lg); /* 16px */
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  letter-spacing: -0.005em;
  color: var(--color-text);
  margin: 0 0 var(--space-2) 0;
}

.card__subtitle {
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  line-height: 1.4;
  margin: 0;
}

.card__body {
  /* Content area; no enforced constraints. */
}

.card__foot {
  margin-top: var(--space-9);
  padding-top: var(--space-8);
  border-top: 1px solid var(--color-border-light);
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.card__actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-left: auto;
}

/* ----- Modifiers (compose) ----- */

/* Padded is the default; no extra rule needed. Class exists
   so it can be applied explicitly when paired with --flush
   children for clarity. */
.card--padded { /* default — see base rule */ }

/* Save bar — card row that right-aligns its save button.
   Used on profile and scheduling settings pages. */
.save-bar {
  display: flex;
  justify-content: flex-end;
}

/* Flush — no internal padding (for tables / lists inside) */
.card--flush {
  padding: 0;
}
.card--flush .card__head {
  padding: var(--space-9) var(--space-10) var(--space-7);
  margin-bottom: 0;
  border-bottom: 1px solid var(--color-border);
}
.card--flush .card__foot {
  padding: var(--space-7) var(--space-10) var(--space-9);
  margin-top: 0;
  border-top: 1px solid var(--color-border);
}

/* Bordered is the default; explicit class for composition clarity. */
.card--bordered { /* default — see base rule */ }

/* Elevated — no border, has shadow */
.card--elevated {
  border-color: transparent;
  box-shadow: var(--shadow-md);
}

/* Interactive — hover state for clickable cards */
.card--interactive {
  cursor: pointer;
}
.card--interactive:hover {
  border-color: var(--color-border-hover);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}
.card--interactive:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.card--interactive.card--elevated:hover {
  box-shadow: var(--shadow-lg);
}


/* ===========================================================
   22. BANNER — .banner
   Inline, appears at the top of a section or page.
   Visual reference: Contact Detail banner (cdv-banner);
   Settings - API Key warning note (ap-key-note) for --warning
   =========================================================== */

.banner {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-7) var(--space-8);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  line-height: 1.5;
  color: var(--color-text);
}

.full-width__banner {
  position: relative;
  padding: 12px;
  border-radius: 0;
  z-index: 999;
  justify-content: center;
}
.full-width__banner .banner__body {
  flex: none;
}

.banner__icon {
  font-size: 14px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: currentColor;
}
.banner__icon > svg { width: 18px; height: 18px; }

.banner__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.banner__title {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  margin: 0;
}

.banner__message {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-secondary);
  margin: 0;
}

.banner__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
  margin-left: var(--space-5);
}

.banner__close {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.banner__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.banner__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.banner__close > svg { width: 14px; height: 14px; }

/* ----- Tone modifiers ----- */
.banner--info {
  background: var(--color-blue-glow);
  border-color: var(--color-blue-pale);
}
.banner--info .banner__icon { color: var(--color-blue); }

.banner--success {
  background: var(--color-green-glow);
  border-color: var(--color-green-pale);
}
.banner--success .banner__icon { color: var(--color-green-dark); }

.banner--warning {
  background: var(--color-yellow-glow);
  border-color: var(--color-yellow-pale);
}
.banner--warning .banner__icon { color: var(--color-yellow); }

.banner--danger {
  background: var(--color-red-glow);
  border-color: var(--color-red-pale);
}
.banner--danger .banner__icon { color: var(--color-red); }

/* ===========================================================
   22b. DONE BAR — .done-bar
   A full-width band across the top of a record that is FINISHED.
   -----------------------------------------------------------
   It started life on the estimate screen as `.lock-banner`: a
   won or lost estimate draws one under the stage row, saying so
   and offering the one way back. JP, 2026-10-09, asked for the
   same thing on a finished task — *"let's make it feel similar
   and consistent with how it works on estimates, so that the
   system feels uniform."*

   **SO IT IS ONE COMPONENT AND NOT TWO.** The rules moved here
   from `pages/estimates/builder.css` unchanged — every value is
   the same number it was, with the estimate screen's own
   spacing aliases swapped for the global scale they already
   pointed at. Both screens draw the same band.

   It is NOT `.banner`. That one sits inside a section with a
   rounded edge and a border all round; this one runs the full
   width of the page and is held on by a single line underneath.

   Markup:
     <div class="done-bar" hidden>
       <svg>…</svg>
       <span class="done-bar__what">Completed · Oct 4, 2026</span>
       <button class="done-bar__action">Reopen</button>   <- optional
     </div>
   =========================================================== */

.done-bar {
  display: flex;
  align-items: center;
  gap: var(--space-5);                  /* 10px */
  padding: var(--space-6) var(--space-12);   /* 12px 32px */
  background: var(--color-green-glow);
  border-bottom: 1px solid var(--color-green-pale);
  color: var(--color-green-dark);
}
.done-bar[hidden] { display: none; }
.done-bar svg { flex: none; }

/* **A LOST ESTIMATE READS RED, THE WAY A WON ONE READS GREEN** (JP,
   2026-10-15: *"I'm in agreement"*).

   It used to be grey, and the reason written here was that a settled record is
   not a failure — finished, not wrong. That reasoning did not survive the
   border going on the estimate itself: from 2026-10-14 the sheet is outlined
   in red when it is lost and green when it is won, so a lost estimate was
   saying two things at once, red on the paper and grey on the band directly
   above it. JP saw it and asked the question: *"the lost section has no
   colour, whereas when an invoice is won, it shows a colour."*

   One answer now. The three colours mirror the won band exactly — the palest
   red behind, the next red up as the line under it, and the darkest for the
   words — and all three are EveryChat's own.

   It is named for what it IS rather than for how it looks, so nothing has to
   be renamed the next time a colour is settled. */
.done-bar--lost {
  background: var(--color-red-glow);
  border-bottom-color: var(--color-red-pale);
  color: var(--color-red-dark);
}

.done-bar__what {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
}

/* Pushed to the far end of the band. */
.done-bar__action {
  margin-left: auto;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  padding: var(--space-2) var(--space-6);
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text-secondary);
  cursor: pointer;
  font-family: inherit;
}
.done-bar__action:hover { background: var(--color-bg-hover); color: var(--color-text); }
.done-bar__action[hidden] { display: none; }

/* On a phone the band keeps the page's narrower gutter. */
@media (max-width: 720px) {
  .done-bar { padding: var(--space-6) var(--space-8); }   /* 12px 16px */
}

/* ===========================================================
   22b2. A ROW THAT OPENS SOMETHING — .opens-row
   -----------------------------------------------------------
   JP, 2026-10-10, looking at the contact screen: *"When I roll
   my mouse over a property, it's black and underlined. When I
   roll my mouse over a service, it's blue and underlined. When
   I roll my mouse over a task, the row highlights… but it
   doesn't underline. When I roll my mouse over an estimate, it
   underlines but it's black. We're not being consistent. They
   all need to act exactly the same way."*

   Four sections, four different answers, each written in its own
   file. **So there is one answer and it lives here:**

     the ROW lightens, and the NAME turns blue and underlines.

   He liked the row highlight on tasks, which is the half that
   says "this line is live"; the blue underline is the half that
   says WHICH BIT opens it. A property's address, a service's
   name, a task's name and an estimate's number are the same
   thing in four sections, so they all get it.

   Markup: `.opens-row` on the row, `.opens-row__name` on the
   thing that identifies it. Nothing else changes — each section
   keeps its own layout, its own padding and its own borders.
   =========================================================== */

.opens-row {
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.opens-row:hover { background: var(--color-bg-hover); }

/* **THE LINE IS DRAWN ON THE WORDS, AND ON NOTHING ELSE.**
   The first version underlined the name's container AND the link
   inside it. A container's underline is drawn THROUGH its inline
   children, so an estimate number — a plain `<a>` in a cell — got
   two lines stacked on it and read as bold or doubled. JP spotted
   it: *"the line under the link is like a double line or a bold
   line. Something's different about the line under the estimate
   number."*

   So only the innermost thing is ever underlined, and every name
   has one: a link, a label, a button, or `.opens-row__text` where
   the markup had nothing to hang it on. It keeps the line off the
   badges and tags that share the cell, too. */
.opens-row__name a,
.opens-row__name label,
.opens-row__name button,
.opens-row__text {
  color: inherit;
  text-decoration: none;
}
.opens-row__name { color: inherit; text-decoration: none; }

/* **ON THE ROW'S HOVER, NOT THE NAME'S.** Hovering anywhere on
   the line tells you what it opens — on three of these four the
   whole row is what you press, and hunting for the one word that
   lights up is the thing being fixed. */
.opens-row:hover .opens-row__name { color: var(--color-action); }
.opens-row:hover .opens-row__name a,
.opens-row:hover .opens-row__name label,
.opens-row:hover .opens-row__name button,
.opens-row:hover .opens-row__text {
  color: var(--color-action);
  text-decoration: underline;
}

/* A row nobody can open — a previous contact, a canceled line —
   keeps the highlight off and the name plain. */
.opens-row.is-static,
.opens-row.is-static:hover { background: transparent; cursor: default; }
.opens-row.is-static:hover .opens-row__name,
.opens-row.is-static:hover .opens-row__name a,
.opens-row.is-static:hover .opens-row__name label,
.opens-row.is-static:hover .opens-row__name button,
.opens-row.is-static:hover .opens-row__text {
  color: inherit;
  text-decoration: none;
}

/* ===========================================================
   22c. REACH MENU — .reach-menu
   The panel under Call, Text and Email: every number or
   address on a contact, labelled, with when it was last used.
   -----------------------------------------------------------
   JP, 2026-10-10. The three buttons used to do the thing; they
   now show what they could do it with, and a second press
   picks one. Shared by the contact screen's blue bar, a
   property's contact cards and a task's action bar — one
   panel, so the three cannot drift apart.

   **IT IS THE MORE MENU'S CHROME**, deliberately: same white
   panel, same border, same shadow, same hover. Nothing new to
   learn. What is different is inside a row — a label, the
   number itself, and a quiet line under it.
   =========================================================== */

.reach-menu {
  position: absolute;
  z-index: var(--z-dropdown, 1000);
  min-width: 260px;
  max-width: 340px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--space-3) 0;
}

.reach-menu__row { display: block; }
.reach-menu__row:hover { background: var(--color-bg-hover); }

/* **THE PREFERRED ROW IS TINTED, NOT LABELLED** (JP, 2026-10-14). The light
   blue is the one the old Preferred pill used as its own background — JP
   picked it by pointing at the pill. Hover goes one step deeper so the row
   still answers the mouse; the grey hover every other row gets would have
   read as the tint going away. */
/* **NAMED FOR ITS BLOCK, NOT `is-preferred`.** A bare state class is anybody's
   — `.is-hidden` is already a global `display: none !important` in
   `utilities.css`, and a modifier that happens to collide with one of those
   takes the whole row with it. */
.reach-menu__row--preferred { background: var(--color-action-bg-subtle); }
.reach-menu__row--preferred:hover { background: var(--color-blue-pale); }

/* The dot that goes with it, on the number itself, in the solid brand blue —
   the same 6px as the dot on the button the row belongs to. */
.reach-menu__pref-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--color-action);
  /* Level with the middle of the digits rather than their baseline. */
  vertical-align: 0.1em;
}

.reach-menu__main {
  padding: var(--space-4) var(--space-3) var(--space-4) var(--space-6);
}

/* **THE LINE THE COPY BUTTON BELONGS TO.** The button used to be
   a sibling of the whole row and centred on it, so on a row with
   a "Texted Oct 2" line under it the icon floated above the
   number (JP, 2026-10-10). It is a sibling of this line instead,
   centred on it, and no amount of text underneath can move it. */
.reach-menu__line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.reach-menu__item {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
}

/* Cell / Home / Work — the thing being chosen between, so it
   leads, small and dim the way a column heading does. It does
   not shrink: the number wraps before the label does. */
.reach-menu__label {
  flex: none;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}

/* The number itself is what somebody came to read, so it is
   the biggest thing in the row and never truncated. */
.reach-menu__value {
  font-size: var(--font-size-small);
  color: var(--color-text);
  overflow-wrap: anywhere;
}

.reach-menu__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: 2px;
}

.reach-menu__last {
  font-size: var(--font-size-micro);
  color: var(--color-text-dim);
}

/* **COPY IS ON EVERY ROW**, because capturing the number is
   half of why this menu exists — not a fallback for when
   nothing is connected. */
.reach-menu__copy {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--color-text-dim);
  cursor: pointer;
}
.reach-menu__row:hover .reach-menu__copy { background: var(--color-bg); }
.reach-menu__copy:hover { color: var(--color-text); }
.reach-menu__copy.is-copied { color: var(--color-green-dark); }
.reach-menu__copy:focus-visible,
.reach-menu__item:focus-visible {
  outline: 2px solid var(--color-blue);
  outline-offset: -2px;
}

.reach-menu__empty {
  margin: 0;
  padding: var(--space-5) var(--space-6);
  font-size: var(--font-size-small);
  color: var(--color-text-dim);
}

/* ----- when they can be reached, and when they cannot -----
   Under the list, after the choice and before the press. Divided
   off, because these are facts about the contact rather than more
   things to pick. */
.reach-menu__notes {
  margin-top: var(--space-3);
  padding: var(--space-4) var(--space-6) var(--space-3);
  border-top: 1px solid var(--color-border-light);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.reach-menu__note {
  display: flex;
  gap: var(--space-4);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.4;
}

.reach-menu__note-label {
  flex: none;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-dim);
  /* Level with the first line of the words beside it. */
  padding-top: 1px;
}

/* A restriction can stop somebody, so it is the one that is marked. */
.reach-menu__note--warn { color: var(--color-red); }
.reach-menu__note--warn .reach-menu__note-label { color: var(--color-red); }

/* ----- the dot that says "this is how they like to be reached" -----
   JP chose a dot over a star: a star means favourite or rating
   everywhere else, and this is a statement about the contact. It is
   drawn on whichever button carries the preferred way, and the word
   Preferred inside the menu explains it. */
/* **BESIDE THE WORD, NOT IN THE CORNER.** It was pinned to the
   top-right of the button, which put it above and right of the
   label with nothing lining it up — JP, 2026-10-10: *"the dot
   appears in the top-right corner of the button. It's not beside
   or to the right, justified with the word."* It sits in the flow
   now, after the label, on the same line. */
.reach-preferred::after {
  content: "";
  display: inline-block;
  flex: none;
  width: 6px;
  height: 6px;
  margin-left: var(--space-3);
  border-radius: 50%;
  background: currentColor;
  opacity: 0.9;
  /* Level with the middle of the word rather than its baseline. */
  vertical-align: 0.1em;
}

/* On a button laid out as a flex row — the blue bar's — the dot is
   a flex item and centres itself; the icon-only buttons on a
   property's contact cards have no label, so there it keeps to the
   end of the icon. */
.reach-preferred { align-items: center; }

/* ----- Hourly send-usage banner (automations list + pipeline) -----
   Composes .banner + two .progress meters. Shared across both pages, so it
   lives here rather than in a page stylesheet. The banner body stacks meters,
   so pin the icon/close to the top rather than vertically centering them. */
.au-usage-banner-mount { display: block; }
.au-usage-banner-mount[hidden] { display: none !important; }
.au-usage-banner { align-items: flex-start; margin-bottom: var(--space-7); }
.au-usage-banner .banner__icon,
.au-usage-banner .banner__close { margin-top: 2px; }

.au-usage-meters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  margin: var(--space-2) 0 var(--space-1);
}
.au-usage-meter {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-width: 240px;
  flex: 1 1 240px;
}
.au-usage-meter__name {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
  min-width: 42px;
}
.au-usage-meter__bar { flex: 1; min-width: 60px; }
.au-usage-meter__count {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-secondary);
}
.au-usage-meter__frees {
  font-size: var(--text-2xs);
  color: var(--color-text-dim);
  white-space: nowrap;
}

/* ----- Automation limits info popover (list + pipeline toolbars) -----
   An ⓘ trigger + a read-only popover of the account's automation ceilings.
   Anchored to its trigger; the wrapper is the positioning context. */
.al-wrap { position: relative; display: inline-flex; }
.al-trigger svg { margin-right: 5px; }

.al-pop {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: var(--z-popover, 60);
  width: 340px;
  max-width: calc(100vw - 32px);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg, var(--shadow-md));
  overflow: hidden;
}
.al-pop[hidden] { display: none; }

.al-pop__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-7);
  border-bottom: 1px solid var(--color-border);
}
.al-pop__title {
  margin: 0;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.al-pop__sub {
  margin: 2px 0 0;
  font-size: var(--text-2xs);
  color: var(--color-text-dim);
}
.al-pop__close {
  margin-left: auto;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--color-text-dim);
  cursor: pointer;
}
.al-pop__close:hover { background: var(--color-bg-hover); color: var(--color-text); }

.al-pop__body { padding: 0 var(--space-7); }
.al-pop__loading {
  padding: var(--space-8) 0;
  text-align: center;
  font-size: var(--font-size-body);
  color: var(--color-text-dim);
}

.al-block { padding: var(--space-6) 0; border-bottom: 1px solid var(--color-border-light, var(--color-border)); }
.al-block:last-child { border-bottom: 0; }
.al-block__top {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.al-block__icon { color: var(--color-text-secondary); }
.al-block__icon svg { width: 15px; height: 15px; align-self: center; }
.al-block__label {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.al-block__val {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.al-block__val b { font-weight: var(--font-weight-semibold); color: var(--color-text); }
.al-block__unit { color: var(--color-text-dim); }
.al-block__note {
  margin-top: var(--space-4);
  font-size: var(--text-2xs);
  color: var(--color-text-dim);
  line-height: 1.5;
}

.al-pop__foot {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-7);
  background: var(--color-bg-off, var(--color-bg-hover));
  border-top: 1px solid var(--color-border);
  font-size: var(--text-2xs);
  color: var(--color-text-secondary);
  line-height: 1.5;
}
.al-pop__foot svg { flex-shrink: 0; margin-top: 1px; color: var(--color-text-dim); }
.al-pop__foot a { color: var(--color-blue); }


/* ===========================================================
   23. TOAST — .toast
   Floating, typically bottom-right or top-right.
   Visual reference: Settings - Connected Calendars / Contact
   Cards / Email Inboxes toast notifications
   =========================================================== */

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  max-width: 420px;
  padding: var(--space-6) var(--space-8);
  background: var(--color-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  color: var(--color-text);
  z-index: var(--z-toast);
  /* Visibility/fade is driven by jQuery .fadeIn()/.fadeOut() (see activate*Message
     in application_footer.hbs). A CSS opacity transition here double-animates against
     jQuery — it dips from the default opacity:1 to 0 before jQuery fades it back in,
     producing a visible flash — so no opacity transition is declared. */
  opacity: 1;
}

.toast__icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  color: inherit;
}
.toast__icon > svg { width: 18px; height: 18px; }

.toast__message {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  color: inherit;
}

/* Every .toast--* variant is a filled color with white text, so the action
   inherits that white and leans on an underline to read as actionable. A themed
   color here (e.g. blue) disappears into the variant's background. */
.toast__action {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 0;
  padding: 0 var(--space-3);
  font: inherit;
  font-weight: var(--font-weight-semibold);
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.toast__action:hover {
  background: rgba(255, 255, 255, 0.18);
}
.toast__action:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7);
}

.toast__close {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: var(--space-2);
  background: transparent;
  border: 0;
  border-radius: 4px;
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.toast__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.toast__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-focus-ring);
}
.toast__close > svg { width: 12px; height: 12px; }

/* ----- Tone modifiers ----- */
.toast--info {
  background: var(--color-blue);
  color: #fff;
}

.toast--success {
  background: var(--color-green-dark);
  color: #fff;
}

.toast--warning {
  background: var(--color-yellow);
  color: #fff;
}

.toast--danger {
  background: var(--color-red);
  color: #fff;
}

/* ----- Animation states (toggled by JS) ----- */
.toast.is-entering {
  opacity: 0;
  transform: translateY(8px);
}
.toast.is-leaving {
  opacity: 0;
  transform: translateY(-4px);
}


/* ===========================================================
   24. SEGMENT — .segment
   Visual reference: tweak segments in Contact Detail / Property
   Detail / Task Detail filter drawers (tweak-segment / cdv-tw-seg)
   Use anywhere you need a 2–4 option selector with a single
   active state. Distinct from .tabs--pills: tabs change page
   content; segments change a setting/filter/display preference.
   =========================================================== */

.segment {
  display: inline-flex;
  align-items: center;
  background: var(--color-bg-off);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-1); /* 2px */
  gap: 0;
  width: 100%;
}

.segment__item {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-6);
  background: transparent;
  border: 0;
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--easing-default), color var(--duration-fast) var(--easing-default), box-shadow var(--duration-fast) var(--easing-default);
}
.segment__item:hover:not(.is-active) {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.segment__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.segment__item:disabled,
.segment__item.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.segment__item.is-active {
  background: var(--color-bg);
  color: var(--color-text);
  font-weight: var(--font-weight-semibold);
  box-shadow: var(--shadow-sm);
}

/* ===========================================================
   KEY CAP — .kbd
   A single keyboard key drawn inline in a sentence, for telling
   somebody which key does the thing they are already doing:
   "Enter saves · Esc closes · next time press N".

   Visual reference: the note box on a task (Tasks > a task >
   Task Activity), where it teaches the N shortcut at the moment
   the shortcut would have helped.

   It is deliberately quiet. A row of bright chips under a text
   box competes with the box; this is the weight of the small
   print it sits in, with just enough edge to read as a key.

   `line-height: 1` and the explicit padding are what keep it
   from making its line taller than the sentence around it.
   =========================================================== */
.kbd {
  display: inline-block;
  min-width: 18px;
  padding: 1px var(--space-2);
  border: 1px solid var(--color-border-hover);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--color-bg);
  font-family: var(--font-body);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  text-align: center;
  color: var(--color-text-secondary);
  white-space: nowrap;
}


/* ----- `.segment--toolbar` — THE ONE THAT SITS IN A TABLE'S WHITE BAR -------
   The price book's category filter (All · Lawn · Pest · Discounts · Packages)
   is this shape, and JP asked for the custom fields screen's two filters to
   look *"exactly the same"* as it (2026-10-19). So the shape is here, in the
   shared component, rather than copied into a second page's stylesheet.

   The base `.segment` is built for a filter drawer: full width, with its three
   or four choices sharing the space equally. In a toolbar it has to be as wide
   as its words and no wider, sit beside other controls at a control's height,
   and wrap onto a second line on a narrow window instead of pushing the
   toolbar sideways.

   Everything that makes it LOOK like itself — the tinted trough, the white
   raised chip on the chosen one, the weight change — is the base component and
   is untouched. The hover is a colour change only, because a tinted hover on a
   28px chip inside a tinted trough reads as a second selection.

   `price-book.css`'s own `.cat-tabs`/`.cat-tab` are the same measurements and
   predate this; they are the visual reference above, so the two have to stay in
   step. `docs/screens/probe60.js` measures one against the other. */
.segment--toolbar {
  width: auto;
  gap: 1px;
  padding: 3px;
  row-gap: 3px;
  flex-wrap: wrap;
  border-color: var(--color-border-light);
  border-radius: 10px;
}
.segment--toolbar .segment__item {
  flex: 0 0 auto;
  gap: 5px;
  height: 28px;
  padding: 0 var(--space-4);
  border-radius: 7px;
  font-size: var(--font-size-caption);
  white-space: nowrap;
}
.segment--toolbar .segment__item:hover:not(.is-active) {
  background: transparent;
  color: var(--color-text);
}

/* ----- Size modifiers ----- */
.segment--sm .segment__item {
  padding: var(--space-2) var(--space-5);
  font-size: var(--font-size-micro);
}

.segment--lg .segment__item {
  padding: var(--space-5) var(--space-7);
  font-size: var(--font-size-small);
}


/* ===========================================================
   25. SIDE PANEL — .side-panel
   Persistent right-side panel adjacent to main content. Not a
   modal — no backdrop, content behind stays interactive.
   Visual reference: Inbox right-side context panel
   (contact info, tasks, helpful videos — content is page-
   specific and not part of the component).

   This component is chrome only: surface, left border,
   padding, height modifiers, and slide-in/out animation
   states. Content inside is composed from existing primitives
   in page CSS, the same way .card and .modal work.

   Distinct from .modal--drawer: that's a centered modal that
   slides in (with a backdrop, modal-blocking). This is a
   layout region (no backdrop, persistent).
   =========================================================== */

.side-panel {
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border-left: 1px solid var(--color-border);
  /* No top, right, or bottom border — top is handled by the
     navigation bar above; right is the page edge; bottom flows
     with content or container. */
  overflow: hidden; /* __body is the scrolling region */
  font-family: var(--font-body);
  color: var(--color-text);
  /* Default state at rest, ready for JS to toggle .is-entering /
     .is-leaving on mount/unmount. */
  transform: translateX(0);
  opacity: 1;
  transition:
    transform var(--duration-slow) var(--easing-default),
    opacity var(--duration-slow) var(--easing-default);
}

/* ----- Optional sub-elements -----
   Pages may use any subset of these. Body is the only one
   that's typically present in every panel. */

.side-panel__head {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-7) var(--space-9); /* 14 20 */
  border-bottom: 1px solid var(--color-border-light);
  flex-shrink: 0;
  position: relative; /* anchors __close */
}

.side-panel__title {
  font-family: var(--font-display);
  font-size: var(--font-size-h4); /* 18px */
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-h4);
  letter-spacing: var(--letter-spacing-display);
  color: var(--color-text);
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.side-panel__close {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--easing-default),
    color var(--duration-base) var(--easing-default);
}
.side-panel__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.side-panel__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.side-panel__close > svg { width: 18px; height: 18px; }

.side-panel__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-9); /* 20px — generous, content sits inside */
}

.side-panel__foot {
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  padding: var(--space-7) var(--space-9); /* 14 20 */
  border-top: 1px solid var(--color-border-light);
  flex-shrink: 0;
  background: var(--color-bg);
}

/* ----- Height modifiers ----- */

/* Default (no modifier): contained — height inherited from
   container. Sits within the bounds of whatever parent grid
   or flex row it's placed in. */

/* Full — spans full viewport height. Use when the panel is a
   top-level page region rather than nested inside a card. */
.side-panel--full {
  height: 100vh;
  position: sticky;
  top: 0;
}

/* ----- Animation states (JS toggles these on mount/unmount) -----
   Same contract as .toast: JS adds .is-entering on mount,
   removes it after animation completes; adds .is-leaving
   before unmount, then removes the element after the
   transition. */
.side-panel.is-entering {
  transform: translateX(100%);
  opacity: 0;
}
.side-panel.is-leaving {
  transform: translateX(100%);
  opacity: 0;
}

/* ----- The fixed-overlay info panel — `.ctd-info-panel` -----
   Moved here from pages/contacts/details.css (2026-10-24, plan
   docs/features/contact-property-status/
   plan-cancellation-reasons-and-lifecycle-panels.md §3.5) when the
   property screen grew the exact same panel. It was keyed to
   `#contact-info-panel` by id, which is why it never applied to anything
   but the contact page — generalized to the class every panel of this
   kind already carries (`class="side-panel ctd-info-panel"`) rather than
   duplicated per id. `.ctd-info-panel` carries no rules of its own beyond
   these; the `ctd-` prefix is a naming leftover from where this started
   (Contact Details), not a sign it is contacts-only.

   Positioning + hidden baseline only — the rest of `.side-panel`'s own
   chrome (background, border-left, transition) above still applies. */
.ctd-info-panel {
  position: fixed;
  top: var(--topbar-height);
  right: 0;
  width: var(--width-side-panel);
  height: calc(100vh - var(--topbar-height));
  z-index: var(--z-dropdown);
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
  box-shadow: var(--shadow-lg);
}

.ctd-info-panel.visible {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

@media (max-width: 835px) {
  .ctd-info-panel {
    width: 100%;
    box-shadow: none;
    border-left: none;
    border-top: 1px solid var(--color-border);
  }
}

/* ----- Info section chrome — `.ctd-info-section` -----
   A labeled block inside a `.ctd-info-panel`'s body. Moved here the same
   day, for the same reason: the property panel's one section (Lifecycle)
   needs the identical chrome, not a second copy of it. */
.ctd-info-section {
  border-bottom: 1px solid var(--color-border-light);
  padding: var(--space-7) 0;
}

.ctd-info-section:first-child { padding-top: 0; }
.ctd-info-section:last-child  { border-bottom: none; }

.ctd-info-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.ctd-info-section__title {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.ctd-info-section__body {
  /* wrapper — no extra padding beyond the section's own */
}

.ctd-info-empty {
  font-size: var(--font-size-small);
  color: var(--color-text-dim);
  font-style: italic;
}

/* ----- Lifecycle timeline — `.lifecycle-timeline` -----
   Moved here the same day, for the same reason as the two above: the
   property panel's Lifecycle section reads this exact structure, with its
   own statuses instead of a contact's. `dotClass`/`itemClass` are emitted
   by whichever service built the timeline — `services/contactLifecycle.js`
   for a contact, `services/propertyLifecycle.js` for a property — using
   the SAME small set of color names (`blue`, `green`, `red`, `orange`,
   `gray`) so a status reads the same color on either screen.

   The TYPE-NAME modifiers that recolor the two most recent rows' own text
   (`.lifecycle-lead`, `.lifecycle-customer`, `.lifecycle-former-customer`,
   `.lifecycle-lost-lead`) are contact vocabulary and stay in
   pages/contacts/details.css; a property's equivalents live in its own
   page stylesheet, following the same pattern with its own names. */
.lifecycle-timeline {
  padding-top: var(--space-2);
}

.lifecycle-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

.lifecycle-item:last-child {
  margin-bottom: 0;
}

.lifecycle-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border);
  margin-top: 4px;
  flex-shrink: 0;
}

.lifecycle-dot--active {
  background: var(--color-blue);
}

/* **EVERY ROW CARRIES ITS STATUS COLOR** — the same colors as the status
   chips (`.chip--customer` green, `.chip--former-customer` red, `.chip--lead`
   blue, `.chip--lost-lead` orange; a property's Active green and Former
   red). Until 2026-11-01 only the final two rows were colored and older
   history sat in neutral grey; once a contact's list began carrying its
   properties' own lines, those two slots went to the property rows and the
   contact's own statuses showed no color at all. JP, 2026-11-01: *"Under
   lifecycle in the panel, there are currently no colors applied… we want
   to match those colors."* */
.lifecycle-dot.blue {
  background: var(--color-blue);
}

.lifecycle-dot.green {
  background: var(--color-green);
}

.lifecycle-dot.red {
  background: var(--color-red);
}

.lifecycle-dot.orange {
  background: var(--color-task);
}

.lifecycle-dot.gray {
  background: var(--color-text-dim);
}

.lifecycle-content {
  min-width: 0;
}

.lifecycle-type {
  display: block;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  margin-bottom: var(--space-1);
}

.lifecycle-date {
  display: block;
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
}

/* Which property a row is about, after the status word (2026-10-30) — a
   contact's list carries its properties' own start/cancel lines. Lighter
   than the status word so "Former" still reads first; it may wrap, since an
   address cut short is an address nobody can tell apart from the next. */
.lifecycle-where {
  font-weight: var(--font-weight-regular);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

/* The reason underneath a row (a lost lead's, a cancellation's) — same
   small, quiet treatment as the date above it, not a second thing
   competing for attention. Italic is the only difference: it reads as
   somebody's own words, not a fact like the date is. */
.lifecycle-reason {
  display: block;
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  font-style: italic;
  margin-top: 2px;
}

/* ----- A correctable row's reason line, with its pencil -----
   JP, 2026-10-24: one pencil, always shown on a row that can be corrected,
   never a text link that only appears once there is something to correct —
   the reason is required going forward, so a blank one is only ever an old
   row, and the pencil sits where the reason line would be rather than the
   row changing shape depending on what it has to say. `.lifecycle-reason`
   keeps its own look; this only lays the two out side by side. */
.lifecycle-reason-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 2px;
}
.lifecycle-reason-row .lifecycle-reason {
  flex: 1;
  min-width: 0;
  margin-top: 0;
}
.lifecycle-edit-reason {
  flex-shrink: 0;
}


/* ===========================================================
   26. MESSAGE — .message
   Visual reference: SMS Inbox conversation thread.
   Used in SMS Inbox, Contact Detail, Property Detail, and
   Task Detail wherever a chronological message thread renders.

   Anatomy:
     __avatar          — positioning slot for an .avatar
     __bubble          — the text-holding shape
     __meta            — timestamp + sender footer
     __actions         — hover-only floating action toolbar
     __task-indicator  — cream badge shown when the message
                         has been converted to a task

   Modifiers:
     --inbound (default) — grey bubble, avatar left
     --outbound          — blue bubble, avatar right

   States:
     .is-converted-to-task — shows __task-indicator
   =========================================================== */

.message {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-5); /* 10px between avatar and bubble */
  align-items: flex-start;
  position: relative;
  font-family: var(--font-body);
}

/* The bubble + meta column. A wrapper inside the grid so the
   meta sits flush under the bubble, not under the avatar. */
.message__bubble-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  max-width: 70%;
  position: relative;
}

.message__avatar {
  flex-shrink: 0;
  /* Avatar size is whatever the .avatar inside this slot
     specifies — typically .avatar (32px). */
}

.message__bubble {
  padding: var(--space-6) var(--space-7); /* 12 14 */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 4px var(--radius-lg) var(--radius-lg) var(--radius-lg);
  /* 4px top-left (tucks against avatar), 16px elsewhere — matches
     SMS Inbox source design (.msg-bub). Outbound flips the corners. */
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  color: var(--color-text);
  white-space-collapse: preserve-breaks;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.message__meta {
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  line-height: 1.3;
}

/* ----- Hover action toolbar -----
   Floating cluster of icon buttons that appears above the
   bubble on hover. Hidden by default; shown when the parent
   .message is hovered or focused-within.

   Sizes match the SMS Inbox source design (.msg-hover-actions):
   26×26 buttons with 14×14 SVG icons, 2px container padding,
   8px radius. Buttons are NOT .btn instances — sized too small
   for the standard btn system. Use the __action sub-element. */
.message__actions {
  position: absolute;
  top: -14px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  padding: 2px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); /* 8px */
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
  opacity: 0;
  transform: translateY(4px);
  transition:
    opacity var(--duration-fast) var(--easing-default),
    transform var(--duration-fast) var(--easing-default);
  pointer-events: none;
  z-index: 1;
}
.message:hover .message__actions,
.message:focus-within .message__actions,
.message__actions:hover {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Action button — sized to fit the compact hover toolbar.
   Distinct from .btn because .btn--icon's smallest size (.btn--sm
   at 28px) is too tall for the 14px-tall floating toolbar. */
.message__action {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 5px;
  color: var(--color-text-secondary);
  cursor: pointer;
  padding: 0;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.message__action:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.message__action:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-focus-ring);
}
.message__action > svg {
  width: 14px;
  height: 14px;
}

/* TeamChat action — purple hover state (matches source). */
.message__action--teamchat:hover {
  background: #F5F3FF;
  color: #6D28D9;
}
.message__action--teamchat.is-active {
  background: #F5F3FF;
  color: #6D28D9;
}

/* ----- Task indicator -----
   Peach circular badge in the top-right of the bubble.
   Persistent (not hover-only) when the parent has
   .is-converted-to-task. Matches SMS Inbox source design
   (.msg-bub-pin). */
.message__task-indicator {
  position: absolute;
  top: -8px;
  right: -10px;
  width: 28px;
  height: 28px;
  display: none; /* hidden until .is-converted-to-task on parent */
  align-items: center;
  justify-content: center;
  background: #FFEDD5;
  color: #C2410C;
  border: 2px solid var(--color-bg);
  border-radius: 50%;
  box-shadow:
    0 2px 4px rgba(154, 52, 18, 0.18),
    0 0 0 1px rgba(194, 65, 12, 0.08);
  cursor: pointer;
  padding: 0;
  z-index: 2; /* above __actions */
  transition: transform var(--duration-fast) var(--easing-default);
}
.message__task-indicator:hover {
  transform: scale(1.05);
}
.message__task-indicator > svg {
  width: 13px;
  height: 13px;
}
.message.is-converted-to-task .message__task-indicator {
  display: inline-flex;
}

/* ----- Inbound (default) -----
   Avatar left, grey bubble, meta left-aligned. */
.message,
.message--inbound {
  /* Default grid: avatar | bubble. Already set above. */
}
.message--inbound .message__bubble {
  background: var(--color-bg);
  border-color: var(--color-border);
  color: var(--color-text);
  /* Inherits the inbound 4px-top-left bubble shape from the
     base __bubble rule. */
}
.message--inbound .message__actions {
  /* Default position is right: 10px (set on .message__actions
     above). Inbound bubbles inherit it as-is. */
}
/* When a task is attached, the indicator sits at right: -10px
   and overlaps the actions cluster. Slide actions further left
   to clear it. Matches source .has-task .msg-hover-actions. */
.message--inbound.is-converted-to-task .message__actions {
  right: 36px;
}
.message--inbound .message__task-indicator {
  /* Stays at top-right of the bubble (default position). */
}

/* ----- Outbound -----
   Avatar right, blue bubble, meta right-aligned. Reverses
   the grid columns so the bubble comes before the avatar. */
.message--outbound {
  grid-template-columns: 1fr auto;
}
.message--outbound .message__avatar {
  grid-column: 2;
  grid-row: 1;
}
.message--outbound .message__bubble-wrap {
  grid-column: 1;
  grid-row: 1;
  margin-left: auto; /* pushes the bubble to the right within its column */
  align-items: flex-end;
}
.message--outbound .message__bubble {
  background: var(--color-blue);
  border-color: var(--color-blue);
  color: var(--color-text-on-action);
  /* Flip the small corner from top-left to top-right since
     outbound messages have the avatar on the right. */
  border-radius: var(--radius-lg) 4px var(--radius-lg) var(--radius-lg);
}
.message--outbound .message__meta {
  text-align: right;
}
.message--outbound .message__actions {
  right: auto;
  left: 10px;
}
/* When converted to task on outbound, slide actions further
   right to clear the (top-left) task indicator. */
.message--outbound.is-converted-to-task .message__actions {
  left: 36px;
}
/* On outbound messages, the task indicator (when present)
   sits top-LEFT of the bubble so it doesn't collide with the
   avatar on the right. Not commonly used since outbound
   messages aren't typically converted to tasks, but supported
   for completeness. */
.message--outbound .message__task-indicator {
  right: auto;
  left: -10px;
}


/* ===========================================================
   27. MESSAGE DAY SEPARATOR — .message-day-separator
   Centered uppercase label between groups of messages
   ("MONDAY, APR 13"). Sits in the message thread flow as a
   sibling of .message instances, not nested inside one.
   =========================================================== */

.message-day-separator {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-7) 0; /* 14px vertical breathing room */
  font-family: var(--font-body);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-uppercase);
  color: var(--color-text-dim);
  text-align: center;
}


/* ===========================================================
   28. EMAIL MESSAGE — .email-message
   Visual reference: Email Thread View. Used in Email Inbox
   thread view and any detail-page email thread (Contact,
   Property, Task) wherever an email renders as a message
   in a chronological thread.

   Rendering model: pages render the FULL anatomy at all times.
   The --collapsed modifier hides the sub-elements that don't
   belong on a collapsed row (display: none) and restructures
   the head into a single-row layout. This means switching
   between modes is a single class flip — no re-render needed.

   Two visual modes (modifiers):
     --expanded (default) — full anatomy visible
     --collapsed          — single-row preview (avatar + sender
                            + truncated body + timestamp)

   Anatomy:
     __avatar              — slot for an .avatar
     __task-indicator      — cream task badge, child of __avatar;
                              visibility driven by .is-converted-to-task
     __head                — top row: sender (left) + meta (right)
     __sender              — sender info column (or row in collapsed)
     __sender-name         — name (always visible)
     __unread-dot          — blue dot, shown when .is-unread
     __sender-line         — wrapper for __sender-email + __recipients
                              (hidden in collapsed)
     __sender-email        — "<email@domain.com>"
     __recipients          — "to X · cc Y" line
     __preview             — single-line truncated preview
                              (visible only in collapsed mode)
     __meta                — right side of head: timestamp + actions
     __timestamp
     __head-actions        — cluster of icon buttons (reply, info)
                              (hidden in collapsed)
     __body                — paragraphs of email content
                              (hidden in collapsed)
     __linked-task         — cream task strip (email-specific design;
                              hidden in collapsed). Has its own anatomy:
                              __linked-task-icon, __linked-task-body,
                              __linked-task-eyebrow, __linked-task-title,
                              __linked-task-meta, __linked-task-status,
                              __linked-task-due, __linked-task-open
     __attachments         — wrapper for Attachment Box components
                              (hidden in collapsed)
     __action-bar          — Reply / Reply all / Forward / Send TeamChat
                              row. Latest message in a thread only.
                              (hidden in collapsed). Sub-elements:
                              __action (positioning slot), with
                              __action--teamchat color modifier
     __info-popover        — floating panel with TO/FROM/DATE/etc.
                              (hidden in collapsed). Sub-elements:
                              __info-row, __info-label, __info-value,
                              __info-sender, __info-sender-name,
                              __info-sender-role

   States:
     .is-unread            — shows __unread-dot
     .is-converted-to-task — shows __task-indicator on __avatar
     .is-info-open         — shows __info-popover
   =========================================================== */

.email-message {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); /* 16px */
  font-family: var(--font-body);
  color: var(--color-text);
  overflow: hidden; /* keeps __action-bar's bottom edge inside the radius */
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}

.email-message:focus-visible {
  outline: none;
}

/* --latest modifier — blue-tinted border + soft glow. Indicates
   the latest message in a thread (typically the most-recently-
   received email, often shown expanded with the action bar
   visible). Matches source .msg-unread treatment. */
.email-message--latest {
  border-color: var(--color-blue-pale);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.06);
}

/* ----- __head: top bar with avatar, sender info, meta ----- */
.email-message__head {
  display: flex;
  align-items: center;
  gap: var(--space-7); /* 14px */
  padding: var(--space-7) var(--space-9) var(--space-5); /* 14 20 10 */
  border-bottom: 1px solid var(--color-border-light);
  position: relative; /* anchors __info-popover */
}

.email-message__avatar {
  flex-shrink: 0;
  display: inline-flex; /* wraps tightly around .avatar */
  position: relative; /* anchors task indicator overlay */
}
/* Task indicator overlay on the avatar — peach circle, same
   visual language as .message__task-indicator. Hidden until
   parent has .is-converted-to-task. Matches source .msg-av-pin. */
.email-message__avatar .email-message__task-indicator {
  position: absolute;
  top: -3px;
  right: -3px;
  width: 18px;
  height: 18px;
  display: none;
  align-items: center;
  justify-content: center;
  background: #FFEDD5;
  color: #C2410C;
  border: 1.5px solid var(--color-bg);
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(154, 52, 18, 0.25);
  z-index: 1;
}
.email-message__avatar .email-message__task-indicator > svg {
  width: 10px;
  height: 10px;
  color: inherit;
}
.email-message.is-converted-to-task .email-message__task-indicator {
  display: inline-flex;
}

.email-message__sender {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* 4px between name line and email/recipients line */
}

/* First line: name + unread dot */
.email-message__sender-name {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  color: var(--color-text);
}

.email-message__unread-dot {
  display: none; /* hidden until .is-unread on parent */
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-blue);
  flex-shrink: 0;
}
.email-message.is-unread .email-message__unread-dot {
  display: inline-block;
}

/* Second line: <email> · to X · cc Y. Each piece is its own
   sub-element so pages can render only what's relevant. */
.email-message__sender-email,
.email-message__recipients {
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.3;
}

/* Inline these on the same line. The middle-dot separator is a
   ::before on __recipients so the markup stays clean. */
.email-message__sender-email {
  display: inline;
}
.email-message__recipients {
  display: inline;
}
.email-message__recipients::before {
  content: none;
}
/* Wrap email + recipients so they share one flex/text line */
.email-message__sender-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0;
}

/* ----- __meta: right side of head ----- */
.email-message__meta {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  margin-left: auto;
}

.email-message__timestamp {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  white-space: nowrap;
}

.email-message__head-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1); /* 2px */
}
/* Active state for buttons in __head-actions — used when the
   info toggle is open (parent has .is-info-open). Mirrors hover
   state. .btn does not yet have a generic .is-active treatment;
   when it does, this can be removed. */
.email-message__head-actions .btn.is-active {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
/* Action icon buttons inside __head-actions are .btn instances:
   <button class="btn btn--ghost btn--icon btn--sm"> for each.
   When the info button is in the active state, page CSS toggles
   .is-info-open on the parent .email-message, which surfaces
   __info-popover (rule below). */

/* ----- __body: paragraphs (expanded only) ----- */
.email-message__body {
  padding: var(--space-6) var(--space-9) var(--space-7); /* 12 20 14 */
  font-size: var(--font-size-body);
  line-height: var(--line-height-body-lg);
  color: var(--color-text);
}
.email-message__body > p + p {
  margin-top: var(--space-6); /* 12px between paragraphs */
}

/* ----- __preview: single-line truncated body, collapsed-mode only -----
   Hidden by default (expanded mode shows __body instead).
   The --collapsed modifier flips both --body and --preview. */
.email-message__preview {
  display: none; /* shown when parent has --collapsed */
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.email-message__preview::before {
  content: "—";
  margin-right: var(--space-3);
  color: var(--color-text-dim);
}

/* ----- email-reply-quote: prior message quoted into a reply's body,
   inserted only while a CC is present so the new recipient has context ----- */
.email-reply-quote__meta {
  font-size: var(--font-size-small);
  color: var(--color-text-dim);
  margin-bottom: var(--space-3);
}

/* ----- __linked-task: cream task strip with its own anatomy -----
   Email-specific design. Distinct from the future Task Row
   component (which appears in detail-page sidebars). The strip
   communicates "this email created/linked a task" with full
   task context inline.

   Anatomy:
     __linked-task-icon     — small cream-bordered icon box
     __linked-task-body     — left wrapper (eyebrow + title)
     __linked-task-eyebrow  — "LINKED TASK" warm uppercase label
     __linked-task-title    — the task name
     __linked-task-meta     — right wrapper (status + due + open)
     __linked-task-status   — slot for a .chip showing task status
     __linked-task-due      — due date text
     __linked-task-open     — external-link icon button (slot for .btn) */
.email-message__linked-task {
  display: flex;
  align-items: center;
  gap: var(--space-6); /* 12px between icon, body, and meta */
  margin: 0 var(--space-9) var(--space-7); /* 0 20 14 */
  padding: var(--space-6) var(--space-7); /* 12 14 */
  background: #FFF7ED;
  border: 1px solid #FED7AA;
  border-left: 4px solid #C2410C;
  border-radius: var(--radius-sm);
  color: #9A3412;
}

.email-message__linked-task-icon {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #FFEDD5;
  border-radius: 6px;
  color: #C2410C;
}
.email-message__linked-task-icon > svg {
  width: 16px;
  height: 16px;
}

.email-message__linked-task-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* 4px between eyebrow and title */
}

.email-message__linked-task-eyebrow {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-uppercase);
  color: #C2410C;
  line-height: 1;
}

.email-message__linked-task-title {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.email-message__linked-task-meta {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
}

/* The status chip slot composes .chip — page CSS picks the
   right semantic chip modifier (e.g. .chip--waiting-on-customer
   for "Waiting"). No additional styling needed here. */
.email-message__linked-task-status {
  /* Positioning hook only — visual treatment from .chip */
}

.email-message__linked-task-due {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  white-space: nowrap;
}

/* External-link button. Markup uses .btn composition with this
   positioning class:
     <button class="btn btn--ghost btn--icon btn--sm
                    email-message__linked-task-open"> */
.email-message__linked-task-open {
  color: var(--color-cream-text-dim);
}
.email-message__linked-task-open:hover {
  background: rgba(180, 83, 9, 0.12);
  color: var(--color-cream-text-strong);
}

/* ----- __attachments: wrapper for one or more .email-attachment ----- */
/* Flex-wrapped so multiple attachments flow horizontally and
   wrap to a new line when the row is full. */
.email-message__attachments {
  margin: 0 var(--space-9) var(--space-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4); /* 8px between attachments */
}

/* ----- __action-bar: Reply / Reply all / Forward / Send TeamChat ----- */
/* Latest message in a thread only — page CSS decides which
   message gets this rendered. */
.email-message__action-bar {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--space-4); /* 8px */
  padding: var(--space-6) var(--space-9) var(--space-7); /* 12 20 14 */
  border-top: 1px solid var(--color-border-light);
  overflow-x: auto;
}

/* Each action is a pill-shaped button. Self-contained — does
   NOT compose .btn. Renders correctly with just the local
   class, since the visual treatment is specific to this
   action bar (pill shape, this exact padding, this exact
   font weight). */
.email-message__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px between icon and label */
  padding: var(--space-4) var(--space-6); /* 8 12 */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    border-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-message__action:hover {
  background: var(--color-bg-off);
  border-color: var(--color-border-hover);
  color: var(--color-text);
}
.email-message__action:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.email-message__action > svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* Divider — vertical line between bordered actions and the
   Send TeamChat button. Real sub-element (not a pseudo) so
   spacing/alignment is predictable. Sits as a sibling between
   the last bordered action and __action--teamchat. */
.email-message__action-divider {
  width: 1px;
  align-self: stretch;
  margin: var(--space-2) 0; /* 4px top/bottom — matches source .msg-quick-sep */
  background: var(--color-border);
  flex-shrink: 0;
}

/* Send TeamChat — same chrome as other actions, only the
   text/icon color differ. On hover: purple-tinted bg with
   purple text/icon. Border behaves identically to siblings. */
.email-message__action--teamchat {
  color: var(--color-purple);
}
.email-message__action--teamchat:hover {
  background: #F5F3FF;
  border-color: var(--color-border-hover);
  color: #5B21B6;
}
.email-message__action--teamchat > svg {
  color: var(--color-purple);
}
.email-message__action--teamchat:hover > svg {
  color: #5B21B6;
}

/* ----- __info-popover: floating TO/FROM/DATE/SENT-WITH/SENT-BY panel -----
   Anchored to the info button in __head-actions. The button toggles
   .is-info-open on the parent .email-message to surface the popover.
   Has a small arrow at the top-right pointing back at the trigger.

   Vertical position: calc(100% - 12px) overlaps into the head's
   bottom padding so the popover sits right below the info icon
   (rather than below the entire head — which would leave a
   large dead gap between the trigger and the panel). */
.email-message__info-popover {
  position: absolute;
  top: calc(100% - 12px);
  right: var(--space-9); /* aligns with the head's right padding (20px) */
  z-index: var(--z-popover);
  width: 340px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius); /* 12px */
  box-shadow:
    0 16px 40px -8px rgba(15, 23, 42, 0.18),
    0 4px 12px -2px rgba(15, 23, 42, 0.08);
  padding: var(--space-7) var(--space-8); /* 14 16 */
  display: none; /* shown only when .is-info-open on parent */
  flex-direction: column;
  gap: var(--space-4); /* 8px between rows */
  font-size: var(--font-size-small);
  cursor: default;
}
/* Arrow tip pointing up at the info button trigger. */
.email-message__info-popover::before {
  content: "";
  position: absolute;
  top: -5px;
  right: 18px;
  width: 10px;
  height: 10px;
  background: var(--color-bg);
  border-top: 1px solid var(--color-border);
  border-left: 1px solid var(--color-border);
  transform: rotate(45deg);
}
.email-message.is-info-open .email-message__info-popover {
  display: flex;
}

.email-message__info-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-5); /* 10px */
  font-size: var(--font-size-caption); /* 12px */
  line-height: 1.45;
}
/* No row dividers — source design keeps them as a clean stack. */

.email-message__info-label {
  flex-shrink: 0;
  padding-top: 1px; /* visual baseline alignment with value */
  font-size: 10.5px;
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-dim);
}

.email-message__info-value {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  word-break: break-word;
}
.email-message__info-value--muted {
  color: var(--color-text-dim);
  font-style: italic;
}

/* The "SENT BY" row offsets its label slightly to align with
   the avatar (which is taller than a normal text line). */
.email-message__info-row--sender .email-message__info-label {
  padding-top: 5px;
}
.email-message__info-sender {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
}
.email-message__info-sender-name {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.email-message__info-sender-name-line {
  font-size: 12.5px;
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  line-height: 1.2;
}
.email-message__info-sender-role {
  font-size: var(--font-size-micro);
  color: var(--color-text-dim);
  line-height: 1.25;
}

/* ----- --collapsed: single-row dense layout -----
   Same markup as expanded; the modifier hides everything that
   doesn't belong on a collapsed row, restructures __head into
   a flat row, and reveals __preview.

   What the modifier does:
     - Hides __sender-line (email + recipients), __head-actions,
       __body, __linked-task, __attachments, __action-bar,
       and __info-popover.
     - Reveals __preview.
     - Restructures __head and __sender for a single-row layout.
     - Tightens padding and changes border radius. */
.email-message--collapsed {
  border-radius: var(--radius);
}
.email-message--collapsed .email-message__head {
  align-items: center;
  padding: var(--space-5) var(--space-7); /* 10 14 */
  border-bottom: 0;
  gap: var(--space-5);
}
.email-message--collapsed .email-message__sender {
  flex-direction: row;
  align-items: center;
  gap: 0; /* preview's ::before "—" handles the spacing */
  flex: 1;
  min-width: 0;
}
.email-message--collapsed .email-message__sender-name {
  flex-shrink: 0;
}

/* Reveal __preview only in collapsed mode */
.email-message--collapsed .email-message__preview {
  display: block;
}

/* Hide everything that doesn't belong on a collapsed row */
.email-message--collapsed .email-message__sender-line,
.email-message--collapsed .email-message__head-actions,
.email-message--collapsed .email-message__body,
.email-message--collapsed .email-message__linked-task,
.email-message--collapsed .email-message__attachments,
.email-message--collapsed .email-message__action-bar {
  display: none;
}

/* Info popover suppression — even if .is-info-open is somehow
   set on a collapsed row, the popover stays hidden because
   collapsed rows don't have an info button visible. */
.email-message--collapsed.is-info-open .email-message__info-popover {
  display: none;
}

/* Scheduled (queued, not yet sent) variant */
.email-message--scheduled {
  border-color: var(--color-yellow);
  border-style: dashed;
}
.email-message--scheduled .email-message__head {
  background: var(--color-yellow-glow);
}
.email-message--scheduled .email-message__body {
  padding: 12px 16px 0;
}
.email-message--scheduled .email-message__attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 16px 14px;
}

/* Attachment pill used inside scheduled email cards */
.email-attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--color-border);
  border-radius: 100px;
  background: var(--color-bg-off);
  font-size: var(--font-size-caption);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--duration-base), border-color var(--duration-base);
}
.email-attachment-chip:hover {
  background: var(--color-bg-hover);
  border-color: var(--color-blue);
}
.email-attachment-chip__size {
  color: var(--color-text-dim);
}


/* ===========================================================
   29. ACTION BAR — .action-bar
   Visual reference: Property/Contact/Task Detail header pill.
   The blue rounded-pill bar with primary actions
   (Text / Email / Call / More).

   The bar appears on Contact, Task, and Property detail pages.
   Visual treatment is identical across all three; only the
   menu items inside the More dropdown vary per page. The
   component is chrome only — pages provide the action markup.

   Anatomy:
     __action          — each action button (Text / Email / Call / More)
     __action-label    — label text inside an action
     __divider         — thin vertical translucent-white line
                          between adjacent actions
     __menu            — dropdown panel (typically anchored to More)
     __menu-item       — each option inside the menu
     __menu-icon       — icon slot inside a menu item
     __menu-label      — label slot inside a menu item
     __menu-divider    — horizontal divider between menu groups

   Modifiers:
     __menu-item--danger — red text for destructive options
                            (e.g., "Archive contact")

   States:
     __action.is-active — pressed/highlighted (translucent-white bg)
     .is-menu-open      — shows __menu
   =========================================================== */

.action-bar {
  position: relative; /* anchors __menu */
  display: inline-flex;
  align-items: center;
  background: var(--color-blue);
  border-radius: var(--radius-pill);
  padding: 3px;
  box-shadow: var(--shadow-button-action-rest);
  font-family: var(--font-body);
}

.action-bar__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  padding: 9px 18px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: rgba(255, 255, 255, 0.92);
  cursor: pointer;
  white-space: nowrap;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.action-bar__action:hover {
  background: rgba(255, 255, 255, 0.14);
  color: var(--color-text-on-action);
}
.action-bar__action:focus-visible {
  outline: 0;
  background: rgba(255, 255, 255, 0.14);
  color: var(--color-text-on-action);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
}
.action-bar__action.is-active {
  background: rgba(255, 255, 255, 0.22);
  color: var(--color-text-on-action);
}
.action-bar__action:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: transparent;
}
.action-bar__action > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: currentColor;
}

.action-bar__action-label {
  /* Hook only; styled inline by parent. */
}

/* Vertical divider between actions. Sits as a sibling of
   __action elements directly inside .action-bar. */
.action-bar__divider {
  width: 1px;
  height: 16px;
  background: rgba(255, 255, 255, 0.22);
  align-self: center;
  flex-shrink: 0;
}

/* ----- Menu (dropdown anchored to a __action) -----
   Hidden by default; revealed by .is-menu-open on the parent
   .action-bar. Page CSS / JS positions it relative to the
   triggering action (typically More). */
.action-bar__menu {
  position: absolute;
  top: calc(100% + var(--space-3)); /* 6px below the bar */
  right: 0;
  min-width: 240px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3); /* 6px */
  display: none;
  z-index: var(--z-popover);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  color: var(--color-text);
}
.action-bar.is-menu-open .action-bar__menu {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.action-bar__menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  width: 100%;
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.action-bar__menu-item:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.action-bar__menu-item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.action-bar__menu-item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.action-bar__menu-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-secondary);
}
.action-bar__menu-icon > svg {
  width: 18px;
  height: 18px;
}
.action-bar__menu-item:hover .action-bar__menu-icon {
  color: var(--color-text);
}

.action-bar__menu-label {
  flex: 1;
  min-width: 0;
}

.action-bar__menu-divider {
  height: 1px;
  background: var(--color-border-light);
  margin: var(--space-3) 0; /* 6px vertical breathing room */
  border: 0;
}

/* Danger menu item — for destructive options (e.g., Archive). */
.action-bar__menu-item--danger {
  color: var(--color-red);
}
.action-bar__menu-item--danger .action-bar__menu-icon {
  color: var(--color-red);
}
.action-bar__menu-item--danger:hover {
  background: var(--color-red-glow);
  color: var(--color-red);
}
.action-bar__menu-item--danger:hover .action-bar__menu-icon {
  color: var(--color-red);
}


/* ===========================================================
   30. ATTACHMENT — .attachment
   Visual reference: Task Detail / Property Detail attachments
   grid (the td-att / pd-att pattern).

   Card-style file preview with a colored thumbnail block on top
   and filename + meta below. Used in attachment grids on
   detail pages. For email-attached files, use .email-attachment
   (Component #31) — the visual is meaningfully different.

   Thumb color is driven by file-type modifier on __thumb. For
   custom file types not covered, pages can apply inline style
   overrides on __thumb (same pattern as custom chip colors).

   Anatomy:
     __thumb   — colored thumbnail block
     __icon    — icon inside thumb (e.g., image-placeholder for
                  png/jpg/gif)
     __type    — text label inside thumb (e.g., "PDF", "DOC")
     __body    — wrapper for filename + meta
     __name    — filename (truncates with ellipsis)
     __meta    — size + uploader line
     __remove  — X close button (top-right of thumb)

   Modifiers on __thumb:
     --doc     blue
     --pdf     red
     --image   green
     --video   purple
     --audio   yellow
     --archive neutral
     (no modifier — neutral fallback for unknown file types)

   States:
     none — interaction is hover/click on the X button only.

   Wrapper:
     Use .attachments for the responsive grid wrapper.
   =========================================================== */

/* ----- Grid wrapper for attachment cards ----- */
.attachments {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-5); /* 10px */
}

/* ----- Base attachment ----- */
.attachment {
  position: relative;
  display: flex;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  color: var(--color-text);
  min-width: 0;
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}
.attachment:hover {
  border-color: var(--color-border-hover);
}

/* ----- __thumb: colored thumbnail block (base — color comes from modifier) ----- */
.attachment__thumb {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: var(--color-bg-off); /* default fallback */
  color: var(--color-text-secondary);
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
  flex-shrink: 0;
}

.attachment__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.attachment__icon > svg {
  width: 24px;
  height: 24px;
  color: currentColor;
}

.attachment__type {
  /* Hook only; styling comes from __thumb. */
}

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

.attachment__name {
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.attachment__meta {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.3;
  margin-top: 2px;
}

.attachment__remove {
  position: absolute;
  top: var(--space-2); /* 4px */
  right: var(--space-2);
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  color: var(--color-text-on-action);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  z-index: 2;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.attachment__remove:hover {
  background: rgba(0, 0, 0, 0.85);
}
.attachment__remove:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-text-on-action),
              0 0 0 4px var(--color-focus-ring);
}
.attachment__remove > svg {
  width: 12px;
  height: 12px;
}

/* ----- Layout: thumb on top, body below (the only layout) ----- */
.attachment {
  flex-direction: column;
  padding: var(--space-5); /* 10px */
  gap: var(--space-5);
}
.attachment .attachment__thumb {
  width: 100%;
  height: 80px;
}

/* ----- Thumb color modifiers by file type ----- */
.attachment__thumb--doc {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}
.attachment__thumb--pdf {
  background: var(--color-red-pale);
  color: var(--color-red-dark);
}
.attachment__thumb--image {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}
.attachment__thumb--video {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}
.attachment__thumb--audio {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.attachment__thumb--archive {
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
}
/* --other / default: no modifier needed; the base __thumb rule
   already provides the neutral fallback. */


/* ===========================================================
   31. EMAIL ATTACHMENT — .email-attachment
   Visual reference: Email Thread View — attached files in
   the email body (e.g., "Quote-RT-2026-0412.pdf · 184 KB").

   Compact row-style attachment for files attached to emails.
   Distinct from .attachment (Component #30) — the email
   variant is denser, has a paperclip indicator, and is
   non-removable (you can't un-attach a sent email).

   Lives inside .email-message__attachments, which flex-wraps
   so multiple attachments flow horizontally and wrap when
   the row is full.

   Anatomy:
     __icon       — colored file-icon box (left)
     __body       — wrapper for filename + size
     __name       — filename text
     __size       — file size text
     __indicator  — paperclip icon (right)

   Modifiers on __icon (same color contract as .attachment__thumb):
     --doc     blue
     --pdf     red
     --image   green
     --video   purple
     --audio   yellow
     --archive neutral
     (no modifier — neutral fallback for unknown file types)

   States:
     none — interaction is hover/click on the whole row
     (download/open).
   =========================================================== */

.email-attachment {
  display: inline-flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-4) var(--space-5); /* 8 10 */
  max-width: 320px;
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
  transition:
    background-color var(--duration-base) var(--easing-default),
    border-color var(--duration-base) var(--easing-default);
}
.email-attachment:hover {
  background: var(--color-bg);
  border-color: var(--color-border-hover);
}
.email-attachment:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
  border-color: var(--color-blue);
}

/* ----- __icon: file-icon box (left) ----- */
.email-attachment__icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-off); /* default neutral fallback */
  color: var(--color-text-secondary);
  border-radius: 6px;
}
.email-attachment__icon > svg {
  width: 18px;
  height: 18px;
  color: currentColor;
}

/* ----- __body: filename + size stack ----- */
.email-attachment__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.email-attachment__name {
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.email-attachment__size {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.3;
  margin-top: 1px;
}

/* ----- __indicator: paperclip icon (right) ----- */
.email-attachment__indicator {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: var(--space-5); /* 10px — extra breathing room between
                                  the filename and the paperclip beyond
                                  the parent's flex gap */
  color: var(--color-text-dim);
}
.email-attachment__indicator > svg {
  width: 16px;
  height: 16px;
}

/* ----- File-type icon color modifiers ----- */
.email-attachment__icon--doc {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}
.email-attachment__icon--pdf {
  background: var(--color-red-pale);
  color: var(--color-red-dark);
}
.email-attachment__icon--image {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
}
.email-attachment__icon--video {
  background: var(--color-purple-pale);
  color: var(--color-purple);
}
.email-attachment__icon--audio {
  background: var(--color-yellow-pale);
  color: var(--color-yellow);
}
.email-attachment__icon--archive {
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
}


/* ===========================================================
   32. TASK TABLE — .task-table
   Visual reference: Task Detail subtasks list; Contact Detail
   and Property Detail related-tasks lists (the cdv-tasktbl
   pattern in the source designs).

   NOT to be used on the main Task List screen — that page
   uses a different, denser visual treatment.

   Tabular layout with sortable column headers and rows of
   task data. Built on a real <table> element for semantic
   correctness and screen-reader compatibility.

   Anatomy:
     .task-table              — the <table> root
     .task-table thead        — header row container
     .task-table tbody        — body row container
     .task-table__header-cell — sortable column header (a <th>)
     .task-table__header-label — text label inside header cell
     .task-table__sort-icon   — sort direction arrow (SVG)
     .task-table__row         — body <tr>
     .task-table__cell        — body <td>

   Cell modifiers (apply per column to control width / alignment
   / typography):
     --checkbox  — narrow column, checkbox-only
     --name      — task name, normal weight, flex-grows
     --status    — composes .status-dot + label text
     --time      — duration estimate (e.g., "10 wks")
     --assignee  — assignee name or "Unassigned"
     --due       — due date with calendar icon, or em-dash if none

   Row states:
     .is-done     — task completed (strikethrough name, dim text)
     .is-selected — subtle blue tint background

   Header cell states:
     .is-sorted      — column is the current sort key (shows arrow)
     .is-sorted-desc — sort is descending (rotates arrow)

   Cell states:
     .task-table__cell--due.is-overdue — red text, semibold

   Status pattern:
     The status cell composes .status-dot:
       <td class="task-table__cell task-table__cell--status">
         <span class="status-dot status-dot--success"></span>
         Completed
       </td>
     No new status classes needed — uses existing primitive.
   =========================================================== */

.task-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  color: var(--color-text);
}

/* ----- Header row -----
   SMALL CAPITALS, LIKE THE OTHER LISTS (JP, 2026-11-02). The contacts,
   properties and estimates lists all label their columns in 11-12px grey
   capitals with letter spacing; this table was the one main screen in
   sentence case and medium weight. Now the estimates list's exact
   treatment — 11px, semibold, uppercase, micro tracking — on every table
   that uses this component (task list, contact, property, team member). */
.task-table thead th {
  padding: var(--space-3) var(--space-6) var(--space-4); /* 6 12 8 */
  border-bottom: 1px solid var(--color-border-light);
  text-align: left;
  white-space: nowrap;
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-micro);
  color: var(--color-text-dim);
  vertical-align: middle;
}

.task-table__header-cell {
  /* Inherits from `thead th` above; this class is here as a
     hook so pages can target sortable cells specifically. */
}

/* When a header is sortable, wrap its content in a button so
   keyboard users can activate the sort. The button strips its
   own visual treatment to inherit the th styling. */
.task-table__header-cell button {
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  /* A browser's own button style resets these two, so a sortable column
     would print in lowercase next to the plain ones. */
  text-transform: inherit;
  letter-spacing: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2); /* 4px */
  text-transform: inherit;
  letter-spacing: inherit;
}
.task-table__header-cell button:hover {
  color: var(--color-text);
}
.task-table__header-cell button:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
  border-radius: var(--radius-xs);
}

.task-table__header-label {
  /* Hook only; styled by parent button. */
}

.task-table__sort-icon {
  display: none; /* shown only when .is-sorted is on the parent header cell */
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  transition: transform var(--duration-fast) var(--easing-default);
}
.task-table__header-cell.is-sorted {
  color: var(--color-text);
}
.task-table__header-cell.is-sorted .task-table__sort-icon {
  display: inline-block;
}
.task-table__header-cell.is-sorted-desc .task-table__sort-icon {
  transform: rotate(180deg);
}

/* ----- Body rows ----- */
.task-table tbody tr {
  border-bottom: 1px solid var(--color-border-light);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.task-table tbody tr:last-child {
  border-bottom: 0;
}
/* Hover is `.opens-row`'s — the task table is the one that already did it the
   way JP wanted, and the other three were brought to it. Rows that carry no
   `.opens-row` (a header, a table used for something other than opening a
   task) keep this, which is why it is still here. */
.task-table tbody tr:hover {
  background: var(--color-bg-hover);
}

.task-table__row.is-selected {
  background: var(--color-blue-glow);
}
.task-table__row.is-selected:hover {
  background: var(--color-blue-glow); /* keep on hover */
}

/* Done state — strikes through the task name and dims the row. */
.task-table__row.is-done .task-table__cell--name {
  text-decoration: line-through;
  color: var(--color-text-dim);
  /* The weight reset that used to be here did nothing once the name stopped
     being semibold above. */
}
.task-table__row.is-done .task-table__cell {
  color: var(--color-text-dim);
}

/* ----- Body cells ----- */
.task-table__cell {
  padding: var(--space-5) var(--space-6); /* 10 12 */
  vertical-align: middle;
}

/* Cell modifiers by column ---------------------------------- */

/* Checkbox cell — narrow, just holds the checkbox. */
.task-table__cell--checkbox {
  width: 40px;
  padding-right: 0;
}

/* Name cell — primary text, flex-grows by default.
   NOT BOLD (JP, 2026-10-04): *"under Tasks, the name itself is in bold. Don't
   make it bold."* It was semibold, and the Estimates table right beside it on
   the same screens is not — so two tables of the same shape, one above the
   other, were shouting at different volumes. The name is already the leftmost
   and widest thing in the row; it does not need weight as well. */
.task-table__cell--name {
  color: var(--color-text);
  /* Pages can add max-width if needed; default lets it grow. */
}

/* The to-do's name. Until 2026-11-02 a second line under it carried the
   client (`.task-table__client-name`); the client now has a column. */
.task-table__task-name {
  display: block;
}

/* Status cell — composes .status-dot. Cell itself just lays out. */
.task-table__cell--status {
  /* position:relative anchors the SingleSelectDropdown panel to this
     cell. <td> is position:static by default, so without this the
     panel escapes to a far ancestor (bottom-left-of-page bug). */
  position: relative;
  cursor: pointer;
  white-space: nowrap;
  color: var(--color-text-secondary);
}
.task-table__cell--status .status-dot {
  margin-right: var(--space-3); /* 6px between dot and label */
  /* Centred on the capitals (JP, 2026-11-02: "the green dot appears to be
     bottom-justified versus centered with the letter N"). Measured on the
     page: at -1px the 6px dot's centre sat 2px above the baseline while the
     13px capitals' centre is 4.55px above it. 1px puts the dot's centre at
     4px — within a pixel of the capitals and of the whole mixed-case word,
     and on whole pixels so it does not blur. */
  vertical-align: 1px;
}

/* The picture at the end of a status's name — 💬 for a text, 📧 for an
   email — drawn by us as an outline icon, the calendar icon's size and
   style, in the full text colour where the words are secondary grey. JP,
   2026-11-01: *"the icon itself is very difficult to see"* — the e-mail
   glyph every system draws is a pale smudge at this size. The markup comes
   from `helpers/statusLabel.js` and its two client copies; a picture we do
   not draw shows as itself, a step larger. */
/* Everywhere a status's name is printed with its picture: the task rows,
   and the status pickers (the row's own and the toolbar's Status filter),
   which draw the same markup through taskList.getStatusDropdownOption. */
.status-picture,
.task-table__cell--status .status-picture {
  font-size: 1.4em;
  line-height: 1;
  /* ON the baseline. This span and the svg inside it are each placed
     against a baseline, so an offset here STACKS with the svg's: at -1px
     here and -1px there the icon sat 2px down, and JP saw it "a little
     lower than the letters" (2026-11-02). The svg's own -1px is the whole
     offset now. A picture we do not draw (an emoji) sits on the baseline. */
  vertical-align: 0;
  color: var(--color-text);
}
.status-picture svg,
.task-table__cell--status .status-picture svg {
  display: inline-block; /* base.css makes every svg a block */
  width: 11px;  /* JP, 2026-11-02: "reduce the pixel size to 11 and position to -1" */
  height: 11px;
  vertical-align: -1px;
}

/* The time a task has sat in its status, at the end of the status's words
   (JP, 2026-11-02: "status and then maybe a dot, and then the time"). It was
   a column of its own; the room went to Contact. The sort is unchanged —
   status, then longest-in-status first. */
.task-table__status-time {
  color: var(--color-text-secondary);
  white-space: nowrap;
}

/* COLUMNS SIZED TO THEIR WORDS, TASK TAKES THE REST (JP, 2026-11-02). The
   task tables on the contact, property and team-member pages and a task's
   subtasks. Fixed pixel widths were tried first and failed the same day:
   the page's content column is narrower than the task list's and narrower
   still with the info panel open, so "Need to Respond [icon] · 14 min" in a
   186px Status spilled into Assignee, and Assignee into Due (JP: "smashed
   together at the very end of the screen, and assignee overlaps due date").
   Automatic layout with a 1% width on every column but Task makes each of
   those exactly as wide as its longest cell, never narrower, and gives the
   rest to Task — which is what "give as much space as possible to the Task
   section" meant. The task list's own page keeps its fixed widths, because
   there the point is that five separate tables line up. */
.task-table--snug .task-table__col--status,
.task-table--snug .task-table__col--contact,
.task-table--snug .task-table__col--who,
.task-table--snug .task-table__col--due { width: 1%; }
.task-table--snug .task-table__cell--status,
.task-table--snug .task-table__cell--contact,
.task-table--snug .task-table__cell--assignee,
.task-table--snug .task-table__cell--due,
.task-table--snug thead th { white-space: nowrap; }
.task-table--snug .task-table__cell--name { overflow-wrap: anywhere; }
/* ROOM BETWEEN THE COLUMNS (JP, 2026-11-02: "It just feels incredibly
   crowded... I would at least double the spacing between each column...
   You might even triple the space"). Tripled: 36px each side of every
   column but Task, so neighbouring columns sit 72px apart and Task's words
   sit 48px from Status. These tables have the room — Task takes whatever
   is left and still has most of the width. */
.task-table--snug .task-table__cell--status,
.task-table--snug .task-table__cell--contact,
.task-table--snug .task-table__cell--assignee,
.task-table--snug .task-table__cell--due,
.task-table--snug thead th:not(.task-table__header-cell--name) {
  padding-left: 36px;
  padding-right: 36px;
}
/* On a narrower window a property's five columns at 36px a side left Task
   narrower than Status (probe100 at 1100px); step the air down, still well
   above the 12px it was. */
@media (max-width: 1200px) {
  .task-table--snug .task-table__cell--status,
  .task-table--snug .task-table__cell--contact,
  .task-table--snug .task-table__cell--assignee,
  .task-table--snug .task-table__cell--due,
  .task-table--snug thead th:not(.task-table__header-cell--name) {
    padding-left: 20px;
    padding-right: 20px;
  }
}

/* Contact cell — who the to-do is for, in a column of its own since
   2026-11-02 (it was a second line under the name from 2026-10-11, when
   there was no spare width; the Time column's room is now its room). A long
   name is cut rather than allowed to widen the column. */
.task-table__cell--contact {
  color: var(--color-text);
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Assignee cell — primary text by default; pages can render
   "Unassigned" with __cell--assignee.is-unassigned to dim it. */
.task-table__cell--assignee {
  position: relative;
  cursor: pointer;
  white-space: nowrap;
  color: var(--color-text);
}
.task-table__cell--assignee.is-unassigned {
  color: var(--color-text-secondary);
  font-weight: var(--font-weight-regular);
}

/* Due cell — calendar icon + date. Default is dim (no due
   set). Has-value adds normal text color; overdue is red.

   **IT STAYS A TABLE CELL.** This was `display: flex` to line the icon up
   with the date — which it did, and which also stopped the `<td>` being a
   cell at all: a flex box inside a table row sits at the TOP of the row,
   so on any row taller than one line (a to-do with the client's name on
   its second line) the date and its icon rode 10px above Status, Time and
   Assignee. JP, 2026-11-01: *"the dates and the date icon are not
   justified... they are top-justified in the row."* The icon is inline
   now and sits on the text's own baseline, and the cell centres with the
   rest of the row the way the other three do. */
.task-table__cell--due {
  position: relative;
  cursor: pointer;
  white-space: nowrap;
  color: var(--color-text-dim);
  font-weight: var(--font-weight-regular);
}
.task-table__cell--due svg {
  display: inline-block; /* base.css makes every svg a block; this one sits in the line */
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  margin-right: 1px; /* plus the space in the markup ≈ the old 4px gap */
  color: var(--color-text-dim);
}
.task-table__cell--due.has-value {
  color: var(--color-text);
  font-weight: var(--font-weight-medium);
}
.task-table__cell--due.has-value svg {
  color: var(--color-text-dim);
}
.task-table__cell--due.is-overdue {
  color: var(--color-red);
  font-weight: var(--font-weight-semibold);
}
.task-table__cell--due.is-overdue svg {
  color: var(--color-red);
}

/* Horizontal-scroll wrapper — pair with .task-table on narrow-viewport contexts */
.task-table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 835px) {
  .task-table-scroll .task-table {
    min-width: 560px;
  }

  .task-table-scroll .task-table__header-cell--name,
  .task-table-scroll .task-table__cell--name {
    min-width: 275px;
  }
}


/* ── Inline task dropdowns (SingleSelectDropdown) ────────────────
   Used wherever a task-table row has clickable status / assignee /
   due-date cells. The .inline-task-dropdown customClass is passed
   to SingleSelectDropdown so all overrides are scoped here without
   touching general-application-styles.css.

   The triggering <td> must carry position:relative so the
   position:absolute container anchors to that cell. Pages apply
   that rule via their own page-scoped wrapper class.

   Element map:
     .multiselect-dropdown.inline-task-dropdown  → dropdown chrome
     .multiselect-display                        → hidden title strip
     .singleselect-options                       → flat panel inside
     .singleselect-search-container              → search wrapper
     .singleselect-search-input                  → search <input>
     .singleselect-options-wrapper               → scrollable list
     .singleselect-option                        → one option row
     .singleselect-option.selected               → active row
     .singleselect-option-label                  → label text
     .singleselect-check-icon                    → check mark
   ──────────────────────────────────────────────────────────── */

.inline-task-dropdown {
  top: calc(100% + var(--space-2)) !important;
  left: 0 !important;
  z-index: var(--z-popover);
  min-width: 240px;
  max-width: 320px;
  padding: var(--space-3) !important;
  background: var(--color-bg) !important;
  border: 1px solid var(--color-border) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-lg) !important;
  font-family: var(--font-body) !important;
}

.inline-task-dropdown .multiselect-display {
  display: none !important;
}

.inline-task-dropdown .singleselect-options {
  position: static !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
  max-height: none !important;
  width: 100% !important;
  overflow: visible !important;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.inline-task-dropdown .singleselect-search-container {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-1) var(--space-2);
}

.inline-task-dropdown .singleselect-search-input {
  width: 100%;
  height: 32px;
  padding: 0 var(--space-5) !important;
  background: var(--color-bg) !important;
  border: 1px solid var(--color-border) !important;
  border-radius: var(--radius-sm) !important;
  outline: 0;
  font-family: var(--font-body) !important;
  font-size: var(--font-size-small) !important;
  color: var(--color-text) !important;
  box-shadow: none !important;
  transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
}
.inline-task-dropdown .singleselect-search-input::placeholder {
  color: var(--color-text-dim);
}
.inline-task-dropdown .singleselect-search-input:focus {
  border-color: var(--color-blue) !important;
  box-shadow: 0 0 0 3px var(--color-focus-ring) !important;
}

.inline-task-dropdown .singleselect-options-wrapper {
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.inline-task-dropdown .singleselect-option {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  width: 100%;
  padding: var(--space-4) var(--space-5) !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: var(--radius-sm) !important;
  font-family: var(--font-body) !important;
  font-size: var(--font-size-small) !important;
  font-weight: var(--font-weight-medium) !important;
  line-height: 1.2;
  color: var(--color-text) !important;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.inline-task-dropdown .singleselect-option:hover {
  background: var(--color-bg-hover) !important;
}

.inline-task-dropdown .singleselect-option.selected {
  color: var(--color-blue) !important;
  font-weight: var(--font-weight-semibold) !important;
}

.inline-task-dropdown .singleselect-option-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: inherit !important;
  color: inherit !important;
  cursor: pointer;
}

.inline-task-dropdown .singleselect-check-icon {
  flex-shrink: 0;
  font-size: 12px;
  color: var(--color-blue);
  margin-left: auto;
}

.inline-task-dropdown a.multiselect-clear-link {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-blue);
  text-decoration: none;
  cursor: pointer;
  align-self: flex-start;
  padding: 0 var(--space-2);
}
.inline-task-dropdown a.multiselect-clear-link:hover {
  text-decoration: underline;
}


/* ===========================================================
   33. COLLAPSIBLE SECTION — .collapsible-section
   Visual reference: Task Detail "Subtasks" header strip
   (the cdv-sec / cdv-sec-h pattern). Generic clickable header
   strip that sits above page content groups — subtask lists,
   notes lists, files lists, activity lists, etc. The same
   chrome will appear in many places across the product.

   IMPORTANT — scope of this component:
     This component is *only the header strip*. It does NOT
     wrap or render the content below it. The header itself
     does not change visually when its content is collapsed —
     only the chevron rotates as a visual cue.

     Pages are responsible for:
       - Rendering the content below the header
       - Show/hiding that content when the user clicks the head

     The component provides a single state flag (.is-collapsed)
     on the wrapper that rotates the chevron. Pages should
     reuse the same flag to drive their content show/hide,
     so chevron and content stay in sync from one source of
     truth.

   Distinct from:
     .card    — content surface, not header chrome
     .banner  — inline callout, not a section divider
     .tabs    — switches between content; section organizes one

   Anatomy:
     __head     — clickable header strip (the visible chrome)
     __title    — section title text (display font)
     __count    — count pill next to title (e.g., "4")
     __actions  — right-side action cluster (add button + chevron)
     __add      — positioning slot for the add button
                  (composes .btn, e.g., .btn .btn--ghost
                  .btn--icon .btn--sm .collapsible-section__add)
     __chevron  — collapse/expand chevron (a <button>)

   Modifiers:
     --outlined — white background head with visible border
                  (vs default bg-off fill)

   States:
     .is-collapsed — rotates __chevron 90° to indicate the
                     content below (owned by the page) is hidden.
                     Pages key off this same class to hide
                     their own content.

   Accessibility note:
     The __head is intentionally NOT a <button> because it
     contains other buttons (__add, __chevron). Keyboard users
     activate the section by tabbing to __chevron and pressing
     Enter/Space. The whole __head still has cursor: pointer
     and can be clicked to collapse for mouse users.
   =========================================================== */

.collapsible-section {
  width: 100%;
  margin-bottom: var(--space-16);
  font-family: var(--font-body);
}

/* ----- __head: clickable header strip ----- */
.collapsible-section__head {
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  padding: var(--space-5) var(--space-7); /* 10 14 */
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-sm);
  margin-bottom: 15px;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.collapsible-section__head:hover {
  background: var(--color-bg-hover);
}

/* ----- __title: section name ----- */
.collapsible-section__title {
  font-family: var(--font-display);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  color: var(--color-text);
  margin: 0;
  /* **SAID HERE SO THE ELEMENT STOPS MATTERING.** `base.css` gives every
     `h1`–`h6` this letter-spacing, and this rule did not — so the same class
     rendered differently on an `<h2>` than on a `<span>`, and the difference
     only showed up when the estimate builder used both in one row (2026-10-19:
     "Template" on a span measured `normal` against "Notes" on an h2 at
     -0.13px). Nothing changes for the headings that already use it: this is
     the value they were already inheriting. */
  letter-spacing: var(--letter-spacing-display);
}

/* ----- __title--quiet: a heading that is not the focus -----
   JP, 2026-10-19, about the estimate builder: *"the font for Template and
   Property at the top versus Notes and Activity at the bottom is different.
   They need to be the same… I do, however, think that for Template and
   Property, the font color, which is more of a grayed-out color where it's less
   of the focus, is okay, whereas at the bottom, Notes and Activity are more of
   a bold color."*

   So the TYPE is one thing for every section heading in the product, and the
   COLOUR is the one thing a screen may step down where a heading is a label on
   something rather than the thing a person came to read. Colour only — size,
   typeface and weight are not negotiable here, because that is what made the
   four headings on that screen look like four different kinds of object. */
.collapsible-section__title--quiet {
  color: var(--color-text-dim);
}

/* ----- __count: count pill next to title ----- */
.collapsible-section__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  padding: 1px var(--space-3); /* 1px 6px */
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-bold);
  line-height: 1.4;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

/* ----- __actions: right-side action cluster ----- */
.collapsible-section__actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1); /* 2px */
  flex-shrink: 0;
}

/* Add button slot — composes .btn. The class adds nothing to
   the styling; it's a hook for selection / future tweaks. */
.collapsible-section__add {
  /* Hook only; visual treatment from .btn modifiers. */
}

/* Chevron toggle button. Rotates when section is collapsed.
   JS adds/removes .open on the chevron; .open = expanded.
   ::before suppressed — legacy JS (normalizeChevronState) adds
   fa-chevron-right class to this element, which would inject a
   Font Awesome pseudo-element alongside the SVG. */
.collapsible-section__chevron::before {
  display: none;
}
.collapsible-section__chevron {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    transform var(--duration-fast) var(--easing-default),
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
/* .open = section is expanded — rotate SVG to point downward */
.collapsible-section__chevron.open > svg {
  transform: rotate(90deg);
}
.collapsible-section__chevron:hover {
  background: var(--color-bg);
  color: var(--color-text);
}
.collapsible-section__chevron:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.collapsible-section__chevron > svg {
  width: 14px;
  height: 14px;
}

/* ----- States ----- */
/* .is-collapsed lives on the wrapper. It rotates the chevron
   to indicate the page-owned content below is hidden. The
   component does NOT hide any content itself — pages key off
   this same class to hide their own markup. */
.collapsible-section.is-collapsed .collapsible-section__chevron {
  transform: rotate(-90deg);
}

/* ----- --outlined: white head with visible border ----- */
.collapsible-section--outlined .collapsible-section__head {
  background: var(--color-bg);
  border-color: var(--color-border);
}
.collapsible-section--outlined .collapsible-section__head:hover {
  background: var(--color-bg-hover);
}


/* ===========================================================
   34. SMS COMPOSER POPUP — .sms-composer
   Visual reference: SMS Inbox new-message popup.

   Modal-mounted composer for sending new SMS messages. Used
   on Inbox, Contact Detail, Property Detail, and anywhere
   else the product offers a "send a new SMS" affordance.

   Distinct from .composer (Component #15), which is the
   inline reply composer — no head, no recipient fields, no
   modal frame. The two are separate components because the
   anatomy and surrounding context differ substantially.

   Mounting:
     The component sits inside a .modal__backdrop:
       <div class="modal__backdrop">
         <section class="sms-composer">…</section>
       </div>
     The backdrop handles dim-the-page behavior; the composer
     handles its own surface, chrome, and content.

   Anatomy:
     __head            — header strip (icon + title + close)
     __head-icon       — blue-pale rounded square w/ chat-bubble icon
     __title           — "New Message" display-font heading
     __close           — X close button
     __body            — fields + textarea + counter
     __field           — horizontal row (label left, value right)
     __field-label     — uppercase eyebrow ("FROM" / "TO")
     __field-value     — right side of a field, holds the control
     __field-input     — borderless inline input for To field
     __from-display    — styled From button (icon + text + chevron)
     __from-icon       — phone-icon box inside __from-display
     __from-text       — number + line description stack
     __from-number     — the phone number
     __from-line       — the line label ("Main Line")
     __from-chevron    — dropdown chevron
     __textarea-wrap   — wrapper that gets the focus ring
     __textarea        — the message textarea
     __counter         — char/segment count below textarea
     __foot            — footer strip
     __tools           — left cluster of tool icons (slot)
     __schedule        — Schedule button (composes .btn)
     __send            — Send button (composes .btn)

   States:
     none — focus state on __textarea-wrap is CSS-driven via
     :focus-within. No JS-driven class flips needed.
   =========================================================== */

.sms-composer {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  /* Same viewport ceiling as .email-composer-popup (#35). The <textarea> here
     scrolls on its own rather than growing, but `resize: vertical` still lets
     it be dragged past the screen edge. */
  max-height: 90dvh;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  font-family: var(--font-body);
  color: var(--color-text);
}

/* Overrides the base overflow:hidden (kept for rounded corners) so an
   anchored .dropdown (e.g. the To field's contact suggestions) isn't
   clipped — same override pattern as .modal--overflow-visible. */
.sms-composer.sms-composer--overflow-visible {
  overflow: visible;
}

/* ----- __head: header strip ----- */
.sms-composer__head {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-7) var(--space-9); /* 14 20 */
  border-bottom: 1px solid var(--color-border-light);
}

.sms-composer__head-icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-blue-glow);
  color: var(--color-blue);
  border-radius: var(--radius-sm);
}
.sms-composer__head-icon > svg {
  width: 14px;
  height: 14px;
}

.sms-composer__title {
  flex: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--font-size-h5); /* 15px — popup-chrome title size */
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-h5);
  letter-spacing: var(--letter-spacing-display);
  color: var(--color-text);
}

.sms-composer__close {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.sms-composer__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.sms-composer__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.sms-composer__close > svg {
  width: 18px;
  height: 18px;
}

/* ----- __body: fields + textarea + counter ----- */
.sms-composer__body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* Scrolls the fields + textarea + counter when the popup hits its ceiling,
     keeping __head and __foot (Send / Schedule) pinned and reachable. */
  overflow-y: auto;
  padding: 0 var(--space-9) var(--space-7); /* 0 20 14 */
}

/* ----- __field: horizontal row (FROM, TO) ----- */
.sms-composer__field {
  display: flex;
  align-items: center;
  gap: var(--space-7); /* 14px between label and value */
  padding: var(--space-7) 0; /* 14px vertical */
  border-bottom: 1px solid var(--color-border-light);
}

.sms-composer__field-label {
  flex-shrink: 0;
  width: 64px;
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-uppercase);
  color: var(--color-text-dim);
  line-height: 1;
}

.sms-composer__field-value {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}

/* Borderless inline input — used for the To field. Styled
   minimally because the field already provides padding and
   the bottom-border row treatment handles separation. */
.sms-composer__field-input {
  flex: 1;
  min-width: 0;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-regular);
  color: var(--color-text);
}
.sms-composer__field-input::placeholder {
  color: var(--color-text-dim);
}

/* ----- __from-display: styled From button ----- */
.sms-composer__from-display {
  flex: 1;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--color-text);
}

.sms-composer__from-icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
  border-radius: var(--radius-sm);
}
.sms-composer__from-icon > svg {
  width: 14px;
  height: 14px;
}

.sms-composer__from-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sms-composer__from-number {
  font-size: var(--font-size-body); /* 14px */
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sms-composer__from-line {
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.2;
}

.sms-composer__from-chevron {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--color-text-dim);
}

/* ----- __textarea-wrap: focus-ring container ----- */
.sms-composer__textarea-wrap {
  position: relative;
  margin-top: var(--space-7); /* 14px gap below the To field */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  transition:
    border-color var(--duration-base) var(--easing-default),
    box-shadow var(--duration-base) var(--easing-default);
}
.sms-composer__textarea-wrap:focus-within {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.sms-composer__textarea {
  width: 100%;
  min-height: 140px;
  padding: var(--space-6) var(--space-7); /* 12 14 */
  background: transparent;
  border: 0;
  outline: 0;
  resize: vertical;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  color: var(--color-text);
}
.sms-composer__textarea::placeholder {
  color: var(--color-text-dim);
}

/* ----- __counter: char + segment count ----- */
.sms-composer__counter {
  margin-top: var(--space-3); /* 6px gap above */
  text-align: right;
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
}

/* ----- __foot: footer strip with tools, schedule, send ----- */
.sms-composer__foot {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-6) var(--space-9); /* 12 20 */
  border-top: 1px solid var(--color-border-light);
}

/* Tool cluster — slot for icon buttons. Pages render
   .btn--ghost.btn--icon.btn--sm instances inside. */
.sms-composer__tools {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2); /* 4px */
  flex: 1;
}

/* Schedule button — positioning slot. Composes .btn (typically
   .btn--secondary or .btn--ghost). */
.sms-composer__schedule {
  flex-shrink: 0;
}

/* Send button — positioning slot. Composes .btn--primary. */
.sms-composer__send {
  flex-shrink: 0;
}


/* ===========================================================
   35. EMAIL COMPOSER POPUP — .email-composer-popup
   Visual reference: Email Inbox new-email popup.

   Modal-mounted composer for sending new emails. Sibling of
   .sms-composer (#34) and .email-composer-inline (TBD); same
   structural family, different anatomy specific to email
   (subject line, Cc/Bcc fields, formatting toolbar, send-with-
   dropdown for Schedule/Send-and-compose-another).

   Mounting: same as .sms-composer — sits inside
   .modal__backdrop if dimming is needed:
     <div class="modal__backdrop">
       <section class="email-composer-popup">…</section>
     </div>
   The backdrop dims the page; the popup handles its own
   surface and chrome.

   Anatomy:
     __head            — header strip (icon + title + actions)
     __head-icon       — blue-pale rounded square w/ edit icon
     __title           — "New message" display-font heading
     __head-actions    — right-side cluster (currently just close)
     __close           — X close button

     __body            — fields + textarea
     __field           — horizontal row (From / To / Cc / Bcc / Subject)
     __field-label     — left-side label ("From" / "To" / etc.)
     __field-value     — right side of field, holds control(s)
     __field-input     — borderless inline input (To/Cc/Bcc/Subject)
     __from-display    — styled From row (avatar + name/email + chevron)
     __from-avatar     — slot for an .avatar
     __from-name       — display name
     __from-email      — "<email@domain.com>" dim text
     __from-chevron    — From dropdown chevron
     __cc-link         — "Cc" expand link
     __bcc-link        — "Bcc" expand link
     __textarea        — message body textarea (borderless)

     __foot            — footer area (toolbar + send bar)
     __toolbar         — formatting toolbar strip
     __toolbar-group   — visual group of related buttons
     __word-count      — word count text on the right
     __send-bar        — bottom strip with Send button
     __send            — main Send button (slot, composes .btn--primary)
     __send-toggle     — chevron sub-button to open send menu
     __send-menu       — dropdown panel (Schedule, Send & compose...)
     __send-menu-item  — each item in the menu
     __send-menu-icon  — icon slot inside a menu item

   States:
     .is-send-menu-open — shows __send-menu
   =========================================================== */

.email-composer-popup {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 720px;
  /* The popup grows with its body field, so it needs a viewport ceiling or a
     long message pushes the head and the Send bar off-screen. min() keeps the
     600px comfort height from fighting the ceiling on short viewports — a bare
     min-height would win over max-height and reintroduce the overflow. */
  min-height: min(600px, 90dvh);
  max-height: 90dvh;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  font-family: var(--font-body);
  color: var(--color-text);
}

/* ----- __head: header strip ----- */
.email-composer-popup__head {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-7) var(--space-9); /* 14 20 */
  background: var(--color-bg-off);
  border-bottom: 1px solid var(--color-border-light);
}

.email-composer-popup__head-icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-blue-glow);
  color: var(--color-blue);
  border-radius: var(--radius-sm);
}
.email-composer-popup__head-icon > svg {
  width: 14px;
  height: 14px;
}

.email-composer-popup__title {
  flex: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--font-size-h5); /* 15px — popup-chrome title size */
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-h5);
  letter-spacing: var(--letter-spacing-display);
  color: var(--color-text);
}

.email-composer-popup__head-actions {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.email-composer-popup__close {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-popup__close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.email-composer-popup__close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.email-composer-popup__close > svg {
  width: 18px;
  height: 18px;
}

/* ----- __body: fields + textarea ----- */
.email-composer-popup__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Backstop for when the fields themselves outgrow the capped popup (many Cc
     recipients, a long attachment chip list). The textarea scrolls on its own
     first, so this rarely engages and there is no double scrollbar. */
  overflow-y: auto;
}

/* ----- __field: horizontal row ----- */
.email-composer-popup__field {
  display: flex;
  align-items: center;
  gap: var(--space-7); /* 14px between label and value */
  padding: var(--space-6) var(--space-9); /* 12 20 */
  border-bottom: 1px solid var(--color-border-light);
}

.email-composer-popup__field-label {
  flex-shrink: 0;
  width: 64px;
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1;
}

.email-composer-popup__field-value {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px between input and trailing icons/links */
}

.email-composer-popup__field-input {
  flex: 1;
  min-width: 0;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-regular);
  color: var(--color-text);
}
.email-composer-popup__field-input::placeholder {
  color: var(--color-text-dim);
}

/* ----- __from-display: From row ----- */
.email-composer-popup__from-display {
  flex: 1;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--color-text);
}

.email-composer-popup__from-avatar {
  flex-shrink: 0;
}

.email-composer-popup__from-name {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  line-height: 1.2;
  flex-shrink: 0;
}

.email-composer-popup__from-email {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.email-composer-popup__from-chevron {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--color-text-dim);
  margin-left: auto;
}

/* ----- __cc-link / __bcc-link: expand affordances ----- */
.email-composer-popup__cc-link,
.email-composer-popup__bcc-link {
  flex-shrink: 0;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  cursor: pointer;
  transition: color var(--duration-fast) var(--easing-default);
}
.email-composer-popup__cc-link:hover,
.email-composer-popup__bcc-link:hover {
  color: var(--color-blue);
}

/* Mobile-only expand button — hidden by default, shown at ≤835px */
.email-composer-popup__mobile-expand {
  display: none;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-dim);
  font-size: 11px;
  cursor: pointer;
  transition: background var(--duration-fast) var(--easing-default), color var(--duration-fast) var(--easing-default);
}
.email-composer-popup__mobile-expand:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

/* ----- __textarea: message body ----- */
.email-composer-popup__textarea {
  flex: 1;
  width: 100%;
  min-height: 240px;
  /* contenteditable, so it grows with its content. Scrolling internally keeps
     the ceiling on .email-composer-popup effective; the flex-resolved height
     is the max, so it tracks the viewport rather than a magic number. */
  overflow-y: auto;
  padding: var(--space-7) var(--space-9); /* 14 20 */
  background: transparent;
  border: 0;
  outline: 0;
  resize: none;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body-lg);
  color: var(--color-text);
}
.email-composer-popup__textarea::placeholder {
  color: var(--color-text-dim);
}

/* ----- __foot: footer area ----- */
.email-composer-popup__foot {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--color-border-light);
}

/* ----- __toolbar: formatting strip ----- */
.email-composer-popup__toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px between groups */
  padding: var(--space-5) var(--space-9); /* 10 20 */
  border-bottom: 1px solid var(--color-border-light);
}

.email-composer-popup__toolbar-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1); /* 2px between buttons within a group */
}

.email-composer-popup__word-count {
  margin-left: auto;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  flex-shrink: 0;
}

/* ----- __send-bar: bottom strip with Send button ----- */
.email-composer-popup__send-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: var(--space-6) var(--space-9); /* 12 20 */
  position: relative; /* anchors __send-menu */
}

/* ----- Split Send button (Send + chevron) -----
   Two adjacent buttons styled to merge visually. The main
   __send button uses .btn .btn--primary; __send-toggle is
   a chevron sub-button right next to it. Markup:
     <button class="btn btn--primary email-composer-popup__send">
       <svg/> Send
     </button>
     <button class="btn btn--primary email-composer-popup__send-toggle">
       <svg/>
     </button>
*/
.email-composer-popup__send {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  /* Provides the rounded-left, flat-right pill shape that
     joins with __send-toggle visually. */
}

.email-composer-popup__send-toggle {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 1px solid rgba(255, 255, 255, 0.22);
  padding-left: var(--space-4);
  padding-right: var(--space-4);
  /* Flat-left pill, joins with __send via the divider line. */
}

/* ----- __send-menu: dropdown panel ----- */
.email-composer-popup__send-menu {
  position: absolute;
  bottom: calc(100% + var(--space-3)); /* 6px above the bar */
  right: var(--space-9);
  min-width: 240px;
  padding: var(--space-3); /* 6px */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  display: none;
  z-index: var(--z-popover);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
}
.email-composer-popup.is-send-menu-open .email-composer-popup__send-menu {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.email-composer-popup__send-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  width: 100%;
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-popup__send-menu-item:hover {
  background: var(--color-bg-hover);
}
.email-composer-popup__send-menu-item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.email-composer-popup__send-menu-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-secondary);
}
.email-composer-popup__send-menu-icon > svg {
  width: 16px;
  height: 16px;
}


/* ===========================================================
   36. EMAIL COMPOSER INLINE — .email-composer-inline
   Visual reference: Email Thread View — inline reply
   composer at the bottom of an email thread.

   Inline reply composer that lives at the bottom of an email
   thread (Inbox thread view, Contact / Property / Task detail
   timelines). Sibling of .email-composer-popup (#35) — same
   purpose (composing emails) but different mounting context
   (inline-in-page vs full popup overlay).

   Two visual states (modifier):
     default (no modifier) — full expanded composer
     --collapsed           — single-row strip with avatar +
                             "Reply to X..." placeholder + three
                             mode buttons (Reply / Reply all /
                             Forward); clicking a mode button
                             expands the composer

   Rendering model (display-none, same as .email-message):
     Pages render the full anatomy at all times. The
     --collapsed modifier hides the sub-elements that don't
     belong on the collapsed strip and restructures __modes
     to render as outlined pill buttons on the right.

   Anatomy:
     __avatar          — slot for .avatar (sender avatar)
     __placeholder     — "Reply to X..." text (collapsed only)

     __modes           — top strip: Reply / Reply all / Forward
     __mode            — each mode button
     __collapse        — X button on top-right (expanded only)

     __field           — horizontal row (To, Cc, Bcc)
     __field-label     — "To" / "Cc" / "Bcc" label
     __field-value     — right side of field, holds chip(s) /
                          input + Cc/Bcc links
     __recipient-chip  — recipient pill (avatar + name + X)
     __recipient-avatar — slot for .avatar inside chip
     __recipient-name  — display name
     __recipient-email — email address (dim)
     __recipient-remove — X to remove recipient
     __cc-link         — "Cc" expand link
     __bcc-link        — "Bcc" expand link

     __textarea-wrap   — wrapper for textarea
     __textarea        — the reply textarea

     __foot            — footer strip
     __send-group      — wraps __send + __send-toggle as a single split button
     __send            — main Send button (slot, .btn--primary)
     __send-toggle     — chevron sub-button next to Send
     __send-menu       — dropdown panel
     __send-menu-item  — item inside menu
     __send-menu-icon  — icon slot inside menu item
     __tools           — slot for attachment/format icons
                          (paperclip, image, link, emoji)
     __schedule        — clock icon button (schedule send)
     __discard         — trash icon button (discard draft)

   States:
     __mode.is-active   — soft-blue active reply mode
     .is-send-menu-open — shows __send-menu

   Tokens used: existing tokens only. No new tokens.
   =========================================================== */

.email-composer-inline {
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  font-family: var(--font-body);
  color: var(--color-text);
}

/* ----- __avatar (visible in collapsed mode only) ----- */
.email-composer-inline__avatar {
  flex-shrink: 0;
}

/* ----- __placeholder (collapsed mode only) ----- */
.email-composer-inline__placeholder {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-body);
  color: var(--color-text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: none; /* shown by --collapsed */
}

/* ----- __modes: top strip with mode buttons ----- */
/* In expanded state: a strip across the top with the three
   mode buttons + a close X on the right.
   In collapsed state: outlined pill buttons sitting next to
   the placeholder text. */
.email-composer-inline__modes {
  display: flex;
  align-items: center;
  gap: var(--space-2); /* 4px */
  padding: var(--space-5) var(--space-7); /* 10 14 */
  background: var(--color-bg-off);
  border-bottom: 1px solid var(--color-border-light);
}

.email-composer-inline__mode {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  padding: var(--space-4) var(--space-6); /* 8 12 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__mode:hover {
  color: var(--color-text);
}
.email-composer-inline__mode:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.email-composer-inline__mode > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
/* Active mode (expanded state) — soft-blue tint */
.email-composer-inline__mode.is-active {
  background: var(--color-blue-glow);
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
}

/* ----- __collapse: top-right close X (expanded only) ----- */
.email-composer-inline__collapse {
  margin-left: auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-dim);
  cursor: pointer;
  flex-shrink: 0;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__collapse:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.email-composer-inline__collapse:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.email-composer-inline__collapse > svg {
  width: 16px;
  height: 16px;
}

/* ----- __field: horizontal row (To/Cc/Bcc) ----- */
.email-composer-inline__field {
  display: flex;
  align-items: center;
  gap: var(--space-7); /* 14px */
  padding: var(--space-6) var(--space-9); /* 12 20 */
  border-bottom: 1px solid var(--color-border-light);
}

.email-composer-inline__field-label {
  flex-shrink: 0;
  width: 40px;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1;
}

.email-composer-inline__field-value {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3); /* 6px between chips and trailing controls */
}

.email-composer-inline__field-value > input[readonly] {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: transparent;
  border: none;
  outline: none;
  font-size: var(--font-size-small, 13px);
  color: var(--color-text-secondary, #4B5563);
}

/* ----- __recipient-chip: pill with avatar + name + remove X ----- */
.email-composer-inline__recipient-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  padding: 2px var(--space-2) 2px 2px; /* 2 4 2 2 — tight on left for avatar */
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-pill);
  max-width: 100%;
  min-width: 0;
}

.email-composer-inline__recipient-avatar {
  flex-shrink: 0;
}

.email-composer-inline__recipient-name {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
  line-height: 1.2;
  flex-shrink: 0;
}

.email-composer-inline__recipient-email {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.email-composer-inline__recipient-remove {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--color-text-dim);
  cursor: pointer;
  margin-left: var(--space-2); /* 4px gap from email */
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__recipient-remove:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.email-composer-inline__recipient-remove > svg {
  width: 11px;
  height: 11px;
}

/* ----- __cc-link / __bcc-link ----- */
.email-composer-inline__cc-link,
.email-composer-inline__bcc-link {
  margin-left: auto;
  flex-shrink: 0;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
  cursor: pointer;
  transition: color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__cc-link:hover,
.email-composer-inline__bcc-link:hover {
  color: var(--color-blue);
}
/* When both Cc and Bcc are present, only the first one needs
   the auto-margin push. Subsequent links sit next to it. */
.email-composer-inline__cc-link + .email-composer-inline__bcc-link {
  margin-left: 0;
}

/* Mobile-only expand button — hidden by default, shown at ≤835px via page-scoped rule */
.email-composer-inline__mobile-expand {
  display: none;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-left: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-dim);
  font-size: 11px;
  cursor: pointer;
  transition: background var(--duration-fast) var(--easing-default), color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__mobile-expand:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

/* ----- __textarea-wrap: container for textarea + quote toggle ----- */
.email-composer-inline__textarea-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
}

.email-composer-inline__textarea {
  width: 100%;
  min-height: 200px;
  /* Also a contenteditable div, so it grows with its content. There is no popup
     root to inherit a ceiling from here (it sits inside the scrolling thread
     panel), so the cap is explicit — without it a long draft pushes the
     formatting toolbar and the Send bar below the fold. */
  max-height: 40dvh;
  overflow-y: auto;
  padding: var(--space-7) var(--space-9) var(--space-6); /* 14 20 12 */
  background: transparent;
  border: 0;
  outline: 0;
  /* Was `resize: vertical`, which never applied — `resize` is ignored while
     computed overflow is visible. Adding overflow-y above would have switched
     on a drag grabber that has never been part of this component's look. */
  resize: none;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body-lg);
  color: var(--color-text);
}
.email-composer-inline__textarea::placeholder {
  color: var(--color-text-dim);
}

/* ----- __foot: footer strip ----- */
.email-composer-inline__foot {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-6) var(--space-9); /* 12 20 */
  background: var(--color-bg-off);
  border-top: 1px solid var(--color-border-light);
  position: relative; /* anchors __send-menu */
}

/* ----- Split Send button -----
   The Send button and its chevron toggle need to touch (no gap)
   to read as a single split button. The parent __foot uses
   flex `gap` for spacing between unrelated controls — wrapping
   the two halves in __send-group isolates them from that gap. */
.email-composer-inline__send-group {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}

.email-composer-inline__send {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.email-composer-inline__send-toggle {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 1px solid rgba(255, 255, 255, 0.22);
  padding-left: var(--space-4);
  padding-right: var(--space-4);
}

/* ----- __send-menu: dropdown panel above send button ----- */
.email-composer-inline__send-menu {
  position: absolute;
  bottom: calc(100% + var(--space-3));
  left: var(--space-9); /* aligns with Send button on the left */
  min-width: 240px;
  padding: var(--space-3);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  display: none;
  z-index: var(--z-popover);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
}
.email-composer-inline.is-send-menu-open .email-composer-inline__send-menu {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.email-composer-inline__send-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  width: 100%;
  padding: var(--space-4) var(--space-5);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.email-composer-inline__send-menu-item:hover {
  background: var(--color-bg-hover);
}
.email-composer-inline__send-menu-item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.email-composer-inline__send-menu-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-secondary);
}
.email-composer-inline__send-menu-icon > svg {
  width: 16px;
  height: 16px;
}

/* ----- __tools: cluster of attachment/format icons ----- */
.email-composer-inline__tools {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1); /* 2px */
}

/* ----- __schedule / __discard: right-side icon buttons ----- */
.email-composer-inline__schedule,
.email-composer-inline__discard {
  flex-shrink: 0;
}
/* Push schedule and discard to the right */
.email-composer-inline__tools + .email-composer-inline__schedule {
  margin-left: auto;
}


/* ===========================================================
   --collapsed: single-row strip layout
   ===========================================================
   Pages render full anatomy; this modifier hides everything
   that doesn't belong on a collapsed strip and restructures
   __modes to render as outlined pills next to the placeholder.

   What the modifier does:
     - Hides __collapse, __field, __textarea-wrap, __foot.
     - Reveals __placeholder.
     - Restructures __modes: removes background, removes border,
       drops out of strip layout, lets pills sit inline.
     - Modes get an outlined-pill treatment (border) since they
       no longer have a tinted strip background to set them off.
     - Wraps __avatar + __placeholder + __modes in a single row.
   =========================================================== */
.email-composer-inline--collapsed {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-5) var(--space-7); /* 10 14 */
}

.email-composer-inline--collapsed .email-composer-inline__placeholder {
  display: block;
}

/* In collapsed mode, the modes strip becomes an inline cluster
   of outlined pills. */
.email-composer-inline--collapsed .email-composer-inline__modes {
  background: transparent;
  border: 0;
  padding: 0;
  gap: var(--space-3); /* 6px between mode pills */
  flex-shrink: 0;
}
.email-composer-inline--collapsed .email-composer-inline__mode {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  padding: var(--space-3) var(--space-5); /* 6 10 */
}
.email-composer-inline--collapsed .email-composer-inline__mode:hover {
  border-color: var(--color-border-hover);
  color: var(--color-text);
}

/* Hide everything not used in collapsed mode */
.email-composer-inline--collapsed .email-composer-inline__collapse,
.email-composer-inline--collapsed .email-composer-inline__field,
.email-composer-inline--collapsed .email-composer-inline__textarea-wrap,
.email-composer-inline--collapsed .email-composer-inline__foot {
  display: none;
}

/* Suppress send-menu in collapsed mode even if state class
   somehow lingers */
.email-composer-inline--collapsed.is-send-menu-open .email-composer-inline__send-menu {
  display: none;
}

@media (max-width: 445px) {
  .email-composer-inline__field {
    padding: var(--space-6) var(--space-6); /* 12 12 — was 12 20 */
    gap: var(--space-4);                    /* 8px — was 14px */
  }

  .email-composer-inline__textarea {
    padding-left: var(--space-6);  /* 12px — was 20px */
    padding-right: var(--space-6);
  }

  .email-composer-inline__foot {
    padding: var(--space-5) var(--space-6); /* 10 12 — was 12 20 */
  }
}


/* ===========================================================
   37a. EMAIL COMPOSER TOOLBAR — .fp-email-composer-toolbar
   Formatting toolbar that sits below the contenteditable body
   in both the full-page popup and the inline thread composer.
   =========================================================== */

.fp-email-composer-toolbar {
  overflow-x: scroll;
  display: flex;
  align-items: center;
  gap: var(--space-2, 4px);
  padding: var(--space-3, 6px) var(--space-7, 14px);
  border-top: 1px solid var(--color-border-light, #e5e7eb);
  background: var(--color-bg-off, #F8FAFC);
}

.fp-email-composer-toolbar .td-description-toolbar__group {
  display: flex;
  align-items: center;
  gap: var(--space-1, 2px);
}

.fp-email-composer-toolbar .email-composer-popup__word-count {
  margin-left: auto;
  font-size: var(--font-size-caption, 12px);
  color: var(--color-text-dim, #9CA3AF);
  white-space: nowrap;
  flex-shrink: 0;
}

.fp-email-composer-toolbar .td-description-toolbar__sep {
  display: inline-block;
  width: 1px;
  height: 16px;
  background: var(--color-border, #e0e0e0);
  flex-shrink: 0;
  margin: 0 var(--space-2, 4px);
}

/* ===========================================================
   37b. EMAIL PILL INPUT — .email-pill-input / .email-pill
   Recipient chip input used in composer To / CC / BCC fields.
   =========================================================== */

.email-pill-input {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  border-radius: 6px;
  background: var(--color-bg, #fff);
  position: relative;
  flex: 1;
  max-width: none;
  padding: 2px 6px 2px 0;
  min-height: 34px;
  gap: 2px;
}

.email-pill {
  display: inline-flex;
  align-items: center;
  background: var(--color-bg, #fff);
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: var(--radius-pill, 999px);
  padding: var(--space-1, 4px) var(--space-5, 10px);
  margin: 2px 2px;
  font-size: var(--font-size-small, 13px);
  color: var(--color-text-secondary, #4B5563);
  transition: border-color 120ms ease;
}

.email-pill-input input {
  border: none;
  background: transparent;
  outline: none;
  font-size: 13px;
  flex: 1;
  min-width: 140px;
  margin-left: 5px;
}

/* ===========================================================
   37c. DELAY SENDING SECTION — .delay-sending-section
   Countdown bar shown after clicking Send, before the email
   is dispatched.
   =========================================================== */

.delay-sending-section {
  background-color: #F6F6F6;
  border-radius: 12px;
  padding: 18px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  box-shadow: 0 2px 8px rgba(30, 67, 255, 0.04);
}

.delay-sending-section span {
  font-weight: bold;
  color: #222;
  font-size: 15px;
}

/* ===========================================================
   37. TASK ITEM — .task-item
   Visual reference: Email Thread View / SMS Inbox right
   sidebar — "Open Tasks" cards.

   Card-style task representation that lives in vertical
   stacks inside collapsible sections on right sidebars.
   Used for related tasks, open tasks, etc.

   Distinct from .task-table (Component #32). They serve
   different layout needs — the table for dense tabular
   review (detail page subtasks), the item for sparse
   sidebar context. Do NOT combine them.

   Note: this component does NOT include a checkbox. Tasks
   in sidebars are read-references to tasks elsewhere; the
   sidebar isn't where you mark them done.

   Anatomy:
     __title  — task title text (top)
     __meta   — status + due row (bottom)
     __status — status (.status-dot + label text)
     __due    — due date text

   Modifiers on __status (label color matches the dot):
     --success  — green ("New")
     --info     — blue ("Working")
     --warning  — yellow
     --danger   — red
     --neutral  — grey (default)

   States:
     __due.is-overdue — red, semibold
   =========================================================== */

.task-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 6px between title and meta */
  padding: var(--space-6) var(--space-7); /* 12 14 */
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  color: var(--color-text);
  transition:
    background-color var(--duration-fast) var(--easing-default),
    border-color var(--duration-fast) var(--easing-default);
}
.task-item:hover {
  background: var(--color-bg-hover);
  border-color: var(--color-border);
}
.task-item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* Stacking: provides spacing between adjacent task items.
   Pages don't need a flex-gap parent — items handle their
   own bottom spacing. Last item drops the margin. */
.task-item + .task-item {
  margin-top: var(--space-4); /* 8px */
}

/* ----- __title: task title ----- */
.task-item__title {
  font-size: var(--font-size-body); /* 14px */
  font-weight: var(--font-weight-semibold);
  line-height: 1.3;
  color: var(--color-text);
}

/* ----- __meta: status + due row ----- */
.task-item__meta {
  display: flex;
  align-items: center;
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
}

/* ----- __status: dot + colored label ----- */
.task-item__status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2); /* 4px between dot and label */
  color: var(--color-text-secondary); /* default neutral */
  flex-shrink: 0;
}
.task-item__status .status-dot {
  /* Dot color comes from .status-dot's own modifier. */
}

/* Status color modifiers — color the label text to match
   the dot. The dot itself gets its color from its own
   .status-dot--* modifier. Pages should keep these in sync. */
.task-item__status--success {
  color: var(--color-green);
}
.task-item__status--info {
  color: var(--color-blue);
}
.task-item__status--warning {
  color: var(--color-yellow);
}
.task-item__status--danger {
  color: var(--color-red);
}
.task-item__status--neutral {
  color: var(--color-text-secondary);
}

/* ----- __due: due date text ----- */
.task-item__due {
  color: var(--color-text-dim);
  flex-shrink: 0;
}
/* Middle-dot separator between status and due. Generated
   by ::before so the markup stays clean. */
.task-item__due::before {
  content: "·";
  margin: 0 var(--space-3); /* 0 6px */
  color: var(--color-text-dim);
}

/* Overdue state — red, semibold to draw attention. */
.task-item__due.is-overdue {
  color: var(--color-red);
  font-weight: var(--font-weight-semibold);
}

/* ----- __body: content wrapper used alongside a prefix (e.g. drag handle) ----- */
.task-item__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ----- --draggable modifier: row layout to place handle left of content ----- */
.task-item--draggable {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-3);
}


/* ===========================================================
   38. NOTE ITEM — .note-item
   Visual reference: Email Thread View / SMS Inbox right
   sidebar — "Notes" cards.

   Card-style note representation that lives in vertical
   stacks inside collapsible sections on right sidebars.
   Used for contact notes, property notes, task notes — any
   place where notes attached to an entity render as a list.

   Mirrors .task-item's surface treatment and stacking
   convention so sidebar cards stay visually consistent.

   Note: the source designs used a cream/yellow note treatment
   (cs-note pattern). Current design moves notes to the same
   neutral off-white surface used by .task-item — a deliberate
   design evolution. The .modal--note variant still uses the
   cream palette for full-screen note modals.

   Anatomy:
     __head    — top row: author + timestamp
     __avatar  — slot for .avatar (typically .avatar--xs/sm)
     __actor   — author name
     __time    — timestamp text (right-aligned, dim)
     __body    — note body text (multi-line, free-form)

   Modifiers: none

   States: none
   =========================================================== */

.note-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 6px between head and body */
  padding: var(--space-6) var(--space-7); /* 12 14 */
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  color: var(--color-text);
  transition:
    background-color var(--duration-fast) var(--easing-default),
    border-color var(--duration-fast) var(--easing-default);
}
.note-item:hover {
  background: var(--color-bg-hover);
  border-color: var(--color-border);
}
.note-item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* Stacking — same convention as .task-item. */
.note-item + .note-item {
  margin-top: var(--space-4); /* 8px */
}

/* ----- __head: author + timestamp ----- */
.note-item__head {
  display: flex;
  align-items: center;
  gap: var(--space-3); /* 6px between avatar and actor */
}

.note-item__avatar {
  flex-shrink: 0;
}

.note-item__actor {
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  color: var(--color-text);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.note-item__time {
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-regular);
  line-height: 1.2;
  color: var(--color-text-dim);
  flex-shrink: 0;
}

/* ----- __body: note text ----- */
.note-item__body {
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-regular);
  line-height: 1.45;
  color: var(--color-text);
  /* Free-form text — pages can render plain text or rich
     content. The component sets baseline typography. */
}

/* ----- --thread modifier: yellow/cream surface for thread-attached notes ----- */
.note-item--thread {
  background: linear-gradient(180deg, #FEF9C3 0%, #FEF3C7 100%);
  border-color: #FDE68A;
  color: #713F12;
}
.note-item--thread:hover {
  background: linear-gradient(180deg, #FEF3C7 0%, #FDE68A 100%);
  border-color: #FCD34D;
}
.note-item--thread .note-item__actor {
  color: #78350F;
}
.note-item--thread .note-item__time {
  color: #A16207;
}
.note-item--thread .note-item__body {
  color: #713F12;
}


/* ===========================================================
   39. TEAMCHAT THREAD — .teamchat-thread
   Visual reference: SMS Inbox / Email Inbox — TeamChat
   block (purple-themed thread) that appears inline within
   message and email lists.

   A self-contained purple-tinted block representing a
   TeamChat conversation attached to an SMS message or
   email. Lives between regular messages/emails in the
   thread view. Includes a label, a stack of messages, and
   a composer at the bottom.

   Distinct from .message (SMS bubble) and .email-message
   (full email). Distinct from any of the four standalone
   composers (.composer / .sms-composer / .email-composer-
   popup / .email-composer-inline) — the teamchat composer
   is purple-themed, smaller, and only appears inside this
   block, so it lives here as a sub-element rather than a
   separate component.

   Anatomy:
     __label        — top label strip (icon + "TEAMCHAT — …")
     __label-icon   — speech-bubble icon in the label

     __messages     — vertical stack of teamchat messages
     __message      — each individual message row
     __avatar       — slot for .avatar (sender avatar)
     __main         — wrapper for byline + body
     __byline       — actor name + timestamp line
     __byline-time  — italic timestamp portion
     __body         — message text
     __mention      — @mention pill inside body (blue)

     __composer     — input row at bottom
     __input        — text input with purple focus
     __emoji        — emoji icon button
     __send         — Send button (slot, composes .btn--primary)

   States:
     .is-flash — brief purple glow when a new message arrives
                 (typically toggled by JS, removed after ~600ms)

   Tokens: uses existing tokens where possible. Several
   purple shades are inlined because the token palette
   currently lacks the fuller --color-purple-{glow,pale,deep}
   range used here. See decisions doc for follow-up to
   expand the purple palette.
   =========================================================== */

.teamchat-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 10px */
  padding: var(--space-7) var(--space-8) var(--space-6); /* 14 16 12 */
  background: linear-gradient(180deg, #FAF7FF 0%, #F6F1FE 100%);
  border: 1px solid #E9E2FB;
  border-radius: var(--radius);
  font-family: var(--font-body);
  color: var(--color-text);
  transition:
    box-shadow var(--duration-slow) var(--easing-default),
    border-color var(--duration-slow) var(--easing-default);
}

/* Flash state — brief purple glow when a new message arrives. */
.teamchat-thread.is-flash {
  border-color: #A78BFA;
  box-shadow: 0 0 0 3px rgba(167, 139, 250, 0.25);
}

/* ----- __label: top label strip ----- */
.teamchat-thread__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px */
  padding-bottom: 2px;
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: var(--font-weight-bold);
  line-height: 1;
  letter-spacing: var(--letter-spacing-uppercase);
  text-transform: uppercase;
  color: #6D28D9; /* purple-dark */
}

.teamchat-thread__label-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-purple);
}
.teamchat-thread__label-icon > svg {
  width: 12px;
  height: 12px;
}

/* ----- __messages: stack of message rows ----- */
.teamchat-thread__messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 10px between messages */
}

.teamchat-thread__message {
  display: flex;
  align-items: flex-start;
  gap: var(--space-5); /* 10px */
}

.teamchat-thread__avatar {
  flex-shrink: 0;
  margin-top: auto;
  margin-bottom: auto;
}

.teamchat-thread__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* ----- __byline: actor + timestamp ----- */
.teamchat-thread__byline {
  font-size: var(--font-size-micro); /* 11px */
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  color: var(--color-text-dim);
}

.teamchat-thread__byline-time {
  font-style: italic;
  font-weight: var(--font-weight-regular);
  color: var(--color-text-dim);
}

/* ----- __body: message text ----- */
.teamchat-thread__body {
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: var(--font-weight-semibold);
  line-height: 1.5;
  color: var(--color-text);
}

/* ----- __mention: @mention pill inline in body ----- */
.teamchat-thread__mention {
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
  margin-right: var(--space-3); /* 6px — breathing room before message content */
}

/* ----- __composer: input row at bottom ----- */
.teamchat-thread__composer {
  position: relative; /* anchors the mention-assist dropdown */
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  padding-top: var(--space-3); /* 6px */
  margin-top: 2px;
  border-top: 1px solid rgba(124, 58, 237, 0.12);
}

/* ----- __input: text input with purple focus ----- */
.teamchat-thread__input {
  flex: 1;
  min-width: 0;
  height: 34px;
  padding: 0 var(--space-6); /* 0 12px */
  background: var(--color-bg);
  border: 1px solid #D4CCEC;
  border-radius: var(--radius-sm);
  outline: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  color: var(--color-text);
  transition:
    border-color var(--duration-fast) var(--easing-default),
    box-shadow var(--duration-fast) var(--easing-default);
}
.teamchat-thread__input::placeholder {
  color: var(--color-text-dim);
}
.teamchat-thread__input:focus {
  border-color: #A78BFA; /* purple-light */
  box-shadow: 0 0 0 3px rgba(167, 139, 250, 0.22);
}

/* ----- __emoji: icon button with purple hover ----- */
.teamchat-thread__emoji {
  position: relative; /* anchors the .emoji-popup absolutely positioned child */
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.teamchat-thread__emoji:hover {
  background: #F5F3FF; /* purple-glow */
  color: #6D28D9; /* purple-dark */
}
.teamchat-thread__emoji:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(167, 139, 250, 0.22);
}
.teamchat-thread__emoji > svg {
  width: 15px;
  height: 15px;
}

/* ----- __send: Send button slot ----- */
.teamchat-thread__send {
  flex-shrink: 0;
  /* Composes .btn--primary. Markup:
       <button class="btn btn--primary teamchat-thread__send">Send</button>
  */
}


/* ===========================================================
   40. DROPDOWN — .dropdown
   Visual reference: any anchored floating panel that
   contains menu-style content (action menus, option pickers,
   account switchers, etc.).

   Pure container chrome — white panel with rounded corners,
   subtle border, drop shadow, padding. No item styling.
   Other components (e.g., .action-menu, .option-list) mount
   inside .dropdown and provide their own item structure.

   Positioning:
     The component sets position: absolute; pages provide a
     position: relative parent to anchor against. Default is
     anchored bottom-left (top: 100% + 6px; left: 0). Pages
     override top/bottom/left/right directly when a different
     anchor is needed.

   Width:
     Component does NOT set width. Pages size via wrapper
     class or inline style. Common widths: 220px (action
     menus), 280px (option pickers), 320px (account lists).

   Anatomy: none. Single-element primitive.

   Modifiers: none.

   States: none.
   =========================================================== */

.dropdown {
  position: absolute;
  top: calc(100% + var(--space-3)); /* 6px below the trigger */
  left: 0;
  z-index: var(--z-popover);
  padding: var(--space-3); /* 6px */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-body);
  color: var(--color-text);
}


/* ===========================================================
   41. ACTION MENU — .action-menu
   Visual reference: Contact / Task / Property Detail "More"
   action dropdown; any kebab/overflow menu where each item
   triggers an action.

   List of action items with optional icons and group dividers.
   The current internal menus on .action-bar, .email-composer-
   popup, and .email-composer-inline duplicate this pattern;
   future refactor will replace those with .action-menu
   instances. (See decisions doc follow-ups.)

   Composition:
     Mounts inside .dropdown for chrome. Markup:
       <div class="dropdown">
         <ul class="action-menu">
           <li><button class="action-menu__item">…</button></li>
           …
         </ul>
       </div>

   Anatomy:
     __item     — each action row (a <button>)
     __icon     — icon slot inside an item (left)
     __label    — label text inside an item
     __divider  — horizontal divider between groups

   Modifiers:
     __item--danger — destructive action (red text, red icon,
                       red-glow hover) for items like
                       "Delete task", "Archive contact"

   States: none — actions fire on click, no persistent state.
   =========================================================== */

.action-menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-family: var(--font-body);
}

.action-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  width: 100%;
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.action-menu__item:hover {
  background: var(--color-bg-hover);
}
.action-menu__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.action-menu__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.action-menu__icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-secondary);
}
.action-menu__icon > svg {
  width: 18px;
  height: 18px;
}
.action-menu__item:hover .action-menu__icon {
  color: var(--color-text);
}

.action-menu__label {
  flex: 1;
  min-width: 0;
}

.action-menu__divider {
  height: 1px;
  background: var(--color-border-light);
  margin: var(--space-3) 0; /* 6px vertical */
  border: 0;
  list-style: none;
}

/* Danger modifier — destructive actions */
.action-menu__item--danger {
  color: var(--color-red);
}
.action-menu__item--danger .action-menu__icon {
  color: var(--color-red);
}
.action-menu__item--danger:hover {
  background: var(--color-red-glow);
  color: var(--color-red);
}
.action-menu__item--danger:hover .action-menu__icon {
  color: var(--color-red);
}


/* ===========================================================
   42. OPTION LIST — .option-list
   Visual reference: Task screen status picker — searchable
   dropdown with status-dot per option and a selected
   checkmark. Used for any "pick one value from a list"
   affordance: status pickers, assignee pickers, category
   pickers, tag selectors, etc.

   Composition:
     Mounts inside .dropdown for chrome. Markup:
       <div class="dropdown">
         <div class="option-list">
           <div class="option-list__search">
             <input class="option-list__search-input">
             <button class="option-list__clear">Clear</button>
           </div>
           <ul class="option-list__items">
             <li><button class="option-list__item">…</button></li>
             …
           </ul>
         </div>
       </div>

   Anatomy:
     __search        — search input wrapper at top
     __search-input  — the search <input>
     __clear         — "Clear" reset link button
     __items         — wrapper for the option list (a <ul>)
     __item          — each option row (a <button>)
     __indicator     — left visual cue: slot for .status-dot,
                        an icon, or a color swatch
     __label         — option label text
     __check         — right-side checkmark (shown via .is-selected)

   Modifiers: none

   States:
     __item.is-selected — currently-selected value (shows checkmark,
                           label color shifts to blue)
     __item.is-active   — keyboard-focused or pointer-highlighted
                           (soft-blue background tint)

     Both states co-exist. The selected item is the persistent
     value; the active item is the transient focus/hover.

   Tokens: existing only.
   =========================================================== */

.option-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* 4px between search block and items */
  font-family: var(--font-body);
}

/* ----- __search: search input wrapper ----- */
.option-list__search {
  display: flex;
  flex-direction: column;
  gap: var(--space-2); /* 4px between input and clear link */
  padding: var(--space-2); /* 4px */
}

.option-list__search-input {
  width: 100%;
  height: 32px;
  padding: 0 var(--space-5); /* 0 10px */
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  outline: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  color: var(--color-text);
  transition:
    border-color var(--duration-fast) var(--easing-default),
    box-shadow var(--duration-fast) var(--easing-default);
}
.option-list__search-input::placeholder {
  color: var(--color-text-dim);
}
.option-list__search-input:focus {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.option-list__clear {
  align-self: flex-start;
  padding: 0 var(--space-2);
  background: transparent;
  border: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-caption); /* 12px */
  font-weight: var(--font-weight-medium);
  color: var(--color-blue);
  cursor: pointer;
}
.option-list__clear:hover {
  text-decoration: underline;
}
.option-list__clear:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
  border-radius: var(--radius-xs);
}

/* ----- __items: option list wrapper ----- */
.option-list__items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* ----- __item: each option row ----- */
.option-list__item {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  width: 100%;
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1.2;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.option-list__item:hover {
  background: var(--color-bg-hover);
}
.option-list__item:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.option-list__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ----- __indicator: left visual cue (dot / icon / swatch) ----- */
.option-list__indicator {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-secondary);
}
.option-list__indicator > svg {
  width: 16px;
  height: 16px;
}

/* ----- __label: option text ----- */
.option-list__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----- __check: right-side selected checkmark ----- */
.option-list__check {
  flex-shrink: 0;
  display: none; /* shown when __item.is-selected */
  align-items: center;
  justify-content: center;
  color: var(--color-blue);
}
.option-list__check > svg {
  width: 16px;
  height: 16px;
}

/* States */
.option-list__item.is-selected {
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
}
.option-list__item.is-selected .option-list__check {
  display: inline-flex;
}

.option-list__item.is-active {
  background: var(--color-blue-glow);
}
/* When an item is BOTH active and selected, the active tint
   stays (no need to override). */

/* ----- Grouped variant: .option-list__group ----- */
/* Buckets an .option-list__items list into named, collapsible
   sections (e.g. one per team member) instead of one flat list.
   Reuses .avatar (indicator), .chip--team (owner tag),
   .collapsible-section__count (count pill), and
   .collapsible-section__chevron/.open (expand/collapse chevron —
   same rotate-on-open animation as the page-level collapsible
   section) rather than redefining any of them. Only the group
   wrapper, its header row, and the item-nesting override below
   are new.

   Markup:
     <li class="option-list__group [is-open]">
       <div class="option-list__group-header">
         <span class="avatar avatar--xs" style="background:...">
           <span class="avatar__initials">AG</span>
         </span>
         <span class="option-list__group-name">Alex Garcia</span>
         <span class="chip chip--team"><span class="chip__label">You</span></span>
         <span class="collapsible-section__count">3</span>
         <button class="collapsible-section__chevron [open]">…</button>
       </div>
       <ul class="option-list__group-items">
         <li><button class="option-list__item">…</button></li>
       </ul>
     </li>

   __group-header is a <div>, not a <button> — same reasoning as
   .collapsible-section__head (#33): it nests a real __chevron
   button, and a button can't contain another button. Bind one
   click listener to the div; the chevron's native click bubbles
   up to it, so mouse clicks anywhere on the row and keyboard
   activation of the chevron both reach the same handler.

   .is-open on the group is the single source of truth: it shows
   __group-items AND wraps header+items in one border (so the
   group reads as one bordered object instead of each item having
   its own border/radius). Collapsed, the header is just another
   flush option-list row. */
.option-list__group {
  border-radius: var(--radius-sm);
  /* .option-list__items is a column flex container with a capped max-height
     (JS-set inline, so the list scrolls instead of growing unbounded) —
     without flex-shrink:0 here, a flex child's default shrink behavior
     compresses every group to fit once total content exceeds that cap,
     instead of the intended scroll. */
  flex-shrink: 0;
}
.option-list__group.is-open {
  border: 1px solid var(--color-border);
  overflow: hidden;
}
.option-list__group-header {
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  width: 100%;
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  cursor: pointer;
  text-align: left;
}
.option-list__group:not(.is-open) .option-list__group-header:hover {
  background: var(--color-bg-hover);
}
.option-list__group.is-open .option-list__group-header {
  border-radius: 0;
}
.option-list__group.is-open .option-list__group-header:hover {
  background: var(--color-bg-hover);
}
.option-list__group-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.option-list__group-items {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0; /* 4px */
  display: none;
}
.option-list__group.is-open .option-list__group-items {
  display: block;
}
/* Items nested in a group sit flush against the group's own
   border — no border/radius of their own, indented under the
   avatar so they read as children of the header above them. */
.option-list__group .option-list__item {
  border-radius: 0;
  padding-left: 36px;
}


/* ===========================================================
   43. TOPBAR — .topbar
   App-level top navigation bar. Contains the logo, primary
   nav, and right-side utilities (notifications, profile).
   System-level alert strip (.topbar__system-banner) renders
   below the topbar as a sibling — both live inside the layout
   wrapper .app__topbar (positioning of .app__topbar itself
   is layout.css's job; this component handles internal layout
   only).

   Distinct from:
     .banner            — inline rounded callout. .topbar__system-banner
                          is a full-width edge-to-edge strip with
                          a different layout contract.
     .nav-sidebar (#3a) — left-rail vertical nav. .topbar is horizontal,
                          app-chrome.
     .action-bar (#29)  — page-level action cluster on a blue pill. The
                          topbar is the white app shell at the top.

   Anatomy:
     __logo               — wrapper for mark + wordmark on the far left
     __logo-mark          — slot for the logo SVG/image
     __logo-text          — wordmark text next to the mark
     __divider            — vertical line separating logo from nav
     __nav                — primary nav list container
     __nav-link           — individual nav link
     __spacer             — flex spacer pushing utils to the right edge
     __utils              — right-side utilities cluster (notifications,
                            profile)
     __notification-bell  — notification icon button
                            (composes .btn .btn--ghost .btn--icon — only
                            adds positioning + the pulse-dot child)
     __notification-pulse — small absolutely-positioned dot for unread
     __profile            — wrapper for avatar trigger + dropdown
     __avatar-trigger     — clickable button surrounding .avatar
     __profile-dropdown   — dropdown panel (composes .dropdown — only
                            adds topbar-specific positioning override:
                            hangs from the right edge instead of left)
     __dropdown-link      — individual link inside the profile dropdown
     __dropdown-divider   — thin horizontal divider between groups

     __system-banner      — full-width alert strip rendered below the
                            topbar. Sibling of .topbar, NOT a child.

   Modifiers (on __system-banner):
     --info     — blue tint
     --warning  — yellow tint
     --danger   — red tint

   States:
     .topbar__nav-link.is-active   — current page indicator
     .topbar__profile.is-open      — dropdown open. Toggled by the
                                     trigger; same pattern as
                                     .email-message.is-info-open.

   Composition notes:
     - .topbar__notification-bell is just a positioning hook — the
       button itself is .btn .btn--ghost .btn--icon. Markup:
         <button class="btn btn--ghost btn--icon topbar__notification-bell"
                 aria-label="Notifications">
           <svg>…</svg>
           <span class="topbar__notification-pulse"></span>
         </button>
     - .topbar__avatar-trigger is the borderless button wrapping
       .avatar. Markup:
         <button class="topbar__avatar-trigger" aria-label="Open menu">
           <span class="avatar avatar--sm">…</span>
         </button>
     - .topbar__profile-dropdown composes .dropdown. The component
       only adds the right: 0 / left: auto override so the dropdown
       hangs from the right edge instead of the left.
   =========================================================== */

.topbar {
  display: flex;
  align-items: center;
  gap: var(--space-9); /* 20px between logo, nav, utils */
  height: 100%;
  width: -webkit-fill-available;
  padding: 0 var(--space-9); /* 0 20 — vertical comes from container */
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border-light);
  font-family: var(--font-body);
  color: var(--color-text);
}

/* ----- __logo: brand mark + wordmark on the far left ----- */
.topbar__logo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  flex-shrink: 0;
  text-decoration: none;
  color: inherit;
}

.topbar__logo-img {
  height: 21px;
  width: auto;
}

.topbar__logo-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  /* The mark itself (SVG/image) is provided by the consumer.
     This slot only handles sizing + alignment. */
}
.topbar__logo-mark > svg,
.topbar__logo-mark > img {
  width: 100%;
  height: 100%;
}

.topbar__logo-text {
  font-family: var(--font-display);
  font-size: var(--font-size-h5); /* 15px — chrome scale */
  font-weight: var(--font-weight-bold);
  line-height: 1;
  letter-spacing: var(--letter-spacing-display);
  color: var(--color-text);
  white-space: nowrap;
}

/* ----- __divider: vertical line between logo and nav ----- */
.topbar__divider {
  width: 1px;
  height: 24px;
  flex-shrink: 0;
  background: var(--color-border);
  /* Use as <span class="topbar__divider"></span> between
     __logo and __nav. */
}

/* ----- __nav: primary nav list ----- */
.topbar__nav {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2); /* 4px between nav links */
  flex-shrink: 0;
  /* No <ul> styling assumptions — markup may use <nav> with
     <a> children or <ul><li> with <a>. The flex layout works
     either way. */
}

.topbar__nav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px between optional icon and label */
  padding: var(--space-4) var(--space-6); /* 8 12 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-body); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: var(--color-text-secondary);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.topbar__nav-link:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.topbar__nav-link:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.topbar__nav-link.is-active {
  background: var(--color-blue-glow);
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
}

/* The count pill stays out of the layout until a count > 0 is known (its display rule would otherwise win). */
.tabs__count[hidden] { display: none !important; }
/* Urgent: something waiting is about to expire. */
.tabs__count.is-urgent { background: var(--color-danger); color: var(--color-text-on-action); }

/* Approvals quick-review: the Approvals button on the Automations pages with a popover that hangs from it.
   Starts hidden until ai-approvals-badge.js confirms this person may review drafts. */
.apop { position: relative; display: inline-flex; }
.apop[hidden] { display: none; }
.apop__panel {
  left: auto;
  right: 0;
  width: 340px;
  max-width: calc(100vw - var(--space-8));
  padding: 0;
  display: none;
  z-index: 50;
}
.apop.is-open .apop__panel { display: block; }
.apop__head { display: flex; align-items: center; justify-content: space-between; padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--color-border); }
.apop__title { margin: 0; font-size: var(--font-size-small); font-weight: var(--font-weight-semibold); }
.apop__all { font-size: var(--font-size-small); color: var(--color-blue); text-decoration: none; }
.apop__list { max-height: min(60vh, 420px); overflow-y: auto; }
.apop__item { padding: var(--space-5) var(--space-6); display: flex; flex-direction: column; gap: var(--space-3); }
.apop__item + .apop__item { border-top: 1px solid var(--color-border); }
.apop__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.apop__name { font-size: var(--font-size-small); font-weight: var(--font-weight-semibold); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apop__when { font-size: var(--font-size-micro); color: var(--color-text-secondary); display: inline-flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.apop__quote, .apop__draft, .apop__note, .apop__error, .apop__empty { margin: 0; font-size: var(--font-size-small); line-height: 1.4; }
.apop__quote { color: var(--color-text-secondary); }
.apop__draft { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.apop__note { font-size: var(--font-size-micro); color: var(--color-text-secondary); display: flex; align-items: center; gap: var(--space-3); }
.apop__error { font-size: var(--font-size-micro); color: var(--color-danger); }
.apop__actions { display: flex; gap: var(--space-3); margin-top: var(--space-2); }
.apop__empty { padding: var(--space-8) var(--space-6); text-align: center; color: var(--color-text-secondary); }
.apop__foot { padding: var(--space-4) var(--space-6); border-top: 1px solid var(--color-border); font-size: var(--font-size-micro); color: var(--color-text-secondary); text-align: center; }
@media (max-width: 640px) {
  /* Anchored to the button the popover would run off a narrow screen, so it spans the width under the top bar. */
  .apop { position: static; }
  .apop__panel { position: fixed; left: var(--space-5); right: var(--space-5); top: calc(var(--topbar-height, 56px) + var(--space-3)); width: auto; max-width: none; }
}

/* Getting started — subtle pill treatment so it reads as special without shouting */
.topbar__nav-link[data-js="nav-link-onboarding"] {
  background: var(--color-blue-glow);
  color: var(--color-blue);
  font-weight: var(--font-weight-semibold);
  border: 1px solid var(--color-blue-pale);
}
.topbar__nav-link[data-js="nav-link-onboarding"]:hover {
  background: var(--color-blue-pale);
  color: var(--color-blue);
}

/* ----- __spacer: flex spacer ----- */
.topbar__spacer {
  flex: 1;
  min-width: 0;
}

/* ----- __utils: right-side utilities cluster ----- */
.topbar__utils {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3); /* 6px between bell, profile, etc. */
  flex-shrink: 0;
}

/* ----- __notification-bell: positioning hook ----- */
/* The button itself is .btn .btn--ghost .btn--icon. This class
   only adds the relative positioning context for the pulse dot. */
.topbar__notification-bell {
  position: relative;
}

.topbar__notification-pulse {
  position: absolute;
  top: var(--space-3); /* 6px from top */
  right: var(--space-3);
  width: 8px;
  height: 8px;
  background: var(--color-red);
  border: 2px solid var(--color-bg);
  border-radius: 50%;
  pointer-events: none;
  /* The white border lifts the dot off the button's hover bg
     so it stays visible during hover. */
}

@keyframes topbar-bell-alert-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5); }
  70%  { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
  100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
}

/* Pseudo-element is sized to the icon (18×18) and centered on the button
   so the ripple starts at the icon edge rather than the button padding edge.
   inset:0 + margin:auto is used instead of top/left+translate because the
   button is display:inline-flex and percentage positioning can mis-resolve. */
.topbar__notification-bell--alert::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 18px;
  height: 18px;
  margin: auto;
  border-radius: var(--radius-sm);
  animation: topbar-bell-alert-pulse 1.6s ease-out infinite;
  pointer-events: none;
}

/* ===========================================================
   NOTIFICATION CARDS — .ec-notifications / .notification-card
   Global toast stack (incoming calls, team chat) rendered by
   NotificationCards in public/main.js, plus the "all
   notifications" .right-side-panel opened by clicking a card.

   Cards stack top-right: newest on top, older cards fan out
   behind it (offset + narrower + grayed via .gray-overlay).
   Cards inside the side panel drop .fixed and flow in a list.
   =========================================================== */

.ec-notifications {
  position: fixed;
  top: 56px;
  right: 0;
  z-index: var(--z-toast);
  margin: 0;
  padding: 0;
  list-style: none;
}

.notification-card {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-6);
  box-sizing: border-box;
  width: 340px;
  max-width: 340px;
  padding: var(--space-6) var(--space-7);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
}

.notification-card.fixed { position: fixed; }
.notification-card.shadow { box-shadow: var(--shadow-md); }

.ec-notifications li:nth-child(1) { z-index: 3; right: 20px; top: 60px; }
.ec-notifications li:nth-child(2) { z-index: 2; right: 26px; top: 66px; width: 328px; }
.ec-notifications li:nth-child(3) { z-index: 1; right: 32px; top: 72px; width: 316px; }
.ec-notifications li:nth-child(n+4) { z-index: 0; right: 32px; top: 72px; width: 316px; }

/* Older cards sit behind the newest: dimmed with a white wash instead of gray */
.ec-notifications li:nth-child(2) .gray-overlay,
.ec-notifications li:nth-child(3) .gray-overlay {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  transition: background-color 0.5s ease-in-out;
  pointer-events: none;
}

.ec-notifications li:nth-child(2) .gray-overlay { background-color: rgba(255, 255, 255, 0.55); }
.ec-notifications li:nth-child(3) .gray-overlay { background-color: rgba(255, 255, 255, 0.85); }

/* Icon chip — tinted rounded square, tone follows the notification type */
.notification-card .icon-container {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
}

.notification-card .icon-container .fa-comment { font-size: 15px; }

.notification-card.phone .icon-container { background: var(--color-green-glow); color: var(--color-green-dark); }
.notification-card.sms .icon-container { background: var(--color-blue-glow); color: var(--color-blue); }
.notification-card.teamChat .icon-container { background: var(--color-purple-glow); color: var(--color-purple); }

.notification-card-body {
  flex: 1;
  min-width: 0;
  padding-right: var(--space-6);
}

.notification-card__title {
  margin: 0;
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  line-height: 1.3;
  color: var(--color-text);
}

.notification-card__text {
  margin: 2px 0 0;
  line-height: 1.4;
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.notification-card__link {
  display: inline-block;
  margin-top: var(--space-3);
  font-weight: var(--font-weight-medium);
  color: var(--color-blue);
  text-decoration: none;
}

.notification-card__link:hover { color: var(--color-blue-dark); text-decoration: underline; }

.notification-card-close,
.notification-panel-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-xs);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}

.notification-card-close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  width: 22px;
  height: 22px;
}

.notification-panel-close {
  position: absolute;
  top: 50%;
  right: 0;
  width: 26px;
  height: 26px;
  transform: translateY(-50%);
}

.notification-card-close:hover,
.notification-panel-close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

.notification-card-close:focus-visible,
.notification-panel-close:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 2px var(--color-focus-ring);
}

@keyframes notification-scale-up {
  from { transform: scale(1.2) translateY(50px); opacity: 0; }
  to   { transform: scale(1) translateY(0); opacity: 1; }
}

@keyframes notification-scale-down {
  from { transform: scale(1) translateY(0); opacity: 1; }
  to   { transform: scale(1.2) translateY(25px); opacity: 0; }
}

.notification-card.moveToFront { animation: notification-scale-up 250ms; }
.notification-card.moveToBack  { animation: notification-scale-down 250ms; }

/* "All notifications" side panel */
.right-side-panel {
  position: fixed;
  top: 0;
  right: 0;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  width: 300px;
  height: 100vh;
  max-height: 100vh;
  padding: var(--space-9);
  box-sizing: border-box;
  overflow-y: hidden;
  background: var(--color-bg);
  box-shadow: var(--shadow-lg);
  font-family: var(--font-body);
  transition: transform 0.5s ease-in-out;
}

.right-side-panel h2 {
  position: relative;
  margin: 0 0 var(--space-6);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border-light);
  font-family: var(--font-display);
  font-size: var(--font-size-h5);
  font-weight: var(--font-weight-semibold);
  text-align: left;
  color: var(--color-text);
}

.right-side-panel h2 .todo-popup-close {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  cursor: pointer;
}

.right-side-panel.offscreen { transform: translateX(100%); }

.notification-card-list {
  max-height: 88vh;
  padding: 0;
  overflow-x: hidden;
  overflow-y: auto;
}

.notification-card-list ul {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin: 2px 0 4px 2px;
  padding: 0;
}

.right-side-panel .notification-card {
  width: auto;
  max-width: none;
}

/* ----- __phone-btn: dialer button + incoming-call ring ----- */
.topbar__phone-btn {
  position: relative;
}

@keyframes topbar-phone-ring-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(0, 202, 54, 0.55); }
  70%  { box-shadow: 0 0 0 10px rgba(0, 202, 54, 0); }
  100% { box-shadow: 0 0 0 0 rgba(0, 202, 54, 0); }
}

.topbar__phone-btn--ringing {
  color: var(--color-green);
}

.topbar__phone-btn--ringing::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 18px;
  height: 18px;
  margin: auto;
  border-radius: 50%;
  animation: topbar-phone-ring-pulse 1.6s ease-out infinite;
  pointer-events: none;
}

/* Mini dialer — iframe container toggled by openCallDialog() (main.js).
   Fixed under the topbar so it never affects page flow. */
.dialpad-mini-dialer {
  position: fixed;
  top: 56px;
  right: var(--space-9);
  z-index: var(--z-popover);
  max-width: 420px;
  max-height: 520px;
  background: var(--color-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.dialpad-mini-dialer iframe {
  display: block;
  border: none;
}

/* ----- __profile: avatar trigger + dropdown wrapper ----- */
.topbar__profile {
  position: relative;
  /* Anchors __profile-dropdown's absolute positioning. */
  /* Flex, not block: as a block the inline-flex avatar trigger sits on a text
     baseline, which moves once JS fills in the initials and made the avatar
     jump ~3px on every page load. */
  display: flex;
}

.topbar__avatar-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: box-shadow var(--duration-fast) var(--easing-default);
}
.topbar__avatar-trigger:hover {
  box-shadow: 0 0 0 2px var(--color-border);
}
.topbar__avatar-trigger:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.topbar__profile.is-open .topbar__avatar-trigger {
  box-shadow: 0 0 0 2px var(--color-blue-pale);
}

/* ----- __profile-dropdown: composes .dropdown ----- */
/* .dropdown provides chrome (white surface, border, shadow,
   z-index). This class adds topbar-specific positioning:
   hangs from the right edge of __profile instead of the left. */
.topbar__profile-dropdown {
  left: auto;
  right: 0;
  min-width: 200px;
  padding: var(--space-3); /* 6px — overrides .dropdown's default if needed */
  display: none; /* shown only when parent has .is-open */
}
.topbar__profile.is-open .topbar__profile-dropdown {
  display: block;
}

.topbar__dropdown-link {
  display: flex;
  align-items: center;
  gap: var(--space-4); /* 8px */
  padding: var(--space-4) var(--space-5); /* 8 10 */
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  color: var(--color-text);
  text-decoration: none;
  cursor: pointer;
  width: 100%;
  text-align: left;
  transition: background-color var(--duration-fast) var(--easing-default);
}
.topbar__dropdown-link:hover {
  background: var(--color-bg-hover);
}
.topbar__dropdown-link:focus-visible {
  outline: 0;
  background: var(--color-bg-hover);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.topbar__dropdown-link > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  color: var(--color-text-dim);
}

.topbar__dropdown-divider {
  height: 1px;
  margin: var(--space-2) 0; /* 4px top/bottom */
  background: var(--color-border-light);
  border: 0;
  /* Use as <hr class="topbar__dropdown-divider"> */
}

/* ----- __system-banner: full-width strip below the topbar -----
   Sibling of .topbar, NOT a child. Both live inside .app__topbar
   (or whatever container layout.css uses). This is a full-width
   edge-to-edge strip — distinct from .banner (which is a rounded
   inline card with margins). */
.topbar__system-banner {
  display: flex;
  align-items: center;
  gap: var(--space-5); /* 10px */
  padding: var(--space-4) var(--space-9); /* 8 20 */
  font-family: var(--font-body);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  border-bottom: 1px solid var(--color-border-light);
  /* Default neutral chrome — modifiers below override bg / color. */
  background: var(--color-bg-off);
  color: var(--color-text);
}

.topbar__system-banner > svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

/* Push trailing actions (buttons, dismiss) to the right. */
.topbar__system-banner > :last-child {
  margin-left: auto;
}

/* Modifiers — semantic color treatments. Map to the same tokens
   .banner uses for visual consistency (info=blue, warning=yellow,
   danger=red), but as full-width strip chrome. */
.topbar__system-banner--info {
  background: var(--color-blue-glow);
  color: var(--color-text);
  border-bottom-color: var(--color-blue-pale);
}
.topbar__system-banner--info > svg {
  color: var(--color-blue);
}

.topbar__system-banner--warning {
  background: var(--color-yellow-glow);
  color: var(--color-text);
  border-bottom-color: var(--color-yellow-pale);
}
.topbar__system-banner--warning > svg {
  color: var(--color-yellow);
}

.topbar__system-banner--danger {
  background: var(--color-red-glow);
  color: var(--color-text);
  border-bottom-color: var(--color-red-pale);
}
.topbar__system-banner--danger > svg {
  color: var(--color-red);
}

/* ----- Responsive: topbar collapses at ≤1000px ----- */
/* Mobile-only dropdown nav items are hidden by default and
   revealed only when the primary nav disappears. */
.topbar__dropdown-link--mobile-only,
.topbar__dropdown-divider--mobile-only {
  display: none;
}

/* Icon-only logo is hidden by default; swapped in on phones. */
.topbar__logo-img--icon {
  display: none;
  height: 27px;
}

@media (max-width: 1055px) {
  /* Primary nav and its separating divider hide — nav moves into the profile dropdown */
  .topbar__nav,
  .topbar__divider {
    display: none;
  }

  .topbar__dropdown-link--mobile-only {
    display: flex;
  }

  .topbar__dropdown-divider--mobile-only {
    display: block;
  }
}

@media (max-width: 835px) {
  .topbar__logo-img--full {
    display: none;
  }

  .topbar__logo-img--icon {
    display: inline-block;
  }
}


/* ===========================================================
   44. URL FIELD — .url-field
   Visual reference: Settings → Basic Information page,
   "Scheduling page URL" and "Your company EveryChat URL" rows.
   Source pattern: .ps-url-field with .ps-url-fix prefix/suffix.

   Input field with a non-editable adornment on one or both
   sides. The adornment displays in monospace on a tinted
   surface, separated from the input by a vertical divider —
   visually communicates "you can't edit this part."

   Use cases:
     - URL slugs:    https://everychat.com/[your-slug]
     - Subdomains:   [your-subdomain].everychat.com
     - Extensions:   [phone] x [extension]
     - Currency:     $ [amount]

   Distinct from:
     .field / .input (#7) — the standard form field with label
                            + bare input. .url-field is a
                            specialized input replacement that
                            slots INSIDE .field (alongside the
                            label and hint chrome).
     .search-input (#12)  — has an inline icon, not a divided
                            adornment slot.

   Anatomy:
     __fix     — prefix or suffix slot (mono font, tinted bg)
     __input   — the editable input (borderless, fills space)

   Modifiers (on __fix):
     --left   — fix sits BEFORE the input (right-side divider)
     --right  — fix sits AFTER the input (left-side divider)

   Composition:
     This component is the input REPLACEMENT, not the whole row.
     Compose with .field for the label / hint chrome:

       <label class="field">
         <span class="field__label">Scheduling page URL</span>
         <div class="url-field">
           <span class="url-field__fix url-field__fix--left">
             https://everychat.com/
           </span>
           <input class="url-field__input" type="text" value="itsmerileyp">
         </div>
         <span class="field__hint">Share this link so customers can book.</span>
       </label>

     Both __fix modifiers can co-exist (prefix AND suffix on the
     same field) — useful for ranges or wrapped values.

   States:
     :hover         — border darkens (whole field reacts as one)
     :focus-within  — blue border + focus ring (whole field
                      reacts when input is focused)
=========================================================== */

.url-field {
  display: flex;
  align-items: stretch;
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); /* 8px */
  overflow: hidden; /* prevents __fix bg from bleeding past corners */
  transition:
    border-color var(--duration-fast) var(--easing-default),
    box-shadow var(--duration-fast) var(--easing-default);
}
.url-field:hover {
  border-color: var(--color-border-hover);
}
.url-field:focus-within {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* ----- __fix: prefix or suffix slot ----- */
.url-field__fix {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  padding: var(--space-5) var(--space-6); /* 10 12 */
  background: var(--color-bg-off);
  font-family: var(--font-mono);
  font-size: var(--font-size-small); /* 13px */
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text-dim);
  white-space: nowrap;
  user-select: all; /* one click selects the whole adornment for copy */
}

/* Modifiers — divider sits on the side facing the input. */
.url-field__fix--left {
  border-right: 1px solid var(--color-border);
}
.url-field__fix--right {
  border-left: 1px solid var(--color-border);
}

/* ----- __input: borderless input filling remaining space ----- */
.url-field__input {
  flex: 1;
  min-width: 0; /* allows flex shrinking below content width */
  padding: var(--space-5) var(--space-6); /* 10 12 — matches __fix */
  background: transparent;
  border: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-body); /* 14px */
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text);
}
.url-field__input::placeholder {
  color: var(--color-text-dim);
}
.url-field__input:focus {
  outline: 0;
  /* Focus styling lives on the parent .url-field via :focus-within
      so the whole box reacts as a unit. */
}
.url-field__input:disabled {
  background: transparent;
  color: var(--color-text-dim);
  cursor: not-allowed;
}


/* ===========================================================
   45. SCHEDULING SIDEBAR — .sched-sidebar
   Public-facing scheduling pages. Sticky host-info panel
   shown on the left side of the two-column scheduling layout.
   Displays the host avatar, booking context, appointment
   title, an accent rule, and meta rows (duration, location…).

   Mobile: horizontal row stacked above the main content.
   Tablet+: sticky vertical column, 280px, border-right.
   Desktop: 300px.

   Anatomy:
     __info       — text block (kicker + title)
     __kicker     — "Booking with …" eyebrow label
     __title      — appointment name (h1)
     __accent     — decorative blue rule (tablet+ only)
     __meta       — wrapper for detail rows (hidden mobile)
     __meta-row   — individual detail row: icon + text
     __meta-icon  — icon slot inside a meta row
   =========================================================== */

.sched-sidebar {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-6) var(--space-8);
  border-bottom: 1px solid var(--color-border-light);
  flex-shrink: 0;
}

.sched-sidebar__info {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.sched-sidebar__kicker {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  font-weight: var(--font-weight-regular);
  margin: 0;
}

.sched-sidebar__title {
  font-family: var(--font-display);
  font-size: var(--font-size-h4);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
  margin: 0;
  line-height: 1.2;
  letter-spacing: var(--letter-spacing-h4);
}

.sched-sidebar__accent {
  display: none;
}

.sched-sidebar__meta {
  display: none;
}

@media (min-width: 768px) {
  .sched-sidebar {
    width: 280px;
    flex-shrink: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-6);
    padding: var(--space-12) var(--space-10);
    border-bottom: none;
    border-right: 1px solid var(--color-border-light);
  }

  .sched-sidebar__info {
    gap: var(--space-2);
  }

  .sched-sidebar__kicker {
    font-size: var(--font-size-caption);
  }

  .sched-sidebar__title {
    font-size: var(--font-size-h3);
    letter-spacing: var(--letter-spacing-h3);
    margin-top: var(--space-1);
  }

  .sched-sidebar__accent {
    display: block;
    width: 48px;
    height: 3px;
    background: var(--color-action);
    border-radius: 2px;
    flex-shrink: 0;
  }

  .sched-sidebar__meta {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .sched-sidebar__meta-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--font-size-small);
    color: var(--color-text-secondary);
  }

  .sched-sidebar__meta-icon {
    width: 14px;
    text-align: center;
    flex-shrink: 0;
    color: var(--color-text-dim);
  }
}

@media (min-width: 1024px) {
  .sched-sidebar {
    width: 300px;
    padding: var(--space-14) var(--space-12);
  }
}


/* ===========================================================
   46. COMMUNICATION TIMELINE — .cdv-tlc + .timeline-filters
   Visual reference: Team Member Detail, Task Detail, Contact
   Detail — chronological list of messages, calls, notes, and
   other communication events with a vertical dot-and-line
   spine.

   Composition:
     Optionally preceded by a .timeline-filters pill bar that
     filters visible entries by channel type. The timeline
     list itself is a .cdv-tlc wrapper that contains one
     .cdv-tlc-i per event.

   Anatomy (.timeline-filters):
     .timeline-filters       — flex row of filter pills
     .timeline-filter-btn    — individual pill toggle button
     .timeline-filter-btn.is-active — selected filter state

   Anatomy (.cdv-tlc):
     .cdv-tlc                — flex column list container
     .cdv-tlc-i              — one timeline entry (flex row)
     .cdv-tlc-dotcol         — left column: dot + connecting line
     .cdv-tlc-dot            — 9px circle, channel-coloured
     .cdv-tlc-dot.{type}     — sms / email / call / estimate / facebook /
                               instagram / note / task / review /
                               form / attachment
     .cdv-tlc-line           — 1px vertical line between entries
     .cdv-tlc-body           — right column: header + content
     .cdv-tlc-head           — flex row: type · tag · timestamp
     .cdv-tlc-type           — channel label (uppercase micro)
     .cdv-tlc-tag.ct         — direction pill (Outbound / Inbound)
     .cdv-tlc-tm             — timestamp (pushed right)
     .cdv-tlc-c              — message body text
   =========================================================== */

/* ── Filter pill bar ─────────────────────────────────────── */

/* Canonical wrapper for all communication timeline filter bars.
   Used by _setupTeamMemberLayout() and _setupContactViewLayout() in
   communication-timeline.js across all four detail pages. */
.timeline-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-5) 0;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-6);
}

/* Pill buttons inside .timeline-filters */
.timeline-filter-btn {
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: none;
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default),
    border-color var(--duration-fast) var(--easing-default);
}
.timeline-filter-btn:hover:not(.is-active) {
  background: var(--color-bg-hover);
  border-color: var(--color-border-hover);
  color: var(--color-text);
}
.timeline-filter-btn.is-active {
  background: var(--color-blue);
  border-color: var(--color-blue);
  color: var(--color-text-on-action);
  font-weight: var(--font-weight-semibold);
}

/* ── Timeline list ───────────────────────────────────────── */

.cdv-tlc {
  display: flex;
  flex-direction: column;
}

/* Empty state rendered inside a cdv-tlc container */
.cdv-tlc-empty-state {
  padding: var(--space-16) var(--space-9);
}

/* Each entry */
.cdv-tlc-i {
  display: flex;
  gap: 12px;
  padding: var(--space-7) 0;
  border-bottom: 1px solid var(--color-border-light);
}
.cdv-tlc-i:last-child {
  border-bottom: none;
}

/* Left column — dot + connecting line */
.cdv-tlc-dotcol {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 4px;
  width: 11px;
  flex-shrink: 0;
}

.cdv-tlc-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--color-text-dim);
}

/* Channel dot colours */
.cdv-tlc-dot.sms,
.cdv-tlc-dot.text       { background: var(--color-blue); }
.cdv-tlc-dot.email      { background: var(--color-purple); }
.cdv-tlc-dot.call       { background: var(--color-green); }
.cdv-tlc-dot.facebook   { background: #1877F2; }
.cdv-tlc-dot.instagram  { background: var(--color-pink); }
.cdv-tlc-dot.note       { background: var(--color-text-dim); }
.cdv-tlc-dot.task       { background: #F59E0B; }
.cdv-tlc-dot.estimate   { background: var(--color-teal); }
.cdv-tlc-dot.review     { background: #F59E0B; }
.cdv-tlc-dot.form       { background: var(--color-purple); }
.cdv-tlc-dot.attachment { background: var(--color-pink); }

/* Connecting line — hidden via CSS on last entry, no JS needed */
.cdv-tlc-line {
  width: 1px;
  flex: 1;
  background: var(--color-border-light);
  margin-top: 4px;
}
.cdv-tlc-i:last-child .cdv-tlc-line {
  display: none;
}

/* Right column — header + body */
.cdv-tlc-body {
  flex: 1;
  min-width: 0;
}

.cdv-tlc-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 3px;
  flex-wrap: wrap;
}

/* Channel label e.g. "TEXT", "EMAIL" */
.cdv-tlc-type {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Direction pill e.g. "Outbound", "Inbound" */
.cdv-tlc-tag {
  font-size: 10px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  font-weight: var(--font-weight-medium);
}
.cdv-tlc-tag.ct {
  background: var(--color-blue-pale);
  color: var(--color-blue-dark);
}

/* Timestamp — pushed to the right */
/* ── The right-hand end of a history row: the buttons, then the time ───
   JP, 2026-10-10: *"The formatting of the pin and the trash can is very
   tight, and the justification is wrong… They're especially bad on the
   property screen."*

   **A ROW, NOT A LINE OF TEXT.** This used to be a plain inline span with the
   buttons dropped into it, each nudged along by its own right margin. Inline
   buttons sitting next to text line up on the text's baseline, not on its
   middle, so the icons rode low against the timestamp and the gaps between
   them were whatever the margins happened to add up to. A flex row with one
   gap centres everything on the same line and spaces it evenly, and the
   spacing is now stated once instead of being the sum of three margins. */
.cdv-tlc-tm {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-micro);
  color: var(--color-text-dim);
  margin-left: auto;
  white-space: nowrap;
}

/* ── The pin and the bin on a note's row in the history ───────────────
   Moved here from `pages/contacts/details.css` on 2026-10-10. **That is why
   they looked so much worse on a property**: the property screen never loads
   the contact screen's stylesheet, so over there these buttons had no styling
   at all — no size, no centring, no spacing — and were simply two raw browser
   buttons jammed together.

   Outline thumbtack = not pinned; filled = pinned. */
.timeline-note-pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* A SQUARE BIG ENOUGH TO HIT. It was 2px of padding round a 12px icon,
     which is a 16px target — under half what a finger needs, and it made the
     two buttons look crushed together. The row's `gap` now does the spacing;
     these carry no margins of their own. */
  width: 22px;
  height: 22px;
  padding: 0;
  margin: 0;
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-dim);
  border-radius: var(--radius-xs);
  transition: color var(--duration-fast) var(--easing-default),
              background var(--duration-fast) var(--easing-default);
}

.timeline-note-pin:hover {
  color: var(--color-text);
  background: var(--color-bg-hover);
}

.timeline-note-pin--pinned {
  color: var(--color-blue);
}

.timeline-note-pin--pinned:hover {
  color: var(--color-blue);
  background: var(--color-blue-glow, var(--color-bg-hover));
}

.timeline-note-pin--delete:hover {
  color: var(--color-red);
  background: var(--color-red-glow, var(--color-bg-hover));
}

/* Message body */
.cdv-tlc-c {
  font-size: var(--font-size-small);
  color: var(--color-text);
  line-height: 1.55;
  word-break: break-word;
}
.cdv-tlc-c .muted {
  color: var(--color-text-dim);
}

/* ── Rich content sub-elements (email, attachments, calls, reviews) ── */

/* Collapsible email body */
.cdv-tlc-email-details {
  margin-top: var(--space-3);
}

.cdv-tlc-email-summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-blue);
  cursor: pointer;
  list-style: none;
  padding: var(--space-2) 0;
}

.cdv-tlc-email-summary::-webkit-details-marker { display: none; }

.cdv-tlc-email-summary::before {
  content: '▸';
  font-size: 10px;
  transition: transform var(--duration-fast) var(--easing-default);
}

.cdv-tlc-email-details[open] .cdv-tlc-email-summary::before {
  transform: rotate(90deg);
}

.timeline-email-body-iframe {
  display: block;
  width: 100%;
  border: none;
  overflow: hidden;
  margin-top: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
}

/* SMS/MMS and email attachment grid */
.timeline-attachments-scroll {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.timeline-attachment-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  width: 80px;
  flex-shrink: 0;
}

.timeline-attachment-thumb-wrapper {
  position: relative;
  width: 80px;
  height: 80px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-bg-off);
  border: 1px solid var(--color-border);
  display: flex;
  align-items: center;
  justify-content: center;
}

.timeline-attachment-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

video.timeline-attachment-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.timeline-attachment-audio-thumb,
.timeline-attachment-pdf-thumb,
.timeline-attachment-generic-thumb {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  color: var(--color-text-dim);
}

.timeline-attachment-download {
  display: block;
  width: 100%;
  height: 100%;
}

.timeline-attachment-download-btn {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 16px;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--easing-default);
  border-radius: var(--radius-sm);
}

.timeline-attachment-thumb-wrapper:hover .timeline-attachment-download-btn {
  opacity: 1;
}

.timeline-attachment-filename {
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}

/* Call record / voicemail / recording links */
.conversation-link {
  color: var(--color-blue);
  text-decoration: none;
  font-size: var(--font-size-caption);
}

.conversation-link:hover {
  text-decoration: underline;
}

.cdv-tlc-call-links {
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  margin-left: var(--space-3);
}

/* Review rating + message body */
.cdv-tlc-review-rating {
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

.cdv-tlc-review-body {
  margin-top: var(--space-3);
  font-size: var(--font-size-small);
  color: var(--color-text-secondary);
  line-height: 1.5;
  white-space: pre-wrap;
}

/* The Personal Tasks panel's list view has to FILL the drawer, or the hint
   at its foot lands just under the last task instead of at the bottom of the
   screen — JP asked for it *"at the very, very bottom"*. The task list in the
   middle takes the slack; the box at the top and the hint at the bottom do
   not stretch. */
#personalTasksModal #todoList {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* ── "Personal task" on an open task in the panel ─────────────────────
   JP, 2026-10-10. The same checkbox and the same words the Create Task box
   uses, so the two screens say one thing one way. All this adds is the line
   underneath, which matters because unticking makes the task leave the panel
   — something that vanishes with no warning reads as deleted. */
.todo-personal-note {
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.4;
  margin: var(--space-2) 0 0 26px;
}

/* ── THE HOTKEY HINT at the foot of the Personal Tasks panel ───────────
   JP, 2026-10-10. Quiet on purpose: it is a thing to notice once and then
   stop seeing. It sits at the bottom of the panel, outside the scrolling
   list, so it stays put while the tasks move. */
.personal-tasks-hint {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
  padding: var(--space-4) var(--space-8) var(--space-6);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-dim);
  font-size: var(--font-size-caption);
  line-height: 1.4;
}

.personal-tasks-hint svg {
  flex-shrink: 0;
  opacity: 0.7;
}

.personal-tasks-hint__key {
  display: inline-block;
  padding: 1px 5px;
  border: 1px solid var(--color-border);
  border-bottom-width: 2px;
  border-radius: var(--radius-xs);
  background: var(--color-bg-off);
  color: var(--color-text-secondary);
  font-family: inherit;
  font-size: 0.92em;
  font-weight: var(--font-weight-medium);
  white-space: nowrap;
}


/* ── A TAG, AS A PILL ─────────────────────────────────────────────────
   JP, 2026-10-10, from his Details screenshot: a tag with a colour is shown
   in that colour, carries an x to take it off, and shows a pin if it is
   pinned.

   **ONE CLASS CARRIES BOTH HALVES.** `.tag-pill--rose` sets the pale
   background and the ink that reads on it together, from the pair in
   tokens.css. Nothing anywhere computes a text colour from a background —
   that is the whole reason for a fixed set.

   Drawn on the contact screen's Details row, its hero, and the Assign a Tag
   box, so it lives here rather than in any one screen's stylesheet. */
.tag-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  /* A long tag name is cut rather than allowed to set the row's width. */
  min-width: 0;
}

.tag-pill__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The pin, when the tag is pinned. It is a STATEMENT, not a button — you
   cannot unpin from here (JP: *"That's a setting, a global setting, but it
   does show that that's pinned"*). */
.tag-pill__pin {
  flex-shrink: 0;
  opacity: 0.85;
}

/* The x that takes the tag off this contact. Inherits the pill's own ink, so
   it is never a grey cross on a coloured pill. */
.tag-pill__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  padding: 0;
  margin-right: -2px;
  border: none;
  border-radius: 50%;
  background: none;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  transition: opacity var(--duration-fast) var(--easing-default),
              background var(--duration-fast) var(--easing-default);
}

.tag-pill__remove:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.08);
}

/* ── The eight, from tokens.css. A pair each, never computed. ───────── */
.tag-pill--slate { background: var(--tag-slate-bg); color: var(--tag-slate-ink); }
.tag-pill--rose { background: var(--tag-rose-bg); color: var(--tag-rose-ink); }
.tag-pill--amber { background: var(--tag-amber-bg); color: var(--tag-amber-ink); }
.tag-pill--green { background: var(--tag-green-bg); color: var(--tag-green-ink); }
.tag-pill--teal { background: var(--tag-teal-bg); color: var(--tag-teal-ink); }
.tag-pill--blue { background: var(--tag-blue-bg); color: var(--tag-blue-ink); }
.tag-pill--purple { background: var(--tag-purple-bg); color: var(--tag-purple-ink); }
.tag-pill--pink { background: var(--tag-pink-bg); color: var(--tag-pink-ink); }
.tag-pill--brown { background: var(--tag-brown-bg); color: var(--tag-brown-ink); }

/* ── "Add tag" — a dashed outline, so it reads as a slot to fill rather
      than as another tag (JP's screenshot). ─────────────────────────── */
.tag-pill--add {
  background: none;
  border: 1px dashed var(--color-border-hover);
  color: var(--color-text-secondary);
  cursor: pointer;
  font-family: inherit;
}

.tag-pill--add:hover {
  border-color: var(--color-action);
  color: var(--color-action);
  background: var(--color-blue-glow);
}

/* ── THE SYSTEM-TAG PILL, AND THE PILLS IT REVEALS ──────────────────
   JP, 2026-10-12: *"we want to change the design of the one system tag. We
   want to make it look more like a pill, similar to the Add Tag."* It was a
   small text toggle under the row, with the system pills stacked in a block
   below that. Now it is a pill in the row like everything else, and the
   system tags append along the same line.

   **IT BORROWS `.tag-pill--add`'s DASHED OUTLINE** rather than inventing a
   third look: both are controls rather than labels, which is exactly what
   the dashed edge says on this screen. The only difference is the chevron,
   because this one opens something and Add tag does not. */
.tag-pill--system {
  background: none;
  border: 1px dashed var(--color-border-hover);
  color: var(--color-text-secondary);
  cursor: pointer;
  font-family: inherit;
}

.tag-pill--system:hover {
  border-color: var(--color-action);
  color: var(--color-action);
  background: var(--color-blue-glow);
}

/* **THE CHEVRON POINTS THE WAY THE PRESS WILL GO.** JP, 2026-10-12: *"Let's
   point the arrow to the left. When it's expanded, point to the left to
   close. When it's not expanded, point the arrow to the right to show
   open."*

   So: RIGHT means "press me and the tags come out to the right", which is
   where they appear, in this same row. LEFT means "press me and they go back
   in". It pointed DOWN when open, which is the ordinary disclosure gesture
   for a section that opens BENEATH its heading — and this one does not open
   beneath anything, it extends along the line.

   The arrow turning is the whole of the feedback, since the label says how
   many and that does not change when you open it. */
.tag-pill__chevron {
  flex-shrink: 0;
  opacity: 0.7;
  transition: transform var(--duration-base) var(--easing-default);
}

/* Drawn pointing right; half a turn points it back the other way. */
.tag-pill--system.is-open .tag-pill__chevron {
  transform: rotate(180deg);
}

/* **THE SYSTEM PILLS SIT IN THE ROW, NOT UNDER IT.** `display: contents`
   makes this wrapper no box at all, so its pills become direct items of the
   pill row and take the row's own gap and wrapping. A plain span would be
   one flex item holding several pills, with its own spacing to keep in step
   — and anything with a width of its own would push Add tag onto a new line
   the moment the tags were revealed, which is the stacking JP asked to be
   rid of. */
.tag-pill-group {
  display: contents;
}

/* **AND THIS LINE IS NOT OPTIONAL.** The browser hides a `hidden` element with
   `[hidden] { display: none }` from its own stylesheet, which is one attribute
   — the same weight as the one class above it. A page stylesheet is read after
   the browser's, so `display: contents` WINS and the element stays on screen:
   the system tags would be visible before anybody pressed anything, and the
   button would appear to do nothing on the first press. */
.tag-pill-group[hidden] {
  display: none;
}

/* The row they sit in. Wraps; never stretches. */
.tag-pill-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}


/* ── PINNED NOTE STRIP — the yellow bar at the top of a record ─────────
   Moved here from `pages/contacts/details.css` on 2026-10-10, when JP asked
   for pinning on the property screen too: *"on the property screen, there is
   no pinning of a note, and there is no trash can"*.

   It is one component on two screens now, so it cannot live in one screen's
   stylesheet — copying it into the property's would have been the second
   record of one rule, which is the failure this project spends most of its
   time undoing. */
.ctd-pin {
  display: flex;
  align-items: flex-start;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
  background: var(--color-yellow-glow);
  border-left: 3px solid var(--color-yellow);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  margin-bottom: var(--space-4);
}

.ctd-pin__icon {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--color-yellow);
}

.ctd-pin__body {
  flex: 1;
  min-width: 0;
}

.ctd-pin__label {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #92400E; /* amber-800 — dark amber text on yellow background */
  margin-bottom: var(--space-2);
}

.ctd-pin__text {
  font-size: var(--font-size-small);
  color: var(--color-text);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/* WHICH HOUSE THIS NOTE IS ABOUT (JP, 2026-10-10: *"when a note is shown on
   the contact screen if it is associated with a property we need to show for
   which address"*). The little house carries the meaning, so the line is the
   address and nothing else — no "Property:" in front of it. Darker than the
   date beside it, because it is a fact about the note rather than a
   footnote. */
.ctd-pin__prop {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: #92400E; /* amber-800, the same dark amber as the label above */
}

.ctd-pin__prop svg {
  flex-shrink: 0;
  opacity: 0.85;
}

.ctd-pin__date {
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  margin-top: var(--space-2);
}

.ctd-pin__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex-shrink: 0;
}

.ctd-pin__action {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-xs);
  background: none;
  border: none;
  cursor: pointer;
  color: #92400E;
  opacity: 0.6;
  transition: opacity var(--duration-fast) var(--easing-default),
              background var(--duration-fast) var(--easing-default);
}

.ctd-pin__action:hover {
  opacity: 1;
  background: rgba(146, 64, 14, 0.08);
}

.ctd-pin__action.note-action-delete:hover {
  color: var(--color-red);
  background: var(--color-red-glow);
}


/* WHICH HOUSE A NOTE IS ABOUT (JP, 2026-10-10). A line of its own under the
   note's text, reading as a label rather than as part of the sentence the
   note ends with. `display:flex` is what puts it on its own line: the note's
   text is inline, and an inline span would have sat on the end of it. */
.cdv-tlc-noteprop {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
}

.cdv-tlc-noteprop svg {
  flex-shrink: 0;
  opacity: 0.8;
}


/* ===========================================================
   47. PERSONAL TASK ITEM — .personal-task-item
   Visual reference: Personal Tasks Drawer (global topbar
   task icon).

   Actionable task card for the personal tasks sidebar.
   Distinct from .task-item (Component #37), which is a
   read-reference card with no action controls. This
   component includes an action button slot because the
   personal tasks drawer is where tasks are marked complete
   or deleted.

   Anatomy:
     __content  — clickable left region (title + body)
     __title    — task name (semibold, 14px, truncated)
     __body     — description or due date (12px, dim, truncated)
     __actions  — right-anchored action slot
     __action   — single icon action button (28×28)

   Modifiers on __action:
     --complete  — green hover tint; marks task complete
     --delete    — red hover tint; deletes completed task

   States:
     __body.is-overdue — red, semibold (past-due date)
   =========================================================== */

.personal-task-item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-6) var(--space-7);
  background: var(--color-bg-off);
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  color: var(--color-text);
  transition:
    background-color var(--duration-fast) var(--easing-default),
    border-color var(--duration-fast) var(--easing-default);
}
.personal-task-item:hover {
  background: var(--color-bg-hover);
  border-color: var(--color-border);
}

/* Stacking — same convention as .task-item. */
.personal-task-item + .personal-task-item {
  margin-top: var(--space-4);
}

/* ----- __content: clickable left region ----- */
.personal-task-item__content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  cursor: pointer;
}

/* ----- __title: task name ----- */
.personal-task-item__title {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  line-height: 1.3;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----- __body: description or due date ----- */
.personal-task-item__body {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-regular);
  line-height: 1.3;
  color: var(--color-text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Overdue state — red, semibold to draw attention. */
.personal-task-item__body.is-overdue {
  color: var(--color-red);
  font-weight: var(--font-weight-semibold);
}

/* ----- __actions: right-anchored action slot ----- */
.personal-task-item__actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

/* ----- __action: icon action button ----- */
.personal-task-item__action {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xs);
  color: var(--color-text-dim);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}
.personal-task-item__action:hover {
  background: var(--color-bg-hover);
}

/* Complete action — green on hover */
.personal-task-item__action--complete:hover {
  background: var(--color-green-glow);
  color: var(--color-green);
}

/* Delete action — red on hover */
.personal-task-item__action--delete:hover {
  background: var(--color-red-glow);
  color: var(--color-red);
}


/* ============================================================
   END — components.css
   ============================================================
   ============================================================
   SUMMARY
   ============================================================

   Component count by category
   ---------------------------
   Action / control          (3): Button, Toggle, Segment
   Selection control         (2): Checkbox, Radio
   Container / surface       (4): Card, Modal, Filter Drawer,
                                  Side Panel
   Navigation                (2): Sidebars (4 canonical), Tabs
   Form / input              (2): Form Field (.field/.input),
                                  Search Input
   Composition               (2): Composer, Email Composer
   Display primitive         (4): Avatar, Chip, Status Dot,
                                  Tooltip
   Feedback / system         (4): Spinner + Loading Overlay,
                                  Banner, Toast, Empty State
   Onboarding / flow         (2): Onboarding Banner, Load More
   Conversation              (2): Message, Message Day Separator
                              ----
                              27 components — 24 from the
                              decisions doc plus .side-panel,
                              .message, and .message-day-separator
                              promoted later (see decisions doc
                              updates below).

   Decisions doc updates needed (component-decisions.md)
   -----------------------------------------------------
   The following changes have been made to the components in
   this file but are NOT yet reflected in component-decisions.md.
   Apply these in the next decisions-doc edit:

   - Component #3 (Sidebars), .profile-sidebar:
     Add canonical sub-elements: __header, __eyebrow, __title.
     These render the eyebrow + display-title block above the
     items (e.g. "SETTINGS / My Account") that ships on every
     Settings page.

   - Component #3 (Sidebars), .scheduling-sidebar:
     (1) Surface changed from dark navy (rail-bg) to white.
         The scheduling editor reads as an editor flow, not a
         Settings page; white surface matches that context.
     (2) Active-state changed from solid-blue fill to soft
         blue-glow background with blue text. Matches the
         visual reference (the scheduling editor in the source
         design).
     (3) Add canonical sub-element: __exit. Positioning slot
         only; the actual button uses .btn composition (e.g.
         .btn.btn--primary.btn--block.scheduling-sidebar__exit).
         Same pattern as .composer__send / .email-composer__send.

   - Component #3 (Sidebars), .form-builder-sidebar:
     Substantively rewritten. This sidebar is a SETTINGS PANEL,
     not a navigation list — it holds live form controls (text
     inputs, toggles, color swatches), not item links.
     (1) Surface: dark navy (--color-rail-bg). Same family as
         .inbox-rail and .profile-sidebar.
     (2) Anatomy replaced. Old: __section / __item / __icon /
         __label / __count. New: __top, __home, __selector,
         __title, __body, __divider, __section-label, __row,
         __row-label, __row-control, __foot. Three of these
         (__home, __selector) are positioning slots that
         compose existing primitives.
     (3) Form controls inside __body use the new --on-rail
         primitive modifiers (see below).

   - New primitive modifiers across multiple components:
     Add `.btn--on-rail`, `.input--on-rail`, `.field--on-rail`,
     `.toggle--on-rail`, `.checkbox--on-rail`, `.radio--on-rail`.
     Each re-skins its parent component for dark-rail surfaces
     (white text, dark fill, translucent borders, light-slate
     placeholders). Composes additively — apply to existing
     components alongside their normal modifiers, e.g.
     `<input class="input input--on-rail">` or
     `<button class="btn btn--secondary btn--icon btn--on-rail">`.
     These should be added to each component's "Modifiers" list
     in the decisions doc.

   - NEW Component #25: .side-panel
     A persistent right-side panel adjacent to main content
     (Inbox contact info, task lists, helpful videos, etc.).
     Distinct from .modal--drawer — no backdrop, content behind
     stays interactive, panel is a layout region rather than a
     transient overlay.

     Anatomy: __head, __title, __close, __body, __foot (all
     optional; pages compose what they need).

     Modifiers: --full (spans full viewport height), default
     (contained — height inherited from parent).

     States: .is-entering, .is-leaving (JS-toggled on
     mount/unmount; same contract as .toast).

     Surface: white background, 1px left border only (top is
     handled by the navigation bar above; right is the page
     edge). Always positioned on the right.

     Content inside is page-specific and composed from existing
     primitives (.avatar, .chip, .btn, etc.). The component is
     chrome only — same model as .card and .modal. Recurring
     content patterns inside side panels (e.g. the
     contact-detail action row, info-with-icon rows, task
     cards) are intentionally NOT promoted to the system at
     this time; revisit when patterns repeat across 2+ pages.

   - NEW Component #26: .message
     Conversation message bubble. Used in SMS Inbox and any
     detail-page thread (Contact, Property, Task) wherever a
     chronological message thread renders.

     Anatomy: __avatar (slot for an .avatar), __bubble (the
     text-holding shape), __bubble-wrap (positioning wrapper
     around bubble + meta inside the grid), __meta (timestamp +
     sender), __actions (hover-only floating action toolbar),
     __task-indicator (cream badge shown when the message has
     been converted to a task).

     Modifiers: --inbound (default; grey bubble, avatar left),
     --outbound (blue bubble, avatar right).

     States: .is-converted-to-task (shows __task-indicator).

     Notes:
     - Bubble has max-width: 70% by default. Pages can override.
     - __actions visibility is purely CSS-driven (parent :hover /
       :focus-within). No JS class toggle needed.
     - __task-indicator visibility is state-driven via the
       .is-converted-to-task class on the parent. The task
       indicator is a state communicator, NOT an action button —
       clicking it (if interactive at all) navigates to the
       linked task; it does not perform conversion.

   - NEW Component #27: .message-day-separator
     Centered uppercase label between groups of messages
     ("MONDAY, APR 13"). No sub-elements, modifiers, or states.
     Sits as a sibling of .message instances in the thread flow.

   - Note on .modal--drawer (Component #2):
     Currently unused in the product. .side-panel (above)
     handles all the side-anchored use cases that previously
     would have used .modal--drawer. Kept in the system anyway
     for a possible future "modal that slides in from the side
     with a backdrop" use case (wizard modals, etc.). No code
     change; leaving as-is. Worth noting in the decisions doc
     that .side-panel and .modal--drawer are distinct things
     and shouldn't be confused.

   TODO stubs (need clearer designs)
   ---------------------------------
   - .modal--full: stub layout (full-viewport with 24px gutter).
     TODO: confirm whether --full means truly edge-to-edge,
        or window-with-gutter as currently implemented

   - .empty-state--inline diagonal-stripe pattern:
     fallback uses repeating-linear-gradient with
     rgba(229, 231, 235, 0.3) (matching --color-border value)
     because no --color-stripe-bg token exists.
     TODO: needs the canonical stripe pattern color

   - .composer__textarea minimum height (64px) and
     .email-composer__body minimum height (240px) are best-
     guess based on source designs; confirm exact values when
     SMS Inbox composer redesign lands.
     TODO: confirm composer min-heights against final
        SMS Inbox / Email Thread View redesigns

   Missing tokens (referenced by decisions doc, not in tokens.css)
   --------------------------------------------------------------
   The decisions doc references many tokens whose names do not
   match tokens.css. For most of these the file resolves to the
   correct existing token (see "Token-mapping notes" at top).
   The list below is the tokens that have no clean equivalent
   and either fall back to a hex value, an inline rgba, or a
   close substitute. Adding these to tokens.css would clean up
   the file:

   1. --color-stripe-bg (diagonal-stripe pattern color for
        .empty-state--inline)
        Currently: inline rgba(229, 231, 235, 0.3)

   2. --color-blue-tone / --color-yellow-tone /
        --color-purple-tone (decorative tones for
        .empty-state__icon--tone-*)
        Currently mapped to *-glow / *-pale combinations,
        which works but conflates "tinted background" with
        "decorative empty-state tone". Worth a separate
        token family if the visual stays decorative-only.

   3. --color-{lifecycle/status/content}-bg (per-modifier
        chip background tokens)
        Currently: chip uses the *-pale token directly as bg
        and a darker matching token as text color. The
        decisions doc lists separate -bg aliases — those don't
        exist in tokens.css. Resolution choice: skip the
        aliases. If the alias layer is desired later, it can
        be added to tokens.css without changing this file.

   4. --shadow-blue-sm / --shadow-blue-md (referenced by .btn
        in the decisions doc)
        Currently mapped to --shadow-button-action-rest /
        --shadow-button-action-hover (the actual names in
        tokens.css). The decisions doc names should probably
        be reconciled with the tokens.css names in a follow-up.

   5. --color-border-dashed (for dashed empty-state border)
        Currently: --color-border. Acceptable, but a slightly
        lighter dashed token (e.g. --color-border-light) might
        read better. Worth a designer pass.

   6. --z-drawer / --z-overlay / --z-tooltip / --z-backdrop
        (referenced by decisions doc)
        Resolved against tokens.css's actual z-scale:
          --z-backdrop / --z-overlay → not present; .modal__
            backdrop uses --z-modal directly
          --z-drawer → mapped to --z-popover
          --z-tooltip → mapped to --z-popover
        Dedicated values would help if drawers and modals ever
        need to coexist.

   7. --transition-fast / --transition-base / --transition-tooltip
        (referenced by decisions doc)
        Resolved to --duration-fast / --duration-base. The
        tooltip's 250ms show delay is implemented as a literal
        transition-delay on parent hover, not a token.

   8. --anim-spin (referenced by .spinner in the decisions doc)
        Implemented inline as @keyframes ec-spin; the tokens
        spec mentions it but tokens.css does not currently
        export an animation timing variable. Acceptable as a
        component-local keyframe, but worth promoting if other
        components animate.

   9. --color-green-focus-ring (for field--success)
        Currently: literal rgba(16, 185, 129, 0.12). tokens.css
        defines --color-focus-ring (blue) and
        --color-focus-ring-danger (red); a green equivalent
        would round out the set.

  10. --color-on-action-subtle (translucent-white tint for
        elements layered on a blue-active background, e.g.
        the count pill on profile-sidebar__item.is-active)
        Currently: literal rgba(255, 255, 255, 0.2)

  11. --color-overlay-soft (very light black overlay used by
        chip__clear hover for a "press down" feel without
        a visible background color)
        Currently: literal rgba(0, 0, 0, 0.06)

   Disagreements between the design guide and the actual designs
   ------------------------------------------------------------
   1. Chip text color
        Guide: chip text = chip semantic color (e.g. lead chip
          text is blue-dark on blue-pale).
        Some source designs (e.g. Contact List filter chips
          when active) use the chip background as a fill and
          white text. The decisions doc clearly says outline is
          for filter chips and solid is for category labels.
        Resolution: implemented per the decisions doc — semantic
          modifiers are solid, filter chips use --outline. If
          the contact list "active filter chip" treatment
          (blue-glow background, blue text, blue border) is
          expected to be reachable, add a `.chip--outline.is-active`
          pattern in a follow-up, or model that as a separate
          state.

   2. Modal body / note dialog [RESOLVED]
        The Email Inbox source HTML (`note-dialog`) used a
        linear-gradient cream background and warm-brown text
        (#FEF9C3 / #FEF3C7 / #FDE68A / #713F12 / #78350F).
        These are now first-class tokens in tokens.css under
        the new "Cream" family (--color-cream, --color-cream-deep,
        --color-cream-border, --color-cream-text, etc.) plus
        the --gradient-note-surface gradient. .modal--note now
        reads correctly as a warm note surface, not a yellow
        warning surface.

   3. Toggle track sizing
        Guide says 36×20 px. Source designs use 40×22 px (the
        ps-switch implementation, which is the visual reference
        in the decisions doc). Implemented at 40×22 to match the
        reference. Consider updating the guide or the size
        modifiers if 36×20 is canonical.

   4. Button height
        Guide rules say "10px vertical · 20px horizontal" padding
        for primary buttons (≈ 36 line-height + padding ≈ 38–40px
        tall). The Profile Settings ps-btn uses 9px / 16px,
        landing at 36px height. Implemented at 36px to match the
        Profile Settings reference (which the decisions doc names
        as canonical). The guide should be reconciled.

   5. Search input background
        Guide says inputs default to white background. The
        Email Inbox / Contact List search bars use a tinted
        --color-bg-off background that switches to white only on
        focus. The decisions doc names Contact List as the
        reference, so .search-input is implemented with the
        bg-off → bg-on-focus pattern. The plain white .input is
        kept for form fields.

   6. Tabs visual reference
        Decisions doc points to "SMS Inbox tabs" as canonical
        for both --underline and --pills variants. SMS Inbox in
        the source designs uses the underline pattern; pills
        appear in Profile Settings (`.ps-tabs`). Both
        implementations here pull from those references — flag
        this if the SMS Inbox redesign produces a different
        pills treatment.
   ============================================================ */


/* ===========================================================
   36. TASK CREATOR POPUP — .ct-modal
   Design reference: docs/designs/create-task.jsx + create-task.css
   Reuses tk-editor / tk-cal picker styling from the Tasks page.
   =========================================================== */

/* Backdrop uses the existing .modal__backdrop component — no custom rules needed */

@keyframes ct-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---- Modal shell ---- */
.ct-modal {
  position: relative;
  width: min(640px, 100%);
  background: var(--color-bg, #fff);
  border-radius: var(--radius-lg, 16px);
  box-shadow: var(--shadow-xxl, 0 24px 64px rgba(15, 23, 42, 0.18));
  font-family: var(--font-body, 'DM Sans', system-ui, sans-serif);
  color: var(--color-text, #1A1D23);
  opacity: 1;
  animation: ct-pop-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ct-modal.ct-layout-grid { width: min(680px, 100%); }
.ct-modal.ct-layout-inline { width: min(720px, 100%); }
@keyframes ct-pop-in {
  from { transform: translateY(8px) scale(0.985); }
  to   { transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .ct-modal { animation: none; }
}

/* Header uses .sms-composer__head / __head-icon / __title / __close — no custom rules needed */

/* ---- Body ---- */
.ct-body {
  padding: 22px 24px 8px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ---- Labels & primary text inputs ---- */
.ct-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-secondary, #4B5563);
  margin-bottom: 7px;
}
.ct-block { display: block; }

/* .ct-textarea extends .input — only adds resize behaviour */
.ct-textarea { resize: vertical; min-height: 92px; }

/* ---- Field grid container (stacked layout only in this widget) ---- */
.ct-fields { display: flex; }
.ct-layout-stacked .ct-fields { flex-direction: column; gap: 16px; }
.ct-layout-grid .ct-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px 18px;
}
.ct-layout-inline .ct-fields {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
}

.ct-field { position: relative; min-width: 0; }
/* Hide field labels in inline (compact) layout */
.ct-layout-inline .ct-field .ct-label { display: none; }

/* Full-width compact triggers for the inline row */
.ct-layout-inline .ct-trigger {
  display: flex;
  width: 100%;
  gap: 6px;
  padding: 9px 9px;
  font-size: 13px;
  border-radius: var(--radius-sm, 8px);
}
.ct-layout-inline .ct-trigger-lead { gap: 6px; }
.ct-layout-inline .ct-trigger-tail { margin-left: auto; }

/* ---- Field trigger (the clickable control) ---- */
.ct-trigger {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text, #1A1D23);
  background: var(--color-bg, #fff);
  border: 1px solid var(--color-border, #E5E7EB);
  border-radius: var(--radius-sm, 10px);
  padding: 10px 12px;
  cursor: pointer;
  transition: border-color 0.12s, box-shadow 0.12s, background 0.12s;
  text-align: left;
}
.ct-trigger:hover { border-color: var(--color-border-hover, #CBD5E1); }
.ct-trigger.is-open {
  border-color: var(--color-blue, #2563EB);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
}
.ct-layout-stacked .ct-trigger,
.ct-layout-grid .ct-trigger { display: flex; width: 100%; }

.ct-trigger-lead { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.ct-trigger-lead svg { color: var(--color-text-dim, #9CA3AF); flex: none; }
.ct-trigger-val {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ct-trigger-val.is-placeholder { color: var(--color-text-dim, #9CA3AF); font-weight: 400; }
.ct-trigger-tail {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  color: var(--color-text-dim, #9CA3AF);
  flex: none;
}

/* count pill (tags) */
.ct-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 100px;
  background: var(--color-blue, #2563EB);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
}

/* inline avatar inside a trigger */
.ct-av {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  flex: none;
}
.ct-av-sm { width: 20px; height: 20px; font-size: 9px; }


/* Popovers use the existing .dropdown component directly — no custom rules needed */

/* tag option dot */
.ct-opt-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* contact option: name + sub */
.ct-opt-contact { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; flex: 1; min-width: 0; }
.ct-opt-sub { font-size: 11px; color: var(--color-text-dim, #9CA3AF); }

/* disabled assignee trigger when personal task is checked */
.ct-trigger--disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* Personal task toggle uses the existing .checkbox component — no custom rules needed */

/* ---- Footer ---- */
.ct-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 24px 18px;
  margin-top: 4px;
  border-top: 1px solid var(--color-border, #E5E7EB);
}
.ct-foot-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* ---- Split create button ---- */
/* .ct-split-main and .ct-split-caret extend .btn.btn--primary —
   only the split border-radius and divider are new here */
.ct-split { position: relative; display: inline-flex; }
/* Suppress the hover lift on split button parts (they're joined, not independent) */
.ct-split > .btn { transform: none !important; }
.ct-split-main {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.ct-split-caret {
  background: var(--color-blue-dark, #1D4ED8);
  border-left: 1px solid rgba(255, 255, 255, 0.22);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.ct-split-caret:hover,
.ct-split-caret.is-open { background: #1843b0; }

.ct-split-menu {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  min-width: 240px;
  background: var(--color-bg, #fff);
  border: 1px solid var(--color-border, #E5E7EB);
  border-radius: var(--radius, 12px);
  box-shadow: var(--shadow-lg, 0 8px 24px rgba(15, 23, 42, 0.08));
  padding: 6px;
  z-index: 50;
  animation: ct-fade 0.14s ease;
}
.ct-split-opt {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  text-align: left;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text, #1A1D23);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm, 8px);
  padding: 10px 10px;
  cursor: pointer;
}
.ct-split-opt:hover { background: var(--color-bg-hover, #F1F5F9); }
.ct-split-opt svg { color: var(--color-blue, #2563EB); flex: none; }
.ct-split-opt .ct-split-opt-sub {
  display: block;
  font-size: 12px;
  font-weight: 400;
  color: var(--color-text-dim, #9CA3AF);
  margin-top: 1px;
}
.ct-split-opt-text { min-width: 0; }


/* =============================================================
   Toolbar Colour Picker
   Shared by the email composer and the task description toolbar.
   A button (icon + coloured-bar indicator) that opens a small
   floating palette of swatches below it.

   Anatomy:
     .tb-color-wrap          — position:relative anchor
       button.tb-color-btn   — ghost square button; composes .btn .btn--ghost .btn--icon .btn--sm
         span.tb-color-icon  — flex-column: SVG glyph on top, colour bar below
           svg               — letter-A or marker icon (stroke colour = currentColor)
           span.tb-color-bar — 14px × 3px slab; background updated by JS on selection
       .tb-color-palette     — absolute dropdown, hidden by default
         .tb-color-palette__grid   — swatch grid
           button.tb-color-swatch  — one colour circle; data-color="#hex"
         button.tb-color-palette__clear — "Remove" reset row
============================================================= */

.tb-color-wrap {
  position: relative;
  display: inline-flex;
}

/* The button itself uses .btn .btn--ghost .btn--icon .btn--sm from the .btn component.
   .tb-color-btn overrides the flex axis so the glyph + bar stack vertically. */
.tb-color-btn {
  flex-direction: column !important;
  gap: 2px !important;
}

.tb-color-icon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: none;
  line-height: 1;
}

/* Colour indicator bar — JS sets background on selection */
.tb-color-bar {
  display: block;
  width: 14px;
  height: 3px;
  border-radius: 2px;
  flex-shrink: 0;
}

/* Floating palette panel */
.tb-color-palette {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: var(--z-popover);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  min-width: 172px;
}

/* Swatch grid — 8 columns */
.tb-color-palette__grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 3px;
}

/* Individual colour swatch */
.tb-color-swatch {
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--radius-xs);
  border: 1px solid rgba(0, 0, 0, 0.14);
  cursor: pointer;
  padding: 0;
  position: relative;
  transition:
    transform var(--duration-fast) var(--easing-default),
    box-shadow var(--duration-fast) var(--easing-default);
}

.tb-color-swatch:hover {
  transform: scale(1.2);
  box-shadow: 0 0 0 2px var(--color-border-hover);
  z-index: 1;
}

/* White swatch needs a visible border so it's distinct from the palette background */
.tb-color-swatch[data-color="#ffffff"] {
  border-color: var(--color-border);
}

/* Remove / clear button at the bottom of the palette */
.tb-color-palette__clear {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-1) 0;
  margin-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  font-size: var(--font-size-small);
  color: var(--color-text-secondary);
  background: none;
  border-left: none;
  border-right: none;
  border-bottom: none;
  cursor: pointer;
  text-align: left;
  transition:
    background var(--duration-fast) var(--easing-default),
    color var(--duration-fast) var(--easing-default);
}

.tb-color-palette__clear:hover {
  color: var(--color-text);
  background: var(--color-bg-hover);
}


/* ===========================================================
   43. EMOJI PICKER — .emoji-popup
   Floating emoji selection panel. Appended to a position:relative
   trigger element; appears above it via bottom: calc(100% + 8px).
   Used in TeamChat composers and reactions. Overrides legacy
   styles in general-application-styles.css.
   =========================================================== */

.emoji-popup {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  width: 252px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3);
  z-index: var(--z-popover);
  display: none;
  box-sizing: border-box;
}

.emoji-search {
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-caption);
  font-family: var(--font-body);
  background: var(--color-bg-off);
  color: var(--color-text);
  outline: none;
  margin-bottom: var(--space-2);
}
.emoji-search:focus {
  border-color: var(--color-blue);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.emoji-search::placeholder {
  color: var(--color-text-dim);
}

.emoji-list {
  display: flex;
  flex-wrap: wrap;
  height: 180px;
  overflow: hidden;
  gap: 2px;
  font-size: 18px;
  box-sizing: border-box;
  width: 100%;
  min-width: unset;
  max-width: unset;
  max-height: unset;
  min-height: unset;
}

.tc-emoji {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--duration-fast);
}
.tc-emoji:hover {
  background: var(--color-bg-hover);
}

.emoji-picker-pagination {
  display: flex !important;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border-light);
}

.emoji-picker-page-btn {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  background: var(--color-bg-off);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-caption);
  font-family: var(--font-body);
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.emoji-picker-page-btn:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}
.emoji-picker-page-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.clear-emoji-btn {
  border: none;
  background: none;
  font-size: var(--font-size-caption);
  font-family: var(--font-body);
  color: var(--color-red);
  cursor: pointer;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  transition: background var(--duration-fast);
}
.clear-emoji-btn:hover {
  background: var(--color-red-glow);
}


/* ===== Component #44 — NOTE CREATOR POPUP ===== */

.ctd-note-textarea {
  height: 120px;
  resize: vertical;
}

.ctd-note-property-section {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg-off);
}

#sms-modal-property-section {
  margin-top: var(--space-5);
}

.ctd-note-property-section__icon {
  flex-shrink: 0;
  color: var(--color-text-dim);
  margin-top: 2px;
}

.ctd-note-property-section__text {
  flex: 1;
  min-width: 0;
}

.ctd-note-property-section__title {
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  margin-bottom: var(--space-1);
}

.ctd-note-property-section__sub {
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  margin-top: 0;
}

.ctd-note-property-section__select {
  flex-shrink: 0;
  width: 160px;
  align-self: center;
  padding: var(--space-3) var(--space-5);
}


/* -- Responsive Breakpoints -- */

.btn {
  height: 29px;
  font-size: 12px;
}

.btn--icon {
  width: 36px;
}

/* Hide features that are not available on mobile */
@media (max-width: 1055px) {
  .desktop_only_feature {
    display:none;
  }
}

@media (max-width: 835px) {
  .card {
    padding: var(--space-10) var(--space-9);
  }

  .modal__backdrop {
    padding: var(--space-4);
  }

  .modal__head {
    padding: var(--space-10) var(--space-9) var(--space-3);
  }

  .modal__title {
    font-size: 18px;
  }

  .modal__body {
    padding: var(--space-7) var(--space-9) var(--space-4);
  }

  .modal__foot {
    padding: var(--space-8) var(--space-9) var(--space-10);
  }

  /* Email Composer Popup */

  .email-composer-popup__head {
    padding: var(--space-4) var(--space-8);
  }

  .email-composer-popup__title {
    font-size: 14px;
  }

  .email-composer-popup__field-label {
    width: 50px;
  }

  .email-composer-popup__from-name {
    font-size: 13px;
  }

  .email-composer-popup__from-email {
    font-size: 12px;
  }

  .email-composer-popup__field-value {
    width: 100%;
  }

  .email-composer-popup__field {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }

  .email-composer-popup__from-avatar {
    display: none;
  }

  .email-composer-popup__cc-link,
  .email-composer-popup__bcc-link {
    display: none !important;
  }

  .email-composer-popup__mobile-expand {
    display: flex;
  }

  .email-composer-popup__toolbar {
    overflow-x: scroll;
  }

  /* Cap the message body so a long message scrolls internally
     instead of growing the popup past the viewport and hiding
     the send button in __foot. */
  .email-composer-popup__textarea {
    max-height: 36vh;
    overflow-y: auto;
  }

  .email-composer-popup__body {
    overflow: scroll;
    max-height: 495px;
  }

  /* Inline Email Composer Element */

  .full-page-email-conversation-content .email-composer-inline__cc-link,
  .full-page-email-conversation-content .email-composer-inline__bcc-link {
    display: none !important;
  }

  .full-page-email-conversation-content .email-composer-inline__mobile-expand {
    display: flex;
  }

  .email-composer-inline__foot {
    overflow-x: scroll;
  }

  /* SMS Composer Popup */

  .sms-composer__head {
    padding: var(--space-4) var(--space-8);
  }

  .sms-composer__title {
    font-size: 14px;
  }

  .sms-composer__field {
    gap: 6px;
  }

  .sms-composer__field-label {
    width: 45px;
  }

  .sms-composer__from-number {
    font-size: 13px;
  }

  .sms-composer__from-line {
    font-size: 12px;
  }

  /* Task Creator Popup */

  #task-creator-cancel {
    display:none;
  }

  .ct-layout-inline .ct-trigger {
    padding: 5px 9px;
  }

  /* Text, Facebook, and Instagram messages */

  .message__bubble-wrap {
    max-width: 90%;
  }
}
/* Ported from hotfix/1.7.0 — cell-phone collection popup. Also lives in
   legacy/general-application-styles.css for legacy pages. Markup in
   views/partials/application_header.hbs; behavior in sitewide-app-features.js. */
/* =============================================
   Phone Number Collection Popup
   ============================================= */

#phone-popup-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  z-index: 10000;
  display: none;
}

#phone-collection-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #ffffff;
  border-radius: 18px;
  width: 92%;
  max-width: 460px;
  z-index: 10001;
  display: none;
  overflow: hidden;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
}

.phone-popup-header {
  background: linear-gradient(135deg, #1d43ff 0%, #0a28cc 100%);
  padding: 28px 28px 24px 28px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.phone-popup-header-icon {
  width: 52px;
  height: 52px;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  flex-shrink: 0;
}

.phone-popup-headline {
  font-family: 'poppins-semibold', 'poppins-medium', sans-serif;
  font-size: 22px;
  color: #ffffff;
  margin: 0;
  line-height: 1.3;
}

.phone-popup-body {
  padding: 24px 28px 28px 28px;
}

.phone-popup-description {
  font-family: 'poppins-regular', sans-serif;
  font-size: 14px;
  color: #3a3a3a;
  line-height: 1.6;
  margin: 0 0 10px 0;
}

.phone-popup-description b {
  background-color: #ffe066;
  padding: 1px 3px;
  border-radius: 3px;
}

.phone-popup-sub-description {
  font-family: 'poppins-regular', sans-serif;
  font-size: 13px;
  color: #888888;
  line-height: 1.5;
  margin: 0 0 22px 0;
}

.phone-popup-input-wrap {
  margin-bottom: 18px;
}

.phone-popup-label {
  display: block;
  font-family: 'poppins-medium', sans-serif;
  font-size: 13px;
  color: #333333;
  margin-bottom: 8px;
}

.phone-popup-input {
  width: 100%;
  padding: 13px 16px;
  border: 2px solid #e0e0e0;
  border-radius: 10px;
  font-family: 'poppins-regular', sans-serif;
  font-size: 15px;
  color: #111111;
  outline: none;
  transition: border-color 0.2s ease;
  box-sizing: border-box;
}

.phone-popup-input:focus {
  border-color: #1d43ff;
}

.phone-popup-error {
  font-family: 'poppins-regular', sans-serif;
  font-size: 12px;
  color: #e53935;
  margin-top: 6px;
  min-height: 16px;
}

.phone-popup-save-btn {
  width: 100%;
  background: #1d43ff;
  color: #ffffff;
  border: none;
  border-radius: 10px;
  padding: 15px;
  font-family: 'poppins-medium', sans-serif;
  font-size: 15px;
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
  margin-bottom: 14px;
}

.phone-popup-save-btn:hover {
  background: #1535e0;
}

.phone-popup-save-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.phone-popup-dismiss-btn {
  display: block;
  width: 100%;
  background: none;
  border: none;
  font-family: 'poppins-regular', sans-serif;
  font-size: 13px;
  color: #e53935;
  cursor: pointer;
  text-align: center;
  padding: 4px 0;
  transition: color 0.2s ease;
}

.phone-popup-dismiss-btn:hover {
  color: #b71c1c;
}

@media (max-width: 480px) {
  .phone-popup-headline {
    font-size: 19px;
  }

  .phone-popup-body {
    padding: 20px 20px 24px 20px;
  }

  .phone-popup-header {
    padding: 22px 20px 20px 20px;
  }
}


/* ===========================================================
   48. STAT TILE — .stat-tile
   Metric tile: a large value with a small label, optionally a
   status dot. Two contexts: a bordered, clickable filter tile
   (--interactive + .is-active) and a compact borderless
   right-aligned row cell (--compact). Status color is
   data-driven via a --tone custom property (Approach A, same
   as .chip custom colors) so runtime status hues need no new
   modifier classes.

   Replaces: .apl-stat* (Automation Pipeline count strip),
   .au-runs / .au-last (Automation List row metric cells).
   =========================================================== */

.stat-tile {
  text-align: left;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-5);
  position: relative;
  overflow: hidden;
  font-family: var(--font-body);
}

.stat-tile--interactive {
  cursor: pointer;
  transition: border-color var(--duration-fast),
              box-shadow var(--duration-fast),
              transform var(--duration-fast);
}

.stat-tile--interactive:hover {
  border-color: var(--color-border-hover);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}

.stat-tile.is-active {
  border-color: var(--tone, var(--color-blue));
  box-shadow: 0 0 0 1px var(--tone, var(--color-blue));
}

.stat-tile.is-active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--tone, var(--color-blue));
}

.stat-tile__top {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.stat-tile__label {
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}

.stat-tile__value {
  font-family: var(--font-display);
  font-size: var(--font-size-h4);
  font-weight: var(--font-weight-bold);
  color: var(--color-text);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Compact variant — dense, borderless, right-aligned cell used
   inside list rows. No chrome; just value over label. */
/* **A SECOND LINE UNDER THE NUMBER.** Snapshot's Open estimates and Won this
   month tiles each carry a count and what it is worth, and the money is the
   quieter of the two — "7" is the answer, "$12,500" is the detail. A genuine
   gap in the component rather than a one-page style: any tile whose figure
   needs a qualifier wants this. */
.stat-tile__sub {
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  line-height: 1.3;
}

.stat-tile--compact {
  display: block;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  overflow: visible;
  text-align: right;
}

.stat-tile--compact .stat-tile__value {
  display: block;
  font-size: var(--font-size-body);
  line-height: 1.2;
}

.stat-tile--compact .stat-tile__label {
  display: block;
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-dim);
  line-height: 1.2;
}


/* ===========================================================
   49. PROGRESS BAR — .progress
   Horizontal determinate progress indicator. Fill width is set
   inline (style="width: N%"); fill color is data-driven via a
   --tone custom property (defaults to accent blue).

   Replaces: .apl-progress* (Automation Pipeline run progress).
   =========================================================== */

.progress {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.progress__track {
  flex: 1;
  min-width: 36px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-hover);
  overflow: hidden;
}

.progress--sm .progress__track {
  height: 4px;
}

.progress__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--tone, var(--color-blue));
}

.progress__label {
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-dim);
  white-space: nowrap;
}


/* ===========================================================
   50. CODE BLOCK — .code-block
   Monospace block for raw payloads / JSON. Wraps long lines.

   Replaces: .apl-code-block (Automation Pipeline webhook
   request/response payloads).
   =========================================================== */

.code-block {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--color-text);
  white-space: pre-wrap;
  word-break: break-word;
}


/* ===========================================================
   51. BLOCKER CHECKLIST — .blocker-checklist
   Checklist of still-active features a user must resolve before
   a gated action (plan downgrade, account deletion) proceeds.
   Lives inside a .modal__body.

   Visual reference: Subscription Plan downgrade-blocked modal,
   Basic Info delete-account-blocked modal.
   =========================================================== */

ul.blocker-checklist {
  list-style: none;
  padding: 0;
  margin: 18px 0 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.blocker-checklist__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--color-red-glow);
  border: 1px solid var(--color-red-pale);
  border-radius: var(--radius-sm);
  padding: var(--space-6) var(--space-8);
}

.blocker-checklist__instruction {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

.blocker-checklist__count {
  font-size: var(--font-size-small);
  color: var(--color-text-secondary);
}

.blocker-checklist__link {
  align-self: flex-start;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
  color: var(--color-blue);
}

/* ===========================================================
   52. SECTION SCOPE SWITCH — .section-scope
   -----------------------------------------------------------
   The two-button pair that sits in a collapsible section's heading and says
   which set of rows the section is showing: Open | All.

   Sub-elements:
     .section-scope       — the pill-shaped container (a role="group")
     .section-scope-btn   — one button inside it

   States:
     .section-scope-btn.is-on — the set you are looking at (filled in)
     .section-scope-btn[hidden] — hidden entirely

   **IT WAS WRITTEN TWICE AND IS NOW USED FOUR TIMES.** It started as
   `.est-panel-scope` on the Estimates section, copied character for character
   into the property stylesheet and the contact stylesheet. On 2026-10-04 JP
   asked for the same pair on Tasks — *"The label just says 'Estimates', and
   then we have an 'Open' and an 'All'. I want to apply that exact same concept
   to tasks"* — and Riley asked that tasks reuse what estimates already have
   rather than grow its own. Two copies about to become four is what a shared
   component is for.

   Usage:
     <div class="section-scope" role="group" aria-label="Which tasks to show">
       <button class="section-scope-btn is-on" aria-pressed="true">Open</button>
       <button class="section-scope-btn" aria-pressed="false">All</button>
     </div>
   =========================================================== */

/* **TWO SHADES OF GREY, NOT BLUE** (JP, 2026-10-14) — *"it's really calling the
   eye to that area, when that's unnecessary... I'd like to take it to two
   different shades of gray that show which is selected and which is not."* The
   pair used to fill the chosen side in EveryChat blue, and four of these sit on
   a contact's screen at once, so four spots of the brand colour were pulling
   the eye to a control nobody came to the screen for. Both sides are grey now:
   the pair's own light grey is the side you are NOT on, and the chosen side is
   the next grey up with the darkest text. It also came down a size — JP:
   *"wondering if we should take it down in size again. Probably not a lot."* —
   11px text in a shallower pill. */
.section-scope {
  display: inline-flex; align-items: center;
  background: var(--color-bg-hover);
  border: 1px solid var(--color-border); border-radius: 999px;
  overflow: hidden; margin-right: var(--space-2);
}
.section-scope-btn {
  font-family: inherit; font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  padding: 2px 10px; border: 0; background: transparent;
  color: var(--color-text-secondary); cursor: pointer; line-height: 1.7;
}
.section-scope-btn:hover { color: var(--color-text); }
/* THE ONE YOU ARE ON IS STILL FILLED IN, not merely darker — the whole point of
   two buttons over a link is that nobody has to work out which set they are
   looking at. The fill is just grey instead of blue, and the text goes to the
   darkest so the two sides read apart at a glance. */
.section-scope-btn.is-on {
  background: var(--color-border-hover);
  color: var(--color-text);
}
.section-scope-btn[hidden] { display: none !important; }

/* ===========================================================
   53. SERVICES WIDGET — .services-row
   -----------------------------------------------------------
   The Services widget, on a contact's screen and on a property's.
   `docs/features/services/build-spec-services-widget.md` §10.1 and §10.2,
   and `docs/features/services/everychat-services-widget.html`.

   HERE RATHER THAN IN TWO PAGE STYLESHEETS, and the reason is
   written three screens above: the estimates panel put its rules
   in `contacts/details.css` AND `properties/details.css`, and
   when Tasks grew the same Open | All pair the duplication had
   to be unwound into #52. This widget serves two screens from
   its first day, so it starts where that one ended up.

   WHAT COMES FROM WHERE (JP, 2026-10-07): *"EveryChat dictates
   the design. The design file... dictates the layout and what's
   included and what shows up on the screens. You're taking that
   layout and then you're layering over top of it EveryChat's
   design language."*

     From the design file   the three-column row, cells aligned
                            to the TOP, chips stacking at three,
                            "Show n more", where the tags sit
     From EveryChat         every colour, size, radius, font and
                            spacing — and the components already
                            here: `.chip` (#?), `.section-scope`
                            (#52), `.btn--icon`, `.modal--drawer`

   The design's own class names map onto these as:
     .svcRow    -> .services-row        .chipBox  -> .services-chips
     .svcHead   -> .services-head       .moreBtn  -> .services-more
     .flag      -> .services-tag        .histLink -> .services-name
     .syncIcon  -> .services-sync

   NO COLOUR IS NAMED BY THE JAVASCRIPT. `helpers/serviceSummarize.js`
   hands back a word for what a tag MEANS — churn, pause, winback,
   upgrade — and the mapping to a token happens here. JP settled
   which token on 2026-10-07; the reasoning and the alternatives
   are in `docs/known-issues/resolved/services-widget-tag-colours.md`.

   Markup:
     <div class="services-table">
       <div class="services-head">…</div>
       <div class="services-row">
         <div class="services-row__name">
           <button class="services-name">Lawn Mowing</button>
           <span class="services-tag services-tag--flag">…</span>
         </div>
         <div class="services-chips">
           <span class="chip chip--service-active"><span class="chip__dot"></span>…</span>
         </div>
       </div>
     </div>
   =========================================================== */

.services-table {
  width: 100%;
  /* **THE SAME 13px THE TASK AND ESTIMATE TABLES USE.** JP, 2026-10-07:
     *"the services themselves, like Lawn Mowing and Tree and Shrub, their font
     size does not match the font size of a task or an estimate."* This table
     was inheriting the page's 14px body size because it is built from divs
     rather than a `<table>`, so it never picked up `.task-table`'s rule — and
     a row a shade larger than the rows above and below it reads as a mistake
     even when nobody can say why. */
  font-family: var(--font-body);
  font-size: var(--font-size-small);
  color: var(--color-text);
}

/* Below 720px the table scrolls inside its own box rather than
   squeezing the status out of the chips (§10.1). */
.services-scroll { overflow-x: auto; }
.services-scroll > .services-table { min-width: 640px; }

.services-head,
.services-row {
  display: grid;
  /* TWO COLUMNS. There was a third, 96px wide, holding History › and
     the three dots; the service's own name opens the history now and
     the actions moved inside the drawer (JP, 2026-10-09). */
  grid-template-columns: minmax(160px, 2fr) 4fr;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-4);
  /* **TOP, NOT CENTRE.** §10.1: the service name lines up with the
     first chip, "never vertically centered in a tall row". A service
     on eight properties is eight lines tall, and a name floating in
     the middle of it reads as belonging to the fourth chip. */
  align-items: start;
}

.services-head {
  align-items: center;
  padding-top: var(--space-3);
  padding-bottom: var(--space-4);
  /* And the column headings match `.task-table thead th` — 11px, medium,
     dim — for the same reason. */
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-dim);
  border-bottom: 1px solid var(--color-border-light);
}

.services-row { border-bottom: 1px solid var(--color-border-light); }
.services-row:last-child { border-bottom: 0; }
/* Hover is `.opens-row`'s, shared with the other three sections. */

.services-row__name {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  /* Inherits the table's 13px. The weight is the only thing that sets a
     service name apart from the rest of its row. */
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  /* A long service name wraps rather than pushing the chips out
     of the row. */
  overflow-wrap: anywhere;
}

/* ----- the chips, one per property ----- */

.services-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
}

/* §10.1: three or more chips, or two that will not fit on one
   line, stack ONE PER LINE and left-aligned. The script adds this
   when it counts three, and again after measuring a wrap. */
.services-chips--stacked {
  flex-direction: column;
  flex-wrap: nowrap;
}

/* A chip here carries an address AND a status, which is longer
   than the filter chips `.chip` was drawn for — so it keeps the
   component's colour, radius and weight and gives up only the
   `nowrap`, which would otherwise push the row sideways on every
   screen rather than only below 720px. */
.chip--service {
  max-width: 100%;
  white-space: normal;
  text-align: left;
  padding-top: 3px;
  padding-bottom: 3px;
}
/* **THE DOT IS GONE** (JP, 2026-10-18: *"the dot in front of the address is
   unnecessary. We don't follow that pattern much throughout Service Autopilot
   or throughout EveryChat, so remove the dot."*). It was `background:
   currentColor`, so it was the chip's own colour repeated a fourth time — after
   the fill, the text and, on the contact screen, the group heading's dot. Its
   one rule here existed only to line it up with the first line of a chip that
   wraps onto two, and there is nothing left to line up. */

/* The street line leads, in bold; the property's own name follows,
   lighter (§10.1). Never the city, state or zip — those are the
   tooltip. */
/* **AND THE STATUS IS NOT BOLD.** `.chip` is semibold throughout, which is
   right for a one-word filter chip and wrong here: it made "1081 Elm Creek Dr"
   and "Active since Apr 2019" the same weight, so the address stopped standing
   out from the state and the chip read as one long run of bold. Only the
   street is heavy. Seen in a screenshot — no assertion was going to find it. */
.chip--service .chip__label { font-weight: var(--font-weight-regular); }
.services-chip__street { font-weight: var(--font-weight-semibold); }
.services-chip__name { font-weight: var(--font-weight-regular); opacity: .8; }

/* ═══ ALL THREE LOOK THE SAME, ON PURPOSE ═══════════════════════════════════
   JP, 2026-10-18: *"the pills are color-coded. This is problematic in that it
   pulls your eye to these pills, and they are not the most important thing on
   the screen. What would be the EveryChat design pattern for these pills?
   Should they be a light gray? Should they be a white pill?… but not a color,
   because this is not something that needs to be pulling your eye to it."*

   They were green for Active, amber for Paused and grey for Canceled — which
   came from his own written design for the widget (*"Chip color by status:
   Active = green, Paused = amber, Canceled = gray"*), so this is him changing
   his mind rather than the build drifting. The words already say it: "Active
   since Apr 2019", "Paused until Mar 1, 2027". The colour was saying it twice
   and shouting the second time.

   **THE THREE CLASS NAMES STAY.** The script still writes which status a chip
   is, so the row says what it is to anything inspecting it, and putting colour
   back later is one rule rather than three. They are grouped here so nobody has
   to wonder whether the three are meant to differ.

   **THE BORDER IS DOING THE WORK, AND IT IS A STEP DARKER THAN THE USUAL ONE.
   Both halves of that were measured, not chosen.**

   First, the fill cannot carry this. The obvious quiet grey is
   `--color-bg-hover` (#F1F5F9) — and that is the exact colour
   `.opens-row:hover` turns these rows, so a chip filled with it would vanish
   under the cursor on every row. One step lighter (#F8FAFC) stands out from a
   hovered row by 1.05:1, and plain white by 1.10:1. Neither is a shape; near
   white on near white is near nothing whichever way round you put it.

   So the hairline is what says "pill", and the ordinary `--color-border`
   (#E5E7EB) is not enough for it either: 1.24:1 on a white row and **1.13:1 on
   a hovered one**. The browser run measured that and failed, which is the only
   reason this is not what shipped. `--color-border-hover` (#CBD5E1) gives
   1.48:1 at rest and 1.36:1 under the cursor — a line you can see on both
   surfaces, and still quiet enough that nothing about it pulls the eye, which
   was the whole request.

   Yes, that is a heavier border than a field gets. A field sits on one known
   background; these sit on a row that changes colour when you point at it. */
.chip--service-active,
.chip--service-paused,
.chip--service-canceled {
  background: var(--color-bg-off);
  border-color: var(--color-border-hover);
  color: var(--color-text-secondary);
}

/* ----- Show {n} more ----- */

.services-more {
  font-family: inherit;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
  background: transparent;
  border: 1px dashed var(--color-border-hover);
  border-radius: var(--radius-pill);
  padding: 2px var(--space-4);
  cursor: pointer;
  align-self: flex-start;
}
.services-more:hover { color: var(--color-text); border-color: var(--color-text-dim); }

/* ----- the two tags under a service name ----- */

.services-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  line-height: 1.4;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  padding: 1px var(--space-3);
  cursor: help;
}
.services-tag > svg { flex-shrink: 0; }

/* "Canceled & returned N×" — yellow, which is what yellow already
   means here: a warning worth noticing, not an error. */
.services-tag--flag {
  background: var(--color-yellow-pale);
  color: var(--color-cream-text);
  border-color: var(--color-cream-border);
}

/* "Won back {Mon YYYY}" — green. The design has it blue; EveryChat
   keeps blue for things you press, and a win-back is good news, so
   it takes the colour that already means success. JP, 2026-10-07. */
.services-tag--winback {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
  border-color: var(--color-green-pale);
}

/* ----- the service's name, which is the way into its history ----- */

/* JP, 2026-10-09: *"make the name of the service the link. When I click
   on lawn mowing, the service, it opens what is now the window opened by
   clicking on history."* It is a button because it opens a panel on this
   page rather than going anywhere, and it is drawn as the row's name —
   the weight and colour the name already had — not as a blue link, so a
   table of services does not read as a table of links. */
.services-name {
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
}
/* The blue underline is `.opens-row`'s, on the row's hover rather than the
   name's own — so hovering anywhere on the line says what it opens, the same
   as a task, a property and an estimate (JP, 2026-10-10). */
.services-name:focus-visible {
  outline: 2px solid var(--color-blue);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* ----- the sync icon beside the title (§10.1) ----- */

.services-sync {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: var(--color-bg);
  color: var(--color-text-secondary);
  cursor: help;
}
.services-sync__dot {
  position: absolute; top: 2px; right: 2px;
  width: 7px; height: 7px; border-radius: 50%;
  border: 2px solid var(--color-bg);
  background: var(--color-green);
}
.services-sync--attention {
  color: var(--color-red-dark);
  border-color: var(--color-red-pale);
  background: var(--color-red-glow);
}
.services-sync--attention .services-sync__dot { background: var(--color-red); }

.services-sync-note {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  color: var(--color-red-dark);
}

/* The banner under the header when a full check is unhappy. */
.services-banner {
  padding: var(--space-4) var(--space-5);
  background: var(--color-red-glow);
  border-bottom: 1px solid var(--color-red-pale);
  font-size: var(--font-size-caption);
  color: var(--color-red-dark);
  line-height: 1.5;
}
.services-banner[hidden] { display: none !important; }

/* ----- nothing yet ----- */

.services-empty {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  color: var(--color-text-secondary);
  font-size: var(--font-size-caption);
}
.services-empty__title {
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-empty[hidden] { display: none !important; }

/* **ONE RULE RATHER THAN ONE PER CLASS.** The estimates stylesheets
   had 33 hand-written `.thing[hidden]` rules because any author
   rule setting `display` outranks the browser's own `[hidden]`, and
   five elements were being drawn that had been told to hide. The
   same guard, once, for everything this widget draws. */
.services-table [hidden],
.services-row [hidden] { display: none !important; }

/* -----------------------------------------------------------
   53b. SERVICE HISTORY DRAWER — .services-drawer
   -----------------------------------------------------------
   Inside `.modal.modal--drawer`, which supplies the slide-in,
   the width and the head/body chrome. Spec §10.3; built by
   `public/scripts/services-history.js`.

   THE DOTS ARE THE ONE PLACE A COLOUR HAD TO BE REASONED ABOUT.
   §10.3 asks for green / gray / amber / blue / hollow-gray, and
   blue is EveryChat's action colour (JP, 2026-10-07: honour
   EveryChat's colours). So the "notable" dot — a win-back return
   or a package change — is green with a ring rather than a new
   hue. It is still distinct from an ordinary start, and no colour
   in this file ends up meaning two things.
   ----------------------------------------------------------- */

.services-drawer__note {
  margin: 0;
  padding: var(--space-6) 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-caption);
}

/* ----- the property tabs ----- */

/* §10.3: they WRAP onto more lines and never scroll sideways. A tab
   you have to scroll to find is a tab nobody presses. */
.services-drawer__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-7);
  border-bottom: 1px solid var(--color-border-light);
  margin-bottom: var(--space-7);
}
.services-drawer__tab {
  font-family: inherit;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: var(--space-5) 0;
  cursor: pointer;
}
.services-drawer__tab:hover { color: var(--color-text); }
.services-drawer__tab.is-on {
  color: var(--color-text);
  font-weight: var(--font-weight-semibold);
  border-bottom-color: var(--color-action);
}

/* ----- what you can do to this service ----- */

/* The buttons that were in the row's three-dot menu (JP, 2026-10-09).
   They sit under the property tab and above the figures, so the house
   they apply to is read before they are — on the row they had to stop
   and ask. A synced service has none and this is not drawn at all. */
.services-drawer__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-7);
}

/* ----- the four tiles ----- */

/* ----- the four tiles, and why the numbers line up -----
   JP, 2026-10-07, of the first version: *"notice how the zeros
   are not justified due to the label."* Four tiles across a
   480px drawer left about 82px of text each, so "Counted as
   pause" and "Counted as churn" wrapped onto two lines and
   their zeros sat a line lower than the others'.

   TWO FIXES, because either alone is luck:

   1. **Two columns, not four.** 160px minimum, so at this
      drawer's width it is 2×2 and each label has room to stay
      on one line. On a wider surface it goes back to four.
   2. **The label block is two lines tall whatever it holds.**
      So a label that DOES wrap — a longer word, a translation,
      a narrower window — still cannot push its value down. The
      first fix makes it look right today; the second makes it
      stay right.

   The Status tile's extra "since Apr 2019" line hangs BELOW its
   value rather than above, so it does not move anything either. */
.services-tiles {
  display: grid;
  /* Four across. It was four at minmax(160px) and went 2x2 in a 480px drawer
     because two labels were "Counted as pause" and "Counted as churn"; every
     label is one short word now — Status, Cancels, Returns, Churn — so four
     fit on one line each and their numbers stay level. */
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-5);
  margin-bottom: var(--space-7);
}
.services-tile {
  display: flex;
  flex-direction: column;
  background: var(--color-bg-off, var(--color-bg-hover));
  border-radius: var(--radius-md);
  padding: var(--space-6) var(--space-7);
}
.services-tile__label {
  font-size: var(--font-size-micro);
  line-height: 1.4;
  color: var(--color-text-secondary);
  /* One line, now that every label is one word — and a test holds that rule,
     because the moment one grows the alignment goes with it. */
  min-height: 1.4em;
}
.services-tile__value {
  margin-top: var(--space-2);
  font-size: var(--font-size-h4, 18px);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-tile__value--active { color: var(--color-green-dark); }
.services-tile__value--paused { color: var(--color-cream-text); }
.services-tile__value--canceled { color: var(--color-text-secondary); }
.services-tile__under {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  margin-top: 2px;
}

/* ----- the flag box ----- */

.services-flagbox {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  background: var(--color-yellow-pale);
  border: 1px solid var(--color-cream-border);
  border-radius: var(--radius-md);
  padding: var(--space-6) var(--space-7);
  color: var(--color-cream-text);
  font-size: var(--font-size-caption);
  line-height: 1.45;
  margin-bottom: var(--space-7);
}
.services-flagbox > svg { flex-shrink: 0; margin-top: 2px; }

/* ----- the timeline ----- */

.services-drawer__heading {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-bold);
  letter-spacing: 1px;
  color: var(--color-text-secondary);
  padding-bottom: var(--space-5);
}

.services-timeline { display: flex; flex-direction: column; }
.services-entry { display: grid; grid-template-columns: 14px 1fr; gap: var(--space-7); }
.services-entry__rail { display: flex; flex-direction: column; align-items: center; }
.services-entry__line { flex: 1; width: 2px; background: var(--color-border); margin-top: 4px; }
.services-entry:last-child .services-entry__line { background: transparent; }

.services-entry__dot {
  width: 10px; height: 10px; border-radius: 50%;
  margin-top: 5px; flex: none;
  background: var(--color-text-dim);
}
/* started / returned / resumed */
.services-entry__dot--began { background: var(--color-green); }
/* canceled / pause ran out */
.services-entry__dot--ended { background: var(--color-text-dim); }
/* a planned pause */
.services-entry__dot--paused { background: var(--color-yellow); }
/* a win-back return, or a package change — green with a ring, because
   blue is EveryChat's action colour and is not available as a status */
.services-entry__dot--notable {
  background: var(--color-green);
  box-shadow: 0 0 0 2px var(--color-green-pale);
}
/* brought in at connection: hollow, because the date is unknown */
.services-entry__dot--imported {
  background: transparent;
  border: 2px solid var(--color-text-dim);
}

.services-entry__body {
  padding-bottom: var(--space-9);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.services-entry__head {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4);
}
.services-entry__title {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
/* A manual entry somebody took back stays visible, struck through — hiding
   it would make the record look as though it had never been wrong (§10.3). */
.services-entry__title--removed { text-decoration: line-through; color: var(--color-text-dim); }
.services-entry__date { font-size: var(--font-size-caption); color: var(--color-text-secondary); }
.services-entry__detail {
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.45;
}
.services-entry__source { font-size: var(--font-size-micro); color: var(--color-text-dim); }
.services-entry__remove {
  align-self: flex-start;
  font-family: inherit;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  /* **NOT RED.** JP, 2026-10-07: *"there's a red 'Remove Entry' option.
     What is the point of that?"* Red is EveryChat's destructive colour, and
     this is a correction, not a deletion: it says "I typed this in wrong",
     the fact stays on the record struck through, and nothing a customer has
     is touched. Shouting it from under every manual entry made it look like
     the dangerous thing on the screen, which the actual dangerous thing —
     Cancel service, in the menu — is not drawn as either. The confirmation
     is where the weight belongs. */
  color: var(--color-text-secondary);
  background: transparent; border: 0; padding: 0; cursor: pointer;
}
.services-entry__remove:hover { color: var(--color-danger); text-decoration: underline; }

/* **ONLY WHEN YOU ARE ON THE ENTRY.** JP, 2026-10-07. With no scheduling
   system connected every fact is hand-typed, so every entry earned a Remove
   link — five entries, five links down a timeline nobody came to edit.

   It FADES rather than disappearing: `display: none` would take it out of the
   tab order and off a screen reader, and the one person most likely to need it
   is the one who cannot use a mouse. It keeps its space too, so nothing jumps
   when the row is hovered. */
.services-entry__remove {
  opacity: 0;
  transition: opacity var(--duration-base, .15s) ease;
}
.services-entry:hover .services-entry__remove,
.services-entry:focus-within .services-entry__remove,
.services-entry__remove:focus-visible { opacity: 1; }

/* A touch screen has no hover, so there it is simply always there. */
@media (hover: none) {
  .services-entry__remove { opacity: 1; }
}
.services-entry__remove[disabled] { color: var(--color-text-dim); cursor: default; }

/* ----- the rest of the tag tones (§9) -----
   The widget's two are above; these only ever appear in History. Each takes
   the colour EveryChat already uses for that meaning. */
.services-tag--churn,
.services-tag--undecided,
.services-tag--changed,
.services-tag--removed {
  background: var(--color-bg-hover);
  color: var(--color-text-secondary);
  border-color: var(--color-border);
}
/* A RETURN and a PAUSE are different things and now read differently, but both
   are "they are not gone" — so both take yellow, which is what yellow already
   means here: worth noticing, not an error. */
.services-tag--return,
.services-tag--pause,
.services-tag--until,
.services-tag--downgrade {
  background: var(--color-yellow-pale);
  color: var(--color-cream-text);
  border-color: var(--color-cream-border);
}
.services-tag--upgrade {
  background: var(--color-green-pale);
  color: var(--color-green-dark);
  border-color: var(--color-green-pale);
}

/* -----------------------------------------------------------
   53c. SERVICES — the row menu and its dialogs
   -----------------------------------------------------------
   Spec §7 and §10.4; built by
   `public/scripts/services-manual.js`. The dialogs themselves
   are `.modal.modal--sm` with EveryChat's own `.field`,
   `.input` and `.btn` inside — only the few things those
   components do not already cover are here.
   ----------------------------------------------------------- */

/* ----- inside a dialog ----- */

/* WHICH HOUSE THE ACTION IS ABOUT, under the title (JP, 2026-10-10).
   **SMALLER AND DIMMER THAN A FIELD LABEL**, deliberately. EveryChat's
   usual second line under a modal title (`.modal__meta`) is 13px — the
   same size as "Restart date" directly below it — and JP asked for
   these two not to look alike. 11px and dim puts this with the title
   it belongs to; the address itself is a shade stronger than the word
   in front of it, because the address is the fact. */
.services-dialog__about {
  margin: var(--space-2) 0 0;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  color: var(--color-text-dim);
}
.services-dialog__about span {
  color: var(--color-text-secondary);
  font-weight: var(--font-weight-medium);
}

.services-dialog .modal__body { display: flex; flex-direction: column; gap: var(--space-6); }

.services-dialog__error {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-danger);
  line-height: 1.45;
}
.services-dialog__error[hidden] { display: none !important; }

/* §7: on a paused record the Cancel date is not editable, and this sentence
   stands where the date box would be. */
.services-dialog__fixed {
  margin: 0;
  padding: var(--space-6) var(--space-7);
  background: var(--color-bg-hover);
  border-radius: var(--radius-md);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
}

.services-dialog__note {
  margin: 0;
  font-size: var(--font-size-micro);
  color: var(--color-text-secondary);
  line-height: 1.5;
}
.services-dialog__label {
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-bold);
  letter-spacing: 1px;
  color: var(--color-text-secondary);
}
.services-dialog__managed {
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.6;
}

/* ----- a pickable row (a service, or a property) ----- */

.services-pick-list {
  display: flex; flex-direction: column; gap: var(--space-3);
  max-height: 200px; overflow-y: auto;
}
.services-pick {
  display: flex; align-items: center; gap: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-6);
  font-size: var(--font-size-caption);
  color: var(--color-text);
  cursor: pointer;
}
.services-pick:hover { border-color: var(--color-border-hover); }

/* **THE TICK BOX WAS INVISIBLE.** `base.css` sets `appearance: none` on every
   `input`, which is right for a text box and wipes out the browser's own
   drawing of a radio or a checkbox — so the Add box showed a list of plain
   rows with nothing to tick. Selection still highlighted the row, but nothing
   said you could pick more than one property, and an unticked row looked the
   same as a ticked one until you clicked it.

   The same `appearance: none` already cost this project the number-box arrows
   on the price book. JP's own design file draws a native checkbox here, so the
   browser's own control is put back rather than a new one invented. */
.services-pick input[type="checkbox"],
.services-pick input[type="radio"] {
  appearance: auto;
  -webkit-appearance: auto;
  width: 15px;
  height: 15px;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--color-action);
  cursor: pointer;
}
.services-pick.is-off input { cursor: default; }
.services-pick:has(input:checked) {
  border-color: var(--color-action);
  background: var(--color-action-bg-subtle);
}
/* §7: a property that already has this Service is disabled and says why,
   rather than being pressable and then refused. */
.services-pick.is-off { cursor: default; color: var(--color-text-dim); }
.services-pick.is-off:hover { border-color: var(--color-border); }
.services-pick__status {
  margin-left: auto;
  font-size: var(--font-size-micro);
  color: var(--color-text-secondary);
}

/* **ONE [hidden] GUARD FOR THE WHOLE SECTION, AND IT IS NOT BELT-AND-BRACES.**
   `hidden` is the browser's own `display:none`, and ANY author rule setting
   `display` on a class outranks it. The "+" in this section's header carries
   `.btn`, which sets `display:inline-flex` — so a button told to hide was drawn
   anyway, and on load it appeared and then vanished. JP, 2026-10-07: *"It's
   showing the top-right current and all, and the plus sign, and then the
   current and all disappears."*

   The estimates stylesheets hit exactly this and found 124 hidden elements with
   five of them drawn regardless. They fixed it with one rule per sheet instead
   of 33 per-class patches; this is the same rule, scoped to this section so
   nothing else in EveryChat changes behaviour. */
.services-panel [hidden] { display: none !important; }

/* A date box opens a picker when you click it, so it should say so. JP,
   2026-10-07: *"when I roll my mouse over the date picker, the mouse pointer
   doesn't change... It doesn't move to a pointer."* `base.css` gives every
   input a text caret, which is right for a box you type in and wrong for one
   whose whole job is to open a calendar. */
.services-dialog input[type="date"] { cursor: pointer; }
.services-dialog input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; }

/* The label explains itself on a hover now that it is one word, so the whole
   tile is a help target rather than just the text. */
.services-tile[title] { cursor: help; }

/* ===========================================================
   54. SERVICES SETTINGS — .services-settings
   -----------------------------------------------------------
   Settings › Integrations › CRM Integrations, the Services
   half of it (services build spec §10.6). Five sections on a
   screen EveryChat already had, which is why there is no page
   shell here — `.page`, `.page__header`, `.card`, `.btn`,
   `.field`, `.input`, `.chip` and `.banner--warning` are all
   the components already on it.

   WHAT IS ACTUALLY NEW, AND WHY
   -----------------------------
   1. **The system cards.** Four small cards in a grid, one per
      scheduling system. `.card` gives the surface; this gives
      the grid, the connected ring, and the two text sizes
      inside.
   2. **Two tables that are not `.task-table`.** §10.6 C and D
      are two and three columns of mixed controls — chips,
      dropdowns, an inline search panel — not rows of data with
      a header to sort by. `.task-table` is built for the
      latter and fighting it would have cost more markup than
      this costs CSS.
   3. **The inline link search.** A panel that opens inside a
      table cell. `.dropdown` is anchored to a trigger and
      positioned absolutely; this one has to push the row
      taller so the results are never clipped by the card.
   4. **A sentence with a number box in the middle of it.**
      §10.6 E's two rules read as English with an input where
      the number goes, which no existing component does.

   THE AMBER BOX IS `.banner--warning`, NOT A NEW THING.
   §10.6 E's impact preview is a warning with a list and two
   buttons in it, which is what `.banner` already is — only the
   list needed anything.

   ONE [hidden] GUARD, AND IT IS NOT BELT-AND-BRACES. `hidden`
   is the browser's own `display:none`, and any author rule
   setting `display` on a class outranks it — `.btn` sets
   `display:inline-flex`, which is how the Services widget's
   "+" was drawn on load after being told to hide. Scoped to
   this section so nothing else in EveryChat changes.
   =========================================================== */

.services-settings {
  display: flex;
  flex-direction: column;
  gap: var(--space-10, 24px);
  margin-top: var(--space-12, 32px);
  padding-top: var(--space-12, 32px);
  border-top: 1px solid var(--color-border);
}
.services-settings [hidden] { display: none !important; }
/* The same guard for the half that now lives in Service Autopilot's panel.
   `hidden` is the browser's own display:none and ANY author rule setting
   `display` on a class outranks it — `.btn` sets inline-flex, which is how
   the Services widget's "+" was drawn after being told to hide. */
.integrations-card__panel [hidden] { display: none !important; }

.services-settings__intro { display: flex; flex-direction: column; gap: var(--space-3); }

.services-settings__heading {
  margin: 0;
  font-size: var(--font-size-h3, 20px);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-settings__subheading {
  margin: 0 0 var(--space-8);
  padding-top: var(--space-10, 24px);
  border-top: 1px solid var(--color-border);
  font-size: var(--font-size-h4, 16px);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-settings__block-title {
  margin: 0;
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-settings__help,
.services-settings__note {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.5;
}
.services-settings__note { color: var(--color-text-dim); }

.services-settings__block {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  margin-bottom: var(--space-10, 24px);
}
.services-settings__block:last-child { margin-bottom: 0; }
.services-settings__block-head { display: flex; flex-direction: column; gap: var(--space-3); }

/* ----- A. the Services sync tab, inside a system's own panel -----
   The four-card grid this replaced was moved into Service Autopilot's
   Configure panel on 2026-10-08: JP looked for these settings there twice,
   and the page was already carrying a card per system for another reason.
   What is left is one system's state, two short blocks of text. */

.services-settings__since {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0 0 var(--space-4);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-green-dark);
}
.services-settings__lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-bottom: var(--space-10, 24px);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.5;
}
.services-settings__lines span:last-child { color: var(--color-text-dim); }

/* The section on the page is now the churn rules alone, so it needs no top
   rule of its own — the card carries it. */
.services-settings__cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-6);
}
.services-system {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-8);
  /* **`.card` CARRIES A 16px BOTTOM MARGIN FOR CARDS IN A COLUMN**, and in a
     grid that makes the last card in a row 16px TALLER than its neighbours,
     because a grid item's height is the row height less its margins. Four
     cards side by side, one of them taller: found by looking at the screen,
     measured at 199px against 183px. */
  margin: 0;
}
/* A ring rather than a thicker border, so the card does not change size when
   it becomes the connected one. */
.services-system--on {
  border-color: var(--color-green);
  box-shadow: 0 0 0 1px var(--color-green);
}
.services-system__name {
  margin: 0;
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}
.services-system__since {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-green-dark);
}
.services-system__lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.5;
}
/* The last line is "Last … · next …", which is reference rather than status. */
.services-system__lines span:last-child { color: var(--color-text-dim); }
.services-system__note {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
}
.services-system__actions { margin-top: auto; }

.services-settings__switch { margin: 0; }

/* ----- B. when sync needs attention ----- */

/* **THE TICK BOX, VISIBLE.** `base.css` sets `appearance: none` on every
   input, which is right for a text box and wipes out the browser's own drawing
   of a checkbox — the Services widget's property picker lost its ticks to
   exactly this, and the price book lost its number-box arrows to it before
   that. The browser's own control is put back rather than a new one invented. */
.services-settings__check {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  font-size: var(--font-size-body);
  color: var(--color-text);
  cursor: pointer;
}
.services-settings__check input[type="checkbox"] {
  appearance: auto;
  -webkit-appearance: auto;
  width: 15px;
  height: 15px;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--color-action);
  cursor: pointer;
}

/* The three things that only apply when the tick box is on sit under it. */
.services-settings__indent {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding-left: var(--space-10, 24px);
}
.services-settings__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-6);
}

.services-settings__preview {
  padding: var(--space-6) var(--space-7);
  background: var(--color-bg-hover);
  border-radius: var(--radius-md);
}
.services-settings__preview-label {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-micro);
  color: var(--color-text-dim);
}
.services-settings__preview-body {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.5;
}

/* A box that holds chips and a text box, and looks like one field. */
.services-settings__emails {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  min-height: 40px;
  padding: var(--space-3) var(--space-5);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.services-settings__emails:focus-within {
  border-color: var(--color-action);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
.services-settings__email-input {
  flex: 1;
  min-width: 200px;
  border: 0;
  background: transparent;
  padding: var(--space-2) 0;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  color: var(--color-text);
}
.services-settings__email-input:focus { outline: 0; }

/* ----- C and D. the two tables ----- */

/* The two tables are `.card`s with the padding taken off, because their rows
   supply their own. The margin goes too: they sit in a flex column that already
   has a gap, and `.card`'s own 16px would double it. */
.services-settings__table { padding: 0; margin: 0; overflow: hidden; }

.services-table__head,
.services-table__row {
  display: grid;
  gap: var(--space-8);
  padding: var(--space-6) var(--space-8);
}
.services-table__head {
  background: var(--color-bg-hover);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
}
.services-table__row { border-top: 1px solid var(--color-border); align-items: start; }
.services-table__row--links { grid-template-columns: 1fr 1.8fr; }
.services-table__row--review { grid-template-columns: 1.1fr .5fr 1.5fr; align-items: center; }

.services-table__name {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
  padding-top: var(--space-2);
}
.services-table__note,
.services-table__empty {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  line-height: 1.5;
}
.services-table__empty {
  padding: var(--space-8);
  border-top: 1px solid var(--color-border);
  color: var(--color-green-dark);
}
/* §10.6 D draws the count in amber: it is the number of properties that will
   stop showing a service until somebody links it. */
.services-table__count {
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  /* EveryChat's own amber text, the same pair `.chip--prospect` uses. */
  color: var(--color-yellow);
}
.services-table__pick { display: flex; gap: var(--space-4); align-items: center; }
.services-table__select { flex: 1; min-width: 160px; }

.services-table__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}
/* The text button's own `align-self: flex-start` is right in a column and
   wrong here: beside a row of tags it sat a few pixels high. */
.services-table__links > .services-settings__link-btn { align-self: center; }

/* The PACKAGE / SERVICE mini-label (§10.6 C). Two kinds, two colors, and both
   from EveryChat's own palette — purple is already the account's second accent
   and grey is what every neutral label uses. */
.services-kind {
  font-family: var(--font-body);
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
  letter-spacing: .5px;
  border-radius: var(--radius-xs, 4px);
  padding: 1px var(--space-3);
  white-space: nowrap;
}
.services-kind--package { background: var(--color-purple-pale); color: var(--color-purple-dark); }
.services-kind--service { background: var(--color-bg-hover); color: var(--color-text-secondary); }

/* A linked tag. Draggable, because the order decides which move counts as an
   upgrade (§10.6 C) — so it says so on hover and takes a grab cursor. */
.services-link { cursor: grab; gap: var(--space-3); }
.services-link.is-dragging { opacity: .5; cursor: grabbing; }

/* ----- C. the inline search panel ----- */

/* **IT PUSHES THE ROW TALLER RATHER THAN FLOATING OVER IT.** A dropdown
   positioned absolutely inside a card with `overflow: hidden` is a dropdown
   with its results cut off, and the card needs that overflow so the table's
   first row does not square off its corners. */
.services-search {
  flex-basis: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding: var(--space-6);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md, 0 8px 24px rgba(17, 24, 39, .08));
}
.services-search__results {
  display: flex;
  flex-direction: column;
  max-height: 280px;
  overflow-y: auto;
}
.services-search__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  width: 100%;
  padding: var(--space-5) var(--space-6);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--font-size-body);
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
}
.services-search__option:hover:not(:disabled) { background: var(--color-bg-hover); }
.services-search__option:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}
/* §10.6 C: an item already linked to another Service is shown GREYED with
   where it went, rather than hidden — so an admin looking for it can see why
   it is not available instead of wondering whether search is broken. */
.services-search__option:disabled { cursor: default; color: var(--color-text-dim); }
.services-search__option-name { display: inline-flex; align-items: center; gap: var(--space-4); }
.services-search__option-taken {
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  white-space: nowrap;
}
.services-search__empty,
.services-search__hint {
  margin: 0;
  padding: var(--space-5) var(--space-6);
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
}
.services-search__hint { padding: 0; }
.services-search__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
}
.services-search__paste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-5);
}
.services-search__paste .input { flex: 1; min-width: 220px; }

/* A text button that is a link in everything but the href — "+ Link", "Paste
   an ID instead", "Show ignored (3)". `.btn` would be too heavy for a control
   that sits inside a table cell beside chips. */
.services-settings__link-btn {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  color: var(--color-action);
  cursor: pointer;
}
.services-settings__link-btn:hover { text-decoration: underline; }
.services-settings__link-btn:focus-visible {
  outline: 0;
  border-radius: var(--radius-xs, 4px);
  box-shadow: 0 0 0 3px var(--color-focus-ring);
}

/* ----- E. the churn rules ----- */

/* A sentence with a number box in the middle. It wraps like text, because on a
   narrow screen it has to. */
.services-settings__sentence {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--font-size-body);
  color: var(--color-text);
  line-height: 1.6;
}
.services-settings__number { width: 72px; flex: 0 0 auto; text-align: center; }

.services-settings__impact { margin: 0; }
/* The closing sentence is not another bullet, so it does not sit flush with
   the list. */
.services-settings__impact .banner__message { margin-top: var(--space-5); }
.services-settings__impact-list {
  margin: var(--space-4) 0 0;
  padding-left: var(--space-10, 24px);
  font-size: var(--font-size-caption);
  color: var(--color-text-secondary);
  line-height: 1.6;
}
.services-settings__rule-error { margin: 0; }

.services-settings__history { display: flex; flex-direction: column; gap: var(--space-2); }
.services-settings__history-label {
  margin: 0;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}
.services-settings__history-line {
  margin: 0;
  font-size: var(--font-size-caption);
  color: var(--color-text-dim);
  line-height: 1.6;
}

/* A number box opens with arrows in Chrome and Firefox, and this project has
   already decided they do not belong on a settings number: JP, 2026-10-07 —
   *"Took the up/down arrows off the measurement boxes too."* `appearance:
   none` alone does NOT remove them (it is already set on every input by
   `base.css` and does nothing); the two browsers need different halves. */
.services-settings__number::-webkit-outer-spin-button,
.services-settings__number::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}
.services-settings__number[type="number"] { -moz-appearance: textfield; }

/* The one line an admin sees if the Services settings cannot load. It is the
   whole section in that case, so it sits on its own rather than inside a card.
   See `public/scripts/services-settings.js` for why it exists at all. */
.services-settings__failed {
  margin: 0;
  font-size: var(--font-size-body);
  color: var(--color-text-secondary);
}

/* ===========================================================
   52. FEATURE ANNOUNCEMENT MODAL — .announce
   Visual reference: Automations list / Feedback dashboard
   first-run "what's new" takeover (shown once, dismissed forever)
   =========================================================== */

.announce {
  max-width: 760px;
  max-height: calc(100vh - 100px);
  height: calc(100vh - 100px);
  padding: 0; /* hero band runs edge-to-edge inside the modal card */
}

/* Reskins the shared .modal__close (Component #2) for the colored hero
   it sits on, rather than introducing a second close-button element. */
.announce .modal__close {
  color: #fff;
}
.announce .modal__close:hover {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}

.announce__hero {
  position: relative;
  flex-shrink: 0;
  padding: var(--space-14) var(--space-12) var(--space-12);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--announce-accent, var(--gradient-message-incoming));
  color: #fff;
}

.announce__badge {
  display: none;
}

.announce__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
}

/* Gold "NEW" chip — extra flare, deliberately not tokenized: a one-off
   celebratory accent, not a semantic color (unlike e.g. the tier-1/tier-2
   gold tokens, which mean something specific elsewhere in the app). */
.announce__eyebrow-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, #FDE68A, #D4AF37);
  color: #4A3B00;
}
.announce__eyebrow-badge svg { width: 10px; height: 10px; }

.announce__title {
  max-width: 480px;
  margin: 0 0 var(--space-4);
  font-family: var(--font-display);
  font-size: var(--font-size-h2);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-h2);
  color: #fff;
}

.announce__subtitle {
  max-width: 480px;
  margin: 0;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  opacity: 0.92;
}

.announce__body {
  display: flex;
  flex: 1;
  align-items: baseline;
  min-height: 0;
  padding: var(--space-12);
  overflow-y: auto;
}

/* Wraps multiple __grid sections (+ an optional __divider between them) as
   the single flex child of __body, so __body's own flex/align-items rules
   don't have to change to accommodate a multi-section layout. */
.announce__sections {
  width: 100%;
}

.announce__grid {
  display: grid;
  width: 100%;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-8);
}

.announce__grid + .announce__divider,
.announce__divider + .announce__grid {
  margin-top: var(--space-10);
}

.announce__divider {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
}
.announce__divider::before,
.announce__divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

.announce__feature {
  display: flex;
  gap: var(--space-6);
  padding: var(--space-8);
  border-radius: var(--radius-lg);
  background: var(--color-bg-off);
}

.announce__feature-icon {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  box-shadow: var(--shadow-sm);
  color: var(--announce-icon-color, var(--color-blue));
}
.announce__feature-icon svg { width: 18px; height: 18px; }

.announce__feature-title {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text);
}

.announce__feature-body {
  margin: 0;
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  color: var(--color-text-secondary);
}

.announce__foot {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-5);
  padding: var(--space-8) var(--space-12) var(--space-10);
  border-top: 1px solid var(--color-border);
}

@media (max-width: 640px) {
  .announce__grid { grid-template-columns: 1fr; }
}


/* =============================================================
   ModalPopup dialogs (public/scripts/modalPopup.js)
   ------------------------------------------------------------
   ModalPopup builds its "Are you sure?" style dialogs in script and
   appends an overlay and a container to <body>. It used to be styled
   by legacy stylesheets that were deleted on 2026-08-21, so on any
   page that did not bring its own rules the dialog sat unstyled
   below the page and clicking Delete appeared to do nothing
   (EveryChat Trash, 2026-10-29).

   Only the classes ModalPopup tags itself with are styled here
   (.modal-popup-overlay, .modal-popup-container), so static modals
   that merely share the old `modal-box-container` class are left
   alone. A dialog that already carries the .modal component (the
   Properties page patches it on) keeps its own chrome.
============================================================= */

.modal-popup-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay-modal);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: var(--z-modal);
}

.modal-popup-container {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: calc(var(--z-modal) + 1);
  max-height: calc(100vh - var(--space-12));
  overflow-y: auto;
}

.modal-popup-container:not(.modal) {
  width: min(420px, calc(100vw - var(--space-16)));
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
}

.modal-popup-container .confirm-modal-content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-14) var(--space-10) var(--space-10);
}

.modal-popup-container .confirm-modal-title {
  margin: 0;
  text-align: center;
  font-size: var(--font-size-h4);
  font-weight: var(--font-weight-medium);
  color: var(--color-text);
}

.modal-popup-container .modalPopup-close {
  position: absolute;
  top: var(--space-8);
  right: var(--space-8);
  font-size: var(--font-size-h4);
  color: var(--color-text-dim);
  cursor: pointer;
}

.modal-popup-container .modalPopup-close:hover {
  color: var(--color-text);
}

.modal-popup-container .modal-popup-button,
.modal-popup-container .onpage-upgrade-modal-button {
  display: block;
  width: 100%;
  padding: var(--space-6) var(--space-10);
  border: 0;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-body);
  font-weight: var(--font-weight-medium);
  cursor: pointer;
}

.modal-popup-container .modal-popup-button p,
.modal-popup-container .onpage-upgrade-modal-button p {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  color: inherit;
}

.modal-popup-container .modal-popup-button.save {
  background: var(--color-action);
  color: var(--color-text-on-action);
}

.modal-popup-container .modal-popup-button.save:hover {
  background: var(--color-action-hover);
}

.modal-popup-container .onpage-upgrade-modal-button.cancel {
  background: transparent;
  color: var(--color-text-secondary);
}

.modal-popup-container .onpage-upgrade-modal-button.cancel:hover {
  background: var(--color-bg-hover);
}

.modal-popup-container .modal-popup-button.save.danger {
  background: var(--color-red);
}

.modal-popup-container .modal-popup-button.save.danger:hover {
  background: var(--color-red-dark);
}


/* ===========================================================
   53. IMPORT WIZARD + IMPORT STATUS BANNER — .iw-*
   Visual reference: "Import Contacts Wizard" design
   (import-wizard.css), ported onto the existing .modal, .banner,
   .segment, .field and .btn components. Only what those do not
   already cover is defined here; every colour is a token.

   Markup lives in views/partials/contacts/import/ (wizard.hbs,
   banner.hbs); behaviour in public/scripts/contacts-import-*.js.

   Parts: .iw-modal (the 880px wizard shell), .iw-steps (stepper,
   shown only once a file is chosen), .iw-help, .iw-drop (idle /
   is-over / is-error), .iw-file + .iw-result (the file chip and
   the one line that says if it is ready), .iw-sheets, .iw-stats,
   .iw-ignored, .iw-toggle, .iw-disc, .iw-center (started state),
   .iw-banner (banner extras: meta, live dot, progress, actions).
   =========================================================== */

/* ----- Shell: wider than .modal--lg, tighter than the default padding ----- */
.iw-modal {
  max-width: 880px;
  overflow: hidden;           /* head and foot stay put, only the body scrolls */
}
.iw-head {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-9) var(--space-10) var(--space-8);
  border-bottom: 1px solid var(--color-border-light);
}
.iw-head .modal__close { position: static; margin-left: auto; flex-shrink: 0; }
.iw-head .modal__close[hidden] { display: none; }
.iw-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-9) var(--space-10);
}
.iw-modal .modal__head + .modal__body { padding-top: var(--space-9); }
.iw-foot {
  padding: var(--space-7) var(--space-10);
  border-top: 1px solid var(--color-border);
  background: var(--color-bg-off);
}
.iw-foot [hidden] { display: none; }
.iw-foot__note { margin-right: auto; }
.iw-screen { display: flex; flex-direction: column; gap: var(--space-9); }
.iw-screen[hidden] { display: none; }
.iw-block { display: block; margin-top: 2px; }
.iw-opt { font-weight: var(--font-weight-regular); color: var(--color-text-dim); }
.iw-link { color: var(--color-blue); font-weight: var(--font-weight-medium); text-decoration: none; cursor: pointer; }
.iw-link:hover { color: var(--color-blue-dark); text-decoration: underline; }

/* ----- Stepper ----- */
.iw-steps {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--font-size-small);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-dim);
}
.iw-steps[hidden] { display: none; }
.iw-steps li { display: flex; align-items: center; gap: var(--space-3); }
.iw-steps__n {
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 1.5px solid var(--color-border-hover);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--font-size-micro);
  font-weight: var(--font-weight-semibold);
}
.iw-steps .is-on { color: var(--color-text); }
.iw-steps .is-on .iw-steps__n { background: var(--color-blue); border-color: var(--color-blue); color: var(--color-text-on-action); }
.iw-steps .is-done { color: var(--color-text-secondary); }
.iw-steps .is-done .iw-steps__n { background: var(--color-blue-glow); border-color: var(--color-blue-pale); color: var(--color-blue); }
.iw-steps__sep { width: 18px; height: 1px; background: var(--color-border-hover); }

/* ----- Help strip ----- */
.iw-help { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; font-size: var(--font-size-small); }
.iw-help__link {
  display: inline-flex; align-items: center; gap: var(--space-3);
  background: none; border: 0; padding: 0;
  font: inherit; font-weight: var(--font-weight-medium);
  color: var(--color-blue); cursor: pointer; text-decoration: none;
}
.iw-help__link:hover { color: var(--color-blue-dark); text-decoration: underline; }

/* ----- Dropzone ----- */
.iw-drop {
  border: 1.5px dashed var(--color-border-hover);
  border-radius: var(--radius);
  padding: 30px var(--space-10);
  text-align: center;
  background: var(--color-bg-off);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--easing-default), background-color var(--duration-fast) var(--easing-default);
}
.iw-drop[hidden] { display: none; }
.iw-drop:hover { border-color: var(--color-blue); }
.iw-drop:focus-visible { outline: 0; border-color: var(--color-blue); box-shadow: 0 0 0 3px var(--color-focus-ring); }
.iw-drop__icon {
  width: 44px; height: 44px; margin-bottom: var(--space-3);
  border-radius: var(--radius);
  background: var(--color-blue-glow); color: var(--color-blue);
  display: inline-flex; align-items: center; justify-content: center;
}
.iw-drop__title { font-family: var(--font-display); font-size: 16px; font-weight: var(--font-weight-semibold); color: var(--color-text); }
.iw-drop__sub { font-size: var(--font-size-small); color: var(--color-text-secondary); }
.iw-drop.is-over { border-color: var(--color-blue); background: var(--color-blue-glow); }
.iw-drop.is-error { border-color: var(--color-red); background: var(--color-red-glow); }
.iw-drop.is-error .iw-drop__icon { background: var(--color-red-pale); color: var(--color-red); }

/* ----- File chip + analysis ----- */
.iw-file { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; }
.iw-file[hidden] { display: none; }
.iw-file__top { display: flex; align-items: center; gap: var(--space-6); padding: var(--space-6) var(--space-7); }
.iw-file__icon {
  width: 36px; height: 36px; flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--color-green-glow); color: var(--color-green);
  display: inline-flex; align-items: center; justify-content: center;
}
.iw-file__icon.is-error { background: var(--color-red-glow); color: var(--color-red); }
.iw-file__text { flex: 1; min-width: 0; }
.iw-file__name { font-weight: var(--font-weight-semibold); font-size: 14px; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.iw-file__meta { font-size: var(--font-size-caption); color: var(--color-text-dim); }
.iw-file__sub {
  border-top: 1px solid var(--color-border-light);
  padding: var(--space-6) var(--space-7);
  display: flex; flex-direction: column; gap: var(--space-6);
  background: var(--color-bg-off);
}
.iw-file__sub[hidden] { display: none; }
.iw-file__sub .field[hidden] { display: none; }

.iw-result {
  display: flex; align-items: flex-start; gap: var(--space-5);
  padding: var(--space-6) var(--space-7);
  border-top: 1px solid var(--color-border-light);
  font-size: var(--font-size-small);
}
.iw-result > svg { flex-shrink: 0; margin-top: 1px; }
.iw-result__text { flex: 1; min-width: 0; }
.iw-result__t { font-weight: var(--font-weight-semibold); font-size: 14px; color: var(--color-text); }
.iw-result__d { color: var(--color-text-secondary); margin-top: 2px; }
.iw-result__btn { margin-top: var(--space-4); }
.iw-result--ok > svg { color: var(--color-green); }
.iw-result--warn > svg { color: var(--color-yellow); }
.iw-result--err > svg { color: var(--color-red); }
.iw-result--info > svg { color: var(--color-blue); }
.iw-result--info > svg[data-spin] { animation: iw-spin 1.2s linear infinite; }
@keyframes iw-spin { to { transform: rotate(360deg); } }

.iw-sheets { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.iw-sheet {
  display: inline-flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font: inherit; font-size: var(--font-size-small); color: var(--color-text);
  white-space: nowrap; cursor: pointer;
}
.iw-sheet:focus-visible { outline: 0; box-shadow: 0 0 0 3px var(--color-focus-ring); }
.iw-sheet__r { width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid var(--color-border-hover); }
.iw-sheet.is-on { border-color: var(--color-blue); box-shadow: 0 0 0 3px var(--color-focus-ring); }
.iw-sheet.is-on .iw-sheet__r { border: 4px solid var(--color-blue); }
.iw-sheet small { color: var(--color-text-dim); font-size: var(--font-size-caption); }

.iw-stats { display: flex; gap: var(--space-3); flex-wrap: wrap; margin-top: var(--space-4); }
.iw-stat {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--font-size-caption); font-weight: var(--font-weight-medium);
  padding: 3px var(--space-4); border-radius: 6px;
}
.iw-stat--ok { background: var(--color-green-glow); color: var(--color-banner-success-text); }
.iw-stat--ign { background: var(--color-bg-hover); color: var(--color-text-secondary); }

.iw-ignored { margin-top: var(--space-5); font-size: var(--font-size-small); }
.iw-ignored summary { cursor: pointer; color: var(--color-blue); font-weight: var(--font-weight-medium); }
.iw-ignored ul { list-style: none; margin: var(--space-4) 0 0; padding: 0; max-height: 180px; overflow-y: auto; border: 1px solid var(--color-border-light); border-radius: var(--radius-sm); }
.iw-ignored li { display: flex; gap: var(--space-5); padding: var(--space-3) var(--space-5); border-top: 1px solid var(--color-border-light); }
.iw-ignored li:first-child { border-top: 0; }
.iw-ignored b { font-weight: var(--font-weight-semibold); flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.iw-ignored span { color: var(--color-text-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ----- Options ----- */
.iw-options { display: flex; flex-direction: column; gap: var(--space-8); }
.iw-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8); }
.iw-grid .field { margin: 0; }
.iw-segment { width: auto; align-self: flex-start; }
.iw-segment .segment__item { flex: none; padding-left: var(--space-8); padding-right: var(--space-8); }

.iw-toggle-row, .iw-check-row {
  display: flex; align-items: center; gap: var(--space-5);
  font-size: var(--font-size-small); cursor: pointer;
}
.iw-check-row { align-items: flex-start; }
.iw-toggle-row__t { font-weight: var(--font-weight-medium); color: var(--color-text); }
.iw-toggle {
  position: relative; flex-shrink: 0;
  width: 34px; height: 20px; border-radius: var(--radius-pill);
  background: var(--color-border-hover);
  transition: background-color var(--duration-fast) var(--easing-default);
}
.iw-toggle::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--color-bg); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: left var(--duration-fast) var(--easing-default);
}
.iw-toggle-row.is-on .iw-toggle { background: var(--color-blue); }
.iw-toggle-row.is-on .iw-toggle::after { left: 16px; }
.iw-toggle-row input:focus-visible + .iw-toggle { box-shadow: 0 0 0 3px var(--color-focus-ring); }

.iw-disc { border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.iw-disc__head {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-5) var(--space-7);
  font-size: var(--font-size-small); font-weight: var(--font-weight-semibold); color: var(--color-text);
  cursor: pointer; list-style: none;
}
.iw-disc__head::-webkit-details-marker { display: none; }
.iw-disc__head::before { content: ""; width: 6px; height: 6px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); transition: transform var(--duration-fast) var(--easing-default); }
.iw-disc[open] > .iw-disc__head::before { transform: rotate(45deg); }
.iw-disc__head .field__hint { font-weight: var(--font-weight-regular); }
.iw-disc__body { border-top: 1px solid var(--color-border-light); padding: var(--space-7); display: flex; flex-direction: column; gap: var(--space-7); }

/* ----- Started / uploading state ----- */
.iw-center { flex: 1; align-items: center; justify-content: center; text-align: center; gap: var(--space-5); padding: var(--space-10); }
.iw-center[hidden] { display: none; }
.iw-center__icon {
  width: 52px; height: 52px; margin-bottom: var(--space-3);
  border-radius: 14px; background: var(--color-blue-glow); color: var(--color-blue);
  display: inline-flex; align-items: center; justify-content: center;
}
.iw-center p { margin: 0; color: var(--color-text-secondary); font-size: 14px; max-width: 420px; text-wrap: pretty; }
.iw-center p:empty { display: none; }
.iw-center .iw-big { color: var(--color-text); }
.iw-center .iw-center__note { font-size: var(--font-size-small); }
.iw-big { font-family: var(--font-display); font-size: 24px; font-weight: var(--font-weight-bold); letter-spacing: -0.015em; line-height: 1.2; }

.iw-progress {
  height: 6px; border-radius: var(--radius-pill);
  background: color-mix(in srgb, currentColor 14%, transparent);
  overflow: hidden; margin-top: var(--space-4); max-width: 520px;
}
.iw-progress[hidden] { display: none; }
.iw-progress > i { display: block; height: 100%; background: var(--color-blue); border-radius: var(--radius-pill); transition: width var(--duration-slow) var(--easing-default); }
.iw-progress--wide { width: 320px; max-width: 100%; color: var(--color-blue); margin: var(--space-4) 0; }

.iw-notice { align-items: flex-start; }
.iw-notice[hidden] { display: none; }

/* ----- Status banner extras (builds on .banner) ----- */
.iw-banners { display: flex; flex-direction: column; gap: var(--space-4); padding: 0 var(--space-12) var(--space-6); }
.iw-banners[hidden] { display: none; }
.iw-banner { align-items: flex-start; }
.iw-banner .banner__icon { margin-top: 1px; }
.iw-banner .banner__title { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.iw-banner__meta { font-weight: var(--font-weight-regular); opacity: 0.85; }
.iw-banner__text { font-size: var(--font-size-small); margin-top: 2px; }
.iw-banner__text[hidden] { display: none; }
.iw-banner__byline { opacity: 0.85; }
.iw-banner .iw-progress { color: currentColor; }
.iw-banner .iw-progress > i { background: var(--color-blue); }
.iw-banner__actions { align-self: center; margin-left: 0; }
.iw-banner__actions[hidden] { display: none; }
.iw-banner .banner__close { margin-left: 0; align-self: center; color: inherit; opacity: 0.7; }
.iw-banner .banner__close[hidden] { display: none; }
.banner--info.iw-banner { color: var(--color-banner-info-text); }
.banner--success.iw-banner { color: var(--color-banner-success-text); }
.banner--warning.iw-banner { color: var(--color-banner-warning-text); }
.banner--danger.iw-banner { color: var(--color-banner-danger-text); }
.iw-banner .banner__title, .iw-banner .banner__message { color: inherit; }
.iw-banner .btn--secondary { background: var(--color-bg); color: inherit; border-color: color-mix(in srgb, currentColor 30%, transparent); }
.iw-banner .btn--ghost { color: inherit; }
.iw-live {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--color-blue); box-shadow: 0 0 0 3px var(--color-blue-pale);
  display: inline-block; animation: iw-pulse 1.6s ease-in-out infinite;
}
.iw-live[hidden] { display: none; }
@keyframes iw-pulse { 50% { box-shadow: 0 0 0 5px var(--color-blue-glow); } }

/* The "New" badge on rows from an import nobody has dismissed yet. */
.iw-new-badge {
  margin-left: var(--space-3);
  font-size: var(--font-size-micro); font-weight: var(--font-weight-bold);
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--color-blue); background: var(--color-blue-glow);
  padding: 2px 6px; border-radius: 4px; vertical-align: middle;
}

/* ----- Phase 2: Map columns, Review values, Summary ----- */
.iw-linkbtn { background: none; border: 0; padding: 0; font: inherit; font-weight: var(--font-weight-medium); }
.iw-linkbtn:focus-visible { outline: 0; box-shadow: 0 0 0 3px var(--color-focus-ring); border-radius: 4px; }
.iw-foot__back[hidden] { display: none; }
.iw-lede { margin: 0; color: var(--color-text-secondary); font-size: 14px; }
.iw-sub { font-size: var(--font-size-small); margin-top: var(--space-2); }
.iw-stat--sug { background: var(--color-blue-glow); color: var(--color-banner-info-text); }
.iw-stats--bar { margin-top: 0; }
.iw-errlist { margin: var(--space-2) 0 0; padding-left: var(--space-8); font-size: var(--font-size-small); }
.iw-errlist li + li { margin-top: var(--space-2); }

.iw-summary-bar { display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap; }
.iw-summary-bar .t { font-size: 14px; color: var(--color-text-secondary); }
.iw-summary-bar .t b { color: var(--color-text); }
.iw-filterbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); flex-wrap: wrap; }
.iw-ftabs { background: var(--color-bg-hover); border-radius: var(--radius-sm); padding: 3px; }
.iw-ftabs .tabs__item { padding: 5px var(--space-5); border-radius: 7px; }
.iw-filterbar .iw-linkbtn { font-size: var(--font-size-small); }

.iw-table { border: 1px solid var(--color-border); border-radius: var(--radius); flex-shrink: 0; }
.iw-tr {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr) minmax(0, 1.35fr) 92px;
  gap: var(--space-7); align-items: start; padding: 9px var(--space-7);
  border-top: 1px solid var(--color-border-light); font-size: var(--font-size-small); position: relative;
}
.iw-tr.th {
  align-items: center; border-top: 0; background: var(--color-bg-off); font-size: 11px; font-weight: var(--font-weight-semibold);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-text-dim); padding: var(--space-4) var(--space-7);
  border-radius: var(--radius) var(--radius) 0 0;
}
.iw-col b { font-weight: var(--font-weight-semibold); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.iw-col small { color: var(--color-text-dim); font-size: var(--font-size-caption); }
.iw-samples { color: var(--color-text-secondary); font-size: var(--font-size-caption); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.iw-tr.ign .iw-col b { color: var(--color-text-secondary); }
.iw-tr.sug { background: color-mix(in srgb, var(--color-blue-glow) 55%, transparent); }
.iw-tr.has-error { background: var(--color-red-glow); }
.iw-tr .act { display: flex; justify-content: flex-end; }
.iw-tr__dest { min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.iw-reason { font-size: var(--font-size-caption); color: var(--color-text-dim); }
.iw-reason--err { color: var(--color-red); }
.iw-reason--warn { color: var(--color-yellow-deep); display: flex; align-items: flex-start; gap: var(--space-2); }
.iw-reason--warn svg { flex: none; margin-top: 2px; }
.iw-ok { color: var(--color-green); display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-caption); font-weight: var(--font-weight-medium); }
.iw-group-row {
  padding: 9px var(--space-7); border-top: 1px solid var(--color-border-light); font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold); color: var(--color-text-secondary); background: var(--color-bg-off);
  display: flex; align-items: center; gap: var(--space-4);
}
.iw-group-row .sp { flex: 1; }
.iw-group-row__hint { font-weight: var(--font-weight-regular); }
.iw-empty-table { padding: var(--space-10); text-align: center; color: var(--color-text-secondary); font-size: var(--font-size-small); }

/* The Start step's summary: the list, the four chosen options and the "you can close this" note sit apart */
[data-iw="summary"]:not([hidden]) { display: flex; flex-direction: column; gap: var(--space-7); }

/* ----- Import summary popup (banner "View summary") ----- */
/* Head and Close stay put and only the body scrolls, so the box keeps its rounded corners and nothing nests a scrollbar */
.iw-summary-modal { overflow: hidden; }
.iw-summary-modal .modal__body { flex: 1; min-height: 0; overflow-y: auto; }
.iw-summary__loading, .iw-summary__note { color: var(--color-text-secondary); font-size: var(--font-size-small); margin: 0; }
.iw-summary__note { margin-top: var(--space-6); }
.iw-summary__stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-4); margin: 0;
}
.iw-summary__stats[hidden] { display: none; }
.iw-summary__stat { background: var(--color-bg-off); border-radius: var(--radius); padding: var(--space-5) var(--space-6); }
.iw-summary__stat dt { color: var(--color-text-secondary); font-size: var(--font-size-caption); font-weight: var(--font-weight-regular); margin: 0; }
.iw-summary__stat dd { margin: var(--space-1) 0 0; font-size: var(--font-size-h3); font-weight: var(--font-weight-bold); color: var(--color-text); }
.iw-summary__section { margin-top: var(--space-8); }
.iw-summary__section h3 {
  margin: 0 0 var(--space-3); font-size: 11px; font-weight: var(--font-weight-semibold);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-text-dim);
}
.iw-summary__list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--color-border); border-radius: var(--radius); }
.iw-summary__list li {
  display: flex; justify-content: space-between; gap: var(--space-6); padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--color-border-light); font-size: var(--font-size-small);
}
.iw-summary__list li:first-child { border-top: 0; }
.iw-summary__list li span:first-child { min-width: 0; overflow-wrap: anywhere; }
.iw-summary__list li span:last-child { color: var(--color-text-secondary); white-space: nowrap; }

/* The destination picker button: looks like the select it replaces. */
.iw-select {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; min-height: 34px; padding: 0 var(--space-5);
  background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  font: inherit; font-size: var(--font-size-small); color: var(--color-text); text-align: left; cursor: pointer;
}
.iw-select:hover { border-color: var(--color-border-hover); }
.iw-select:focus-visible, .iw-select[aria-expanded="true"] { outline: 0; border-color: var(--color-blue); box-shadow: 0 0 0 3px var(--color-focus-ring); }
.iw-select > svg { flex-shrink: 0; color: var(--color-text-dim); }
.iw-select > svg:last-child { margin-left: auto; }
.iw-select__grp { color: var(--color-text-dim); white-space: nowrap; }
.iw-select__val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.iw-select.is-suggested { border-color: var(--color-blue-pale); }
.iw-select.is-ignore { color: var(--color-text-secondary); background: var(--color-bg-off); }
.iw-select.is-static { cursor: default; }

/* Grouped destination menu: fixed so the modal body never clips it. */
.iw-menu {
  position: fixed; z-index: var(--z-popover); padding: var(--space-3); overflow-y: auto;
  font-size: var(--font-size-small); border-radius: var(--radius);
}
.iw-menu__search {
  display: flex; align-items: center; gap: var(--space-4); height: 34px; padding: 0 var(--space-5); margin-bottom: var(--space-2);
  border: 1px solid var(--color-border); border-radius: var(--radius-sm); color: var(--color-text-dim);
  position: sticky; top: 0; background: var(--color-bg);
}
.iw-menu__input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; color: var(--color-text); }
.iw-menu__search:focus-within { border-color: var(--color-blue); box-shadow: 0 0 0 3px var(--color-focus-ring); }
.iw-menu__list, .iw-menu__sub { list-style: none; margin: 0; padding: 0; }
.iw-menu__g, .iw-menu__i {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; border: 0; background: transparent; font: inherit;
  text-align: left; cursor: pointer; border-radius: 7px; color: var(--color-text);
}
.iw-menu__g { padding: var(--space-4) var(--space-5); font-weight: var(--font-weight-medium); }
.iw-menu__i { padding: 7px var(--space-5) 7px 34px; color: var(--color-text-secondary); }
.iw-menu__g > svg:first-child { color: var(--color-text-dim); flex-shrink: 0; }
.iw-menu__g:hover, .iw-menu__i:hover, .iw-menu__g:focus-visible, .iw-menu__i:focus-visible { background: var(--color-bg-hover); outline: 0; color: var(--color-text); }
.iw-menu__g:focus-visible, .iw-menu__i:focus-visible { box-shadow: inset 0 0 0 2px var(--color-focus-ring); }
.iw-menu__group.is-open > .iw-menu__g { background: var(--color-bg-hover); }
.iw-menu .c { margin-left: auto; color: var(--color-text-dim); font-size: var(--font-size-caption); font-weight: var(--font-weight-regular); display: flex; align-items: center; gap: var(--space-2); }
.iw-menu__cur { color: var(--color-blue) !important; }
.iw-menu .tag { margin-left: auto; font-size: 11px; font-weight: var(--font-weight-semibold); color: var(--color-blue); }
.iw-menu__i.create { color: var(--color-blue); font-weight: var(--font-weight-semibold); padding-left: var(--space-5); }
.iw-menu__i.create > svg { flex-shrink: 0; }
.iw-menu button:disabled { opacity: 0.55; cursor: not-allowed; }
.iw-menu__heading { padding: var(--space-3) var(--space-5) var(--space-1) 34px; font-size: 11px; font-weight: var(--font-weight-semibold); letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-text-dim); }
.iw-menu__none { padding: var(--space-6); color: var(--color-text-dim); }

/* Inline "Create custom field" form, directly under its row. */
.iw-create { border-top: 1px solid var(--color-border-light); background: var(--color-bg-off); padding: var(--space-7); display: flex; flex-direction: column; gap: var(--space-6); }
.iw-create__head { display: flex; align-items: baseline; gap: var(--space-4); flex-wrap: wrap; font-size: var(--font-size-small); }
.iw-create__grid { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: var(--space-6); }
.iw-create__grid .field { margin: 0; }
.iw-create__foot { display: flex; justify-content: flex-end; gap: var(--space-3); }
.iw-create__err { margin: 0; font-size: var(--font-size-caption); color: var(--color-red); }
.iw-create__err[hidden] { display: none; }

/* Review values */
.iw-cards { display: flex; flex-direction: column; gap: var(--space-8); }
.iw-card { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; flex-shrink: 0; }
.iw-card__head { padding: var(--space-6) var(--space-7); display: flex; align-items: center; gap: var(--space-5); background: var(--color-bg-off); }
.iw-card__titles { flex: 1; min-width: 0; }
.iw-card__head .t { font-weight: var(--font-weight-semibold); font-size: 14px; }
.iw-card__more { padding: var(--space-5) var(--space-7); border-top: 1px solid var(--color-border-light); font-size: var(--font-size-caption); color: var(--color-text-dim); }
.iw-vr { display: grid; grid-template-columns: minmax(0, 1fr) 80px minmax(0, 1.4fr); gap: var(--space-7); align-items: center; padding: 9px var(--space-7); border-top: 1px solid var(--color-border-light); font-size: var(--font-size-small); }
.iw-vr .n { color: var(--color-text-dim); font-size: var(--font-size-caption); text-align: right; }
.iw-vr b { font-weight: var(--font-weight-semibold); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.iw-vr__pick { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.iw-vr__pick .input { padding: 6px 32px 6px var(--space-5); font-size: var(--font-size-small); min-width: 0; flex: 1; }
.iw-vr__tag { font-size: 11px; font-weight: var(--font-weight-semibold); color: var(--color-blue); white-space: nowrap; }
.iw-vr__tag--warn { color: var(--color-yellow-deep); }

/* Summary */
.iw-list { display: flex; flex-direction: column; border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; }
.iw-li { display: flex; align-items: center; gap: var(--space-6); padding: 11px var(--space-7); border-top: 1px solid var(--color-border-light); font-size: 14px; }
.iw-li:first-child { border-top: 0; }
.iw-li > svg { color: var(--color-text-dim); flex-shrink: 0; }
.iw-li .sp { flex: 1; }
.iw-li .r { color: var(--color-text-secondary); font-size: var(--font-size-small); text-align: right; min-width: 0; }
.iw-kv { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-6); }
.iw-kv div { border: 1px solid var(--color-border-light); background: var(--color-bg-off); border-radius: 10px; padding: var(--space-5) var(--space-6); font-size: var(--font-size-small); font-weight: var(--font-weight-medium); min-width: 0; overflow-wrap: anywhere; }
.iw-kv small { display: block; font-size: var(--font-size-caption); color: var(--color-text-dim); font-weight: var(--font-weight-regular); margin-bottom: var(--space-1); }

/* Mapping rows become cards under 640px (design M2). */
@media (max-width: 640px) {
  .iw-tr { grid-template-columns: 1fr auto; gap: var(--space-4) var(--space-6); padding: var(--space-6); }
  .iw-tr.th { display: none; }
  .iw-table { border: 0; display: flex; flex-direction: column; gap: var(--space-5); overflow: visible; }
  .iw-tr:not(.th):not(.iw-tr--empty) { border: 1px solid var(--color-border); border-radius: var(--radius); }
  .iw-tr.sug { border-color: var(--color-blue-pale); }
  .iw-samples { grid-column: 1 / -1; grid-row: 2; }
  .iw-tr__dest { grid-column: 1 / -1; grid-row: 3; }
  .iw-tr .act { grid-column: 2; grid-row: 1; }
  .iw-group-row { border: 0; border-radius: var(--radius-sm); }
  .iw-create__grid { grid-template-columns: 1fr; }
  .iw-vr { grid-template-columns: 1fr auto; gap: var(--space-4); }
  .iw-vr__pick { grid-column: 1 / -1; }
  .iw-kv { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .iw-li { flex-wrap: wrap; }
  .iw-li .r { width: 100%; text-align: left; padding-left: 29px; }
  .iw-ftabs { max-width: 100%; overflow-x: auto; }
  .iw-ftabs .tabs__item { white-space: nowrap; }
  .iw-foot__note { display: none; }
  .iw-steps__t { display: none; }
  .iw-steps .is-on .iw-steps__t { display: inline; }
  .iw-menu { left: var(--space-4) !important; right: var(--space-4); width: auto !important; }
  .iw-foot__back { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .iw-live, .iw-result--info > svg[data-spin] { animation: none; }
  .iw-progress > i, .iw-toggle, .iw-toggle::after { transition: none; }
}

/* ----- Under 640px: the wizard is a full screen, one column ----- */
@media (max-width: 640px) {
  .modal__backdrop:has(> .iw-modal) { padding: 0; }
  .iw-modal { max-width: none; width: 100%; height: 100%; max-height: 100%; border-radius: 0; }
  .iw-head { flex-wrap: wrap; padding: var(--space-7) var(--space-8); gap: var(--space-5); }
  .iw-head .iw-steps { order: 3; width: 100%; }
  .iw-body { padding: var(--space-8); }
  .iw-foot { padding: var(--space-6) var(--space-8); border-radius: 0; }
  .iw-grid { grid-template-columns: 1fr; }
  .iw-drop { padding: var(--space-10) var(--space-8); }
  .iw-foot .btn, .iw-help__link { min-height: 44px; }
  .iw-banners { padding: 0 var(--space-8) var(--space-6); }
  .iw-banner { position: relative; flex-wrap: wrap; padding-right: 40px; }
  .iw-banner__actions { width: 100%; padding-left: 30px; align-self: auto; }
  .iw-banner .banner__close { position: absolute; top: var(--space-4); right: var(--space-4); }
}
