/* === DASHBOARD.CSS === */
/* callcenter/assets/css/dashboard.css */
/* НАЗНАЧЕНИЕ: эстетика "Пульт" - диспетчерская телефонной станции. Теплый уголь,
   янтарь накала как единственный акцент, voltage rail слева от данных.
   Плотность Bloomberg (10px), ноль теней, глубина через лестницу поверхностей. */
/* СВЯЗИ: index.php, login.php, assets/js/app.js */
/* РАЗМЕР: ~390 строк */

/* Имена --surface и --ink НЕ переименовывать: app.js:125-126 читает их
   через getComputedStyle для тем SweetAlert. Смена имени = невидимые тосты. */

:root {
  /* Лестница поверхностей - теплый уголь (hue 68), не сине-серый как вся ниша */
  --bg:         oklch(0.145 0.007 68);
  --surface:    oklch(0.185 0.008 68);
  --surface-2:  oklch(0.225 0.009 68);
  --surface-3:  oklch(0.265 0.010 68);

  /* Иерархия волосяных линий - глубина без теней */
  --line:       oklch(0.300 0.011 68);   /* строка */
  --line-2:     oklch(0.355 0.012 68);   /* группа */
  --line-3:     oklch(0.420 0.014 68);   /* активное */

  /* Лестница чернил - теплый off-white, не чистый белый */
  --ink:        oklch(0.945 0.008 85);
  --ink-2:      oklch(0.840 0.008 85);
  --muted:      oklch(0.630 0.010 78);
  --muted-2:    oklch(0.480 0.010 78);

  /* Единственный акцент: янтарь накала телефонной линии. Скупо:
     фокус, активная вкладка, primary. Никогда не заливка крупной поверхности. */
  --accent:     oklch(0.790 0.140 72);
  --accent-ink: oklch(0.180 0.030 72);   /* текст на янтаре */
  /* Янтарь В КАЧЕСТВЕ ТЕКСТА - отдельный токен. В темной теме это тот же цвет
     (8.62 на surface-2, AA держит), в светлой - темнее заливочного: заливка обязана
     остаться фирменной, а буква обязана читаться. Задача 6.4. */
  --accent-text: var(--accent);

  /* Семантика статусов - живет только в чипах и рельсах */
  --ok:         oklch(0.730 0.150 152);
  --warn:       oklch(0.800 0.130 85);
  --danger:     oklch(0.670 0.190 27);   /* 0.640 давал 4.15 на surface-3 (чипы, статусы) */
  /* Заливка тревожной кнопки диалога (Swal пишет ее подпись белым). Значение ОДНО на обе
     темы и намеренно темнее --danger: сам --danger в темной теме светлый (0.670), белая
     подпись на нем давала 3.27. Подобрано по ДВУМ порогам сразу: белая подпись 5.54 (AA
     текста, нужно 4.5) и сама заливка против фона окна диалога 3.37 в темной теме (граница
     элемента управления, нужно 3.0). Насыщенность 0.175, а не 0.190: на 0.190 цвет выходит
     за sRGB, подъем светлоты упирается в отсечение и границы кнопки не дает. 6.4-добор. */
  --danger-fill: oklch(0.540 0.175 27);
  --info:       oklch(0.700 0.110 232);  /* azure - свободный слот ниши */
  --accent-2:   var(--warn);             /* legacy-имя: старые правила ждут его */

  --rail:       var(--line-2);           /* цвет voltage rail по умолчанию */

  --radius:     12px;   /* карточки */
  --radius-sm:  8px;    /* кнопки, инпуты */
  --radius-xs:  4px;    /* чипы. Pill на CTA запрещен */

  --mono: 'Martian Mono', ui-monospace, monospace;
  --body: 'IBM Plex Sans', system-ui, sans-serif;

  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);

  /* Плотность Bloomberg - единая константа, не "начало отсчета" */
  --pad: 10px;
}

:root.light {
  /* Теплая бумага, а не белый лист */
  --bg:         oklch(0.965 0.008 85);
  --surface:    oklch(0.995 0.004 85);
  --surface-2:  oklch(0.935 0.010 85);
  --surface-3:  oklch(0.900 0.012 85);
  --line:       oklch(0.870 0.012 85);
  --line-2:     oklch(0.800 0.014 85);
  --line-3:     oklch(0.700 0.016 85);
  --ink:        oklch(0.220 0.015 60);
  --ink-2:      oklch(0.350 0.014 60);
  --muted:      oklch(0.500 0.012 70);
  --muted-2:    oklch(0.620 0.010 70);
  --accent:     oklch(0.620 0.145 62);
  /* Задача 6.4. Замеры до правки (WCAG, живьем в браузере): семантика светлой темы
     держалась 2.99-4.15 на --surface-3 (чипы, статусы очереди, баннер тест-режима),
     а белый --accent-ink на янтарной кнопке "Запустить обзвон" - 3.69. Светлота
     подобрана бинарным поиском по худшему фону каждого токена до >=4.5; тон и
     насыщенность (chroma, hue) не тронуты - палитра осталась та же, изменилась
     только глубина. Заливочный --accent НЕ трогаем, для буквы есть --accent-text. */
  --accent-text: oklch(0.505 0.145 62);
  --accent-ink: oklch(0.180 0.030 72);
  --ok:         oklch(0.435 0.140 152);
  --warn:       oklch(0.455 0.130 75);
  --danger:     oklch(0.505 0.190 27);
  --info:       oklch(0.450 0.110 232);
}

.no-transition, .no-transition * { transition: none !important; }

* { box-sizing: border-box; }

body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--body); font-size: 15.5px; line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* Табличные цифры - иначе колонка длительностей и телефонов пляшет по ширине.
   segp-/cmp-/st- (фронт B) шли мимо этого списка и плясали. */
.mono, .price, .val, .num, table td, .tabulator,
.segp-val, .segp-conf, .segp-chip, .cmp-prio, .cmp-meta, .cmp-qid,
.st-fn-val, .st-m-val, .st-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* ---- Шапка ---- */
.topbar {
  display: flex; align-items: center; gap: var(--pad);
  padding: var(--pad) 12px; background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
}
.topbar h1 {
  font-family: var(--mono); font-size: 15.5px; font-weight: 700; margin: 0;
  font-stretch: condensed; letter-spacing: -0.01em;
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
}
/* Точка накала: индикатор живой линии. Правило НЕ привязано к .topbar -
   раньше было .topbar h1 .dot, из-за чего на login.php (предок .login-card)
   точка не рендерилась и оставляла фантомный отступ. */
.dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ok) 18%, transparent);
  animation: pulse 2.4s var(--ease-out-quart) infinite;
  flex: none;
}
@keyframes pulse { 50% { opacity: 0.35; } }

.topbar .sub { font-family: var(--mono); font-size: 12px; color: var(--muted); }

.theme-btn, .logout-btn {
  background: var(--surface-2); color: var(--ink);
  border: none; box-shadow: 0 0 0 1px var(--line);  /* shadow-as-border: hover без сдвига layout */
  height: 28px; border-radius: var(--radius-sm); cursor: pointer;
  font: inherit; font-size: 14.5px;
  transition: box-shadow .15s var(--ease-out-quart), color .15s var(--ease-out-quart);
}
.theme-btn { width: 28px; display: grid; place-items: center; }
.theme-btn svg { width: 15px; height: 15px; }
.theme-btn:hover, .logout-btn:hover { box-shadow: 0 0 0 1px var(--line-3); }
.logout-btn { padding: 0 10px; color: var(--muted); }
.logout-btn:hover { color: var(--ink); }
.logout-form { display: inline-flex; margin: 0; }
.icon-sun { display: block; } .icon-moon { display: none; }
:root.light .icon-sun { display: none; } :root.light .icon-moon { display: block; }

