:root {
  color-scheme: light;
  --bg: #f6f7fb;
  --card: #ffffff;
  --line: #d9e0ea;
  --text: #1f2937;
  --muted: #64748b;
  --accent: #7c3aed;
  --accent-soft: #ede9fe;
  --ok: #15803d;
  --ok-soft: #dcfce7;
  --warn: #b45309;
  --warn-soft: #fef3c7;
  --bad: #b91c1c;
  --bad-soft: #fee2e2;
  font-family: "Segoe UI", Tahoma, Arial, sans-serif;
}
* { box-sizing: border-box; }
/* hidden обязан выигрывать у собственного display элемента: пустое состояние
   прячет .stats (у неё display: grid), и без этого правила она бы осталась. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
}
.page {
  max-width: 1600px;
  margin: 0 auto;
  padding: 28px 20px 40px;
}
.hero,
.filters,
.stats,
.panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: 0 18px 35px rgba(15, 23, 42, 0.08);
}
.hero {
  padding: 24px 28px;
  margin-bottom: 20px;
}
.hero h1 {
  margin: 0 0 8px;
  font-size: 30px;
  line-height: 1.1;
}
.hero p {
  margin: 0;
  color: var(--muted);
  max-width: 980px;
  line-height: 1.55;
}
/* Демо-режим виден постоянно, а не всплывашкой на секунду: страница на
   выдуманных данных не должна ни на минуту сойти за настоящий прогноз. */