/* ---- Вкладки ----
   nowrap + горизонтальный скролл: перенос ломал шапку на узком экране
   (вкладки уезжали в три строки и наезжали на логотип). */
.tabs {
  display: flex; gap: 2px; flex: 1; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  padding: 5px 7px; border-radius: var(--radius-sm); text-decoration: none;
  color: var(--muted); font-size: 13.5px; font-weight: 500; white-space: nowrap;
  font-family: var(--mono); font-stretch: condensed;
  transition: background .15s var(--ease-out-quart), color .15s var(--ease-out-quart);
}
.tabs a { flex: none; }
.tabs a:hover { background: var(--surface-2); color: var(--ink); }
/* Выделение = подъем на ступень поверхности, не цветная рамка */
.tabs a.active { background: var(--surface-3); color: var(--ink); font-weight: 600; }

/* Узкий экран: шапка в две строки - бренд с кнопками сверху, вкладки лентой снизу */
@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; }
  .tabs { order: 3; flex-basis: 100%; }
}

/* ---- Каркас ---- */
.wrap {
  max-width: 1680px; margin: 0 auto; padding: var(--pad);
  display: flex; flex-direction: column; gap: var(--pad);
}
.view { display: flex; flex-direction: column; gap: var(--pad); }
/* класс .view перебивает дефолтный [hidden]{display:none} (равная специфичность,
   но .view идет позже), из-за чего скрытые вкладки показывались друг под другом.
   Возвращаем скрытие явным правилом выше по специфичности. */
.view[hidden] { display: none; }

/* ---- KPI-плитки: voltage rail ----
   Цифра нейтральная, цвет живет только в рельсе слева. Инверсия приема,
   при котором метрика красится целиком - там цвет теряет значение. */
.metrics { display: flex; flex-wrap: wrap; gap: var(--pad); }
.metric {
  flex: 0 0 auto; min-width: 116px; white-space: nowrap;
  position: relative; overflow: visible;
  background: var(--surface); border-radius: var(--radius);
  /* rail слева через inset-тень, а не ::before: тень скругляется по border-radius
     (не торчит за угол) и не требует overflow:hidden - иначе он обрезал бы
     всплывающие подсказки, которые теперь живут в метках метрик. */
  box-shadow: 0 0 0 1px var(--line), inset 2px 0 0 var(--rail);
  padding: var(--pad) 12px var(--pad) 14px;
  display: flex; flex-direction: column; gap: 2px;
  transition: box-shadow .18s var(--ease-out-quart);
}
.metric:hover { box-shadow: 0 0 0 1px var(--line-3), inset 2px 0 0 var(--rail); }
.metric .val {
  font-family: var(--mono); font-size: 26.5px; font-weight: 700;
  font-stretch: condensed; line-height: 1.1; letter-spacing: -0.02em;
  color: var(--ink);
}
.metric .lbl {
  font-size: 12px; color: var(--muted); letter-spacing: 0.02em;
  display: inline-flex; align-items: center; gap: 3px;
}
.metric.accent { --rail: var(--ok); }
.metric.warn   { --rail: var(--accent); }
/* Правило обязано идти ПОСЛЕ .metric{flex:0 0 auto}, иначе базовое
   объявление перебивает его по каскаду и справа зияет дыра. */
@media (max-width: 720px) { .metric { flex: 1 0 auto; } }

/* ---- Секции ---- */
.section {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--line); padding: var(--pad);
}
.section h2 {
  font-family: var(--mono); font-size: 13px; font-stretch: condensed;
  letter-spacing: 0.02em; color: var(--muted); margin: 0 0 var(--pad);
  font-weight: 600;
}
/* Container query вместо media: секция реагирует на свою ширину, не на экран */
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--pad); container-type: inline-size; }
@container (max-width: 720px) { .cols { grid-template-columns: 1fr; } }
@media (max-width: 760px) { .cols { grid-template-columns: 1fr; } }

/* ---- Воронка ---- */
.funnel { display: flex; flex-direction: column; gap: 6px; }
.funnel-row { display: flex; align-items: center; gap: var(--pad); }
.funnel-row .stage {
  font-family: var(--mono); font-size: 12px; font-stretch: condensed;
  color: var(--muted); width: 78px; flex: none;
}
.funnel-bar {
  flex: 1; height: 18px; background: var(--surface-2);
  border-radius: var(--radius-xs); overflow: hidden; position: relative;
}
/* Заливка одноцветная: градиент на данных врет о значениях промежуточных точек */
.funnel-bar .fill {
  height: 100%; background: var(--accent);
  border-radius: var(--radius-xs);
  transition: width .5s var(--ease-out-expo);
}
.funnel-row .num {
  font-family: var(--mono); font-size: 14.5px; font-weight: 600;
  width: 48px; text-align: right; flex: none; white-space: nowrap;
}
/* Нулевое состояние: каркас ступеней виден контуром, а не пустота с текстом */
.funnel-empty { font-size: 14.5px; color: var(--muted); padding: 2px 0 var(--pad); }
.funnel-row.ghost .funnel-bar { box-shadow: inset 0 0 0 1px var(--line); background: transparent; }
/* Названия ступеней и нули - НЕ скелет, а данные (воронка на нуле). Поэтому --muted
   (AA), а "призрачность" держит пустой контур бара. --muted-2 тут давал 2.85/3.60. */
.funnel-row.ghost .num, .funnel-row.ghost .stage { color: var(--muted); }

/* ---- Таблицы: строка = только нижний hairline, без рамок ---- */
table { width: 100%; border-collapse: collapse; font-size: 15px; }
th {
  text-align: left; font-family: var(--mono); font-size: 12px; font-stretch: condensed;
  letter-spacing: 0.02em; color: var(--muted); padding: 5px 7px;
  border-bottom: 1px solid var(--line-2); font-weight: 600;
  position: sticky; top: 0; background: var(--surface);
}
td { padding: 6px 7px; border-bottom: 1px solid var(--line); }
tr:last-child td { border-bottom: none; }
tbody tr { transition: background .12s var(--ease-out-quart); }
tbody tr:hover { background: var(--surface-2); }
td.mono, .price { font-family: var(--mono); font-stretch: condensed; }
td.right { text-align: right; white-space: nowrap; }

/* ---- Чипы: фон @18%, бордер @35%, текст - сам цвет ---- */
.chip {
  display: inline-flex; align-items: center; padding: 1px 6px;
  border-radius: var(--radius-xs); font-size: 12px;
  font-family: var(--mono); font-stretch: condensed; text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* color-mix берет цвет из переменной - чипы адаптируются к светлой теме.
   Прежние rgba-литералы держали темный фон в обеих темах. */
.chip.ro {
  background: color-mix(in oklch, var(--info) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--info) 35%, transparent);
  color: var(--info);
}
.chip.ru {
  background: color-mix(in oklch, var(--accent) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent);
  color: var(--accent-text);
}
.chip.unknown {
  background: var(--surface-2);
  box-shadow: 0 0 0 1px var(--line); color: var(--muted);
}
/* статус обзвона на вкладке "Согласия": можно / нельзя звонить */
.chip.ok {
  background: color-mix(in oklch, var(--ok) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--ok) 35%, transparent);
  color: var(--ok);
}
.chip.danger {
  background: color-mix(in oklch, var(--danger) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--danger) 35%, transparent);
  color: var(--danger);
}
.seg { font-family: var(--mono); font-size: 13px; font-stretch: condensed; }
.seg.high { color: var(--ok); } .seg.mid { color: var(--info); }
.seg.low, .seg.unknown { color: var(--muted); }

/* ---- Пустые состояния с действием ---- */
.empty-hint { font-size: 14.5px; color: var(--muted); padding: 2px 0; }
.empty-cta {
  /* align-self обязателен: внутри .funnel (flex column) inline-flex
     растягивается на всю ширину и читается как сломанная кнопка */
  display: inline-flex; align-self: flex-start; align-items: center; margin-top: 6px;
  font-family: var(--mono); font-size: 13px; font-stretch: condensed;
  color: var(--accent-text); text-decoration: none;
  border-radius: var(--radius-xs); padding: 3px 7px;
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent);
  transition: background .15s var(--ease-out-quart);
}
.empty-cta:hover { background: color-mix(in oklch, var(--accent) 12%, transparent); }

/* ---- Формы ---- */
.sec-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--pad); margin-bottom: var(--pad); flex-wrap: wrap;
}
.sec-head h2 { margin: 0; }
/* Заголовок вкладки живет ВНЕ .section (рядом с переключателем периода), поэтому
   правило .section h2 его не ловит и он падает на дефолт браузера - 2em жирный sans
   посреди mono-системы. Задаем явно. */
.view > .sec-head h2 {
  font-family: var(--mono); font-stretch: condensed; font-size: 15.5px;
  font-weight: 700; letter-spacing: -0.01em; color: var(--ink);
  text-transform: none;
}
.actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

.inp {
  background: var(--bg); color: var(--ink);
  border: none; box-shadow: 0 0 0 1px var(--line);
  padding: 6px 8px; border-radius: var(--radius-sm);
  font: inherit; font-size: 15px; width: 100%;
  transition: box-shadow .15s var(--ease-out-quart);
}
.inp:hover { box-shadow: 0 0 0 1px var(--line-2); }
.inp:focus { outline: none; box-shadow: 0 0 0 2px var(--accent); }
.ta { font-family: var(--mono); font-size: 14px; resize: vertical; }
/* Поля в шапке секции не растягивать: width:100% из .inp распирал поиск
   на всю строку и выбрасывал кнопки на следующую (ломало пару "заголовок - действия").
   .inp{width:100%} нужен внутри форм и модалок, поэтому гасим точечно здесь. */
.sec-head .inp, .actions .inp { width: auto; min-width: 150px; }
#cl-search, #cn-search { min-width: 200px; }
#dnc-phone { min-width: 130px; }

.btn {
  background: var(--accent); color: var(--accent-ink); border: none;
  padding: 6px 11px; border-radius: var(--radius-sm);
  font: inherit; font-size: 14.5px; font-weight: 600;
  font-family: var(--mono); font-stretch: condensed;
  cursor: pointer; white-space: nowrap;
  transition: filter .15s var(--ease-out-quart);
}
.btn:hover { filter: brightness(1.08); }
.btn:active { filter: brightness(0.94); }
.btn.ghost {
  background: var(--surface-2); color: var(--ink);
  box-shadow: 0 0 0 1px var(--line);
}
.btn.ghost:hover { background: var(--surface-3); filter: none; }
.btn[disabled] { opacity: .5; cursor: default; filter: none; }
/* Стоп-кран обзвона: кнопка ОСТАНОВКИ красная, чтобы не путалась с обычным действием
   (класс снимается в settings.js, когда та же кнопка становится "Возобновить"). */
.btn.ks-stop { background: var(--danger); color: var(--surface); }
.lnk {
  background: none; border: none; color: var(--info); font: inherit; font-size: 14px;
  cursor: pointer; padding: 2px 4px; text-decoration: underline; text-underline-offset: 2px;
}
.lnk.danger { color: var(--danger); }

.msg {
  padding: 6px 9px; border-radius: var(--radius-sm); font-size: 15px;
  margin-bottom: var(--pad); white-space: pre-line;
}
.msg.ok { background: color-mix(in oklch, var(--ok) 14%, transparent); color: var(--ok); }
.msg.err { background: color-mix(in oklch, var(--danger) 14%, transparent); color: var(--danger); }
/* Плашка тест-режима: рельс слева как у KPI-плиток - предупреждение, а не ошибка.
   Стоит над метриками и говорит, что цифры получены заглушкой. */
.warn-banner {
  background: color-mix(in oklch, var(--warn) 12%, transparent);
  color: var(--warn); border-left: 2px solid var(--warn);
}
.pager {
  font-size: 13px; color: var(--muted); margin-top: var(--pad);
  display: flex; align-items: center; gap: var(--pad); flex-wrap: wrap;
}
/* Неактивная стрелка пагинации: 1.78/2.01 держались на паре --muted-2 + opacity .6.
   Тише активных ссылок она и без этого - те подчеркнуты и покрашены в --info. */
.pager .pg-off { color: var(--muted); }
.pager .lnk { padding: 2px 6px; }

/* ---- Модалки ---- */
.dlg {
  background: var(--surface); color: var(--ink);
  border: none; box-shadow: 0 0 0 1px var(--line-2);
  border-radius: var(--radius); padding: 22px; max-width: 580px; width: 94%;
}
.dlg::backdrop { background: color-mix(in oklch, var(--bg) 72%, transparent); }
.dlg h3 {
  margin: 0 0 var(--pad); font-size: 15.5px;
  font-family: var(--mono); font-stretch: condensed; font-weight: 700;
}
.dlg form { display: flex; flex-direction: column; gap: 14px; }
.dlg label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
/* .dlg label задает display: flex и перебивает дефолтный [hidden]{display:none} (специфичность
   выше), из-за чего скрытое через .hidden поле формы все равно показывалось - та же грабля,
   что с .view выше. Возвращаем скрытие явным правилом (нужно деталям продажи §9.6). */
.dlg label[hidden] { display: none; }
/* строка метки: текст + значок-подсказка в одну линию, поле - снизу */
.lbl-t { display: inline-flex; align-items: center; gap: 4px; }
.dlg .row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--pad); }
.dlg .chk { flex-direction: row; align-items: center; gap: 6px; cursor: pointer; }
.dlg .chk input { width: auto; }
.dlg .hint { font-size: 14px; color: var(--muted); margin: 0 0 2px; line-height: 1.4; }
/* подсказка под заголовком раздела (вкладка "Согласия") */
.hint { font-size: 14px; color: var(--muted); margin: 0 0 var(--pad); line-height: 1.45; }
.dlg-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 2px; }
/* Вкладка Голос без права manage_campaigns (класс ставит index.php): прячем ряд действий
   формы голоса - там #voice-sample (платный внешний TTS) и #voice-save, оба отдают 403.
   Настоящий запрет стоит на сервере (api/voice.php), это только чтобы не показывать
   заведомо неработающие кнопки. */