.demo-badge {
  display: inline-block;
  margin-left: 12px;
  padding: 4px 10px;
  border: 1px dashed var(--accent);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-strong, #6d28d9);
  font-size: 13px;
  font-weight: 700;
  vertical-align: middle;
}
.filters {
  padding: 18px;
  margin-bottom: 20px;
}
.filters form {
  display: grid;
  grid-template-columns: 260px 320px minmax(0, 1fr);
  gap: 16px;
  align-items: end;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.field label {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}
.field input,
.field select {
  width: 100%;
  min-height: 46px;
  border-radius: 12px;
  border: 1px solid var(--line);
  padding: 0 13px;
  font-size: 15px;
  color: var(--text);
  background: #fff;
}
.actions {
  display: flex;
  gap: 12px;
  align-items: center;
  grid-column: 1 / -1;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1px solid transparent;
  text-decoration: none;
  font-weight: 700;
  cursor: pointer;
  font-size: 15px;
}
.btn-primary { background: var(--accent); color: #fff; }
.btn-secondary { background: transparent; border-color: var(--line); color: var(--text); }
.btn.is-disabled {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
}
.btn-link {
  color: var(--accent);
  font-weight: 700;
  text-decoration: none;
}
.btn-link:hover { text-decoration: underline; }
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 14px;
  padding: 16px;
  margin-bottom: 20px;
}
/* Плитки сводки — кнопки-фильтры: клик оставляет в таблице свой срез. */
.stat {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 16px;
  background: #fff;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.stat:hover { border-color: var(--accent); background: #fbfaff; }
.stat:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.stat.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* Незаполненное — не поломка, а штатное состояние наполовину собранного плана.
   Поэтому пунктирная рамка и глагол в подсказке вместо красного цвета:
   цифра здесь обычная, действие — очевидное. */
.stat.is-todo {
  border-style: dashed;
  border-color: var(--line-strong, #cfd5de);
}
.stat.is-todo:hover { border-color: var(--accent); }
/* Единственная плитка про уверенность, а не про полноту: подписанный
   преддоговор — та часть прогноза, за которую уже держится бумага. Цвет тот же
   --ok, что у полосы карточки и основания столбца: одно значение — один цвет по
   всей странице. */
.stat.is-sure { border-color: #bbf7d0; }
.stat.is-sure .stat-value { color: var(--ok); }
.stat.is-sure:hover { border-color: var(--ok); }
.stat.is-sure.is-active {
  border-color: var(--ok);
  background: var(--ok-soft);
}
.stat-label {
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 8px;
}
.stat-value {
  font-size: 30px;
  font-weight: 800;
  line-height: 1.1;
}
.stat-sub {
  margin-top: 6px;
  color: var(--muted);
  font-size: 12px;
}
.stat-todo {
  display: inline-block;
  margin-top: 8px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
}
.panel {
  padding: 0;
  overflow: hidden;
  margin-bottom: 20px;
}
.panel:last-child { margin-bottom: 0; }
.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line);
}
.panel-head h2 {
  margin: 0;
  font-size: 18px;
}
/* Слот легенды пока пуст: в нём поселится «факт по сделкам». Пустым он места
   не занимает, но и не даёт заголовку разъехаться, когда появится. */
.panel-legend {
  flex: 1 1 auto;
  display: flex;
  gap: 14px;
  align-items: center;
  color: var(--muted);
  font-size: 12px;
}
.meta {
  color: var(--muted);
  font-size: 13px;
  text-align: right;
  white-space: nowrap;
}
.error {
  display: none;
  margin-bottom: 16px;
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--bad-soft);
  color: var(--bad);
  border: 1px solid rgba(185, 28, 28, 0.18);
}

/* ===== календарь месяцев =====
   Визуальный язык взят у «Календаря доставки» (delivery-calendar.css): сетка на
   фоне --line с щелью в 1px вместо рамок, крупная клетка, в клетке — карточки
   заказов с заголовком, клиентом и чипом стадии. Разница одна и она
   принципиальная: клетка здесь — месяц, а не день. Срок в лиде месячной
   точности, и календарь по дням посадил бы все лиды на первые числа.
   Заливки по насыщенности тут нет намеренно: раз в клетке видно сами заказы,
   фон-хитмап только мешает их читать. «Сколько» отвечает цифра в шапке и
   тонкая полоска доли, а не цвет.
   Перетаскивание карточки между месяцами есть, и оформлено оно теми же
   классами, что в календаре доставки: .cal-item[draggable], .is-dragging,
   .cal-cell.is-drop-target и строка .toast под сеткой. */
.calendar { display: block; }
.cal-year { display: block; }
/* Год — метка раздела, а не второй заголовок: жирное в календаре имя месяца, и
   год того же веса спорил бы с ним. Итог года уходит вправо, в ту же колонку,
   где стоят суммы клеток, и читается вместе с ними. */
.cal-year-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 16px 20px 10px;
}
.cal-year + .cal-year .cal-year-head { padding-top: 22px; }
.cal-year-name {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.cal-year-total {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  margin-left: auto;
  color: var(--text);
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cal-year-pre { color: var(--ok); margin-left: 10px; }
/* В «Без срока» метка стоит сразу за заголовком, а не посреди пустоты: шапка
   там растянута между заголовком и кнопкой. */
.cal-noterm-head .cal-year-pre { margin-right: auto; font-size: 13px; }
.cal-year-pre i { font-style: normal; margin-right: 3px; }
/* Год — 12 клеток: 4 колонки на три ряда. Показываются все двенадцать,
   включая месяцы без лидов: дыра в календаре читается как ложь. */
.cal-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
/* Высоту держат только месяцы с заказами. Пустой месяц занимает строку имени и
   не больше: год без лидов раньше разворачивался в три ряда белых плит на пол-
   экрана — календарь превращался в стену пустоты. Ряд грида тянется по самой
   высокой клетке, поэтому ряд, где заказы есть, остаётся крупным. */
.cal-cell {
  position: relative;
  min-height: 0;
  padding: 8px;
  background: #fff;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cal-cell:not(.is-empty) { min-height: 150px; }
.cal-cell.is-empty { padding: 6px 8px; }
/* В пустой клетке полоска доли не нужна — выравнивать нечего, а серая черта под
   именем месяца читается как мусор. Держать шапки на одной высоте она обязана
   только там, где под ней есть карточки. */
.cal-cell.is-empty .cal-cell-share { display: none; }
.cal-cell.is-empty .cal-cell-head { padding-bottom: 0; }
/* Клик по пустому месту клетки — срез таблицы по месяцу. Курсор появляется
   только там, где клик действительно что-то делает. */
.cal-cell[data-cal-filter] { cursor: pointer; }
.cal-cell[data-cal-filter]:hover { background: #f7f8fa; }
/* Месяц без лидов — такая же белая клетка, как все: год читается как год,
   только когда двенадцать клеток равны. «Пусто» видно по отсутствию карточек,
   приглушаем одно имя месяца. Месяц, до которого выборка не дотянулась,
   выглядит так же — заглушки «вне горизонта» больше нет, а про несчитанные
   лиды говорит подсказка под графиком. */
.cal-cell.is-empty .cal-cell-name { color: var(--muted); }
/* Пока карточку тащат, месяц вне выборки гаснет: принять её он не может, и
   молчаливый отказ выглядел бы поломкой. В покое подсветки нет. */
.calendar.is-dragging .cal-cell.is-outside { opacity: 0.45; }
/* Прошедший месяц с висящими лидами: имя тем же --warn, что подпись просрочки
   в таблице. Класс ставился давно, а вида у него не было — слово несла только
   таблетка в шапке клетки. */
.cal-cell.is-past .cal-cell-name { color: var(--warn); }
.cal-cell.is-past .cal-cell-share i { background: var(--warn); }
/* «Сейчас» и «выбрано» — вопросы разные, поэтому и приёмы разные: текущий месяц
   держит рамку, выбранный ещё и залит. Прежние 2px против 3px и два оттенка
   фиолетового на глаз не различались. */
.cal-cell.is-current { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-cell.is-current .cal-cell-name { color: var(--accent-strong, #6d28d9); }
.cal-cell.is-active,
.cal-cell.is-active:hover {
  background: #faf9ff;
  box-shadow: inset 0 0 0 2px var(--accent-strong, #6d28d9);
}
.cal-cell.is-active .cal-cell-name,
.cal-cell.is-active .cal-cell-sum { color: var(--accent-strong, #6d28d9); }

/* Шапка клетки: месяц слева, сумма и число лидов справа — цифрой, а не цветом.
   Кнопкой она сделана ради клавиатуры: у клетки-дива фокуса нет, а срез
   таблицы должен ставиться и без мыши. */
.cal-cell-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 4px 8px;
  width: 100%;
  padding: 2px 2px 6px;
  border: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.cal-cell-head.is-plain { cursor: default; }
.cal-cell-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cal-cell-name {
  font-size: 14px;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-cell-tot {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  white-space: nowrap;
}
/* Сумма тише имени по весу: месяц отвечает «когда» и ведёт взгляд по сетке,
   сумма — уточнение. Двумя восьмисотыми они спорили. */
.cal-cell-sum {
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cal-cell-leads {
  font-size: 11px;
  color: var(--muted);
}
/* Подтверждённая преддоговорами часть месяца — прямо в шапке клетки, под
   общей суммой. Тем же зелёным, что и карточки: одно значение — один цвет по
   всей странице. Галочка отделена в <i>, чтобы не сливаться с цифрой. */
.cal-cell-pre {
  font-size: 11px;
  font-weight: 700;
  color: var(--ok);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cal-cell-pre i {
  font-style: normal;
  margin-right: 3px;
}
/* Прошедший месяц, где ещё висят лиды: маркер --warn и слово, а не цвет фона. */
.cal-flag {
  grid-column: 1 / -1;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* Таблетка должна быть по размеру слова, а не в ширину клетки: в гриде это
     justify-self, align-self растянутость по строке не снимает. */
  justify-self: start;
  padding: 1px 8px 1px 6px;
  border-radius: 999px;
  background: var(--warn-soft);
  color: var(--warn);
  font-size: 10.5px;
  font-weight: 700;
}
.cal-flag::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--warn);
}
/* Тонкая полоска доли месяца — всё, что осталось от хитмапа: она подсказывает
   вес месяца краем глаза и не лезет под карточки. Стоит в каждой клетке, в том
   числе пустой: заодно отбивает шапку от карточек и держит списки соседних
   клеток на одной высоте. */
.cal-cell-share {
  grid-column: 1 / -1;
  display: block;
  height: 3px;
  margin-top: 6px;
  border-radius: 999px;
  background: var(--track, #eceff3);
  overflow: hidden;
}
.cal-cell-share i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}
.cal-cell-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 0;
}
/* Карточка лида — тот же вид, что у карточки заказа в календаре доставки.
   Ссылка ведёт в карточку лида в Bitrix; срез таблицы она не трогает. */
.cal-item {
  display: block;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-left-color: var(--accent);
  border-radius: 8px;
  background: #fff;
  color: inherit;
  text-decoration: none;
  font-size: 12px;
  line-height: 1.35;
}
a.cal-item:hover { background: #faf9ff; border-color: var(--accent); }
a.cal-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Демо и лиды без ссылки: та же карточка, но не кликается. */
.cal-item.is-plain { border-left-color: var(--line-strong, #cfd5de); }
/* Лид с преддоговором. Одной цветной полосы слева оказалось мало — в клетке с
   десятком карточек её просто не замечали. Поэтому подтверждённая карточка
   целиком другая: светло-зелёная заливка, зелёная рамка, широкая полоса слева и
   сумма зелёным. Такие карточки к тому же стоят первыми в клетке (sortCards),
   так что вверху каждого месяца собирается зелёный блок подтверждённого.
   is-plain идёт раньше, поэтому у демо-карточек цвет тоже сохраняется. */
.cal-item.is-pre, .cal-item.is-plain.is-pre {
  border-color: #bbf7d0;
  border-left-width: 5px;
  border-left-color: var(--ok);
  background: #f2fbf5;
}
.cal-item.is-pre .cal-item-sum { color: var(--ok); }
a.cal-item.is-pre:hover { background: #e9f8ef; border-color: var(--ok); }
/* Галочка объясняет цвет тому, кто видит отчёт впервые: цвет без подписи —
   шифр. Стоит у суммы, потому что подтверждены здесь именно деньги месяца. */
.cal-item-pre {
  flex: 0 0 auto;
  margin-right: 1px;
  color: var(--ok);
  font-weight: 900;
  font-size: 13px;
  line-height: 1;
}
.cal-item-title {
  font-weight: 800;
  font-size: 12.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-item-client {
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-item-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-top: 3px;
}
.cal-item-sum {
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cal-item-stage {
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid #ddd6fe;
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--accent-strong, #6d28d9);
  font-size: 10.5px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-item .pill-add {
  border-color: var(--accent);
  color: var(--accent);
  padding: 2px 8px;
  font-size: 11px;
}
/* Перенос карточки в другой месяц. Курсор «схватить» появляется вместе с
   атрибутом draggable, то есть вместе с правом: обещать хватку там, где
   перенос всё равно не выйдет, — обман. */
.cal-item[draggable="true"] { cursor: grab; }
.cal-item.is-dragging { opacity: 0.4; cursor: grabbing; }
/* Единственная насыщенная заливка в календаре — «бросай сюда». Ховер клетки её
   не перебивает: во время переноса курсор всё ещё над клеткой. */
.cal-cell.is-drop-target,
.cal-cell.is-drop-target:hover,
.cal-noterm.is-drop-target {
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* Результат переноса: одна строка в подвале календаря. «Переношу…» приглушена,
   успех гаснет сам, ошибка остаётся красной, пока её не перекроет следующий
   перенос. */
.toast {
  margin: 14px 20px 16px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
}
.toast.is-ok { border-color: #bbf7d0; background: var(--ok-soft); color: var(--ok); }
.toast.is-error { border-color: #fecaca; background: var(--bad-soft); color: var(--bad); }
.toast.is-wait { color: var(--muted); }
/* «ещё N» — клетка разворачивается на месте, а не уводит на другую страницу. */
.cal-more {
  margin-top: auto;
  padding: 5px 8px;
  border: 1px dashed var(--line-strong, #cfd5de);
  border-radius: 8px;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.cal-more:hover { border-color: var(--accent); background: var(--accent-soft); }
.cal-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* «Без срока» — панель под сеткой, а не клетка: таких лидов бывает много и в
   клетку они не влезают. Это приглашение разложить деньги по месяцам. */
.cal-noterm {
  padding: 16px 20px 18px;
  border-bottom: 1px solid var(--line);
  background: #fbfcfe;
}
.cal-noterm.is-active { box-shadow: inset 0 0 0 2px var(--accent-strong, #6d28d9); }
.cal-noterm-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.cal-noterm-title {
  font-size: 15px;
  font-weight: 800;
}
.cal-noterm-pick {
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  color: var(--accent);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.cal-noterm-pick:hover { border-color: var(--accent); background: var(--accent-soft); }
.cal-noterm.is-active .cal-noterm-pick {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.cal-noterm-hint {
  margin: 6px 0 12px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
}
.cal-noterm-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}
.cal-noterm .cal-more { margin-top: 10px; }
/* Загрузка: клетки-призраки, без шиммера — как у столбцов. */
.calendar.is-loading .cal-cell { background: var(--track, #eceff3); }
.calendar.is-loading .cal-cell-head { cursor: default; }

/* ===== график: DOM + CSS, без библиотек =====
   Столбец — это <button>, поэтому клавиатура, фокус и подсказки работают сами.
   Геометрия держится тремя строками грида: подпись значения сверху, трек,
   подпись месяца снизу — иначе столбцы разъезжаются по базовой линии.
   Столбцы стоят под календарём и стали ниже: главный блок теперь календарь и
   отвечает «что и когда», столбцы — «сколько именно». */
.chart-body { padding: 16px 20px 14px; }
.chart-caption {
  margin-bottom: 10px;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 700;
}
.chart {
  --chart-cap: 20px;
  --chart-foot: 34px;
  --chart-h: 180px;
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  gap: 10px;
}
.chart-axis {
  position: relative;
  height: var(--chart-h);
}
.axis-tick {
  position: absolute;
  right: 0;
  bottom: calc(var(--chart-foot) + (var(--chart-h) - var(--chart-cap) - var(--chart-foot)) * var(--tick, 0));
  transform: translateY(50%);
  color: var(--muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.chart-plot {
  position: relative;
  height: var(--chart-h);
  display: flex;
  align-items: stretch;
  gap: 14px;
  min-width: 0;
}
.chart-grid {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--chart-cap);
  bottom: var(--chart-foot);
  pointer-events: none;
  border-bottom: 1px solid var(--line-strong, #cfd5de);
  background-image: repeating-linear-gradient(to top, var(--line) 0 1px, transparent 1px 25%);
}
.chart-scroll {
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
}
.chart-bars {
  display: flex;
  align-items: stretch;
  gap: 6px;
  height: 100%;
  min-width: 100%;
}
/* «Без срока» вынесен из области прокрутки: это приглашение разложить деньги
   по месяцам, и оно должно быть видно всегда, а не уезжать за край. */
.chart-aside {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  padding-left: 16px;
  border-left: 1px dashed var(--line-strong, #cfd5de);
}
/* Ширину столбца «без срока» задаём явно: контейнер сжат по содержимому, а
   столбец растягивается долей — вместе они дали бы нулевую ширину. */
.chart-aside .bar {
  flex: 0 0 64px;
  width: 64px;
}
.bar {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: grid;
  grid-template-rows: var(--chart-cap) 1fr var(--chart-foot);
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: center;
}
.bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bar-cap {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  line-height: var(--chart-cap);
  white-space: nowrap;
  overflow: hidden;
}
.bar-track {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: 0;
}
.bar-fill {
  width: 100%;
  max-width: 64px;
  height: var(--bar-h, 0%);
  background: var(--accent);
  border-radius: 4px 4px 0 0;
  transition: height 0.15s ease;
}
.bar-fill { position: relative; }
/* Подтверждённая преддоговорами часть суммы месяца — зелёное основание столбца.
   Именно основание: подписанное лежит в фундаменте прогноза, а надстройка над
   ним — то, что ещё может не случиться. */
.bar-sure {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: block;
  background: var(--ok);
  border-radius: 0 0 4px 4px;
  opacity: 0.85;
}
.bar:hover .bar-fill { filter: brightness(1.08); }
.bar-foot {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  gap: 1px;
  padding-top: 6px;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.15;
}
.bar-year {
  font-size: 10px;
  opacity: 0.85;
}
/* Состояния кодируются формой, а не цветом: цвет на графике один. */
.bar.is-current .bar-foot {
  color: var(--accent-strong, #6d28d9);
  font-weight: 800;
  text-decoration: underline;
}
/* Просроченный месяц с висящими лидами: срок прошёл, а метка осталась. */
.bar.is-past .bar-fill {
  background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.55) 0 4px, transparent 4px 9px);
}
.bar.is-past .bar-foot { color: var(--warn); font-weight: 700; }
/* Лиды есть, суммы нет — пунктирный след вместо столбца в ноль: ноль рублей и
   «сумму не проставили» это разные вещи. */
.bar.is-no-amount .bar-fill {
  height: 10px;
  background: transparent;
  background-image: none;
  border-top: 2px dashed var(--accent);
  border-radius: 0;
}
.bar.is-no-month .bar-fill {
  background: var(--accent-soft);
  background-image: none;
  border: 1px dashed var(--accent);
}
.bar.is-active .bar-fill { box-shadow: 0 0 0 2px var(--accent-strong, #6d28d9); }
.bar.is-active .bar-foot { color: var(--accent-strong, #6d28d9); font-weight: 800; }
.chart.is-dense .bar-cap { visibility: hidden; }
/* Годовой разделитель на каждом январе: на длинном ряду иначе не понять,
   где кончился один год и начался другой. */
.chart.is-dense .bar.is-year-start::before {
  content: "";
  position: absolute;
  left: -4px;
  top: var(--chart-cap);
  bottom: var(--chart-foot);
  border-left: 1px solid var(--line-strong, #cfd5de);
}
.chart.is-scroll .chart-bars .bar {
  flex: 0 0 44px;
  min-width: 44px;
}
/* Один-три месяца: столбец во всю ширину панели выглядит как заливка экрана,
   поэтому ряд прижимается влево и столбцы не расползаются. */
.chart.is-sparse .chart-bars { justify-content: flex-start; }
.chart.is-sparse .chart-bars .bar {
  flex: 0 1 92px;
  max-width: 92px;
}
/* Суммы не проставлены нигде: сетка и ось молчат — шкалы всё равно нет. */
.chart.is-flat .chart-grid { background-image: none; }
.chart.is-flat .chart-axis { visibility: hidden; }
/* Загрузка: призрачные столбцы фиксированных высот, без шиммера. */
.chart.is-loading .bar { cursor: default; }
.chart.is-loading .bar-fill {
  background: var(--track, #eceff3);
  background-image: none;
  border: 0;
}
.chart-note {
  margin: 12px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}

/* ===== таблица ===== */
.table-wrap { overflow: auto; max-height: 620px; }
table {
  width: 100%;
  border-collapse: collapse;
  min-width: 980px;
}
th,
td {
  padding: 13px 15px;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}
th {
  background: #f8fafc;
  color: #57708f;
  font-size: 12px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
th.sortable { padding: 0; }
th.sortable.is-sorted { color: var(--text); }
.sort-trigger {
  width: 100%;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 15px;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
}
.sort-trigger:hover,
.sort-trigger:focus-visible { color: var(--text); }
.sort-indicator {
  font-size: 11px;
  opacity: 0.9;
}
td a {
  color: var(--accent);
  text-decoration: none;
  font-weight: 700;
}
td a:hover { text-decoration: underline; }
.lead-id {
  margin-top: 4px;
  color: var(--muted);
  font-size: 12px;
}
.nowrap { white-space: nowrap; }
/* В таблице цвет полосы не работает — там метка словом. Сплошная заливка, а не
   пунктир как у .pill-add: это факт, а не приглашение что-то заполнить. */
.pill-sure {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  border: 1px solid #bbf7d0;
  border-radius: 999px;
  background: var(--ok-soft);
  color: var(--ok);
  font-size: 11px;
  font-weight: 700;
  vertical-align: middle;
  white-space: nowrap;
}
.amount { font-weight: 700; font-variant-numeric: tabular-nums; }
.muted {
  color: var(--muted);
  font-size: 12px;
}
.month-cell.is-past { color: var(--warn); font-weight: 700; }
.month-cell.is-current { color: var(--accent-strong, #6d28d9); font-weight: 700; }
/* Пустая ячейка не прочерк, а действие: править срок и сумму на странице нельзя
   (запись требует прав и разрешения конфликтов), а карточка лида решает ту же
   задачу в один клик. */
.pill-add {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border: 1px dashed var(--accent);
  border-radius: 999px;
  background: transparent;
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
td a.pill-add:hover { background: var(--accent-soft); text-decoration: none; }
span.pill-add { border-color: var(--line-strong, #cfd5de); color: var(--muted); }
.empty-row td,
.loading-row td {
  color: var(--muted);
  padding-top: 20px;
  padding-bottom: 20px;
}
.empty-panel .empty-inner {
  padding: 40px 28px;
  max-width: 720px;
}
.empty-panel h2 {
  margin: 0 0 10px;
  font-size: 22px;
}
.empty-panel p {
  margin: 0 0 20px;
  color: var(--muted);
  line-height: 1.6;
}
.empty-actions {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
/* Узкий экран: клетка не мельчает — мельчает сетка. Заказы в клетке должны
   читаться на любой ширине, поэтому колонок становится меньше, а высота клетки
   остаётся прежней. Календарь не превращается во что-то другое и не прячется.
   Раскладку 4×3 держим как можно дольше: именно она читается как год. */
@media (max-width: 1100px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .filters form { grid-template-columns: minmax(0, 1fr); }
  .chart { grid-template-columns: 56px minmax(0, 1fr); }
  .cal-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .cal-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .page { padding: 16px 12px 28px; }
  /* Год лентой: пустые месяцы остаются на месте — год без них соврал бы, — но
     занимают строку, а не экран. */
  .cal-grid { grid-template-columns: minmax(0, 1fr); }
  .cal-cell:not(.is-empty) { min-height: 120px; }
  .cal-year-head,
  .cal-noterm { padding-left: 14px; padding-right: 14px; }
  .chart-body { padding: 14px 14px 12px; }
  .cal-noterm-list { grid-template-columns: minmax(0, 1fr); }
}