.view.vo-no-manage #voice-form .dlg-actions { display: none; }
/* Появление модалки: только при открытии, без бесконечной анимации */
.dlg[open] { animation: dlg-in .18s var(--ease-out-quart); }
@keyframes dlg-in { from { opacity: 0; transform: translateY(-4px); } }

/* ---- Редактор скриптов ---- */
.dlg.wide { max-width: 800px; max-height: 90vh; overflow-y: auto; }
/* fieldset рисует рамку через box-shadow (border:none). box-shadow НЕ разрывается
   под legend, как это делает настоящий border, поэтому текст легенды пересекала
   линия рамки (баг наложения). Чиним так: legend вынесен из потока flex
   (position:absolute у верхнего края) на непрозрачном фоне модалки - фон
   перекрывает линию под текстом, а padding-top у .fs освобождает под него место. */
.fs {
  position: relative;
  border: none; box-shadow: 0 0 0 1px var(--line); border-radius: var(--radius-sm);
  padding: 20px var(--pad) 12px; display: flex; flex-direction: column; gap: 12px; margin: 0;
}
.fs legend {
  position: absolute; top: -8px; left: 10px; margin: 0; white-space: nowrap;
  background: var(--surface); padding: 0 7px; max-width: calc(100% - 20px);
  overflow: hidden; text-overflow: ellipsis;
  font-size: 12px; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; font-family: var(--mono); font-stretch: condensed;
}
/* Цель звонка - единственный блок с янтарным контуром: это главное поле формы */
.fs.goal { box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 45%, var(--line)); }
.fs.goal legend { color: var(--accent-text); }
.row3 { display: grid; grid-template-columns: 1fr 1fr 84px; gap: 8px; }
@media (max-width: 560px) { .row3 { grid-template-columns: 1fr; } }

.sc-cards { display: flex; flex-direction: column; gap: 8px; }
.sc-card {
  position: relative; overflow: hidden;
  background: var(--surface-2); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 1px var(--line);
  padding: var(--pad) var(--pad) var(--pad) 12px;
  display: flex; flex-direction: column; gap: 5px;
  transition: box-shadow .15s var(--ease-out-quart);
}
/* Тот же рельс, что у KPI: активный скрипт горит янтарем, выключенный гаснет */
.sc-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--accent);
}
.sc-card:hover { box-shadow: 0 0 0 1px var(--line-3); }
.sc-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.sc-name { font-weight: 600; font-size: 15.5px; }
.sc-goal { font-size: 14.5px; color: var(--ink-2); }
.sc-goal b { color: var(--accent-text); font-weight: 600; }
.sc-meta { display: flex; gap: 5px; flex-wrap: wrap; font-size: 12px; color: var(--muted); }
.sc-meta span {
  background: var(--bg); padding: 1px 6px; border-radius: var(--radius-xs);
  font-family: var(--mono); font-stretch: condensed;
}
.sc-used { font-size: 12px; color: var(--ok); font-family: var(--mono); font-stretch: condensed; }
.sc-off { opacity: .5; }
.sc-off::before { background: var(--muted-2); }

/* ---- Лента звонков ----
   Атом продукта - один звонок, а не агрегат за месяц. Строка = звонок,
   день читается сверху вниз. Ни один сервис в нише не показывает день как ось. */
.feed { display: flex; flex-direction: column; }
.feed-row {
  display: flex; align-items: center; gap: var(--pad);
  padding: 5px 0; border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-stretch: condensed; font-size: 14px;
}
.feed-row:last-child { border-bottom: none; }
.feed-time { color: var(--muted); flex: none; width: 58px; }
.feed-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-family: var(--body); font-size: 15px; }
.feed-dur { color: var(--muted); flex: none; width: 42px; text-align: right; }
.feed-stage { flex: none; width: 104px; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feed-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--muted-2); }
.feed-dot.ok { background: var(--ok); }
.feed-dot.fail { background: var(--danger); }
/* Каркас до первого звонка: строки-призраки показывают, ЧТО тут появится */
.feed-row.ghost { opacity: .45; }
.feed-row.ghost .feed-time, .feed-row.ghost .feed-dur,
.feed-row.ghost .feed-stage { color: var(--muted-2); }
.feed-row.ghost .feed-name {
  height: 8px; background: var(--surface-2); border-radius: var(--radius-xs);
  max-width: 180px;
}

/* ---- День по часам ----
   Обзвон - процесс во времени. Столбик = час работы клуба. */
.hours { display: flex; align-items: stretch; gap: 3px; height: 72px; }
/* grid, а не flex-column: проценту высоты бара нужна строка известного размера.
   Во flex-колонке без высоты height:% схлопывался в ноль и график читался как сломанный. */
.hour {
  flex: 1; min-width: 0; display: grid;
  grid-template-rows: 1fr auto; gap: 3px; justify-items: center;
}
.hour-bar {
  width: 100%; align-self: end; background: var(--accent);
  border-radius: 2px 2px 0 0; min-height: 2px;
  transition: height .4s var(--ease-out-expo);
}
.hour-bar.empty {
  background: transparent; box-shadow: inset 0 0 0 1px var(--line);
  border-radius: 2px; opacity: .55;
}
/* Ось часов - подписи данных, а не декор: --muted-2 давал 2.85 dark / 3.60 light. */
.hour-lbl {
  font-family: var(--mono); font-stretch: condensed; font-size: 9.5px;
  color: var(--muted); white-space: nowrap;
}

/* ---- База знаний ----
   Реплики бота: тема + текст. Тот же рельс, что у KPI и скриптов -
   он читается как "эта строка данных живая". */
.kb-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--pad); }
.kb-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; margin-bottom: 6px; }

/* Форма настроек голоса */
.voice-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--pad); margin: var(--pad) 0;
}
.voice-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 14.5px; color: var(--muted); }
.voice-grid label.chk { flex-direction: row; align-items: center; gap: 6px; }
.voice-grid input[type="range"], .voice-grid input[type="checkbox"] { accent-color: var(--accent); }

/* Переключатель периода статистики */
.seg-toggle { display: inline-flex; gap: 2px; background: var(--surface-2); border-radius: var(--radius-sm); padding: 2px; }
.seg-btn {
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font-family: var(--mono); font-stretch: condensed; font-size: 14.5px;
  padding: 4px 10px; border-radius: var(--radius-xs);
}
.seg-btn.active { background: var(--surface-3); color: var(--ink); }
.kb-card {
  position: relative; overflow: hidden;
  background: var(--surface-2); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 1px var(--line);
  padding: var(--pad) var(--pad) var(--pad) 12px;
  display: flex; flex-direction: column; gap: 4px;
  transition: box-shadow .15s var(--ease-out-quart);
}
.kb-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--info);
}
.kb-card:hover { box-shadow: 0 0 0 1px var(--line-3); }
/* Тема и действия - в одну строку (горизонтальный layout по умолчанию) */
.kb-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; min-height: 18px;
}
/* Кнопки правки проявляются на hover: в спокойном состоянии карточка
   читается как факт, а не как форма */
.kb-head .lnk { opacity: 0; transition: opacity .12s var(--ease-out-quart); }
.kb-card:hover .kb-head .lnk, .kb-head .lnk:focus-visible { opacity: 1; }
.kb-topic {
  font-family: var(--mono); font-stretch: condensed; font-size: 13px;
  color: var(--info); text-transform: uppercase; letter-spacing: 0.04em;
}
.kb-text { margin: 0; font-size: 15px; color: var(--ink-2); line-height: 1.45; }
.kb-off { opacity: .5; }
.kb-off::before { background: var(--muted-2); }
.kb-badge {
  align-self: flex-start; font-family: var(--mono); font-stretch: condensed;
  font-size: 11px; color: var(--muted); text-transform: uppercase;
  padding: 1px 5px; border-radius: var(--radius-xs);
  box-shadow: 0 0 0 1px var(--line);
}

/* Ф2: governance базы знаний - фильтры, заголовок, ряд метаданных, статусная модель */
/* Переключатель авто-режима в шапке вкладки Знания: подпись и галочка в одну строку,
   рядом с кнопками (.chk сам по себе описан только внутри .dlg). Не переносим - иначе
   ряд действий шапки разъезжается на две строки. */
.kb-auto {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-size: 13px; color: var(--muted); white-space: nowrap;
}
.kb-auto input { width: auto; margin: 0; }
.kb-auto:hover { color: var(--ink-2); }

/* Фильтры над списком: компактные селекты в строку, как .actions в шапке секции */
.kb-filters { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: var(--pad); }
.kb-filters .inp { width: auto; min-width: 150px; }
/* Заголовок записи - главная строка карточки (тег категории живет в .kb-topic выше) */
.kb-name { font-size: 15px; font-weight: 600; color: var(--ink); line-height: 1.35; }
/* Ряд метаданных: бейджи статуса/языка + приглушенные подписи (проверено, окно, блок) */
.kb-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.kb-m {
  font-family: var(--mono); font-stretch: condensed; font-size: 11px;
  color: var(--muted); white-space: nowrap;
}
.kb-m.kb-warn { color: var(--warn); }
/* Запись НЕ уходит боту (не approved / без галочки / вне окна): рельс гаснет, текст
   приглушен. Бейджи в .kb-meta не трогаем - они и есть точный сигнал статуса. */
.kb-card.kb-dim::before { background: var(--muted-2); }
.kb-card.kb-dim .kb-name, .kb-card.kb-dim .kb-text { opacity: .55; }

/* Бейдж статуса pending_review - желтый. ok / danger / unknown / ru / ro у .chip уже есть. */
.chip.warn {
  background: color-mix(in oklch, var(--warn) 18%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--warn) 35%, transparent);
  color: var(--warn);
}

/* ---- Tabulator ---- */
.tabulator { background: transparent; border: none; font-family: var(--body); font-size: 15px; }
.tabulator .tabulator-header { background: var(--surface); border-bottom: 1px solid var(--line-2); }
.tabulator .tabulator-header .tabulator-col { background: var(--surface); border-right: none; }
.tabulator .tabulator-header .tabulator-col-title {
  font-family: var(--mono); font-stretch: condensed; font-size: 12px;
  letter-spacing: .02em; color: var(--muted); font-weight: 600;
}
.tabulator .tabulator-row { background: var(--surface); border-bottom: 1px solid var(--line); }
.tabulator .tabulator-row:hover { background: var(--surface-2); }
.tabulator .tabulator-row .tabulator-cell { border-right: none; padding: 6px 7px; }
/* Текст таблицы следует теме: CDN-тема tabulator_midnight жестко ставит белый на
   .tabulator .tabulator-tableholder .tabulator-table (0,3,0), невидимый в светлой теме.
   Повторяем ТОТ ЖЕ путь (равная специфичность) - dashboard.css грузится ПОСЛЕ midnight,
   поэтому побеждает. Текст на var(--ink), меняется с темой. */
.tabulator .tabulator-tableholder .tabulator-table,
.tabulator .tabulator-tableholder .tabulator-table .tabulator-row,
.tabulator .tabulator-tableholder .tabulator-table .tabulator-row .tabulator-cell { color: var(--ink); }
.tabulator .tabulator-footer { background: var(--surface); border-top: 1px solid var(--line); color: var(--muted); }
.tabulator .tabulator-footer .tabulator-page {
  background: var(--surface-2); border: none; box-shadow: 0 0 0 1px var(--line);
  color: var(--ink); border-radius: var(--radius-xs);
}
.tabulator .tabulator-footer .tabulator-page.active { background: var(--accent); color: var(--accent-ink); }
.tabulator input, .tabulator select {
  background: var(--bg); border: none; box-shadow: 0 0 0 1px var(--line);
  color: var(--ink); border-radius: var(--radius-xs);
}
/* Задача 6.4. Три протечки темы tabulator_midnight в светлую тему. Общее у них одно:
   правило CDN-темы бьет наши по специфичности, поэтому тут ПОВТОРЕН его путь один в один
   (dashboard.css грузится после midnight - при равной специфичности побеждаем мы).

   1) Фильтры шапки. midnight: (0,4,0) background:#444; color:#fff - поля "фильтр.." и
      селекты "все" оставались черными плашками на белой странице. Наше .tabulator input
      было (0,1,1) и не доставало. */
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input,
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter select {
  background: var(--bg); border: 1px solid var(--line); color: var(--ink);
}
/* 2) Футер: счетчик "Показано N из M" и подпись "Строк на странице" покрашены в #fff
      правилом (0,3,0) - на светлом футере это контраст 1.02, текст физически не виден. */
.tabulator .tabulator-footer .tabulator-page-counter,
.tabulator .tabulator-footer .tabulator-paginator label { color: var(--muted); }
/* 3) Заглушка пустой таблицы ("Клиентов нет..") - #ccc правилом (0,3,0), 1.61 в светлой. */
.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents {
  color: var(--muted);
}

.prev-table {
  max-height: 190px; overflow: auto; box-shadow: 0 0 0 1px var(--line);
  border-radius: var(--radius-sm); margin-top: 5px;
}
.prev-table table { font-size: 14px; }
.prev-table td { padding: 4px 6px; white-space: nowrap; }
.prev-head td { color: var(--muted); }
#imp-file { padding: 5px; margin-bottom: 3px; }

/* ---- Вход ---- */
.login-body { display: grid; place-items: center; min-height: 100vh; padding: 16px; }
.login-card {
  background: var(--surface); border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--line);
  padding: 30px 28px; width: 100%; max-width: 420px;
  display: flex; flex-direction: column; gap: 14px;
}
/* Строка бренда и переключатель RU/RO в одну линию - тот же порядок, что в шапке
   кабинета (логотип слева, переключатель справа). Задача 6.3. */
.login-top { display: flex; align-items: center; justify-content: space-between; gap: var(--pad); }
.login-card h1 {
  font-family: var(--mono); font-size: 15.5px; font-weight: 700; margin: 0;
  font-stretch: condensed; display: flex; align-items: center; gap: 8px;
  letter-spacing: -0.01em;
}
.login-sub { font-size: 14px; color: var(--muted); margin: -6px 0 2px; }
.login-card form { display: flex; flex-direction: column; gap: var(--pad); }
.login-card label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
.login-hint { font-size: 12.5px; color: var(--muted); margin: 2px 0 0; line-height: 1.4; }

/* ---- Диалоги SweetAlert: кнопка отказа ----
   Своя заливка кнопке "Отмена" не задается ни одним вызовом, поэтому она оставалась
   фирменно-серой от CDN (#6e7881), а белая подпись на ней давала 4.5001 - формально
   порог, фактически монета на ребре, и вдобавок чужой темам кабинета цвет. Делаем ее
   ghost-ом кабинета. Рамка НЕ волосяная (--line), а --muted: у ghost-заливки ступень
   против фона окна всего 1.3, и тогда единственным, что очерчивает кнопку, остается
   рамка - ей самой нужно 3.0 (WCAG 1.4.11). На --muted это 4.3 светлая / 5.1 темная.
   Специфичность (0,3,0) - правило Swal (0,2,0) слабее, порядок таблиц роли не играет.
   Заливка кнопки согласия не трогается: ее задает сам вызов, подпись доводит core.js. */
.swal2-popup .swal2-styled.swal2-cancel {
  background-color: var(--surface-3);
  color: var(--ink);
  box-shadow: 0 0 0 1px var(--muted);
}

/* ---- Подсказки при наведении (?) ----
   Значок-вопрос рядом с непонятным показателем. При наведении/фокусе всплывает
   пузырь с объяснением: что за метрика и как она считается.
   Сам пузырь (#tip-bubble) - ОДИН на страницу, position:fixed, живет в конце body
   и позиционируется из app.js. Так он выходит за overflow-контейнеры (модалка
   скрипта скроллится и обрезала бы обычный CSS-пузырь) и за края экрана. */
.tip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; flex: none; vertical-align: middle;
  border-radius: 50%; box-shadow: 0 0 0 1px var(--line-2);
  color: var(--muted); background: var(--surface-2);
  font-family: var(--body); font-size: 10px; font-weight: 700;
  font-stretch: normal; letter-spacing: 0; text-transform: none;
  cursor: help; user-select: none;
}
.tip:hover, .tip:focus-visible { color: var(--ink); box-shadow: 0 0 0 1px var(--accent); outline: none; }

#tip-bubble {
  position: fixed; z-index: 9999; max-width: 300px;
  background: var(--surface-3); color: var(--ink);
  box-shadow: 0 0 0 1px var(--line-2), 0 8px 24px color-mix(in oklch, var(--bg) 70%, transparent);
  border-radius: var(--radius-sm); padding: 9px 11px;
  font-family: var(--body); font-size: 12.5px; font-weight: 400; line-height: 1.5;
  text-align: left; letter-spacing: 0; text-transform: none; white-space: normal;
  pointer-events: none; opacity: 0;
  transition: opacity .15s var(--ease-out-quart);
}
#tip-bubble.show { opacity: 1; }
#tip-bubble[hidden] { display: none; }

/* ---- Переключатель языка кабинета (RU/RO) ----
   Компактный тумблер в шапке рядом с темой. Тот же язык, что seg-toggle статистики:
   подъем на ступень поверхности у активного, без цветной заливки. */
.cc-lang {
  display: inline-flex; gap: 2px; flex: none;
  background: var(--surface-2); border-radius: var(--radius-sm); padding: 2px;
}
.cc-lang button {
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font-family: var(--mono); font-stretch: condensed; font-size: 12.5px; font-weight: 600;
  letter-spacing: .04em; padding: 3px 8px; border-radius: var(--radius-xs);
  transition: background .15s var(--ease-out-quart), color .15s var(--ease-out-quart);
}
.cc-lang button:hover { color: var(--ink); }
.cc-lang button.active { background: var(--surface-3); color: var(--ink); }

/* ---- Карточка клиента (Customer 360) ----
   Двухколоночная сетка секций (плотность Bloomberg), гипотезы отличены от фактов. */
.cl-card-dlg { max-width: 760px; max-height: 90vh; overflow-y: auto; }
.cl-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--pad);
  margin-bottom: var(--pad);
}
.cl-card-head h3 {
  margin: 0; font-family: var(--mono); font-stretch: condensed; font-weight: 700;
  font-size: 16px; letter-spacing: -0.01em; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cl-card-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--pad); align-content: start;
}
@media (max-width: 560px) { .cl-card-grid { grid-template-columns: 1fr; } }
.cl-sec {
  background: var(--surface-2); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 1px var(--line); padding: var(--pad);
}
.cl-sec--wide { grid-column: 1 / -1; }
.cl-sec-t {
  margin: 0 0 6px; font-family: var(--mono); font-stretch: condensed; font-size: 12px;
  font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.cl-fields { display: flex; flex-direction: column; }
.cl-f {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--pad);
  padding: 4px 0; border-bottom: 1px solid var(--line); font-size: 14px;
}
.cl-f:last-child { border-bottom: none; }
.cl-f-k { color: var(--muted); flex: none; }
.cl-f-v { color: var(--ink); text-align: right; min-width: 0; overflow-wrap: anywhere; }
.cl-empty { margin: 2px 0; font-size: 14px; color: var(--muted); }

/* Факты: подпись + значение + бейдж достоверности. */
.cl-facts { display: flex; flex-direction: column; }
.cl-fact {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 0; border-bottom: 1px solid var(--line); font-size: 14px;
}
.cl-fact:last-child { border-bottom: none; }
.cl-fact-k { color: var(--ink-2); flex: 1; min-width: 0; }
.cl-fact-v {
  color: var(--ink); font-family: var(--mono); font-stretch: condensed;
  flex: none; text-align: right;
}
.cl-conf { color: var(--muted); font-family: var(--mono); font-size: 11px; flex: none; }
/* Гипотеза (§13.7): значение теряет чернильную яркость - визуально НЕ читается как
   подтвержденный факт. Общая opacity ряда убрана: вместе с --muted она давала 2.91
   light / 3.16 dark на значении, то есть саму гипотезу нельзя было прочитать. Сигнал
   "это не факт" держат курсив, приглушенный цвет и пунктирный бейдж (.cl-badge.hypo). */
.cl-fact.is-hypo .cl-fact-k { color: var(--muted); }
.cl-fact.is-hypo .cl-fact-v { color: var(--muted); font-style: italic; }

/* Бейдж достоверности. Факт - сплошной контур цвета, гипотеза - пунктир (иная форма). */
.cl-badge {
  flex: none; font-family: var(--mono); font-stretch: condensed;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
  padding: 1px 6px; border-radius: var(--radius-xs); white-space: nowrap;
}
.cl-badge.fact {
  color: var(--ok); background: color-mix(in oklch, var(--ok) 16%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--ok) 35%, transparent);
}
.cl-badge.stmt {
  color: var(--info); background: color-mix(in oklch, var(--info) 16%, transparent);
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--info) 35%, transparent);
}
.cl-badge.crm {
  color: var(--info); background: transparent;
  box-shadow: 0 0 0 1px color-mix(in oklch, var(--info) 35%, transparent);
}
.cl-badge.hypo {
  color: var(--muted); background: transparent;
  border: 1px dashed color-mix(in oklch, var(--muted) 55%, transparent);
  padding: 0 5px;
}

/* ---- Доступность ---- */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* === ФРОНТ B: сегментация (segp-) - профиль сегмента клиента (§22) === */
#view-segments .segp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--pad);
  margin-top: var(--pad);
}
/* Тот же voltage rail и shadow-as-border, что у .metric: цвет живет в рельсе слева,
   значение остается нейтральным. Рельс через inset-тень - скругляется по radius. */
.segp-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--pad) 12px var(--pad) 14px;
  background: var(--surface-2);
  box-shadow: 0 0 0 1px var(--line), inset 2px 0 0 var(--rail);
  border-radius: var(--radius-sm);
  transition: box-shadow .18s var(--ease-out-quart);
}
.segp-card:hover { box-shadow: 0 0 0 1px var(--line-3), inset 2px 0 0 var(--rail); }
/* Главная плитка профиля горит янтарем рельсом, а не текстом (:has вместо класса из JS) */
.segp-card:has(.segp-primary) { --rail: var(--accent); }
.segp-lbl { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
/* Юр. статус длиннее плитки (CALL_ALLOWED_WITH_CAUTION ~245px против 180px колонки):
   без обрезки он вылезал за карточку и его закрашивал сосед сверху. Полное значение -
   в подсказке title (app.js:segCard). */
.segp-val {
  font-family: var(--mono); font-stretch: condensed; font-size: 14px; color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.segp-val b { font-weight: 600; }
.segp-block { margin-top: 16px; }
.segp-block h3 {
  font-size: 12px; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; margin: 0 0 8px;
}
.segp-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.segp-chip {
  font-family: var(--mono); font-stretch: condensed; font-size: 12px;
  padding: 3px 8px;
  background: var(--surface-3);
  box-shadow: 0 0 0 1px var(--line-2);
  border-radius: var(--radius-xs);
  color: var(--ink-2);
}
/* Балл гипотезы - цифра, а не акцент: янтарь тут читался бы как чужой прием */
.segp-chip b { color: var(--ink); font-weight: 600; margin-left: 2px; }
.segp-muted { color: var(--muted); font-size: 12px; }
.segp-dims { width: 100%; border-collapse: collapse; font-size: 12px; }
.segp-dims td { padding: 5px 8px; border-bottom: 1px solid var(--line); }
.segp-dk { font-family: var(--mono); font-stretch: condensed; color: var(--muted); }
.segp-conf { text-align: right; color: var(--ink-2); font-family: var(--mono); font-stretch: condensed; width: 60px; }

/* === ФРОНТ B: кампании (cmp-) - Campaign Engine + очередь §21/§23 === */
#view-campaigns .cmp-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--pad);
}
/* Рельс как у .sc-card: живая кампания горит янтарем, выключенная гаснет */
.cmp-card {
  position: relative; padding: var(--pad) var(--pad) var(--pad) 12px;
  background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); border-radius: var(--radius-sm);
  --rail: var(--accent);
  transition: box-shadow .15s var(--ease-out-quart);
}
.cmp-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--rail); border-radius: var(--radius-xs) 0 0 var(--radius-xs);
}
.cmp-card:hover { box-shadow: 0 0 0 1px var(--line-3); }
.cmp-card.cmp-off { opacity: .55; --rail: var(--muted-2); }
.cmp-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cmp-name { font-weight: 600; color: var(--ink); font-size: 14px; }
/* Приоритет - цифра. Нейтральная ступень поверхности вместо заливки янтарем:
   акцент в этой карточке уже занят рельсом. */
.cmp-prio {
  font-family: var(--mono); font-stretch: condensed; font-size: 13px; font-weight: 700;
  color: var(--ink); background: var(--surface-3);
  padding: 1px 7px; border-radius: var(--radius-xs);
}
.cmp-meta { margin-top: 6px; font-family: var(--mono); font-stretch: condensed; font-size: 12px; color: var(--muted); }
.cmp-badge { display: inline-block; margin-top: 6px; font-size: 11px; color: var(--muted); }
.cmp-q { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.cmp-q th {
  text-align: left; padding: 6px 8px; color: var(--muted); font-weight: 500;
  border-bottom: 1px solid var(--line-2); text-transform: uppercase; font-size: 11px; letter-spacing: .03em;
}
.cmp-q td { padding: 5px 8px; border-bottom: 1px solid var(--line); }
.cmp-qid { font-family: var(--mono); font-stretch: condensed; color: var(--muted); }
.cmp-st {
  font-family: var(--mono); font-stretch: condensed; font-size: 11px; padding: 2px 7px;
  border-radius: var(--radius-xs); background: var(--surface-3); color: var(--ink-2);
}
.cmp-st-done, .cmp-st-completed { color: var(--ok); }
.cmp-st-no_answer, .cmp-st-voicemail, .cmp-st-failed, .cmp-st-busy { color: var(--warn); }
.cmp-st-scheduled, .cmp-st-ready, .cmp-st-callback_requested { color: var(--info); }
.cmp-st-blocked, .cmp-st-cancelled, .cmp-st-expired { color: var(--danger); }

/* === ФРОНТ B: аналитика Ф10 (st-) - воронка 13 / метрики / разрезы === */
.st-funnel { display: flex; flex-direction: column; gap: 3px; }
.st-fn-row { display: grid; grid-template-columns: 180px 1fr 120px; align-items: center; gap: 10px; }
.st-fn-label { font-size: 12.5px; color: var(--ink-2); }
.st-fn-bar { height: 16px; background: var(--surface-2); border-radius: var(--radius-xs); overflow: hidden; }
.st-fn-fill { display: block; height: 100%; background: var(--accent); opacity: .85; border-radius: var(--radius-xs); transition: width .3s var(--ease-out-quart); }
.st-fn-val { font-family: var(--mono); font-stretch: condensed; font-size: 12px; color: var(--muted); text-align: right; }
.st-fn-val b { color: var(--ink); font-weight: 600; }
/* Телефон: 180px метки + 120px значения не оставляли бару ширины (0px при 360px,
   30px при 390px - воронка на 13 стадий не читалась). Стадия и цифра - в строку,
   бар уходит под них на всю ширину. Правило обязано идти ПОСЛЕ .st-fn-row. */
@media (max-width: 560px) {
  .st-fn-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: 'label val' 'bar bar';
    gap: 3px 8px;
  }
  .st-fn-label { grid-area: label; }
  .st-fn-val { grid-area: val; }
  .st-fn-bar { grid-area: bar; }
}
.st-metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--pad); }
/* Плитка метрики = .metric с дашборда: рельс слева, значение нейтральное */
.st-metric {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--pad) 12px var(--pad) 14px; background: var(--surface-2);
  box-shadow: 0 0 0 1px var(--line), inset 2px 0 0 var(--rail);
  border-radius: var(--radius-sm);
  transition: box-shadow .18s var(--ease-out-quart);
}
.st-metric:hover { box-shadow: 0 0 0 1px var(--line-3), inset 2px 0 0 var(--rail); }
.st-m-lbl { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
.st-m-val { font-family: var(--mono); font-stretch: condensed; font-size: 18px; font-weight: 600; color: var(--ink); }
/* --muted-2 (0.480/0.620) давал контраст 2.6/3.0 на --surface-2 - ниже AA. Формула = мелкий
   текст (10.5px), нужен >=4.5. Взят готовый токен --muted (уже держит соседний .st-m-lbl на
   том же фоне: 4.89 dark / 4.97 light). --muted-2 не трогаем: на нем скелет ленты, и подъем
   его светлоты слил бы токен с --muted, убив двухуровневую иерархию приглушенности. */
.st-m-formula { font-size: 10.5px; color: var(--muted); }
.st-bd { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.st-bd th { text-align: left; padding: 6px 8px; color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--line-2); text-transform: uppercase; font-size: 11px; }
.st-bd td { padding: 5px 8px; border-bottom: 1px solid var(--line); }
.st-num { font-family: var(--mono); font-stretch: condensed; text-align: right; }

/* === ФРОНТ C: встречи (apt-) - список, карточка передачи, фидбек (§9/§10) === */
/* Строка списка - вход в карточку передачи. Телефон под именем приглушен. */
#view-appointments tbody tr { cursor: pointer; }
.apt-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.apt-dim { color: var(--muted); }
/* Метка "подтверждено симуляцией" (confirmation_is_stub) рядом со статусом - в списке и в
   карточке. Цвет и форма от .chip.warn: тот же янтарь, которым помечен тест-режим на дашборде,
   значит "цифра получена заглушкой" читается по всему кабинету одинаково. Тут только отбивка
   от статуса. */
.apt-stub { margin-left: 6px; vertical-align: middle; }

/* Статус встречи - чип. Цвет по жизненному циклу (как cmp-st у кампаний): база
   нейтральна, модификатор красит только текст. no_appointment/scheduled - нейтраль. */
.apt-st {
  display: inline-flex; align-items: center; padding: 1px 6px;
  border-radius: var(--radius-xs); font-family: var(--mono); font-stretch: condensed;
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  background: var(--surface-3); color: var(--ink-2); white-space: nowrap;
}
.apt-st-confirmed { color: var(--ok); }
.apt-st-scheduled { color: var(--info); }
.apt-st-message_sent, .apt-st-delivered { color: var(--warn); }
.apt-st-reschedule_requested { color: var(--accent-text); }
.apt-st-cancelled, .apt-st-no_response { color: var(--danger); }

/* Карточка передачи (dialog): приоритет-плашка, резюме, двухколоночная сетка секций. */
.apt-card-dlg { max-width: 820px; max-height: 90vh; overflow-y: auto; }
.apt-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--pad);
  margin-bottom: var(--pad);
}
.apt-card-head h3 {
  margin: 0; font-family: var(--mono); font-stretch: condensed; font-weight: 700;
  font-size: 16px; letter-spacing: -.01em; color: var(--ink);
}

/* Результат действия над карточкой. Карточка прокручивается (max-height: 90vh), а кнопки
   действий - в самом низу: сообщение у заголовка менеджер просто не увидит, поэтому липнет
   к верху. Фоны .msg полупрозрачны (color-mix с transparent), а под липким блоком едет текст
   карточки - те же проценты подмешиваем к фону модалки, чтобы блок был непрозрачным. */
#apt-card-msg { position: sticky; top: 0; z-index: 2; }
#apt-card-msg.ok  { background: color-mix(in oklch, var(--ok) 14%, var(--surface)); }
#apt-card-msg.err { background: color-mix(in oklch, var(--danger) 14%, var(--surface)); }
#apt-card-msg.warn-banner { background: color-mix(in oklch, var(--warn) 12%, var(--surface)); }
/* Исход "менять было нечего" (симуляция вернула confirmed:false): не успех и не ошибка -
   нейтральная плашка без цветового сигнала. */
#apt-card-msg.note { background: var(--surface-2); color: var(--ink-2); }

/* Приоритет передачи (§10.6) - voltage rail слева, цвет по срочности.
   URGENT=danger, HIGH=акцент(янтарь), NORMAL/LOW=нейтраль. Тот же прием, что у KPI-плиток. */
.apt-prio {
  --rail: var(--muted-2);
  display: flex; align-items: baseline; gap: 8px;
  padding: var(--pad) 12px var(--pad) 14px; margin-bottom: var(--pad);
  background: var(--surface-2); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 1px var(--line), inset 3px 0 0 var(--rail);
}
.apt-prio-k {
  font-family: var(--mono); font-stretch: condensed; font-size: 12px;
  text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.apt-prio-v {
  font-family: var(--mono); font-stretch: condensed; font-size: 15px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.apt-prio.urgent { --rail: var(--danger); } .apt-prio.urgent .apt-prio-v { color: var(--danger); }
.apt-prio.high   { --rail: var(--accent); } .apt-prio.high .apt-prio-v   { color: var(--accent-text); }
.apt-prio.normal, .apt-prio.low { --rail: var(--muted-2); }

/* Резюме передачи (§10.5) - маркированный список выжимки. */
.apt-summary {
  margin: 0 0 var(--pad); padding: var(--pad) var(--pad) var(--pad) 28px;
  background: var(--surface-2); border-radius: var(--radius-sm); box-shadow: 0 0 0 1px var(--line);
}
.apt-summary li { font-size: 14px; color: var(--ink-2); line-height: 1.5; margin-bottom: 2px; }
.apt-summary li:last-child { margin-bottom: 0; }

/* Сетка секций карточки - две колонки (плотность Bloomberg). */
.apt-card-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--pad); align-content: start;
}
@media (max-width: 560px) { .apt-card-grid { grid-template-columns: 1fr; } }
.apt-sec {
  background: var(--surface-2); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 1px var(--line); padding: var(--pad);
}
.apt-sec--wide { grid-column: 1 / -1; }
.apt-sec-t {
  margin: 0 0 6px; font-family: var(--mono); font-stretch: condensed; font-size: 12px;
  font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.apt-f {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--pad);
  padding: 4px 0; border-bottom: 1px solid var(--line); font-size: 14px;
}
.apt-f:last-child { border-bottom: none; }
.apt-f-k { color: var(--muted); flex: none; }
.apt-f-v { color: var(--ink); text-align: right; min-width: 0; overflow-wrap: anywhere; }

/* Ключевые факты (§13) - чипы. */
.apt-facts { display: flex; flex-wrap: wrap; gap: 6px; }
.apt-fact {
  font-family: var(--mono); font-stretch: condensed; font-size: 12px; padding: 3px 8px;
  background: var(--surface-3); border-radius: var(--radius-xs); color: var(--ink-2);
  box-shadow: 0 0 0 1px var(--line);
}

/* Отправка подтверждения (§12): канал + получатель + кнопка в строку. */
.apt-send { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; }
.apt-send label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
.apt-send .inp { width: auto; min-width: 130px; }
/* Пояснение к кнопке-заглушке: почему ответ клиента вообще можно симулировать. Тише соседних
   подписей - это сноска, а не действие. */
.apt-sim-note { margin: 6px 0 var(--pad); font-size: 13px; }
.apt-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: var(--pad); }
.apt-empty { font-size: 14px; color: var(--muted); margin: 2px 0; }

/* ==== НАСТРОЙКИ: учетки операторов (set-/usr-) - эстетика "Пульт" ==== */
/* Форма в строку (email/имя/роль/пароль/кнопка), как .apt-send: плотно, поля не тянутся. */
.set-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--pad); }
.set-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
.set-form .inp { width: auto; min-width: 190px; }
/* Инлайн-смена роли прямо в строке таблицы: селект не на всю ячейку. */
.usr-role-sel { width: auto; min-width: 150px; }
/* Дата создания - табличные цифры (Пульт: цифры выровнены по разрядам, приглушены). */
.usr-date { font-family: var(--mono); font-stretch: condensed; font-size: 13px; color: var(--muted); white-space: nowrap; }
/* Действия в строке - в один ряд, без переноса. */
.usr-acts { display: flex; gap: 10px; white-space: nowrap; }

