/* @boog-ui-start */
/* Slider UI Kit — primitives (CSS classes over tokens).
   Source of truth for states: frontend/design/kit.html
   Plan: docs/superpowers/plans/2026-07-03-slider-design-system.md */

/* ══ Reset: box-sizing ════════════════════════════ */
/* Без этого width:100% + padding переливается наружу, и каждый потребитель
   объявляет сброс сам (whiteboard: `#organizerRoot *{box-sizing:border-box}`).
   В ките это было залатано точечно у отдельных примитивов — как и [hidden] ниже.
   Ставим общий сброс: предсказуемая математика размеров для всех потребителей.
   Пойман на кабинете Организатора — счётчики в фильтрах вылезали на границу панели. */
*,*::before,*::after{box-sizing:border-box;}

/* ══ Reset: базовый текст документа ═══════════════ */
/* Шрифт, цвет текста и фон страницы задаёт кит, а не каждая страница отдельно.
   Раньше это было обязанностью потребителя, и цена ошибки не читалась глазами:
   элементы со СВОИМИ правилами шрифта (заголовки, кнопки, подписи) выглядели
   верно, а всё, что просто наследует от body, молча падало в Times. Так и было
   поймано — в макете панели ведущего текст диалога оказался с засечками, хотя
   вся страница вокруг выглядела правильно.
   Потребитель, которому нужен другой фон или цвет, переопределяет у себя: одно
   правило по элементу, любой класс или id перебивает его без !important. */
body{font-family:var(--font-body);color:var(--color-text);background:var(--color-bg);}

/* ══ Reset: hidden ════════════════════════════════ */
/* Почти сотня примитивов ниже задаёт явный display (flex/grid/inline-flex).
   Селектор [hidden] по специфичности равен классу, поэтому без этого правила
   `<div class="empty" hidden>` остаётся ВИДИМЫМ — атрибут молча проигрывает классу.
   На это уже наступали и латали точечно (.dialog-overlay[hidden], .inline-edit__*[hidden]);
   здесь общий случай, чтобы hidden работал у любого примитива и у потребителей кита.
   !important — сознательно: «скрыт» не должен зависеть от порядка подключения стилей. */
[hidden]{display:none !important;}

/* ══ Buttons ══════════════════════════════════════ */
/* @kit Кнопка
   @group Действия
   @about Запускает действие. Подпись — всегда глагол: что произойдёт по нажатию.
   @do Одно основное действие на экран, остальные — вторичные
   @do Подпись глаголом: «Сохранить», а не «ОК»
   @do Вторая ДОСТУПНАЯ кнопка в паре — --outline, а не --secondary
   @dont Переход на другой экран — это ссылка, а не кнопка
   @dont Два основных рядом: они конкурируют за внимание
   @dont Серый контур --secondary у доступного действия рядом с другой кнопкой: читается как отключённое
   @mod --primary Главное действие экрана
   @mod --secondary Равнозначное или отменяющее действие
   @mod --outline Вторая доступная кнопка в паре: контур и подпись акцентом
   @mod --ghost Третьестепенное, в плотных местах вроде тулбара
   @mod --danger Необратимое: удаление, отмена подписки
   @mod --sm Уменьшенная — для тулбаров и карточек
   @mod --block На всю ширину контейнера, подпись и иконка по центру — подвал модалки, форма входа
   @state :disabled Действие сейчас невозможно
   @state .is-loading Действие выполняется: кнопка сама крутит значок ожидания,
     повтор заблокирован. Своего значка в разметку не добавлять
   @dont Гасить кнопку на время работы как отключённую: «занято» и «недоступно»
     - разные сообщения
   @sample <button class="btn btn--primary">Сохранить</button> */
/* justify-content:center - на однострочном содержимом обычной ширины не видно
   (кнопка и так обтягивает подпись), но без него растянутая на всю ширину
   кнопка (родитель с align-items:stretch или явный width:100%) прижимает
   подпись к левому краю - выглядит недоверстанным, хотя класс тот же самый. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  font:600 var(--text-md)/1 var(--font-body);padding:var(--space-3) var(--space-4);
  border:1px solid transparent;border-radius:var(--radius-md);cursor:pointer;text-decoration:none;
  transition:background var(--motion-fast) var(--ease-standard),box-shadow var(--motion-fast) var(--ease-standard),border-color var(--motion-fast) var(--ease-standard);}
.btn:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.btn:disabled{opacity:.5;pointer-events:none;}
/* «Идёт работа» и «действие недоступно» - разные сообщения, и выглядеть должны
   по-разному. Выцветшая наполовину кнопка читается как отключённая: человек
   думает, что промахнулся, и жмёт снова. Поэтому у кнопки в работе цвет почти
   нетронутый, повтор заблокирован, а вместо слов крутится значок - единственное,
   что честно показывает «сейчас идёт».
   Значок рисуется самой кнопкой, своего элемента в разметке не нужно, и берёт
   цвет подписи: на синей главной он белый, на светлой вторичной - тёмный.
   Подпись НЕ приглушается ничем: измерение показало, что даже лёгкое
   приглушение роняет её читаемость с 4.56 до 3.89 при норме 4.5. Работающая
   кнопка отличается от обычной движением значка, а не выцветанием. */
/* Кнопку на время работы правильно ещё и запирать признаком «недоступна»:
   иначе её жмут клавишей, минуя запрет мышью. Но тогда она выцветает, и снова
   читается как отключённая. Поэтому работа побеждает: цвет остаётся полным. */
.btn.is-loading,.btn.is-loading:disabled{opacity:1;pointer-events:none;cursor:progress;}
.btn.is-loading::after{content:"";width:var(--space-4);height:var(--space-4);flex:none;
  border-radius:var(--radius-pill);
  border:var(--border-thick) solid color-mix(in srgb, currentColor 35%, transparent);
  border-top-color:currentColor;animation:spin .6s linear infinite;}
/* Просьба системы не двигать картинку сильнее слов: значок замирает, но
   кнопка всё равно читается как занятая - по приглушённой подписи и тому,
   что она не отвечает на нажатие. */
@media (prefers-reduced-motion:reduce){.btn.is-loading::after{animation:none;}}
.btn--primary{background:var(--color-accent);color:var(--color-on-accent);}
.btn--primary:hover{background:var(--color-accent-hover);}
.btn--primary:active{background:var(--color-accent-active);}
.btn--secondary{background:var(--color-surface);color:var(--color-text);border-color:var(--color-border);}
.btn--secondary:hover{background:var(--color-surface-2);}
/* Компаньон главной кнопки: контур и подпись акцентом. Нужен там, где рядом
   стоят два действия одного предмета — «Редактировать» рядом с «Отправить».
   Серый контур --secondary в такой паре читается как отключённая кнопка, а
   вторая заливка акцентом спорила бы с главной за внимание.

   ПРАВИЛО ПАРЫ (решение PO 2026-08-13). Когда у предмета две кнопки, вторая
   ДОСТУПНАЯ обязана быть --outline. Глазами разницы между «серая, потому что
   вторична» и «серая, потому что отключена» нет — человек видит два тусклых
   прямоугольника и не понимает, что из этого можно нажать. Отключённая кнопка
   остаётся --secondary с :disabled, доступная берёт акцентный контур, и пара
   читается сразу. Поймано на панели ведущего: рядом стояли неактивный «Перейти»
   и вполне рабочий «Закрыть доступ», и отличить их было нельзя. */
.btn--outline{background:var(--color-surface);color:var(--color-accent-active);border-color:var(--color-accent);}
.btn--outline:hover{background:var(--color-accent-weak);}
.btn--outline:active{border-color:var(--color-accent-active);}
.btn--ghost{background:transparent;color:var(--color-text);}
.btn--ghost:hover{background:var(--state-hover);}
.btn--danger{background:var(--color-danger);color:var(--color-on-accent);}
.btn--danger:hover{filter:brightness(.95);}
.btn--sm{padding:var(--space-2) var(--space-3);font-size:var(--text-sm);}
/* @mod --lg Экран входа/регистрации: одна главная кнопка на весь блок формы,
   ей нужен рост крупнее рабочего экрана. */
.btn--lg{height:var(--space-12);font:700 var(--text-lg)/1 var(--font-body);}
/* @mod --square Кнопка под один значок без подписи: ширина равна высоте.
   @sample <button class="btn btn--primary btn--square"><span class="icon i-plus"></span></button> */
.btn--square{width:var(--space-10);padding:0;justify-content:center;flex:none;}
/* Значок внутри кнопки — размер по умолчанию токена --size-icon (18px),
   отдельно не переопределяем: раньше здесь дублировалось то же число вручную. */
/* На всю ширину контейнера: одиночная кнопка подвала модалки, форма входа.
   .btn — inline-flex с align-items:center, но без justify-content — при
   растяжении на всю ширину (родитель-flex со stretch, как .modal__footer)
   подпись с иконкой съезжали к левому краю вместо центра. */
.btn--block{width:100%;justify-content:center;}

/* ══ Icon button (toolbar) ════════════════════════ */
/* @kit Кнопка-иконка
   @group Действия
   @about Действие без подписи. Экономит место там, где символ понятен без слов.
   @do Обязательна всплывающая подсказка — иначе смысл угадывают
   @do Для состояния «включено» — атрибут нажатости, а не другой цвет вручную
   @dont Редкое или опасное действие: без подписи в него не попадут осознанно
   @mod --sm Уменьшенная — для плотных тулбаров
   @state [aria-pressed="true"] Режим включён (перо, сетка)
   @state :disabled Действие сейчас невозможно
   @sample <button class="icon-btn" data-tooltip="Отменить"><span class="icon i-undo-2"></span></button> */
/* padding:0 обязателен: примитив задает себе точный размер шириной и
   высотой, а внутренний отступ - нет. Страница-потребитель со своим общим
   правилом для button (а такие есть) навязывала кнопке отступ, и знак
   переставал стоять по центру. Ловилось это только на чужих экранах, куда
   кнопка попадает в составе общего блока. */
.icon-btn{display:inline-grid;place-items:center;padding:0;width:var(--space-8);height:var(--space-8);
  border:1px solid transparent;border-radius:var(--radius-md);background:transparent;color:var(--color-muted);
  cursor:pointer;transition:background var(--motion-fast) var(--ease-standard),color var(--motion-fast) var(--ease-standard);}
.icon-btn:hover{background:var(--state-hover);color:var(--color-text);}
.icon-btn .icon{--size-icon:var(--space-4);}
.icon-btn:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.icon-btn[aria-pressed="true"]{background:var(--color-accent-weak);color:var(--color-accent);}
.icon-btn:disabled{opacity:.4;pointer-events:none;}
.icon-btn--sm{width:var(--space-6);height:var(--space-6);}
/* @kit Кнопка-иконка под палец
   @group Действия
   @about Та же кнопка-иконка, но с областью касания не меньше принятого
     минимума цели касания - там, где человек попадает по кнопке пальцем не
     глядя и решает за секунды (окно входящего вызова, полноэкранное
     управление разговором, мобильная раскладка).
   @do Применять в окнах решения, всплывающих поверх страницы, и в
     раскладках для касания
   @do Держать между такими кнопками зазор не меньше --space-2 - иначе
     крупные цели снова становятся неразличимы пальцем
   @dont Ставить в плотные панели редактора и тулбары рядом с мышью - там
     обычная величина уместнее и экономит место
   @dont Увеличивать вместе с кнопкой саму иконку: растёт область касания,
     знак остаётся прежним
   @sample <button class="icon-btn icon-btn--touch" data-tooltip="Принять"><span class="icon i-phone"></span></button> */
.icon-btn--touch{width:var(--size-touch);height:var(--size-touch);}
.icon-btn svg{width:var(--size-icon);height:var(--size-icon);}

/* ══ Forms ════════════════════════════════════════ */
/* @kit Поле ввода
   @group Ввод
   @about Пользователь сообщает системе данные словами или числом.
   @do Подпись над полем, а не подсказка внутри: подсказка исчезает при вводе
   @do Ошибка объясняет, что сделать, а не только факт «неверно»
   @dont Выбор из двух состояний — это переключатель
   @dont Выбор из готового списка — это выпадающий список
   @state [aria-invalid="true"] Значение не принято
   @state :disabled Поле сейчас недоступно
   @state .is-filtering Поле сейчас сужает список — выбрано непустое значение
   @sample <label class="field"><span class="field__label">Название</span><input class="field__control" /></label> */
.field{display:flex;flex-direction:column;gap:var(--space-1);}
.field__label{font:600 var(--text-sm)/1 var(--font-body);color:var(--color-text);}
.field__control{font:var(--text-md)/1.4 var(--font-body);color:var(--color-text);
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);
  padding:var(--space-2) var(--space-3);
  transition:border-color var(--motion-fast) var(--ease-standard),box-shadow var(--motion-fast) var(--ease-standard);}
.field__control:hover{border-color:var(--slate-300);}
.field__control:focus{outline:none;border-color:var(--color-accent);box-shadow:var(--focus-ring);}
.field__control[aria-invalid="true"]{border-color:var(--color-danger);}
.field__control:disabled{background:var(--color-surface-2);color:var(--color-muted);cursor:not-allowed;}
/* Отбор включён: поле сузило список, и это видно, не читая его значение.
   Иначе «сезон Весна» и «Все сезоны» выглядят одинаково, и человек не понимает,
   почему список короче ожидаемого — ищет пропажу в данных, а не в отборе.
   Акцентный контур, а не заливка: заливка спорит с фокусом и с выделением
   строки, а контур сообщает состояние, не перетягивая внимание.
   Класс ставится, только когда выбрано НЕПУСТОЕ значение: «Все сезоны» ничего
   не сужает, и подсвечивать там нечего. */
.field__control.is-filtering{border-color:var(--color-accent);}
.field__msg{font:var(--text-xs)/1.3 var(--font-body);color:var(--color-muted);}
.field__msg.is-error{color:var(--color-danger);}
select.field__control{appearance:none;padding-right:var(--space-8);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right var(--space-3) center;}

/* ══ Surfaces ═════════════════════════════════════ */
/* @kit Панель
   @group Поверхности
   @about Плоскость, на которой живёт содержимое: свойства объекта, список, форма.
   @do Заголовок называет, чем управляет панель
   @dont Вкладывать панель в панель — глубина перестаёт читаться
   @sample <div class="panel"><div class="panel__head">Свойства</div><div class="panel__body">…</div></div> */
.panel{background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--elev-2);overflow:hidden;}
.panel__header{display:flex;align-items:center;justify-content:space-between;
  padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--color-border);
  font:700 var(--text-md)/1 var(--font-display);}
.panel__body{padding:var(--space-4);}

/* ══ Feedback: chip / badge ═══════════════════════ */
/* @kit Чип
   @group Метки и статусы
   @about Короткая метка объекта: категория («Лидерство», «Метрики») или состояние
     («Черновик», «Оплачено»). Читается, но не нажимается.
   @do Одно-два слова, существительное для категории и причастие для состояния
   @do Цвет по смыслу, а не для украшения
   @dont Как кнопка: чип не запускает действие
   @dont Цветной чип на каждом объекте — цвет перестаёт значить что-либо
   @dont Красить компетенцию статусной ролью («успех», «ошибка») — она начнёт
     читаться как оценка; для компетенции есть цвет кластера, см. раздел «Кластеры»
   @mod --accent Принадлежность к текущему выбору
   @mod --success Успешное завершение
   @mod --warning Требует внимания
   @mod --danger Ошибка или блокировка
   @mod --purple Особая категория вне шкалы статусов
   @mod --outline ВТОРОЙ ВАРИАНТ АКТИВНОЙ МЕТКИ, предпочтительный. Белый фон
     поверхности, контур и подпись акцентом. Брать его для состояния «активно»
     («Активна», «активен», «Действует») везде, где зеленая заливка выбивается из
     общего стиля экрана: зеленый означает УСПЕШНОЕ ЗАВЕРШЕНИЕ, а рабочее состояние
     завершением не является, и статусный цвет под такую роль занимать нельзя.
     Тот же вариант - для рядов однотипных строк, где заливка каждой метки
     превращает список в пестрое полотно, а состояние все равно надо назвать словом.
     Решение PO 2026-08-19.
   @state [data-cluster] Компетенция: красится цветом своего кластера 1–9
   @sample <span class="chip chip--success">Оплачено</span> */
.chip{display:inline-flex;align-items:center;gap:var(--space-1);
  font:600 var(--text-xs)/1 var(--font-body);padding:var(--space-1) var(--space-2);
  border-radius:var(--radius-pill);background:var(--color-surface-2);color:var(--color-muted);}
/* Текст метки красится "усиленным" (-strong) тоном, а не базовым -success/
   -warning/-danger/-accent/-purple: измерением в браузере база на своей -weak
   заливке дает контраст 2.71-4.43 при норме 4.5 для текста 12pt (см. комментарий
   у -strong токенов в tokens.css / tokens-pastel.css). Смысл цвета не меняется -
   это тот же оттенок, просто темнее для читаемости текста. */
.chip--accent{background:var(--color-accent-weak);color:var(--color-accent-strong);}
.chip--success{background:var(--color-success-weak);color:var(--color-success-strong);}
.chip--warning{background:var(--color-warning-weak);color:var(--color-warning-strong);}
.chip--danger{background:var(--color-danger-weak);color:var(--color-danger-strong);}
.chip--purple{background:var(--color-purple-weak);color:var(--color-purple-strong);}
.chip--outline{background:var(--color-surface);color:var(--color-accent-strong);
  box-shadow:inset 0 0 0 1px currentColor;}
/* Контур СОЧЕТАЕТСЯ с тоном: .chip--outline.chip--success и остальные пары
   красят и текст, и контур цветом состояния. Раньше контурная метка задавала
   цвет жестко акцентным, и пара с тоном давала синюю метку с любым смыслом -
   «состоялся», «отказались» и «не ответили» выглядели одинаково. Само по себе
   .chip--outline остается акцентным, как и было: одиночное применение уже
   живет на нескольких экранах. */
.chip--outline.chip--success{color:var(--color-success-strong);}
.chip--outline.chip--warning{color:var(--color-warning-strong);}
.chip--outline.chip--danger{color:var(--color-danger-strong);}
.chip--outline.chip--purple{color:var(--color-purple-strong);}
/* @kit --solid
   @about Плашка-акцент поверх карточки/строки — не статус, а выделение
     («Рекомендуем», «Новое»). Насыщенная заливка вместо -weak: метка должна
     перебивать соседние спокойные чипы, а не сливаться с ними. */
.chip--solid{background:var(--color-accent);color:var(--color-on-accent);box-shadow:var(--elev-1);}

/* ══ Feedback: tooltip ════════════════════════════ */
/* @kit Подсказка
   @group Метки и статусы
   @about Короткая справка по наведению: что делает кнопка-иконка.
   @do Два-три слова: подсказку читают на лету
   @dont Прятать в подсказку важное — на планшете наведения нет
   @dont Путать с «Пояснением» - то видно всегда, эта всплывает по наведению
   @sample <button data-tooltip="Отменить">…</button> */
/* Ширина ограничена, перенос разрешен: подсказка объясняет не только ЧТО не так,
   но и что делать дальше, а такой текст в одну строку растянулся бы через весь
   экран и стал бы нечитаемым. */
.tooltip-bubble{position:fixed;z-index:9999;background:var(--slate-800);color:var(--color-on-inverse);
  font:var(--text-xs)/1.3 var(--font-body);padding:var(--space-1) var(--space-2);
  border-radius:var(--radius-sm);box-shadow:var(--elev-2);pointer-events:none;opacity:0;
  max-width:calc(var(--space-16) * 5);white-space:normal;
  transition:opacity var(--motion-fast) var(--ease-standard);}
.tooltip-bubble.is-visible{opacity:1;}

/* ══ Toggle (switch) ══════════════════════════════ */
/* @kit Переключатель
   @group Ввод
   @about Включает или выключает режим. Действует сразу, без кнопки подтверждения.
   @do Подпись называет включаемое состояние: «Показывать сетку»
   @dont Там, где изменение вступает в силу только по «Сохранить» — это флажок
   @sample <label class="switch"><input type="checkbox" checked /><span class="switch__track"></span> Сетка</label> */
.switch{display:inline-flex;align-items:center;gap:var(--space-2);cursor:pointer;}
.switch input{position:absolute;opacity:0;width:0;height:0;}
/* flex:none — трек фиксированного размера. Без него в тесном flex-ряду (напр.
   действия строки календаря) он сжимается на пару пикселей, а thumb рассчитан
   на полную ширину и вылезает за трек, перекрывая его границу. */
.switch__track{width:var(--space-8);height:var(--space-5);border-radius:var(--radius-pill);flex:none;
  background:var(--slate-300);position:relative;transition:background var(--motion-base) var(--ease-standard);}
.switch__thumb{position:absolute;top:var(--space-05);left:var(--space-05);
  width:var(--size-thumb);height:var(--size-thumb);border-radius:var(--radius-pill);
  background:var(--color-surface);box-shadow:var(--elev-1);transition:transform var(--motion-base) var(--ease-standard);}
.switch input:checked + .switch__track{background:var(--color-accent);}
.switch input:checked + .switch__track .switch__thumb{transform:translateX(var(--space-3));}
.switch input:focus-visible + .switch__track{box-shadow:var(--focus-ring);}
.switch input:disabled + .switch__track{opacity:.5;}

/* ══ Popover / dropdown shell ═════════════════════ */
/* @kit Поповер
   @group Поверхности
   @about Всплывающая плоскость, привязанная к своей кнопке: меню, настройки, палитра.
     Не прерывает работу — щелчок мимо закрывает.
   @do Открывать по нажатию, не по наведению: наведение открывает случайно
   @do Держать рядом с тем, что вызвало — связь должна быть видна
   @dont Заменять модалку, когда нужно решение здесь и сейчас
   @dont Класть длинную форму: поповер для короткого выбора
   @state .is-open Раскрыт
   @sample <div class="popover is-open">…</div> */
.popover{position:absolute;z-index:1000;min-width:var(--space-16);
  background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-md);box-shadow:var(--elev-3);padding:var(--space-2);
  opacity:0;transform:translateY(-4px);pointer-events:none;
  transition:opacity var(--motion-fast) var(--ease-standard),transform var(--motion-fast) var(--ease-standard);}
.popover.is-open{opacity:1;transform:none;pointer-events:auto;}
.popover__item{display:flex;align-items:center;gap:var(--space-2);width:100%;
  padding:var(--space-2) var(--space-3);border:none;background:transparent;border-radius:var(--radius-sm);
  font:var(--text-md)/1 var(--font-body);color:var(--color-text);cursor:pointer;text-align:left;}
.popover__item:hover{background:var(--state-hover);}

/* ══ Slider (range) ═══════════════════════════════ */
/* @kit Ползунок
   @group Ввод
   @about Выбор значения из диапазона, когда важна не точность, а ощущение: прозрачность,
     масштаб, толщина.
   @do Показывать текущее значение рядом — иначе выбор вслепую
   @dont Когда нужна точность: там поле ввода или счётчик
   @sample <label class="slider"><input type="range" min="0" max="100" /><span>60</span></label> */
.slider{display:flex;align-items:center;gap:var(--space-3);}
.slider input[type=range]{-webkit-appearance:none;appearance:none;height:var(--space-1);flex:1;
  border-radius:var(--radius-pill);cursor:pointer;outline:none;--_fill:0%;
  background:linear-gradient(var(--color-accent),var(--color-accent)) 0/var(--_fill) 100% no-repeat,var(--slate-200);}
.slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;
  width:var(--size-thumb);height:var(--size-thumb);border-radius:var(--radius-pill);
  background:var(--color-surface);border:var(--border-thick) solid var(--color-accent);
  box-shadow:var(--elev-1);transition:transform var(--motion-fast) var(--ease-standard);}
.slider input[type=range]::-moz-range-thumb{width:var(--size-thumb);height:var(--size-thumb);
  border-radius:var(--radius-pill);background:var(--color-surface);
  border:var(--border-thick) solid var(--color-accent);box-shadow:var(--elev-1);}
.slider input[type=range]:hover::-webkit-slider-thumb{transform:scale(1.12);}
.slider input[type=range]:focus-visible{box-shadow:var(--focus-ring);}
.slider input[type=range]:disabled{opacity:.5;cursor:not-allowed;}
.slider__value{min-width:var(--space-8);text-align:right;
  font:600 var(--text-sm)/1 var(--font-mono);color:var(--color-muted);}

/* ══ Divider ══════════════════════════════════════ */
/* @kit Разделитель
   @group Поверхности
   @about Черта между смысловыми группами.
   @do Только там, где отступа не хватило: линия — тяжелее пустоты
   @dont Между каждой парой строк: интерфейс превращается в решётку
   @sample <hr class="divider" /> */
.divider{border:none;border-top:1px solid var(--color-border);margin:var(--space-3) 0;}
.divider--v{border:none;border-left:1px solid var(--color-border);align-self:stretch;width:0;margin:0 var(--space-2);}

/* @kit Заголовок раздела контента
   @group Типографика
   @do Один и тот же облик заголовка раздела во всех приложениях: жирный
       xs-капс, приглушенный цвет, тонкая линия сверху отделяет от соседнего
       раздела
   @dont Не путать с .sidenav__cap — та живет внутри бокового меню, эта — в
         потоке основного контента (карточка, панель, форма)
   @dont Не задавать свой шрифт/отступы заголовку раздела в проекте — все
         экраны с одинаковым назначением обязаны выглядеть одинаково
   @sample <div class="section-cap">Опыт работы</div> */
.section-cap{font:700 var(--text-xs)/1.3 var(--font-body);color:var(--color-muted);
  text-transform:uppercase;letter-spacing:.06em;
  margin-block:var(--space-10) var(--space-3);
  padding-block-start:var(--space-6);border-top:1px solid var(--color-border);}
.section-cap:first-of-type{margin-block-start:var(--space-6);padding-block-start:0;border-top:none;}

/* ══ Side nav (боковая навигация приложения) ══════ */
/* @kit Боковая навигация
   @group Навигация
   @do Главное действие — сверху во всю ширину, разделы ниже (.menu)
   @do Отступы — только лестницей 4/8/20 (см. ниже): пункты, заголовок к пунктам, разделы
   @dont Не путать с .rail: та — узкая полоса иконок инструментов
   @dont Не задавать свои отступы разделам: лестница обязательна во всех левых панелях
   @sample <aside class="sidenav"><button class="btn btn--primary">Действие</button>
             <div class="sidenav__cap">Разделы</div><nav class="menu">…</nav></aside> */
.sidenav{width:var(--app-shell-side-w, var(--size-sidenav));min-width:0;flex:none;display:flex;flex-direction:column;gap:var(--space-2);
  padding:var(--space-4) var(--space-3);background:var(--color-surface);
  border-right:1px solid var(--color-border);overflow:hidden;
  transition:width var(--motion-base) var(--ease-standard);}
.sidenav > .btn{width:100%;justify-content:center;}
/* ЛЕСТНИЦА ОТСТУПОВ БОКОВОЙ НАВИГАЦИИ - обязательный стандарт для всех левых
   панелей BOOG. Расстояние показывает силу связи, поэтому шаги разные:
     4px  (--space-1) между пунктами одного раздела;
     8px  (--space-2) от заголовка к его пунктам - это gap самой .sidenav;
     20px (--space-5) между разделами = margin-top заголовка 12px + gap 8px.
   Без среднего шага заголовок либо слипается с первым пунктом, либо висит ровно
   посередине между разделами и не читается как принадлежащий своей группе. */
.sidenav__cap{font:700 var(--text-xs)/1 var(--font-body);color:var(--color-muted);text-transform:uppercase;
  letter-spacing:.05em;padding:0 var(--space-3);margin:var(--space-3) 0 0;}
.sidenav__cap:first-child{margin-top:0;}
.sidenav .menu{gap:var(--space-1);}
.sidenav .menu__item.is-active{background:var(--color-accent-weak);color:var(--color-accent-active);font-weight:700;}
.sidenav .menu__item.is-active .icon{color:var(--color-accent-active);}
.sidenav .menu__item .badge-count{margin-left:auto;}
/* Счётчик прячется признаком hidden, а не классом: у примитива свой display,
   и без этого правила он перебивал бы признак и счётчик оставался виден. */
.badge-count[hidden]{display:none;}
.sidenav.is-collapsed{width:0;padding-left:0;padding-right:0;border-right-color:transparent;}

/* ══ Side nav collapse (ярлык сворачивания боковой панели) ══════ */
/* @kit Сворачивание панели
   @group Навигация
   @about Ярлык-язычок на грани боковой поверхности прячет её за левый край,
     рабочая область занимает освободившуюся ширину. Повторный клик — обратно.
     Реальная ширина боковой панели живёт в переменной --app-shell-side-w:
     любая раскладка (flex через .sidenav, либо grid-template-columns на
     самой странице) читает эту переменную вместо жёсткого --size-sidenav,
     и тогда сворачивание работает без переписывания раскладки.
   @do Оборачивать боковую+рабочую поверхности в общий контейнер position:relative —
     JS кладёт --app-shell-side-w именно на родителя ярлыка, чтобы несколько
     независимых панелей на одной странице (напр. в витрине кита) не путали друг друга
   @do Инициализировать через initSidenavCollapse() из primitives.js
   @dont Дублировать анимацию своим transition — она уже в токене --motion-base
   @sample <div style="position:relative;display:flex">
     <aside class="sidenav">…</aside>
     <button class="sidenav-collapse-tab" aria-pressed="false" aria-label="Свернуть панель">
       <span class="icon i-chevron-left"></span></button>
     <main style="flex:1">…</main></div> */
.sidenav-collapse-tab{position:absolute;top:var(--space-4);
  left:var(--app-shell-side-w, var(--size-sidenav));
  width:22px;height:36px;padding:0;background:var(--color-surface);
  border:1px solid var(--color-border);border-left:none;
  border-radius:0 var(--radius-md) var(--radius-md) 0;
  display:flex;align-items:center;justify-content:center;color:var(--color-muted);
  box-shadow:var(--elev-2);cursor:pointer;z-index:10;
  transition:left var(--motion-base) var(--ease-standard),color var(--motion-fast);}
.sidenav-collapse-tab:hover{color:var(--color-accent);border-color:var(--color-accent);}
.sidenav-collapse-tab:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.sidenav-collapse-tab .icon{--size-icon:var(--space-4);}
/* Панель свернута - выступать язычку больше не из чего: он становится
   самостоятельной кнопкой с полной рамкой и отступом от края. Прижатый к
   нулю выступ читался как прилипший к краю окна огрызок, тогда как с
   противоположной стороны у содержимого поле есть (замечание PO
   2026-09-03). */
.sidenav-collapse-tab[aria-pressed="true"]{left:var(--space-2);
  border-left:1px solid var(--color-border);border-radius:var(--radius-md);}

/* ══ Sidenav на телефоне (наплыв, не делит экран) ═══════════════════ */
/* .sidenav живёт во flex-раскладке .app-shell: на десктопе сворачивание -
   это просто сужение ширины (--app-shell-side-w выше). На узком экране та
   же схема делит экран пополам - панель и рабочая область читаются
   одинаково обрезанными (найдено на Главной Слайдера на телефоне; кит-
   уровня пробел, задевает любую страницу с .app-shell, не Слайдер один).
   Порог 930px - тот же, что уже применяют мобильные правки Слайдера
   (design/surfaces/responsive.css). Ниже него панель выходит из потока
   (position:fixed) и наплывает поверх содержимого, открытая по умолчанию
   закрыта - initSidenavCollapse переключает её отдельным признаком
   (.is-open-mobile), не общим .is-collapsed: семантика последнего на
   десктопе не меняется. */
@media (max-width: 930px) {
  .app-shell{position:relative;overflow-x:hidden;}
  /* Не занижать ширину .app-shell__side отдельным правилом: на реальных
     страницах это тот же узел, что и .sidenav (`class="sidenav app-shell__side"`),
     и width:0!important здесь перебивал бы открытое состояние панели ниже.
     position:fixed уже выводит узел из flex-потока .app-shell - рабочая
     область получает всю ширину без него сама, без второго правила. */
  .sidenav{position:fixed;
    top:var(--chrome-topbars, var(--topbar-height, calc(var(--space-12) + var(--space-2))));
    left:0;bottom:0;z-index:400;width:min(85vw, var(--size-sidenav));
    box-shadow:var(--elev-4);transform:translateX(-100%);
    transition:transform var(--motion-base) var(--ease-standard);}
  .sidenav.is-open-mobile{transform:translateX(0);}
  /* Ярлык становится единственной точкой входа в панель (аналог гамбургера) -
     цель касания увеличена до принятого в ките минимума для таких ролей
     (--size-touch, см. .icon-btn--touch), а не унаследована от узкого
     десктопного язычка 22×36. */
  /* z-index выше подложки (390): иначе клик по видимому язычку при открытой
     панели попадал бы в подложку под ним, а не в саму кнопку - поймано
     измерением (elementFromPoint возвращал подложку, а не кнопку). */
  .sidenav-collapse-tab{left:0!important;z-index:410;width:var(--size-touch, 44px);height:var(--size-touch, 44px);}
  .sidenav.is-open-mobile ~ .sidenav-collapse-tab{left:min(85vw, var(--size-sidenav)) !important;}
}
.sidenav-scrim{position:fixed;inset:0;z-index:390;background:rgb(15 17 21 / 40%);
  opacity:0;pointer-events:none;transition:opacity var(--motion-base) var(--ease-standard);}
.sidenav-scrim.is-visible{opacity:1;pointer-events:auto;}

/* ══ Toolbar (плавающая группа инструментов) ══════ */
/* @kit Тулбар
   @group Навигация
   @about Полоса инструментов над содержимым: собирает действия, применимые к тому,
     что сейчас выбрано.
   @do Группировать по смыслу и разделять разделителем — иначе читается как свалка
   @do Внутри — кнопки-иконки с подсказками, не текстовые кнопки
   @dont Складывать сюда всё подряд: тулбар длиннее 7–9 кнопок никто не сканирует
   @dont Прятать сюда единственное главное действие экрана — ему место кнопкой
   @sample <div class="toolbar"><button class="icon-btn"><span class="icon i-pen"></span></button><span class="toolbar__sep"></span></div> */
.toolbar{display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1);
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);box-shadow:var(--elev-2);}
.toolbar__sep{width:0;align-self:stretch;border-left:1px solid var(--color-border);margin:var(--space-1);}

/* ══ Topbar (верхняя полоса приложения — общая для всех продуктов) ══ */
/* @kit Топбар
   @group Навигация
   @about Верхняя полоса приложения: слева — кто мы и где (логотип, раздел), справа —
     действия и профиль. Одна на весь продукт, всегда на виду. Общий эталон для всех
     продуктов BOOG; продуктовые надстройки (напр. .topbar-slider) наслаиваются поверх.
   @do Слева — навигация и контекст, справа — действия и профиль
   @do Держать сверху и всегда доступной; высота одна на все экраны
   @dont Складывать рабочие инструменты экрана — для них тулбар под топбаром
   @dont Заводить свой топбар на странице: наследовать этот, отличия — надстройкой
   @dont Придумывать свой шрифт названию продукта в топбаре - на этом месте уже
     расходились Слайдер и комната данных: одна показывала его обычным текстом,
     другая - обычным весом. Название рядом с логотипом - это бренд, ему место
     .topbar__title, а не своя строка в надстройке продукта.
   @sample <header class="topbar"><div class="topbar__left"><span class="topbar__title">BOOG</span></div><div class="topbar__right"><button class="icon-btn"><span class="icon i-bell"></span></button></div></header> */
.topbar{position:sticky;top:0;z-index:100;display:flex;align-items:center;gap:var(--space-4);
  height:calc(var(--space-12) + var(--space-2));padding:0 var(--space-4);
  background:var(--color-surface);border-bottom:1px solid var(--color-border);}
.topbar__left,.topbar__right{display:flex;align-items:center;gap:var(--space-2);height:100%;}
.topbar__right{margin-left:auto;}
.topbar__title{font:700 var(--text-md)/1 var(--font-display);color:var(--color-text);}

/* ══ App shell (каркас страницы приложения) ═══════ */
/* @kit Каркас приложения
   @group Навигация
   @about Единая раскладка страницы под топбаром: слева навигация, справа рабочая
     область. ОБЕ поверхности — одного цвета (surface), разделены тонкой границей.
     Один стандарт на все приложения BOOG: и левая, и правая поверхность берутся
     из кита, а не перекрашиваются на каждой странице по-своему.
   @do Левая — .sidenav или .app-shell__side; правая — .app-shell__main
   @dont Красить правую рабочую область в отдельный цвет — обе поверхности surface
   @dont Заводить свой каркас на странице: наследовать этот
   @sample <div class="app-shell"><aside class="sidenav">…</aside><main class="app-shell__main">…</main></div> */
.app-shell{display:flex;align-items:stretch;background:var(--color-surface);
  min-height:calc(100dvh - var(--topbar-height, calc(var(--space-12) + var(--space-2))));}
.app-shell__side{flex:none;width:var(--app-shell-side-w, var(--size-sidenav));min-width:0;background:var(--color-surface);
  border-right:1px solid var(--color-border);overflow-y:auto;overflow-x:hidden;
  transition:width var(--motion-base) var(--ease-standard);}
.app-shell__main{flex:1;min-width:0;background:var(--color-surface);overflow-y:auto;}
/* @kit Правая панель приложения
   @group Раскладка
   @do Панель работы С ТЕМ, что открыто в центре: свойства выбранного объекта,
       замечания к нему, история его изменений. Роль в каркасе, а не облик -
       содержимое кладет приложение
   @dont Не путать с боковой навигацией слева: та отвечает на вопрос «куда
         перейти», эта - «что сделать с этим»
   @dont Не заводить свою правую колонку в проекте: три панели рядом должны
         одинаково вести себя на узком экране, иначе каждый экран ломается
         по-своему
   @sample <div class="app-shell"><aside class="app-shell__side">…</aside>
           <main class="app-shell__main">…</main>
           <aside class="app-shell__aside">…</aside></div> */
.app-shell__aside{flex:none;width:var(--app-shell-aside-w, calc(var(--size-sidenav) + var(--space-16)));
  min-width:0;background:var(--color-surface);border-left:1px solid var(--color-border);
  overflow-y:auto;overflow-x:hidden;}

/* Телефон: третьей колонке места нет, панель становится накладкой снизу - не
   сбоку, как левая: слева уже наплывает навигация, и две накладки с одного
   края накрывали бы друг друга. Снизу человек и ждет второстепенное: там же
   открываются подробности в почте и мессенджерах.
   Открывает ее приложение, добавляя `is-open-mobile`: чем именно (кнопкой в
   полосе, жестом) решает экран.

   ГРАНИЦА ТА ЖЕ, ЧТО У ШТОРКИ, И ЭТО ВАЖНО. На планшете в портрете (768)
   панель остается колонкой: при границе 930 она уходила в накладку, а шторка
   рядом продолжала показываться - двигать было нечего (поймано на препроде
   07.09.2026). Телефон в альбоме шире планшета в портрете, поэтому телефон
   узнаем по высоте. */
@media (max-width: 767px), (max-height: 480px) {
  .app-shell{flex-wrap:wrap;}
  .app-shell__aside{position:fixed;left:0;right:0;bottom:0;z-index:390;
    width:auto;max-height:70dvh;border-left:none;
    border-top:1px solid var(--color-border);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow:var(--elev-4);transform:translateY(100%);
    transition:transform var(--motion-base) var(--ease-standard);}
  .app-shell__aside.is-open-mobile{transform:translateY(0);}
}

/* @kit Нижний переключатель разделов (телефон)
   @group Навигация
   @do Три-пять разделов одного экрана на телефоне: каждый занимает всю
       ширину поровну, цель нажатия 44 точки, подпись под значком
   @do Показывать ТОЛЬКО на узком экране: на широком разделы и так рядом
   @dont Не складывать сюда действия («Согласовать», «Отправить») - это
         переключатель места, а не панель действий
   @dont Не заводить свой в проекте: экраны платформы должны переключаться
         одинаково, иначе человек заново ищет, где он находится
   @sample <nav class="bottom-nav"><button class="bottom-nav__item is-active">
           <span class="icon i-layout"></span>Макет</button>…</nav> */

/* @kit Шторка правой панели
   @group Раскладка
   @do Вертикальная полоса между рабочей областью и правой панелью: тянут -
       меняется ширина панели, нажимают - панель закрывается и открывается
   @do Ширину панель помнит в --app-shell-aside-w на самом каркасе
   @dont Не путать с язычком боковой навигации (.sidenav-collapse-tab): тот
         висит на краю экрана и только сворачивает, эта еще и двигает границу
   @sample <div class="app-shell__curtain" role="separator" tabindex="0"></div> */
.app-shell__curtain{flex:none;width:var(--space-2);cursor:col-resize;
  background:var(--color-border);opacity:.6;position:relative;
  transition:opacity var(--motion-fast) var(--ease-standard);}
.app-shell__curtain:hover,.app-shell__curtain:focus-visible{opacity:1;
  background:var(--color-accent);outline:none;}
/* Полоса тонкая, а хвататься за нее надо пальцем и мышью: расширяем область
   нажатия невидимой накладкой, не двигая саму раскладку. */
.app-shell__curtain::after{content:"";position:absolute;inset:0 calc(var(--space-3) * -1);}
/* Шторка - для ПК и планшета (решение PO 07.09.2026). На телефоне панель
   уезжает вниз накладкой, и двигать границу нечего.
   Телефон от планшета тут отличается НЕ шириной: телефон в альбоме (812)
   шире планшета в портрете (768). Различаем по высоте - у альбомного
   телефона ее всего 375. */
@media (max-width: 767px), (max-height: 480px) {
  .app-shell__curtain{display:none;}
}

.bottom-nav{display:none;}
/* Переключатель - только для телефона (решение PO 07.09.2026). На планшете
   панели уже стоят рядом, а навигация открывается язычком: второй способ
   переключения там сбивает с толку. Телефон узнаем по высоте - в альбоме он
   шире планшета в портрете. */
@media (max-width: 767px), (max-height: 480px) {
  .bottom-nav{position:fixed;left:0;right:0;bottom:0;z-index:395;
    display:flex;background:var(--color-surface);
    border-top:1px solid var(--color-border);box-shadow:var(--elev-2);
    padding-bottom:env(safe-area-inset-bottom, 0);}
  .bottom-nav__item{flex:1;min-width:0;min-height:var(--size-touch);
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:var(--space-05);border:none;background:none;cursor:pointer;
    padding:var(--space-1) var(--space-2);
    font:600 var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);}
  .bottom-nav__item .icon{--size-icon:var(--space-5);color:var(--color-muted);}
  .bottom-nav__item.is-active{color:var(--color-accent-active);}
  .bottom-nav__item.is-active .icon{color:var(--color-accent-active);}
  .bottom-nav__item:focus-visible{outline:none;box-shadow:var(--focus-ring);}
  /* Содержимое не должно прятаться под переключателем. */
  .app-shell:has(~ .bottom-nav){padding-bottom:calc(var(--size-touch) + var(--space-2));}
}

/* @kit Шторка правой панели
   @group Раскладка
   @do Вертикальная полоса между рабочей областью и правой панелью: тянут -
       меняется ширина панели, нажимают - панель закрывается и открывается
   @do Ширину панель помнит в --app-shell-aside-w на самом каркасе
   @dont Не путать с язычком боковой навигации (.sidenav-collapse-tab): тот
         висит на краю экрана и только сворачивает, эта еще и двигает границу
   @sample <div class="app-shell__curtain" role="separator" tabindex="0"></div> */
.app-shell__curtain{flex:none;width:var(--space-2);cursor:col-resize;
  background:var(--color-border);opacity:.6;position:relative;
  transition:opacity var(--motion-fast) var(--ease-standard);}
.app-shell__curtain:hover,.app-shell__curtain:focus-visible{opacity:1;
  background:var(--color-accent);outline:none;}
/* Полоса тонкая, а хвататься за нее надо пальцем и мышью: расширяем область
   нажатия невидимой накладкой, не двигая саму раскладку. */
.app-shell__curtain::after{content:"";position:absolute;inset:0 calc(var(--space-3) * -1);}
/* Шторка - для ПК и планшета (решение PO 07.09.2026). На телефоне панель
   уезжает вниз накладкой, и двигать границу нечего.
   Телефон от планшета тут отличается НЕ шириной: телефон в альбоме (812)
   шире планшета в портрете (768). Различаем по высоте - у альбомного
   телефона ее всего 375. */
@media (max-width: 767px), (max-height: 480px) {
  .app-shell__curtain{display:none;}
}


/* Второй канон кита (ширина рабочей области кабинета) - НЕ на самом
   .app-shell__main: страницы во всю ширину (холст Слайдера, лента, чертёж)
   не должны получать центрированную колонку из коробки. Кабинет со списком/
   формой подключает его точечно на своём внутреннем контейнере. */
.app-shell__main--content{max-width:var(--size-content);margin:0 auto;}

/* ══ Segmented control (режим слоя) ═══════════════ */
/* @kit Сегменты
   @group Навигация
   @about Переключение вида одного и того же содержимого: список или сетка, месяц или год.
   @do 2–4 варианта, короткие подписи в одно слово
   @do Всегда ровно один выбран — пустого состояния не бывает
   @dont Для действий: сегмент переключает вид, а не запускает работу
   @dont Больше пяти вариантов — это уже выпадающий список
   @sample <div class="segmented"><button aria-pressed="true">Список</button><button>Сетка</button></div> */
.segmented{display:inline-flex;background:var(--color-surface-2);border-radius:var(--radius-md);padding:var(--space-05);gap:var(--space-05);}
/* inline-flex обязателен: без него иконка внутри сегмента садится на базовую
   линию строки и утаскивает подпись вниз — «Карта»/«Повестка» выглядели
   съехавшими. Текстовым сегментам правило безразлично. */
.segmented__item{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-1);
  border:none;background:transparent;border-radius:var(--radius-sm);cursor:pointer;
  padding:var(--space-1) var(--space-3);font:600 var(--text-sm)/1 var(--font-body);color:var(--color-muted);}
.segmented__item[aria-selected="true"]{background:var(--color-surface);color:var(--color-text);box-shadow:var(--elev-1);}
.segmented__item:focus-visible{outline:none;box-shadow:var(--focus-ring);}
/* Модификатор «невесомые вкладки»: без заливки-контейнера, выбранный обведён
   тонкой рамкой акцента на поверхности; иконка выбранного акцентная, текст
   спокойный тёмный; невыбранные приглушены. Для переключателей вида
   (Календарь/Таблица, Список/Карта и т.п.), где тяжёлая плашка лишняя. */
.segmented--light{background:transparent;padding:0;gap:var(--space-1);}
.segmented--light .segmented__item{padding:var(--space-2) var(--space-4);border-radius:var(--radius-md);
  border:var(--border-thin) solid transparent;box-shadow:none;font-weight:500;
  transition:color var(--motion-fast),border-color var(--motion-fast),background var(--motion-fast);}
.segmented--light .segmented__item .icon{--size-icon:var(--space-4);transition:color var(--motion-fast);}
.segmented--light .segmented__item:hover{color:var(--color-text);}
.segmented--light .segmented__item:hover .icon{color:var(--color-text);}
.segmented--light .segmented__item[aria-selected="true"]{background:var(--color-surface);
  border-color:var(--color-accent);color:var(--color-text);font-weight:600;box-shadow:none;}
.segmented--light .segmented__item[aria-selected="true"] .icon{color:var(--color-accent);}

/* ══ Tabs (вкладки панели) ════════════════════════ */
/* @kit Вкладки
   @group Навигация
   @about Разделы одного экрана, между которыми переключаются, не теряя контекста.
   @do Подпись — существительное: «Участники», «Расписание»
   @do Первая вкладка — то, ради чего экран открывают чаще всего
   @dont Прятать за вкладку то, что нужно видеть одновременно с текущим
   @dont Менять состав вкладок на лету — человек теряет место
   @sample <div class="tabs"><button class="tabs__item is-active">Участники</button></div> */
/* Компактная плотность - основной вид вкладок (решение PO 2026-08-29,
   перенесено из локального модификатора .ztabs whiteboard-server, где он
   жил дублем рядом с базовым .tabs - см. architectStructurePage.js). Раньше
   основной .tabs был крупнее (--space-4 gap, --text-md, --space-3 0
   padding) - более просторный вариант никому не потребовался отдельно,
   поэтому второго размера не заводим. */
.tabs{display:flex;gap:var(--space-3);border-bottom:1px solid var(--color-border);}
/* @kit-internal кнопка внутри вкладок */
/* border-radius и box-shadow гасятся по той же причине, по которой уже гасились
   border и background: вкладка - плоский текст с полосой снизу, а не кнопка. На
   странице, где у голого <button> есть свой облик (Слайдер: style.css красит
   любую кнопку в пилюлю со скруглением и тенью), незаданные примитивом свойства
   доставались от неё - вкладки вырастали пилюлями, непохожими на те же вкладки
   у Архитектора. Поймано живой проверкой PO 2026-08-29. */
.tab{border:none;background:transparent;cursor:pointer;padding:var(--space-2) 0;margin-bottom:-1px;
  border-radius:0;box-shadow:none;
  font:600 var(--text-sm)/1 var(--font-body);color:var(--color-muted);border-bottom:var(--border-thick) solid transparent;}
.tab[aria-selected="true"]{color:var(--color-accent);border-bottom-color:var(--color-accent);}
.tab:focus-visible{outline:none;box-shadow:var(--focus-ring);}

/* ══ Menu (содержимое дропдауна) ══════════════════ */
/* @kit Меню
   @group Навигация
   @about Список действий или вариантов, раскрывающийся по нажатию. Живёт внутри поповера.
   @do Пункты — глаголы для действий, существительные для выбора
   @do Разрушительное действие — внизу, отдельно от остальных
   @dont Больше 7–9 пунктов без группировки: превращается в список, который читают долго
   @dont Вкладывать меню в меню глубже одного уровня
   @sample <div class="popover menu"><button class="menu__item">Переименовать</button></div> */
.menu{display:flex;flex-direction:column;gap:var(--space-05);min-width:calc(var(--space-16) * 3);}
/* text-decoration:none — пункт меню бывает и <button>, и <a>: в боковой навигации
   (.sidenav) он ссылка по определению. Без сброса ссылка-пункт приезжала
   подчёркнутой, а соседние кнопки-пункты нет — один список выглядел двумя. */
.menu__item{display:flex;align-items:center;gap:var(--space-2);width:100%;border:none;background:transparent;
  border-radius:var(--radius-sm);cursor:pointer;padding:var(--space-2) var(--space-3);
  font:var(--text-md)/1 var(--font-body);color:var(--color-text);text-align:left;
  text-decoration:none;}
.menu__item:hover{background:var(--state-hover);}
.menu__item .icon{color:var(--color-muted);}
.menu__kbd{margin-left:auto;}
.menu__sep{border:none;border-top:1px solid var(--color-border);margin:var(--space-1) 0;}
.menu__section{padding:var(--space-1) var(--space-3);font:600 var(--text-xs)/1 var(--font-body);
  color:var(--color-muted);text-transform:uppercase;letter-spacing:.04em;}

/* ══ Modal / Dialog ═══════════════════════════════ */
/* @kit-internal подложка модалки
   .dialog-overlay — алиас имени класса, которым уже пользуется несколько
   потребителей (окно «Доступ к чертежу», передача чертежа Методологу, три
   диалога кабинета Организатора). Комментарии кита ссылались на .dialog__body
   как на равноправный вариант .modal__body ещё до того, как для него появилось
   правило - алиас закрывает разрыв, а не плодит вторую систему модалок.
   Найдено 2026-08-13: диалог существовал в DOM, но рендерился плоским блоком
   в потоке документа (position:static по умолчанию) - невидим без прокрутки. */
.modal-overlay,.dialog-overlay{position:fixed;inset:0;background:oklch(0.2 0.02 240 / .45);display:grid;place-items:center;z-index:5000;padding:var(--space-4);}
.modal-overlay[hidden],.dialog-overlay[hidden]{display:none;}
/* Подтверждение поверх уже открытого окна кита (напр. окно списка + запрос
   подтверждения действия над строкой) - тот же приём эскалации, что у
   .popover--nested, но для модалок. Без явного уровня оба .modal-overlay
   стоят вровень (z-index:5000), и порядок в DOM решает, кто сверху - при
   динамически открываемых окнах это ненадёжно (найдено 2026-09-05: окно
   подтверждения оказалось визуально под окном списка). */
.modal-overlay--nested{z-index:5010;}
/* @kit Модалка
   @group Поверхности
   @about Окно, которое прерывает работу и требует решения: подтверждение, форма, выбор.
   @do Заголовок — вопрос или задача, кнопка — ответ на него: «Удалить», а не «ОК»
   @do Закрытие по щелчку мимо и по Escape — кроме необратимых действий
   @dont Для сообщения, не требующего решения: это тост
   @dont Модалка поверх модалки — человек теряет, на что отвечает
   @sample <div class="modal-overlay"><div class="modal">…</div></div> */
/* Панель .modal — ТОЛЬКО внутри .modal-overlay (так во всех примерах кита).
   Без scope глобальный .modal налезал на легаси-оверлеи продуктов (напр.
   #shareSlideModal class="modal" в редакторе): те сжимались до max-width панели
   и красились surface. Scope на .modal-overlay изолирует кит-панель от легаси.
   Обратная коллизия (найдено 2026-09-02, whiteboard-server/frontend/home.html):
   легаси `.modal:not(.hidden){position:fixed;inset:0;z-index:9000;...}` в
   style.css продукта задевает и КИТОВУЮ панель — тот же класс "modal" совпал по
   имени, специфичность (0,2,0) равна нашему `.modal-overlay .modal`, а
   position/inset/z-index кит раньше не переобъявлял вовсе, так что легаси-
   правило применялось беспрепятственно: панель растягивалась на весь экран
   и прилипала к левому краю вместо центрирования. Явно сбрасываем эти четыре
   свойства здесь же — оборона того же класса коллизий, что уже описан выше
   для .modal-overlay, доведённая до самой панели. */
/* Три стандартных размера (ADR-обсуждение 2026-08-04, каталог диалоговых окон
   whiteboard-server): вместо разрозненных произвольных ширин. Дефолт без
   модификатора = --md (512px) — так уже жил кит, менять дефолт не стали.
   @mod --sm 384px - подтверждение, информационное окно, форма из 1-2 полей.
   @mod --md 512px (дефолт) - обычная форма из нескольких полей.
   @mod --lg 704px - вкладки, таблицы, двухколоночные формы, витрина карточек. */
.modal-overlay .modal,.dialog-overlay .dialog{background:var(--color-surface);border-radius:var(--radius-lg);box-shadow:var(--elev-4);
  width:100%;max-width:calc(var(--space-16) * 8);overflow:hidden;display:flex;flex-direction:column;
  position:static;inset:auto;z-index:auto;}
.modal-overlay .modal--sm,.dialog-overlay .dialog--sm{max-width:calc(var(--space-16) * 6);}
.modal-overlay .modal--md,.dialog-overlay .dialog--md{max-width:calc(var(--space-16) * 8);}
.modal-overlay .modal--lg,.dialog-overlay .dialog--lg{max-width:calc(var(--space-16) * 11);}
/* Канон Б (утверждён PO 2026-08-04): без горизонтальных разделителей между
   шапкой/телом/подвалом — разделение только отступом. Раньше жило только как
   локальный оверрайд в витрине (design/kit.html), теперь это сам примитив. */
.modal__header,.dialog__header{display:flex;align-items:flex-start;justify-content:space-between;
  padding:var(--space-5) var(--space-5) var(--space-3);font:700 var(--text-lg)/1.1 var(--font-display);}
/* Расстояние между блоками тела окна задаёт САМ примитив (решение PO 2026-09-02).
   Раньше кит задавал только внутренние поля, а расстояние между полями формы и
   между абзацами оставлял потребителю - и каждый, кто об этом не знал, получал
   слипшийся текст: ловушка сработала трижды за одну миграцию окон Слайдера.
   Значение --space-5 (20px) закрепляет то, что и так сложилось в продукте.
   Потребителю остаётся переопределить gap, если ему нужно теснее. */
.modal__body,.dialog__body{padding:var(--space-2) var(--space-5) var(--space-4);
  display:flex;flex-direction:column;gap:var(--space-5);}
.modal__footer,.dialog__footer{display:flex;justify-content:flex-end;gap:var(--space-3);
  padding:var(--space-2) var(--space-5) var(--space-5);}
/* @kit-internal заголовок с подзаголовком - опциональная обёртка внутри .modal__header.
   Без неё заголовок остаётся простым текстовым узлом (наследует font шапки) - обратная
   совместимость с уже существующими простыми диалогами без подзаголовка не нарушена.
   @sample <div class="modal__title"><b>Удалить доску?</b><span>«Спринт-планирование»</span></div> */
.modal__title,.dialog__title{display:flex;flex-direction:column;gap:var(--space-2);}
.modal__title b,.dialog__title b{font:700 var(--text-lg)/1.1 var(--font-display);}
.modal__title span,.dialog__title span{font:var(--text-sm)/1.2 var(--font-body);color:var(--color-muted);font-weight:400;}

/* ══ Игровое окно ═════════════════════════════════ */
/* @kit-internal подложка игрового окна
   Отдельное имя, не .modal-overlay: игры запускаются поверх продуктов, где
   класс .modal уже занят своей легаси-версткой (редактор Slider — окно
   «Поделиться», настройки доски, таймер и т.д., см. .modal-overlay выше).
   Общий примитив вынесен из первой игры «Лабиринт» (whiteboard-server,
   frontend/ui/daily-puzzle.js) — раскладка одна на все игры платформы. */
.game-overlay{position:fixed;inset:0;background:oklch(0.2 0.02 240 / .45);
  display:grid;place-items:center;z-index:5000;padding:var(--space-4);}
.game-overlay[hidden]{display:none;}
/* @kit Игровое окно
   @group Поверхности
   @about Окно одной игры дня: правило, поле, действия. Отдельно от модалки —
     игра держит открытым таймер и позволяет откладывать прогресс, а не только
     подтверждать или отменять решение.
   @do Заголовок из двух строк: рубрика-эйбро́у (имя игры) и задача сверху
   @do Таймер — в шапке, рядом с крестиком закрытия, а не в теле окна
   @do Кнопка закрытия ведёт к «отложить», а не к сбросу прогресса
   @dont Для окна, требующего одного решения без прогресса во времени: это модалка
   @sample <div class="game-overlay"><div class="panel game-window">…</div></div> */
.game-window{width:min(calc(var(--space-16) * 6.5),92vw);max-height:92vh;overflow:auto;
  display:flex;flex-direction:column;gap:0;padding:0;background:var(--color-surface);}
.game-window__head{display:flex;align-items:center;gap:var(--space-3);
  padding:var(--space-4) var(--space-5) var(--space-3);}
.game-window__eyebrow{font:700 var(--text-xs)/1 var(--font-body);color:var(--color-muted);
  text-transform:uppercase;letter-spacing:.04em;}
.game-window__title{margin:2px 0 0;font:700 var(--text-lg)/1.1 var(--font-display);}
/* margin-left:auto толкает таймер и крестик к правому краю независимо от
   длины заголовка слева — тот же прием, что у .topbar__right. */
.game-window__timer{margin-left:auto;display:flex;align-items:center;gap:var(--space-1);
  font:700 var(--text-md)/1 var(--font-body);color:var(--color-text);}
.game-window__timer .icon{--size-icon:var(--space-4);color:var(--color-muted);}
.game-window__body{padding:0 var(--space-5) var(--space-4);display:flex;flex-direction:column;gap:var(--space-3);}
.game-window__foot{border-top:1px solid var(--color-border);padding:var(--space-3) var(--space-5);
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);
  position:sticky;bottom:0;background:var(--color-surface);}
.game-window__note{font:var(--text-xs)/1.4 var(--font-body);color:var(--color-muted);}
.game-window__acts{display:flex;align-items:center;gap:var(--space-2);}

/* ══ Текст диалога и связанные акцентные ссылки ═══ */
/* @kit Текст диалога
   @group Типографика
   @about Обычный текст внутри .modal__body или .dialog__body: пояснение, вопрос, сноска.
   @do Один размер по умолчанию (text-md) для основного текста
   @mod --muted Второстепенный текст (примечание, статус, метаданные)
   @mod --sm Мелкая сноска - всегда через модификатор, никогда инлайн font-size
   @sample <p class="dtext dtext--muted dtext--sm">Действие необратимо.</p> */
.dtext{margin:0;font:var(--text-md)/1.5 var(--font-body);color:var(--color-text);}
.dtext b{font-weight:700;}
.dtext--muted{color:var(--color-muted);}
.dtext--sm{font-size:var(--text-sm);}

/* @kit Акцентная ссылка-подсказка
   @group Типографика
   @about Альтернативный сценарий внутри диалога («передумали — вот другой путь»).
   @do Один и тот же вес/размер для любого акцентного кликабельного текста в диалоге
   @dont Разный font-weight в двух местах с одинаковой ролью - выглядит как две разные системы
   @sample <a href="#" class="link-hint">Хотите вернуть деньги? Прекратить услуги сейчас</a> */
.link-hint{color:var(--color-accent);font:500 var(--text-sm)/1.3 var(--font-body);text-decoration:none;}
/* @kit Добавить в список
   @group Типографика
   @about Действие «добавить ещё одну строку» внутри формы (критерий, поле, элемент).
   @sample <button class="link-add"><span class="icon i-circle-plus"></span> Добавить критерий</button> */
.link-add{color:var(--color-accent);font:500 var(--text-sm)/1 var(--font-body);background:none;border:none;
  display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1) 0;cursor:pointer;}
/* @kit Действие в подвале
   @group Типографика
   @about Второстепенное акцентное действие рядом с основными кнопками подвала диалога
   (например «Копировать ссылку» слева от «Готово»).
   @sample <span class="link-action"><span class="icon i-copy"></span> Копировать ссылку</span> */
.link-action{color:var(--color-accent);font:500 var(--text-sm)/1 var(--font-body);
  display:inline-flex;align-items:center;gap:var(--space-1);}
/* @kit Служебная подсказка
   @group Типографика
   @about Нейтральная метка-подсказка («Esc чтобы закрыть»), не требующая внимания.
   @sample <span class="kbd">Esc</span> <span class="footnote">чтобы закрыть</span> */
.footnote{color:var(--color-muted);font:var(--text-sm)/1 var(--font-body);}
/* @kit Крупная цифра-акцент
   @group Типографика
   @about Единственная важная величина на экране/в диалоге - таймер, сумма к возврату.
   @dont Для суммы внутри списка (это вторая по значимости величина) - см. .amt-lg
   @sample <div class="bignum">616 ₽</div> */
.bignum{font:900 var(--text-2xl)/1 var(--font-display);}
/* @kit Сумма-итог в списке
   @group Типографика
   @about Итоговая величина строки списка (например «Итого» в корзине) - заметнее
   обычного текста, но не самостоятельная крупная цифра. Именованный размер вместо
   магического text-lg на глаз в разметке потребителя.
   @sample <b class="amt-lg">1 380 ₽</b> */
.amt-lg{font-size:var(--text-lg);font-weight:700;}

/* ══ Avatar / presence ═══════════════════════════ */
/* @kit Аватар
   @group Данные
   @about Лицо участника: фотография или инициалы. Отвечает на вопрос «кто это».
   @do Инициалы, когда фотографии нет — не безликий силуэт
   @do Рядом с именем при первом появлении: аватар без имени опознают не все
   @dont Как кнопку профиля без подсказки — непонятно, куда ведёт
   @mod --sm Уменьшенный — в плотных списках и строках
   @mod --lg Увеличенный — на карточке персоны
   @mod --quiet Нейтральный — в длинных списках людей, где акцент занят действием
   @mod --row Размер от строки: 185% высоты строки с именем — списки и таблицы
   @mod --face Размер от строки: 225% — там, где на людей смотрят
   @do Фотографию класть картинкой ВНУТРЬ кружка — он сам подгонит её по размеру
     и обрежет по кругу; своей ширины и высоты картинке не задавать
   @dont Ставить фотографию фоном (background-image): пропадает alt, и кружок
     перестаёт работать со стопкой аватаров
   @sample <span class="avatar">АМ</span>
   @sample <span class="avatar"><img src="/avatars/am.jpg" alt=""></span> */
/* flex:none - кружок не сжимается соседями. В строке списка (.list-row,
   .dtable__row) длинное имя рядом сплющивало его до узкого овала: у аватара
   с фотографией внутри собственная минимальная ширина равна нулю, и флекс
   считал себя вправе его сжать. Размер аватара задан явно и обсуждению
   соседей не подлежит. */
.avatar{display:inline-grid;place-items:center;flex:none;width:var(--space-8);height:var(--space-8);border-radius:var(--radius-pill);
  background:var(--color-accent-weak);color:var(--color-accent);font:600 var(--text-sm)/1 var(--font-body);
  position:relative;border:var(--border-thick) solid var(--color-surface);}
.avatar--sm{width:var(--space-6);height:var(--space-6);font-size:var(--text-xs);}
/* Нейтральный аватар: в списке людей акцентный тон повторяется у каждой строки и
   спорит с языком действий - там акцент занят кнопкой и выбором, а не тем, кто
   это. Один вид у всех: раскрашивать аватары по человеку запрещено. */
.avatar--quiet{background:var(--color-surface-2);color:var(--color-muted);}
.avatar--lg{width:var(--space-10);height:var(--space-10);font-size:var(--text-md);}

/* @kit Размер аватара от строки с именем
   @group Данные
   @about Кружок, размер которого задан ОТНОШЕНИЕМ к высоте строки с именем, а
     не числом. Отношение держится само, даже если шкала текста поменяется, и
     одинаково выглядит там, где имя набрано разным размером.
   @do --row (185%) в списках и таблицах: лицо ОПОЗНАЕТ строку, а не держит
     внимание. Список просматривают сверху вниз, и крупный кружок начинает
     соперничать с данными
   @do --face (225%) там, где на людей СМОТРЯТ: участники разговора, карточка
     человека. Людей там немного, и лицо - то, ради чего экран открыт
   @do Задавать --avatar-base, если имя набрано не --text-md: отношение
     считается от размера букв того имени, рядом с которым кружок стоит
   @dont Ставить --face в таблицу: каждые лишние 10 точек высоты строки - это
     примерно на одну запись меньше на экране
   @dont Подбирать размер числом на месте: одно и то же лицо в двух списках
     разъедется
   @sample <span class="avatar avatar--row">АК</span>
   @sample <span class="avatar avatar--face">АК</span> */
/* Решение PO 2026-08-31 по итогам сравнения трех размеров на живом списке.
   Ориентиры чужих систем: Material 3 в строке списка ставит лицо 40 точек к
   имени 16 - это 250%; Material 2 в таблицах данных лиц не ставит вовсе;
   Carbon дает таблице пять высот строки под разную плотность. Исследований
   именно про эти числа нет - есть разница задач, она и решает.

   Отношение считается от РАЗМЕРА БУКВ имени, а не от высоты его строки
   (подтверждено PO 2026-09-03). Раньше здесь стоял лишний множитель 1.2:
   лицо выходило на пятую часть крупнее объявленного - при имени 14 точек
   --face давал 38 вместо 32, и ни одно из трех чисел, названных в этом же
   месте, не сходилось с тем, что рисовалось на экране. */
.avatar--row,
.avatar--face,
.avatar--hero{
  --_avatar-size: calc(var(--avatar-base, var(--text-md)) * var(--_avatar-ratio));
  width:var(--_avatar-size);height:var(--_avatar-size);
  /* Инициалы растут вместе с кружком - половиной его размера: иначе в большом
     круге болтаются мелкие буквы. */
  font-size:calc(var(--_avatar-size) / 2);
}
.avatar--row{--_avatar-ratio:1.85;}
.avatar--face{--_avatar-ratio:2.25;}
/* @kit Лицо на экране решения
   @group Данные
   @about Тот же закон, что у --row и --face - размер от строки с именем,
     не число. Отличие в задаче: тут лицо ОДНО и оно - всё содержание экрана
     (входящий вызов, приглашение, подтверждение личности), а не метка строки
     и не один из нескольких, на кого смотрят.
   @do Использовать, когда на экране ровно один такой аватар и имя рядом
     набрано крупно (--text-xl и выше) - отношение снова считается от строки,
     рядом с которой аватар стоит
   @dont Ставить в списки и панели: там лицо опознаёт строку или входит в
     число собеседников, а не держит собой весь экран - нужны --row/--face
   @dont Класть второй такой аватар на тот же экран: он перестаёт быть
     главным, а решение делится между двумя лицами
   @dont Подбирать размер числом на месте: одно и то же лицо в списке вызовов
     и на экране входящего вызова обязано пропорционально отличаться
   @sample <span class="avatar avatar--hero">АК</span> */
/* Взято из окна входящего вызова: одно лицо на весь экран решения, имя рядом
   набрано --text-xl. При таком имени 2.7 дают около 65 точек - заметно
   крупнее --face (225%), потому что задача другая: не разглядывание среди
   нескольких, а решение за секунды по одному лицу. */
.avatar--hero{--_avatar-ratio:2.7;}
/* Фотография в аватаре. Размер задаёт сам кружок, картинка его заполняет и
   обрезается по нему. Без этого правила картинка рисовалась в натуральную
   величину, вылезала за кружок и накрывала собой соседний текст: на экране
   «Звонки» одно фото закрыло имена половины списка (препрод 2026-08-30).
   Примитив описан для инициалов, но фотографию в него кладут постоянно -
   значит он обязан её выдерживать сам, а не силами каждого потребителя. */
.avatar img{display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit;}
/* @kit Отметка присутствия на аватаре
   @group Данные
   @about Здесь человек сейчас или нет - точка в углу его кружка.
   @do Показывать ОБА состояния: зелёная «на связи», серая «не на связи».
     Отсутствие точки читается как «неизвестно» или как недоделка разметки,
     а не как «человека нет» - молчание тут двусмысленно
   @dont Ставить точку там, где присутствие не отслеживается: она обещает
     сведения, которых нет
   @sample <span class="avatar">АМ<span class="avatar__dot"></span></span>
   @sample <span class="avatar">АМ<span class="avatar__dot avatar__dot--offline"></span></span> */
.avatar__dot{position:absolute;right:0;bottom:0;width:var(--space-2);height:var(--space-2);
  border-radius:var(--radius-pill);background:var(--color-success);border:var(--border-thick) solid var(--color-surface);}
/* Тот же тон, что у отдельной отметки .status-dot--offline: одно состояние -
   один цвет, где бы отметка ни стояла. Раньше точка на аватаре умела сказать
   только «на связи», и «не на связи» приходилось показывать её отсутствием -
   а это неотличимо от того, что присутствие вообще не отслеживают. */
.avatar__dot--offline{background:var(--slate-300);}
/* @kit Стопка аватаров
   @group Данные
   @about Кто участвует, одной строкой: аватары внахлёст плюс остаток числом.
   @do 3–5 лиц и «+N» — дальше лица не различают
   @sample <div class="avatar-stack"><span class="avatar">АМ</span><span class="avatar">+4</span></div> */
.avatar-stack{display:inline-flex;}
.avatar-stack .avatar + .avatar{margin-left:calc(var(--space-3) * -1);}
/* Нахлёст пропорционален размеру: у мелких аватаров минус 12px съедал половину
   кружка вместе с инициалами, и стопка читалась кашей. */
.avatar-stack .avatar--sm + .avatar--sm{margin-left:calc(var(--space-2) * -1);}

/* ══ Seat grid (наполнение мест) ═══════════════════ */
/* @kit Сетка мест
   @group Данные
   @about Сколько мест занято из отведённых. Клетка — одно место, занятое
     отмечено обводкой. Читается быстрее пары «4 из 20»: наполнение видно
     формой, а не чтением чисел, и строки списка сравниваются взглядом.
   @do Держать рядом текстовую пару «занято из всего» — сетка её показывает,
     а не заменяет: точное число нужно и глазу, и озвучке для незрячих
   @do Больше пятнадцати мест — переходить на --many: считать клетки глазом
     всё равно уже нельзя, а мельчить их значит превращать в шум
   @dont Красить занятые статусным цветом: полный набор не «успех», а пустой
     не «ошибка» — это просто состояние набора
   @dont Заливать занятые насыщенным акцентом: плотный ряд таких плашек
     читается как органы управления, а не как данные
   @dont Использовать для долей, у которых нет штучного смысла (проценты,
     готовность): клетка обязана означать ровно одну считаемую вещь
   @mod --row Одной строкой: когда сетка стоит в ячейке таблицы, второй ряд
     клеток растит высоту строки и ломает выравнивание соседних столбцов
   @mod --many Мест больше пятнадцати: один блок с числом вместо клеток
   @mod --queue Очередь: люди без закреплённых мест, поэтому без обводки
   @mod --count Просто количество: занятости нет вовсе, считается одна величина
     («выделено 5 мероприятий»). Обводки нет ни у клеток, ни у блока с числом -
     она означала бы занятость, которой здесь не существует
   @sample <span class="seatgrid"><i class="is-taken"></i><i class="is-taken"></i><i></i><i></i><i></i></span> */
/* Клетка в пропорции 16:10 — той же, что полотно доски: место читается как
   маленький экран участника, а не как абстрактная точка. */
.seatgrid{display:inline-grid;grid-template-columns:repeat(5,var(--space-4));
  gap:var(--space-05);align-content:start;}
/* Заливка у всех клеток одна и та же — занятость несёт ОБВОДКА. Насыщенная
   заливка занятых спорила с языком действий: ряд плотных синих плашек читался
   как кнопки, а не как данные. Прозрачная граница стоит на всех клетках, иначе
   появление обводки сдвигало бы сетку на пиксель. */
.seatgrid i{aspect-ratio:16/10;border-radius:2px;background:var(--color-accent-weak);
  border:var(--border-thick) solid transparent;}
.seatgrid i.is-taken{border-color:var(--color-accent);}
/* Одной строкой - для ячейки таблицы: там второй ряд клеток растит высоту
   строки, и соседние столбцы перестают читаться по горизонтали. */
.seatgrid--row{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:var(--space-4);}
/* Мест больше, чем имеет смысл считать клетками: один блок с числом. Он
   вдвое уже сетки — крупный прямоугольник перевешивал соседние строки списка
   и читался как что-то более важное, чем обычный набор. */
.seatgrid--many{display:inline-grid;grid-template-columns:1fr;}
.seatgrid--many .seatgrid__n{display:grid;place-items:center;
  width:calc((var(--space-4) * 5 + var(--space-05) * 4) / 2);aspect-ratio:16/10;
  border-radius:var(--radius-sm);background:var(--color-accent-weak);color:var(--color-accent-active);
  border:var(--border-thick) solid var(--color-accent);
  font:800 var(--text-lg)/1 var(--font-display);font-variant-numeric:tabular-nums;}
/* Очередь: люди есть, а мест за ними не закреплено — той же сеткой, но без
   обводки. Обводка в этом примитиве и означает «место закреплено», поэтому
   очередь остаётся заливкой без неё. */
.seatgrid--queue i,
.seatgrid--queue .seatgrid__n{border-color:transparent;}
/* Просто количество: занятости у этих клеток нет вовсе, считается одна величина
   («выделено 5 мероприятий»). Обводка означала бы занятость, которой здесь не
   существует, поэтому ее нет ни у клеток, ни у блока с числом - иначе крупное
   число в рамке читается как «занято», хотя занимать нечего.
   Решение PO 2026-08-19, раздел полученных квот Организатора. */
.seatgrid--count i,
.seatgrid--count .seatgrid__n{border-color:transparent;}

/* ══ Toast ════════════════════════════════════════ */
/* @kit Тост
   @group Метки и статусы
   @about Короткое сообщение о том, что произошло: сохранено, не удалось, отправлено.
     Появляется сам, уходит сам.
   @do Одна мысль в одном предложении, в прошедшем времени: «Доска сохранена»
   @do Ошибка — с действием «Повторить», иначе человек бессилен
   @dont Сообщать успех очевидного: если результат виден на экране, тост лишний
   @dont Класть сюда то, что требует решения — это модалка
   @mod --success Действие получилось
   @mod --warning Получилось, но с оговоркой
   @mod --danger Не получилось
   @sample <div class="toast toast--success"><span class="icon i-check"></span>Доска сохранена</div> */
.toast{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);
  background:var(--color-surface);border:1px solid var(--color-border);border-left:var(--space-1) solid var(--color-accent);
  border-radius:var(--radius-md);box-shadow:var(--elev-3);min-width:calc(var(--space-16) * 4);}
.toast--success{border-left-color:var(--color-success);}
.toast--danger{border-left-color:var(--color-danger);}
.toast--warning{border-left-color:var(--color-warning);}
.toast__msg{flex:1;font:var(--text-md)/1.3 var(--font-body);color:var(--color-text);}
/* @kit Стопка тостов
   @group Метки и статусы
   @about Место, где тосты появляются: правый нижний угол окна, поверх всего,
     новые сверху. Без неё тост встаёт в поток страницы и человек его не видит -
     сообщение формально есть, а отказ выглядит немым.
   @do Держать одну стопку на страницу, класть её последним элементом body
   @dont Задавать положение стопки у себя в проекте - оно живёт здесь
   @do Угол занят своим управлением - поднять стопку через --toast-stack-offset
   @sample <div class="toast-stack"><div class="toast toast--danger"><span class="toast__msg">Не удалось сохранить</span></div></div> */
.toast-stack{position:fixed;right:var(--space-5);
  /* Нижний правый угол бывает занят своим постоянным управлением (в редакторе там
     панель масштаба). Страница поднимает стопку над ним через --toast-stack-offset,
     а не заводит свое расположение: место стопки остается общим. */
  bottom:calc(var(--space-5) + var(--toast-stack-offset, 0px));z-index:10000;
  display:flex;flex-direction:column;gap:var(--space-2);pointer-events:none;
  max-width:min(calc(var(--space-16) * 6), 90vw);}
.toast-stack > .toast{pointer-events:auto;}

/* ══ Stepper (числовое поле) ══════════════════════ */
/* @kit Счётчик
   @group Ввод
   @about Небольшое целое число, которое меняют по одному: участников в команде, минут на акт.
   @do Разумные границы: за пределы диапазона кнопки не пускают
   @dont Для больших чисел — там поле ввода: до 300 не дощёлкаешь
   @sample <div class="stepper"><button>−</button><input value="4" /><button>+</button></div> */
.stepper{display:inline-flex;align-items:center;border:1px solid var(--color-border);
  border-radius:var(--radius-md);background:var(--color-surface);overflow:hidden;}
.stepper input{border:none;outline:none;width:var(--space-16);text-align:center;background:transparent;
  font:var(--text-md)/1 var(--font-mono);color:var(--color-text);padding:var(--space-2);}
.stepper button{border:none;background:transparent;cursor:pointer;color:var(--color-muted);padding:var(--space-2);display:grid;place-items:center;}
.stepper button:hover{background:var(--state-hover);color:var(--color-text);}

/* ══ Color swatch (триггер цвета) ═════════════════ */
/* @kit Образец цвета
   @group Ввод
   @about Выбор цвета: заливка стикера, перо, тон фигуры.
   @do Показывать выбранный обводкой, а не только галочкой
   @dont Давать произвольный цвет там, где цвет что-то значит по договорённости игры
   @sample <button class="swatch-btn" style="--sw:var(--pastel-rose)"></button> */
.swatch-btn{width:var(--space-8);height:var(--space-8);border-radius:var(--radius-md);
  border:1px solid var(--color-border);cursor:pointer;padding:0;}
.swatch-btn:focus-visible{outline:none;box-shadow:var(--focus-ring);}

/* ══ Checkbox / Radio ═════════════════════════════ */
/* @kit Флажок
   @group Ввод
   @about Выбор нескольких из списка или согласие. Вступает в силу по «Сохранить».
   @do Подпись — то, что включаем: «Присылать напоминания»
   @dont Для мгновенного действия — это переключатель
   @dont Отрицание в подписи: «Не показывать» плюс снятый флажок читают неверно
   @sample <label class="check"><input type="checkbox" /> Присылать напоминания</label> */
.check{display:inline-flex;align-items:center;gap:var(--space-2);cursor:pointer;font:var(--text-md)/1 var(--font-body);}
.check input{width:var(--space-4);height:var(--space-4);accent-color:var(--color-accent);}

/* ══ Badge count ══════════════════════════════════ */
/* @kit Счётчик-бейдж
   @group Метки и статусы
   @about Число непрочитанного или требующего внимания. Висит на иконке или пункте.
   @do Только когда число зовёт к действию: уведомления, приглашения
   @do Большие числа сокращать: «99+»
   @dont Показывать ноль — пустой бейдж хуже отсутствующего
   @sample <span class="badge-count">3</span> */
.badge-count{display:inline-grid;place-items:center;min-width:var(--space-4);height:var(--space-4);padding:0 var(--space-1);
  border-radius:var(--radius-pill);background:var(--color-danger);color:var(--color-on-accent);font:600 var(--text-xs)/1 var(--font-body);}

/* ══ Kbd (горячая клавиша) ════════════════════════ */
/* @kit Клавиша
   @group Данные
   @about Изображение клавиши в подсказке: горячее сочетание рядом с действием.
   @do Рядом с пунктом меню или в подсказке — там, где действие и так называется
   @dont В основном тексте интерфейса: сочетания учат в контексте, а не в прозе
   @sample <span class="kbd">⌘</span><span class="kbd">S</span> */
.kbd{display:inline-grid;place-items:center;min-width:var(--space-5);height:var(--space-5);padding:0 var(--space-1);
  border:1px solid var(--color-border);border-bottom-width:var(--border-thick);border-radius:var(--radius-sm);
  background:var(--color-surface-2);font:600 var(--text-xs)/1 var(--font-mono);color:var(--color-muted);}

/* ══ List row (участник / слайд / история) ════════ */
/* @kit Строка списка
   @group Данные
   @about Один объект в перечне: участник, доска, мероприятие. Кликабельна целиком.
   @do Главное — слева, второстепенное и действия — справа
   @do Одинаковая высота строк: список сканируют глазами по вертикали
   @dont Прятать единственное действие строки в наведение — на планшете его не найти
   @sample <div class="list-row"><span class="avatar">АМ</span>Арсений</div> */
/* Сброс нативного вида кнопки: .list-row часто ставят на <button> (вся строка —
   кликабельная цель), и без сброса браузер рисует свою рамку/заливку поверх кита
   (2px outset border, серый фон) — ровно так, как уже сброшено у .menu__item. */
.list-row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);
  border-radius:var(--radius-md);border:none;background:none;appearance:none;-webkit-appearance:none;
  font:inherit;text-align:left;width:100%;cursor:pointer;}
.list-row:hover{background:var(--state-hover);}
.list-row__body{flex:1;min-width:0;}
/* Цвет наследуется от .list-row: контейнер может стоять на своей подложке
   (выбранный пункт меню на --color-accent), и тогда он задаёт --color-on-accent.
   Жёсткий color здесь перебивал родителя и давал тёмный текст на насыщенной
   заливке — контраст 1.65:1 при норме 4.5:1. */
.list-row{color:var(--color-text);}
.list-row__title{font:600 var(--text-md)/1.2 var(--font-body);color:inherit;}
/* Прозрачность 0.85, а не 0.75: на белой поверхности три четверти давали
   контраст 3.36 при норме 4.5 - вторая строка списка во всех продуктах была
   ниже порога читаемости. Роль --color-muted (5.48) сюда ставить нельзя по
   той же причине, что и жёсткий цвет выше: на цветной подложке строка
   задаёт свой цвет, и подпись обязана его наследовать. Прозрачность
   работает в обе стороны - чем она выше, тем ближе подпись к цвету строки,
   каким бы он ни был. Иерархию держат размер и насыщенность: имя набрано
   крупнее и жирнее. */
.list-row__meta{font:var(--text-sm)/1.2 var(--font-body);color:inherit;opacity:.85;}
/* Действия строки - у правого края, в одну линию, с зазором между целями.
   Правило «главное слева, действия справа» кит требовал с самого начала, но
   места для действий не описывал: каждый список заводил свой хвост строки.
   Отдельная кучка нужна и там, где действий два (позвонить и открыть
   переписку) - иначе кнопки слипаются под пальцем. */
.list-row__actions{display:flex;align-items:center;gap:var(--space-2);}

/* @kit Строка добавления
   @group Данные
   @about Последняя строка списка: завести ещё один такой же предмет, не уходя со списка.
   @do Ставить в конец перечня, на всю его ширину - место, где список продолжается
   @do Называть предмет, а не действие вообще: «Добавить игровой день», не «Добавить»
   @dont Заменять ею главное действие экрана - это продолжение списка, а не призыв
   @dont Ставить в пустой список: там работает пустое состояние со своей кнопкой
   @sample <button class="rowadd"><span class="icon i-plus"></span> Добавить игровой день</button> */
/* Пунктир и приглушённый текст - «здесь пока ничего нет, но может быть»:
   сплошная рамка читалась бы как ещё один предмет списка. */
.rowadd{display:flex;align-items:center;gap:var(--space-2);width:100%;text-align:left;
  padding:var(--space-3) var(--space-4);border:1px dashed var(--color-border);
  border-radius:var(--radius-md);background:transparent;cursor:pointer;
  font:var(--text-sm)/1.2 var(--font-body);color:var(--color-muted);}
.rowadd:hover{background:var(--state-hover);color:var(--color-text);border-color:var(--color-accent);}
.rowadd:focus-visible{outline:var(--focus-ring);outline-offset:2px;}

/* ══ Empty state ══════════════════════════════════ */
/* @kit Пустое состояние
   @group Данные
   @about Экран, когда показывать нечего: список пуст, поиск ничего не нашёл, ещё не начинали.
   @do Объяснить, почему пусто, и дать следующий шаг кнопкой
   @do Различать «ещё ничего нет» и «ничего не нашлось» — это разные тексты
   @dont Оставлять просто «Нет данных»: человек не понимает, сломалось или так задумано
   @dont Показывать вместо ошибки загрузки — ошибку надо назвать ошибкой
   @sample <div class="empty"><span class="icon i-inbox"></span><p>Мероприятий пока нет</p></div> */
.empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-3);
  padding:var(--space-10);color:var(--color-muted);}
.empty .icon{width:var(--space-10);height:var(--space-10);color:var(--color-border);}
.empty__title{font:700 var(--text-lg)/1.2 var(--font-display);color:var(--color-text);}

/* ══ Hint ═════════════════════════════════════════ */
/* @kit Пояснение
   @group Данные
   @about Тихая строка рядом с полем или действием: что произойдёт, что необязательно,
     откуда взялось значение. Не сообщение об ошибке и не промо-текст о возможности.
   @do Одна мысль, без «пожалуйста» и без пересказа очевидного
   @do Ставить рядом с тем, к чему относится, а не в конец формы
   @dont Прятать сюда важное условие - его человек прочитает последним или не прочитает
   @dont Набирать основным размером текста: пояснение не должно спорить с самим полем
   @dont Путать с «Подсказкой» - та всплывает по наведению, эта видна всегда
   @sample <div class="hint">Без срока вакансия висит открытой</div> */
/* Правило появилось 2026-09-03: класс `.hint` жил в разметке пяти мест продукта
   («Найм», «Моя карьера», «Организатор»), а оформление для него было только в
   старом стиле-файле редактора Слайдера. На страницах, которые тот файл не
   подключают, подсказки выводились обычным текстом - их не отличить от
   содержания. Примитив закрывает это для всех потребителей разом. */
.hint{display:flex;align-items:center;gap:var(--space-2);
  font:var(--text-xs)/1.4 var(--font-body);color:var(--color-muted);}
.hint .icon{--size-icon:var(--space-4);flex:0 0 auto;}

/* ══ Spinner / progress ═══════════════════════════ */
/* @kit Спиннер
   @group Метки и статусы
   @about Ожидание неизвестной длительности в малом месте: внутри кнопки, строки, карточки.
   @do Заменять им содержимое того места, которое грузится
   @dont На весь экран для долгой загрузки — там живые лоадеры кита
   @dont Показывать меньше секунды: мелькание раздражает сильнее ожидания
   @sample <span class="spinner"></span> */
.spinner{width:var(--space-6);height:var(--space-6);border-radius:var(--radius-pill);
  border:var(--border-thick) solid var(--color-border);border-top-color:var(--color-accent);animation:spin .7s linear infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
/* @kit Полоса выполнения
   @group Метки и статусы
   @about Доля выполненного: загрузка файла, заполненность профиля, прогресс игры.
   @do Только когда долю действительно можно посчитать
   @do Рядом — что именно идёт, иначе полоса без смысла
   @dont Для неизвестной длительности — это лоадер
   @sample <div class="progress"><span style="width:60%"></span></div> */
.progress{height:var(--space-2);background:var(--color-surface-2);border-radius:var(--radius-pill);overflow:hidden;}
.progress__bar{height:100%;background:var(--color-accent);border-radius:var(--radius-pill);}

/* ══ Loaders «живые» — несовпадающие периоды, чтобы не приедалось ══ */
/* Comet — мягкий хвост по кольцу */
/* @kit Живой лоадер
   @group Метки и статусы
   @about Ожидание на весь экран или крупный блок, где нужен именно нейтральный
     живой лоадер, не фирменный знак.
   @do Для долгого ожидания, где спиннер выглядит бедно
   @dont В строке или кнопке — там спиннер
   @dont Как базовый лоадер продукта — базовый теперь фирменный, см. Лоадер-логотип
   @mod .loader-ripple Круги по воде
   @sample <span class="loader-comet"></span> */
.loader-comet{width:var(--space-12);height:var(--space-12);border-radius:var(--radius-pill);
  background:conic-gradient(from 0turn, transparent, var(--color-accent));
  -webkit-mask:radial-gradient(farthest-side, transparent calc(100% - var(--space-2)), black 0);
  mask:radial-gradient(farthest-side, transparent calc(100% - var(--space-2)), black 0);
  animation:aurora-spin 1.2s linear infinite;}
@keyframes aurora-spin{to{transform:rotate(1turn);}}

/* Ripple — расходящиеся круги (капли по воде) */
/* @kit-internal вид живого лоадера (см. Живой лоадер) */
.loader-ripple{position:relative;width:var(--space-12);height:var(--space-12);}
.loader-ripple span{position:absolute;inset:0;border:var(--border-thick) solid var(--color-accent);
  border-radius:var(--radius-pill);animation:ripple 1.8s cubic-bezier(0,.2,.8,1) infinite;}
.loader-ripple span:nth-child(2){animation-delay:.6s;}
.loader-ripple span:nth-child(3){animation-delay:1.2s;}
@keyframes ripple{from{transform:scale(.15);opacity:1;}to{transform:scale(1);opacity:0;}}

/* ══ Монограмма BOOG (знак бренда) ════════════════ */
/* @kit Монограмма
   @group Навигация
   @about Знак бренда: буквы B и G по диагонали и две фирменные точки-спутника
     между ними. Точки неспешно меняются местами по орбите с кометным хвостом:
     два перелёта за 30 секунд, между ними долгие паузы — знак живой, но не
     отвлекает. Перенесён в кит из переключателя приложений Слайдера, где жил
     внутри кода компонента — теперь один знак на все продукты BOOG.
   @do Ставить в шапке первым элементом, обычно кнопкой переключателя приложений
   @do Кнопку-обертку помечать .icon-btn--monogram: знак крупнее обычной
     кнопки-иконки, и без этого он вылезает за ее границы, а название рядом
     уезжает под знак - буквы налезают друг на друга
   @do Размер задавать через font-size контейнера: геометрия знака в em
   @dont Набирать название текстом или рисовать свой знак
   @dont Менять цвета точек: они фирменные и живут в токенах --brand-logo-*
   @dont Ускорять или отключать движение: при prefers-reduced-motion знак сам замирает
   @sample <span class="monogram"><span>B</span><span>O</span><span>O</span><span>G</span></span> */
/* Кнопка-обертка под знак. Монограмма крупнее кнопки-иконки (36 против 32), и в
   обычной .icon-btn знак вылезал за ее границы: соседнее название в шапке
   считало ширину по кнопке и уезжало под знак - буквы налезали друг на друга
   (найдено 2026-08-19 на витрине же). Обертке размер задает сам знак. */
.icon-btn--monogram{width:auto;height:auto;padding:0;}
.monogram{position:relative;box-sizing:border-box;display:inline-grid;place-items:center;
  width:calc(var(--space-8) + var(--space-1));height:calc(var(--space-8) + var(--space-1));
  padding:var(--space-1);border-radius:var(--radius-pill);
  background:var(--color-surface);box-shadow:inset 0 0 0 1.5px var(--color-border);
  font:900 var(--text-xs)/.8 var(--font-body);letter-spacing:-.02em;color:var(--color-accent);}
.monogram > span{display:block;}
/* Буквы и точки — на одной орбите (.72em), поэтому выравнивание не зависит от
   соотношения размера коробки и шрифта: в шапке, где font-size переопределён,
   буквы остаются на местах. Синхронно с .as-boog в whiteboard-server. */
.monogram > span:nth-child(1),.monogram > span:nth-child(4){position:absolute;left:50%;top:50%;
  width:1em;height:1em;margin:-.5em 0 0 -.5em;display:grid;place-items:center;}
.monogram > span:nth-child(1){transform:rotate(225deg) translate(.72em) rotate(-225deg);}
.monogram > span:nth-child(4){transform:rotate(45deg) translate(.72em) rotate(-45deg);}
.monogram > span:nth-child(2),.monogram > span:nth-child(3){position:absolute;left:50%;top:50%;
  width:.71em;height:.71em;margin:-.355em 0 0 -.355em;border-radius:var(--radius-pill);
  color:transparent;overflow:hidden;}
/* Точки-спутники неспешно меняются местами по орбите: два оборота за 30 секунд,
   между перелётами долгие паузы. Знак живой, но не отвлекает — в шапке он висит
   постоянно. Тайминги и траектории синхронны с .as-boog в whiteboard-server. */
.monogram > span:nth-child(2){background:var(--brand-logo-blue);
  animation:mg-dot-a 30s ease-in-out infinite;}
.monogram > span:nth-child(3){background:var(--brand-logo-red);
  animation:mg-dot-b 30s ease-in-out infinite;}
@keyframes mg-dot-a{0%{transform:rotate(-45deg) translate(.72em);}
  10%,50%{transform:rotate(135deg) translate(.72em);}
  60%,100%{transform:rotate(315deg) translate(.72em);}}
@keyframes mg-dot-b{0%{transform:rotate(135deg) translate(.72em);}
  10%,50%{transform:rotate(315deg) translate(.72em);}
  60%,100%{transform:rotate(495deg) translate(.72em);}}

/* Кометный хвост за точкой: виден только в момент перелёта. */
.monogram::before,.monogram::after{content:'';position:absolute;inset:0;
  border-radius:var(--radius-pill);
  /* В маске важна непрозрачность, не сам цвет: берём токен текста, чтобы
     не держать в ките сырой hex. */
  -webkit-mask:radial-gradient(closest-side,transparent 22%,var(--color-text) 31%,
    var(--color-text) 56%,transparent 66%);
  mask:radial-gradient(closest-side,transparent 22%,var(--color-text) 31%,
    var(--color-text) 56%,transparent 66%);
  filter:blur(1.1px);opacity:0;pointer-events:none;}
.monogram::before{background:conic-gradient(from 35deg,transparent,var(--brand-logo-blue) 55deg,transparent 55deg);
  animation:mg-trail-a 30s ease-in-out infinite,mg-trail-fade 30s ease-in-out infinite;}
.monogram::after{background:conic-gradient(from 35deg,transparent,var(--brand-logo-red) 55deg,transparent 55deg);
  animation:mg-trail-b 30s ease-in-out infinite,mg-trail-fade 30s ease-in-out infinite;}
@keyframes mg-trail-a{0%{rotate:-45deg}10%,50%{rotate:135deg}60%,100%{rotate:315deg}}
@keyframes mg-trail-b{0%{rotate:135deg}10%,50%{rotate:315deg}60%,100%{rotate:495deg}}
@keyframes mg-trail-fade{0%,1%{opacity:0}3%{opacity:.75}8%{opacity:.75}11%{opacity:0}
  50%{opacity:0}52%{opacity:.75}57%{opacity:.75}61%{opacity:0}100%{opacity:0}}

@media (prefers-reduced-motion:reduce){
  .monogram > span,.monogram::before,.monogram::after{animation:none !important;}
  .monogram::before,.monogram::after{display:none;}
  .monogram > span:nth-child(2){transform:rotate(-45deg) translate(.72em);}
  .monogram > span:nth-child(3){transform:rotate(135deg) translate(.72em);}
}

/* ══ Переключатель приложений ═════════════════════ */
/* @kit Переключатель приложений
   @group Навигация
   @about Плитки приложений BOOG под монограммой в шапке: две колонки, иконка
     и название. Текущее приложение подсвечено и помечено точкой. Перенесён
     в кит из Слайдера (app-switcher.js), где жил внутри кода компонента —
     чтобы вход в приложения выглядел одинаково во всех продуктах.
   @do Открывать по монограмме, привязывать к шапке через .popover
   @do Держать порядок плиток одинаковым во всех продуктах — люди запоминают место
   @dont Заменять список текстовым меню: плитка с иконкой узнаётся быстрее
   @dont Показывать недоступные приложения: плитка, которая не откроется, злит
   @sample <div class="applauncher"><div class="applauncher__cap">Приложения</div>
     <div class="applauncher__grid">
       <a class="applauncher__item is-current" href="#"><span class="applauncher__dot"></span>
         <span class="applauncher__icon"><span class="icon i-presentation"></span></span>
         <span class="applauncher__name">Слайдер</span></a></div></div> */
.applauncher__cap{font:700 var(--text-xs)/1 var(--font-body);color:var(--color-muted);
  text-transform:uppercase;letter-spacing:.05em;
  padding:var(--space-2) var(--space-2) var(--space-3);}
.applauncher__grid{display:grid;grid-template-columns:repeat(2,calc(var(--space-16) * 1.625));
  justify-content:center;column-gap:var(--space-2);row-gap:var(--space-2);
  padding:0 var(--space-2) var(--space-2);}
.applauncher__item{position:relative;display:flex;flex-direction:column;align-items:center;
  gap:var(--space-2);padding:var(--space-3) var(--space-2);border-radius:var(--radius-md);
  text-decoration:none;color:var(--color-text);background:var(--color-surface-2);
  transition:background var(--motion-fast) var(--ease-standard);}
.applauncher__item:hover{background:var(--color-border);}
.applauncher__item.is-current{background:var(--color-accent-weak);
  box-shadow:inset 0 0 0 1px var(--color-accent);}
.applauncher__item.is-current .applauncher__icon{background:var(--color-accent);
  color:var(--color-on-accent);}
.applauncher__item.is-current .applauncher__name{color:var(--color-accent);}
.applauncher__icon{display:flex;align-items:center;justify-content:center;flex:none;
  width:var(--space-12);height:var(--space-12);border-radius:var(--radius-md);
  background:var(--color-surface-2);color:var(--color-muted);}
.applauncher__icon .icon{--size-icon:var(--space-6);}
.applauncher__name{font:600 var(--text-sm)/1.2 var(--font-body);text-align:center;}
.applauncher__dot{position:absolute;top:var(--space-2);right:var(--space-2);
  width:var(--space-2);height:var(--space-2);border-radius:var(--radius-pill);
  background:var(--color-accent);}

/* Лоадер-логотип BOOG — две кометы (синяя/красная) по часовой друг за другом;
   буквы B/G вспыхивают белым только под плотной головой (в маску входят головы, не хвосты) */
/* @kit Лоадер-логотип
   @group Метки и статусы
   @about Фирменное ожидание на знаке BOOG: две кометы по кольцу вокруг B/G.
     Базовый лоадер продукта - используется на переходах между экранами и на
     первой загрузке содержимого раздела - везде, где до появления настоящего
     контента нужна пауза длиннее мгновения. Показывать через
     .page-loader/createPageLoader с задержкой (см. ниже) — не сразу, иначе
     на быстрой загрузке лоадер мелькает.
   @dont Для мгновенных операций без задержки-охранника — мелькание раздражает больше, чем помогает
   @sample <span class="loader-boog"></span> */
.loader-boog{position:relative;box-sizing:border-box;width:var(--space-12);height:var(--space-12);font-size:var(--space-12);
  border-radius:var(--radius-pill);background:var(--color-surface);overflow:hidden;box-shadow:var(--elev-1);
  font-family:var(--font-display);font-weight:900;}
.loader-boog__arm{position:absolute;inset:0;animation:lb-spin 1.2s linear infinite;}
.loader-boog__arm--red{animation-delay:-.6s;}
.loader-boog__head{position:absolute;top:50%;left:50%;width:.46em;height:.46em;margin:-.23em;
  border-radius:var(--radius-pill);transform:translateY(-.27em);}
.loader-boog__arm--blue .loader-boog__head{background:var(--brand-logo-blue);color:var(--brand-logo-blue);}
.loader-boog__arm--red .loader-boog__head{background:var(--brand-logo-red);color:var(--brand-logo-red);}
/* хвост-комета по кольцу — как в логотипе слайдера (conic-дуга + маска-кольцо), но непрерывный */
.loader-boog__arm::before{content:'';position:absolute;inset:0;border-radius:var(--radius-pill);
  -webkit-mask:radial-gradient(closest-side,transparent 42%,black 50%,black 62%,transparent 70%);
  mask:radial-gradient(closest-side,transparent 42%,black 50%,black 62%,transparent 70%);
  filter:blur(.03em);}
.loader-boog__arm--blue::before{background:conic-gradient(from 300deg,transparent,var(--brand-logo-blue) 52deg,transparent 62deg);}
.loader-boog__arm--red::before{background:conic-gradient(from 300deg,transparent,var(--brand-logo-red) 52deg,transparent 62deg);}
@keyframes lb-spin{to{transform:rotate(360deg);}}
.loader-boog__letters{position:absolute;inset:0;z-index:2;color:var(--color-on-accent);pointer-events:none;
  -webkit-mask:radial-gradient(circle,black 58%,transparent 62%),radial-gradient(circle,black 58%,transparent 62%);
  mask:radial-gradient(circle,black 58%,transparent 62%),radial-gradient(circle,black 58%,transparent 62%);
  -webkit-mask-size:.4em .4em;mask-size:.4em .4em;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  animation:lb-mask 1.2s linear infinite;}
.loader-boog__letters b{position:absolute;font-size:.32em;line-height:1;transform:translate(-50%,-50%);}
.loader-boog__letters .lb{left:31%;top:31%;}
.loader-boog__letters .lg{left:69%;top:69%;}
@keyframes lb-mask{
  0%{-webkit-mask-position:.3em .04em,.3em .58em;mask-position:.3em .04em,.3em .58em;}
  12.5%{-webkit-mask-position:.5em .12em,.12em .5em;mask-position:.5em .12em,.12em .5em;}
  25%{-webkit-mask-position:.58em .3em,.04em .3em;mask-position:.58em .3em,.04em .3em;}
  37.5%{-webkit-mask-position:.5em .5em,.12em .12em;mask-position:.5em .5em,.12em .12em;}
  50%{-webkit-mask-position:.3em .58em,.3em .04em;mask-position:.3em .58em,.3em .04em;}
  62.5%{-webkit-mask-position:.12em .5em,.5em .12em;mask-position:.12em .5em,.5em .12em;}
  75%{-webkit-mask-position:.04em .3em,.58em .3em;mask-position:.04em .3em,.58em .3em;}
  87.5%{-webkit-mask-position:.12em .12em,.5em .5em;mask-position:.12em .12em,.5em .5em;}
  100%{-webkit-mask-position:.3em .04em,.3em .58em;mask-position:.3em .04em,.3em .58em;}
}
@media (prefers-reduced-motion:reduce){.loader-boog__arm,.loader-boog__letters{animation:none;}}

/* Page loader — обёртка для отложенного показа .loader-boog в области контента */
/* @kit Отложенный лоадер контента
   @group Метки и статусы
   @about Заменяет заглушку/пустой блок на время загрузки раздела. Не рисуется
     руками — ставится и убирается через createPageLoader() (primitives.js):
     тот сам ждёт delayMs (по умолчанию 300мс) и не покажет ничего, если
     контент успел прийти раньше — короткая загрузка не мелькает лоадером.
   @do Использовать на первой загрузке раздела приложения (список, каталог, лента)
   @dont Ставить руками без createPageLoader — тогда нет охранника от мелькания
   @mod .page-loader--fill Для главного экрана приложения — центрируется по
     вертикали во всю высоту правого фрейма, а не жмётся к его верху
   @sample <div class="page-loader"><span class="loader-boog">…</span><span class="page-loader__text">Загрузка…</span></div> */
.page-loader{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--space-3);padding:var(--space-10) 0;color:var(--color-muted);
  font:var(--text-sm)/1 var(--font-body);}
.page-loader__text{font:600 var(--text-sm)/1 var(--font-body);color:var(--color-muted);letter-spacing:.01em;}
/* Главный экран приложения — рабочая область под шапкой много выше своего
   контента, .page-loader без этого модификатора жмётся к верху вместо
   середины фрейма. min-height вместо height: фрейм не всегда явно задаёт
   высоту предку, а vh-подход не требует протягивать 100% через всю цепочку
   родителей. */
.page-loader--fill{min-height:60vh;padding:0;}

/* ══════════ Slider-специфичные примитивы (по инвентарю редактора) ══════════ */

/* ══ Choice-card / плитка выбора (текст/шаблоны/импорт/темы) ══ */
/* @kit Плитка выбора
   @group Ввод
   @about Выбор одного варианта, когда вариант надо показать, а не назвать: тип игры,
     макет слайда, роль.
   @do Картинка или иконка плюс короткая подпись
   @do 2–6 плиток: больше — уже список
   @dont Когда варианты различаются только словом: там переключатель или список
   @sample <button class="choice-card"><span class="icon i-layout"></span>Пустой слайд</button> */
.choice-card{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);
  padding:var(--space-4);border:1px solid var(--color-border);border-radius:var(--radius-md);
  background:var(--color-surface);cursor:pointer;text-align:center;
  transition:border-color var(--motion-fast) var(--ease-standard),background var(--motion-fast) var(--ease-standard);}
.choice-card:hover{border-color:var(--slate-300);}
.choice-card:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.choice-card.is-active,.choice-card[aria-selected="true"]{border-color:var(--color-accent);
  box-shadow:0 0 0 1px var(--color-accent);background:var(--color-accent-weak);}
.choice-card[aria-disabled="true"]{opacity:.5;pointer-events:none;}
.choice-card__icon{width:var(--space-8);height:var(--space-8);display:grid;place-items:center;color:var(--color-muted);}
.choice-card.is-active .choice-card__icon,.choice-card[aria-selected="true"] .choice-card__icon{color:var(--color-accent);}
.choice-card__label{font:600 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.choice-card__desc{font:var(--text-xs)/1.3 var(--font-body);color:var(--color-muted);}
.choice-card--full{width:100%;}

/* ══ Swatch-grid (палитра выбора цвета) ══ */
/* @kit-internal сетка образцов цвета */
.swatch-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--space-6),1fr));gap:var(--space-2);}
.swatch-grid__item{aspect-ratio:1;border-radius:var(--radius-sm);border:1px solid var(--color-border);cursor:pointer;padding:0;}
.swatch-grid__item:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.swatch-grid__item[aria-selected="true"]{box-shadow:0 0 0 var(--border-thick) var(--color-surface),0 0 0 calc(var(--border-thick) * 2) var(--color-accent);}

/* ══ Color-picker (выбор цвета: набор компании + поле оттенков) ══ */
/* @kit Выбор цвета
   @group Формы
   @about Полный выбор цвета: отсутствие цвета, фирменный набор компании и поле оттенков.
     Нужен там, где цвет задается произвольно - заливка фигуры, фон записки, цвет линии.
   @why Одной сетки образцов (.swatch-grid) хватает, пока набор короткий. Как только цвет
     нужен любой, появляются три разные задачи: снять цвет вовсе, взять ТОЧНЫЙ цвет бренда,
     подобрать произвольный оттенок. Свалить их в один список нельзя: «прозрачный» потеряется
     среди двухсот квадратиков, а фирменный тон невозможно найти на глаз среди соседних.
   @do Прозрачный - отдельной кнопкой над полем; фирменный набор - выше поля оттенков.
   @dont Класть «прозрачный» очередной ячейкой сетки: это не цвет, а его отсутствие.
   @dont Заводить свои сетки цветов в проектах - разметку собирает colorPickerMarkup().
   @a11y Ячейки - кнопки с title (название цвета); выбранная помечена aria-selected.
   @sample <div class="color-picker">…colorPickerMarkup()…</div> */
.color-picker{width:calc(var(--space-16) * 3.875);background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-md);
  box-shadow:var(--elev-3);padding:var(--space-2);}
.color-picker__clear{display:flex;align-items:center;gap:var(--space-2);width:100%;
  height:var(--space-8);margin-bottom:var(--space-2);padding:0 var(--space-2);
  border:1px solid var(--color-border);border-radius:var(--radius-sm);
  background:var(--color-surface);color:var(--color-text);
  font:var(--text-sm)/1 var(--font-body);cursor:pointer;}
.color-picker__clear:hover{border-color:var(--color-accent);color:var(--color-accent);}
.color-picker__clear[aria-pressed="true"]{border-color:var(--color-accent);
  box-shadow:0 0 0 1px var(--color-accent);}
.color-picker__clear:focus-visible{outline:none;box-shadow:var(--focus-ring);}
/* Шахматка - общепринятый знак отсутствия цвета; свой размер клетки, чтобы читалась
   и в маленькой метке кнопки, и в ячейке поля. */
.color-picker__none{width:var(--space-4);height:var(--space-4);flex:none;border-radius:2px;
  border:1px solid var(--color-border);
  background:
    linear-gradient(45deg,var(--color-surface-2) 25%,transparent 25%,transparent 75%,var(--color-surface-2) 75%) 0 0/var(--space-2) var(--space-2),
    linear-gradient(45deg,var(--color-surface-2) 25%,var(--color-surface) 25%,var(--color-surface) 75%,var(--color-surface-2) 75%) var(--space-1) var(--space-1)/var(--space-2) var(--space-2);}
.color-picker__cap{font:700 var(--text-xs)/1 var(--font-body);letter-spacing:.08em;
  text-transform:uppercase;color:var(--color-muted);margin:var(--space-3) 0 var(--space-2);}
.color-picker__cap:first-of-type{margin-top:0;}
.color-picker__brand{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--space-1);}
.color-picker__brand .color-picker__sw{aspect-ratio:1;border-radius:var(--radius-sm);
  border:1px solid var(--color-border);padding:0;cursor:pointer;}
/* Поле оттенков - без зазоров: разрывы дробят его и мешают вести глазом по тону. */
.color-picker__field{display:grid;grid-template-columns:repeat(18,1fr);gap:0;
  border:1px solid var(--color-border);border-radius:var(--radius-sm);overflow:hidden;}
.color-picker__field .color-picker__sw{aspect-ratio:1;border:none;padding:0;cursor:pointer;}
.color-picker__sw:hover{box-shadow:inset 0 0 0 2px var(--color-surface),inset 0 0 0 3px var(--color-text);}
.color-picker__sw:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-surface),inset 0 0 0 3px var(--color-accent);}
.color-picker__sw[aria-selected="true"]{box-shadow:inset 0 0 0 2px var(--color-surface),inset 0 0 0 3px var(--color-accent);}

/* ══ Menu: чекаемый пункт + горячая клавиша ══ */
.menu__item[aria-checked]::before{content:"";width:var(--space-4);text-align:center;flex:none;}
.menu__item[aria-checked="true"]::before{content:"✓";color:var(--color-accent);font-weight:700;}
.menu__shortcut{margin-left:auto;color:var(--color-muted);font:var(--text-xs)/1 var(--font-mono);}
/* Пункт временно недоступен («скоро», раздел ещё не открыт): полупрозрачный,
   без наведения. По атрибуту, не по классу - один и тот же смысл в любом меню. */
.menu__item[aria-disabled="true"]{opacity:.55;cursor:default;}
.menu__item[aria-disabled="true"]:hover{background:transparent;}

/* Опасный пункт меню: удаление и подобное, откуда нет пути назад. Красим САМ пункт,
   а не только значок - решение принимают до наведения, по беглому взгляду на список.
   Держим последним и отделяем чертой: соседство с обычными действиями делает промах
   вопросом одной строки. */
.menu__item--danger{color:var(--color-danger);}
.menu__item--danger .icon{color:var(--color-danger);}
.menu__item--danger:hover{background:var(--color-danger-weak);}

/* Строка сведений в меню: чей объект, когда создан и т.п. Не пункт - по ней не
   щёлкают. Нужна там, где действие зависит от чужой воли: у закреплённого объекта
   видно, к кому идти за откреплением. Ставится первой, до действий. */
.menu__info{display:flex;align-items:center;gap:var(--space-2);
  padding:var(--space-2) var(--space-3);font:var(--text-sm)/1.2 var(--font-body);
  color:var(--color-muted);}
.menu__info .icon{--size-icon:var(--space-4);flex:none;}
.menu__info-value{color:var(--color-text);font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* ══ Icon-group (сегменты icon-кнопок: слои/выравнивание/рамка/draw) ══ */
/* @kit Группа иконок
   @group Действия
   @about Несколько связанных переключателей рядом: выравнивание, начертание.
   @do Взаимоисключающие — с признаком нажатости
   @sample <div class="icon-group">…</div> */
.icon-group{display:inline-flex;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);overflow:hidden;}
.icon-group .icon-btn{border-radius:0;border:none;}
.icon-group .icon-btn + .icon-btn{border-left:1px solid var(--color-border);}

/* ══ Dimension grid (X/Y/W/H) ══ */
/* @kit-internal сетка полей размеров */
.dim-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-2);}
/* @kit-internal ячейка сетки размеров */
.dim-field{display:flex;flex-direction:column;gap:var(--space-1);}
.dim-field input{width:100%;text-align:center;outline:none;background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-2);
  font:var(--text-md)/1 var(--font-mono);color:var(--color-text);}
.dim-field input:focus{border-color:var(--color-accent);box-shadow:var(--focus-ring);}
.dim-field__cap{text-align:center;font:var(--text-xs)/1 var(--font-body);color:var(--color-muted);}

/* ══ Collapsible / accordion ══ */
/* @kit Сворачиваемый блок
   @group Поверхности
   @about Раздел, который раскрывают по требованию: подробности, редкие настройки.
   @do Заголовок говорит, что внутри, до раскрытия
   @dont Прятать то, что нужно большинству: лишний щелчок на каждом заходе
   @dont Сворачивать ошибки и предупреждения — их не увидят
   @state .is-open Раскрыт
   @sample <div class="collapse"><button class="collapse__head">Подробности</button><div class="collapse__body">…</div></div> */
.collapse{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);overflow:hidden;}
.collapse__header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;
  border:none;background:transparent;cursor:pointer;text-align:left;padding:var(--space-3) var(--space-4);
  font:600 var(--text-md)/1 var(--font-body);color:var(--color-text);}
.collapse__header:hover{background:var(--state-hover);}
.collapse__chevron{color:var(--color-muted);transition:transform var(--motion-fast) var(--ease-standard);}
.collapse.is-open .collapse__chevron{transform:rotate(180deg);}
.collapse__body{padding:var(--space-4);border-top:1px solid var(--color-border);}
.collapse:not(.is-open) .collapse__body{display:none;}

/* ══ Search field (leading icon) ══ */
/* @kit Поиск
   @group Ввод
   @about Поле для отбора по подстроке: фильтрует то, что рядом.
   @do Подсказка внутри называет, по чему ищем: «Поиск по названию»
   @do Фильтровать по мере ввода, без кнопки «Найти»
   @dont Оставлять пустой результат без объяснения — нужно пустое состояние
   @dont Ставить, когда элементов меньше десятка: глазами быстрее
   @sample <label class="search"><span class="icon i-search"></span><input placeholder="Поиск" /></label> */
.search{display:inline-flex;align-items:center;gap:var(--space-2);padding:0 var(--space-3);
  border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);}
.search:focus-within{border-color:var(--color-accent);box-shadow:var(--focus-ring);}
.search .icon{color:var(--color-muted);flex:none;}
.search input{flex:1;border:none;outline:none;background:transparent;padding:var(--space-2) 0;
  font:var(--text-md)/1 var(--font-body);color:var(--color-text);}

/* ══ Chips-input (токены + ввод) ══ */
/* @kit Поле с чипами
   @group Ввод
   @about Ввод списка: участники, теги, адреса. Введённое превращается в чип.
   @do Чип удаляется крестиком и клавишей Backspace
   @dont Для длинных значений: чипы переносятся и поле распухает
   @sample <div class="chips-input"><span class="chip">Арсений ×</span><input /></div> */
.chips-input{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);padding:var(--space-1) var(--space-2);
  border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);}
.chips-input:focus-within{border-color:var(--color-accent);box-shadow:var(--focus-ring);}
.chips-input input{flex:1;min-width:var(--space-16);border:none;outline:none;background:transparent;padding:var(--space-1);
  font:var(--text-md)/1 var(--font-body);color:var(--color-text);}
.chip__x{cursor:pointer;opacity:.6;}
.chip__x:hover{opacity:1;}

/* ══ Status-dot (мультистатус) ══ */
/* @kit Точка состояния
   @group Метки и статусы
   @about Присутствие участника или живость соединения: на месте, отошёл, офлайн.
   @do Рядом с аватаром или именем — точка сама по себе безадресна
   @do Обязательна подсказка: цвет без расшифровки читают по-разному
   @dont Единственным признаком важного состояния — цвет различают не все
   @mod --online На месте
   @mod --away Отошёл
   @sample <span class="status-dot status-dot--online"></span> */
.status-dot{display:inline-block;width:var(--space-2);height:var(--space-2);border-radius:var(--radius-pill);background:var(--color-muted);}
.status-dot--online{background:var(--color-success);}
.status-dot--away{background:var(--color-warning);}
.status-dot--busy{background:var(--color-danger);}
.status-dot--offline{background:var(--slate-300);}

/* ══ Tool-popover (оболочка мини-панели создания) ══ */
/* @kit Поповер инструмента
   @group Редактор Слайдера
   @about Настройки инструмента, привязанные к его кнопке в тулбаре.
   @do Закрывать по выбору значения — работу продолжают на холсте
   @sample <div class="tool-popover">…</div> */
.tool-popover{width:calc(var(--space-16) * 4);background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-lg);box-shadow:var(--elev-3);overflow:hidden;}
.tool-popover__header{display:flex;align-items:center;justify-content:space-between;
  padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--color-border);}
.tool-popover__title{font:700 var(--text-md)/1 var(--font-display);}
.tool-popover__desc{padding:var(--space-2) var(--space-4) 0;font:var(--text-sm)/1.3 var(--font-body);color:var(--color-muted);}
.tool-popover__body{padding:var(--space-4);}

/* ══ Line preview (толщина/стиль связи) ══ */
/* @kit Превью линии
   @group Редактор Слайдера
   @about Образец начертания: толщина, пунктир, стрелка. Показывает, что получится.
   @do Тонируется цветом текста — один образец на все цвета
   @sample <span class="line-preview"></span> */
.line-preview{width:var(--space-12);height:0;border-top:var(--border-thick) solid var(--color-text);}
.line-preview--thin{border-top-width:1px;}
.line-preview--thick{border-top-width:var(--space-1);}
.line-preview--dashed{border-top-style:dashed;}
.line-preview--dotted{border-top-style:dotted;}

/* ══ Inline-editable title ══ */
.inline-edit__view{cursor:text;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);
  font:700 var(--text-lg)/1 var(--font-display);color:var(--color-text);}
.inline-edit__view:hover{background:var(--state-hover);}
.inline-edit__input{padding:var(--space-1) var(--space-2);border:1px solid var(--color-accent);border-radius:var(--radius-sm);
  outline:none;box-shadow:var(--focus-ring);font:700 var(--text-lg)/1 var(--font-display);color:var(--color-text);}
.inline-edit__input[hidden],.inline-edit__view[hidden]{display:none;}

/* ══ Timer pill (обратный отсчёт с заливкой-прогрессом) ══ */
/* @kit Таймер
   @group Метки и статусы
   @about Обратный отсчёт акта игры: сколько осталось команде.
   @do Менять вид на последней минуте — иначе конец времени внезапен
   @sample <span class="timer-pill">04:12</span> */
.timer-pill{position:relative;display:inline-flex;align-items:center;gap:var(--space-2);overflow:hidden;cursor:pointer;
  padding:var(--space-2) var(--space-4);border:1px solid var(--color-border);border-radius:var(--radius-pill);
  background:var(--color-surface);font:600 var(--text-sm)/1 var(--font-mono);color:var(--color-text);}
.timer-pill__fill{position:absolute;inset:0;z-index:0;background:var(--color-accent-weak);transform-origin:left;}
.timer-pill > *{position:relative;z-index:1;}
.timer-pill--paused{color:var(--color-muted);}
.timer-pill--expired{color:var(--color-danger);border-color:var(--color-danger);}
.timer-pill--expired .timer-pill__fill{background:var(--color-danger-weak);}

/* ══ Slide thumbnail (филмстрип) ══ */
/* @kit Миниатюра слайда
   @group Редактор Слайдера
   @about Слайд в ленте: порядок, текущий, переход между слайдами.
   @do Номер и признак текущего — по ним ориентируются во время игры
   @sample <div class="slide-thumb">…</div> */
.slide-thumb{display:flex;flex-direction:column;gap:var(--space-1);cursor:pointer;width:calc(var(--space-16) * 2);}
.slide-thumb__preview{aspect-ratio:16/9;overflow:hidden;background:var(--color-surface-2);
  border:1px solid var(--color-border);border-radius:var(--radius-md);}
.slide-thumb.is-active .slide-thumb__preview{border-color:var(--color-accent);box-shadow:0 0 0 1px var(--color-accent);}
.slide-thumb__title{font:600 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.slide-thumb__meta{font:var(--text-xs)/1 var(--font-body);color:var(--color-muted);}

/* ══ Zoom HUD (пилюля −/значение-меню/+) ══ */
/* @kit Масштаб
   @group Редактор Слайдера
   @about Текущий масштаб холста и быстрый возврат к 100%.
   @do Щелчок по числу — вернуть исходный масштаб: самый частый жест
   @sample <div class="zoom-hud">100%</div> */
.zoom-hud{display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1);
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-pill);box-shadow:var(--elev-2);}
.zoom-hud__value{min-width:var(--space-12);border:none;background:transparent;cursor:pointer;border-radius:var(--radius-md);
  padding:var(--space-1) var(--space-3);font:600 var(--text-sm)/1 var(--font-mono);color:var(--color-text);}
.zoom-hud__value:hover{background:var(--state-hover);}

/* ══ Remote cursor (живой курсор участника) ══ */
/* @kit Курсор соседа
   @group Редактор Слайдера
   @about Указатель другого участника на общей доске: кто где сейчас смотрит и правит.
   @do Подписывать именем — курсор без имени бесполезен
   @do Цвет закреплять за участником на всю сессию
   @dont Показывать курсоры тех, кто просто смотрит: рябь без смысла
   @sample <div class="remote-cursor">Арсений</div> */
.remote-cursor{position:relative;display:inline-block;color:var(--color-accent);}
.remote-cursor__ptr{width:var(--size-icon);height:var(--size-icon);}
.remote-cursor__bubble{position:absolute;left:var(--space-4);top:var(--space-4);white-space:nowrap;
  display:inline-flex;align-items:center;gap:var(--space-1);background:currentColor;
  border-radius:var(--radius-pill);padding:var(--space-05) var(--space-2);}
.remote-cursor__bubble span{color:var(--color-on-accent);font:600 var(--text-xs)/1 var(--font-body);}

/* ══ Мини-карта ══ */
/* @kit Мини-карта
   @group Редактор Слайдера
   @about Обзор всей доски и текущего окна просмотра. Нужна, когда доска больше экрана.
   @do Показывать рамку текущего вида и давать по ней перемещаться
   @dont Держать раскрытой всегда: на маленьком экране съедает холст
   @sample <div class="minimap">…</div> */
.minimap{width:calc(var(--space-16) * 3);background:var(--color-surface);border:1px solid var(--color-border);
  border-radius:var(--radius-md);box-shadow:var(--elev-2);overflow:hidden;}
.minimap__header{display:flex;align-items:center;justify-content:space-between;
  padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--color-border);
  font:600 var(--text-sm)/1 var(--font-body);color:var(--color-muted);}
.minimap__body{position:relative;aspect-ratio:16/10;background:var(--color-surface-2);
  background-image:radial-gradient(var(--color-border) 1px, transparent 0);background-size:var(--space-3) var(--space-3);}
.minimap__viewport{position:absolute;border:var(--border-thick) solid var(--color-accent);
  border-radius:var(--radius-sm);background:oklch(0.55 0.15 235 / .1);}
.minimap__footer{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border-top:1px solid var(--color-border);}

/* ══ Draw-тулбар ══ */
/* @kit Панель рисования
   @group Редактор Слайдера
   @about Инструменты свободного рисования: перо, цвет, толщина, ластик.
   @do Держать выбранный инструмент видимым — рисуют не глядя на панель
   @dont Смешивать с инструментами объектов: рисование — отдельный режим
   @sample <div class="draw-toolbar">…</div> */
.draw-toolbar{display:inline-flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-3);
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-pill);box-shadow:var(--elev-2);}
.draw-toolbar__group{display:inline-flex;align-items:center;gap:var(--space-2);}
/* @kit-internal пресет в панели рисования */
.draw-preset{display:grid;place-items:center;width:var(--space-6);height:var(--space-6);
  border:none;background:transparent;cursor:pointer;border-radius:var(--radius-pill);}
.draw-preset:hover{background:var(--state-hover);}
.draw-preset[aria-pressed="true"]{background:var(--color-accent-weak);}
.draw-preset .dot{border-radius:var(--radius-pill);background:var(--color-text);}
.draw-preset .dot--s{width:var(--space-1);height:var(--space-1);}
.draw-preset .dot--m{width:var(--space-2);height:var(--space-2);}
.draw-preset .dot--l{width:var(--space-3);height:var(--space-3);}

/* ══ Стикер на холсте (с загибом угла) ══ */
/* @kit Стикер
   @group Редактор Слайдера
   @about Записка на доске: одна мысль одного участника. Основной житель холста
     в игровых сессиях.
   @do Одна мысль — один стикер: так их можно перегруппировать
   @do Цвет — по смыслу, о котором договорились в игре, а не по вкусу автора
   @dont Писать абзац: стикер читают издалека, с проектора
   @dont Кодировать цветом больше одного признака — участники не удержат легенду
   @mod --yellow Жёлтый — тон по умолчанию
   @sample <div class="sticky sticky--yellow">Клиент не понимает тариф</div> */
.sticky{position:relative;width:calc(var(--space-16) * 2);aspect-ratio:1;padding:var(--space-4);
  background:var(--pastel-amber, var(--color-warning-weak));border-radius:var(--radius-sm);box-shadow:var(--elev-2);
  font:var(--text-md)/1.4 var(--font-body);color:var(--slate-900);}
.sticky::after{content:"";position:absolute;right:0;bottom:0;width:var(--space-5);height:var(--space-5);
  background:linear-gradient(135deg, transparent 50%, oklch(0 0 0 / .12) 50%);}

/* ══ Picker-grid (выбор фигуры/иконки/коннектора) ══ */
/* @kit Сетка выбора
   @group Ввод
   @about Плотный выбор из однородного набора: цвет, иконка, фигура.
   @do Только для того, что опознаётся видом, без подписи
   @dont Для разнородных вариантов — там плитки выбора с подписями
   @sample <div class="picker-grid">…</div> */
.picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--space-10),1fr));gap:var(--space-2);}
.picker-grid__item{display:grid;place-items:center;aspect-ratio:1;cursor:pointer;color:var(--color-muted);
  border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);}
.picker-grid__item:hover{border-color:var(--slate-300);color:var(--color-text);}
.picker-grid__item[aria-selected="true"]{border-color:var(--color-accent);background:var(--color-accent-weak);color:var(--color-accent);}

/* ══ Vertical tool-rail (левый/правый рельс инструментов) ══ */
/* @kit Рельса
   @group Навигация
   @about Узкая вертикальная полоса у края экрана: разделы или режимы работы.
   @do Только иконки с подсказками — рельса узкая по замыслу
   @dont Больше 5–7 пунктов: дальше не помещается и не запоминается
   @sample <nav class="rail">…</nav> */
.rail{display:inline-flex;flex-direction:column;align-items:center;gap:var(--space-1);padding:var(--space-2);
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--elev-2);}
.rail__divider{width:60%;height:0;border:none;border-top:1px solid var(--color-border);margin:var(--space-1) 0;}

/* ══ Всплывающие панели редактора: якорь/триггеры/выбор фигуры ══
   Примитивы для плавающих панелей выделения объекта (Slider): якорь для
   позиционирования .popover, компактные кнопки-триггеры с текстовым ярлыком,
   плоские плитки выбора типа, сгруппированный выбор фигуры, свотч цвета кисти. */
/* @kit-internal якорь поповера тулбара */
.toolbar-anchor{position:relative;display:inline-flex;}
/* @kit-internal кнопка-триггер тулбара */
.toolbar-trigger{display:inline-flex;align-items:center;width:auto;gap:var(--space-1);padding:0 var(--space-3);
  height:var(--space-8);font:600 var(--text-sm)/1 var(--font-body);color:var(--color-text);white-space:nowrap;}
.toolbar-trigger .icon{--size-icon:14px;color:var(--color-muted);}
/* @kit-internal кнопка цвета текста в тулбаре */
.text-color-trigger{position:relative;font:700 var(--text-md)/1 var(--font-body);color:var(--color-text);}
.text-color-trigger::after{content:"";position:absolute;left:24%;right:24%;bottom:5px;height:3px;border-radius:2px;background:var(--color-text);}

/* ══ Набор свойств (раскрывается из тулбара) ══ */
/* @kit Набор свойств
   @group Навигация
   @about Свойства выбранного объекта, разложенные строками в поповере под тулбаром.
     Одна кнопка тулбара - один набор: «Текст», «Цвет», «Размер». Строка набора =
     подпись слева и органы управления справа.
   @why Тулбар, который держит все свойства в один ряд, разрастается вширь тем
     сильнее, чем богаче объект: у записки двенадцать кнопок подряд, и найти среди
     них нужную нельзя - три одинаковых образца цвета стоят в разных местах ряда.
     Набор оставляет в тулбаре одну кнопку на тему, а ширина панели перестает
     зависеть от количества свойств.
   @do Подпись каждой строки - свойство человеческим словом: «Заливка», «Кегль»
   @do Родственные свойства в одну строку, разделяя пары через .objset__label--far
   @do Больше четырех однотипных кнопок класть сеткой (.line-styles), а не в ряд
   @dont Складывать в набор действия с объектом (удалить, закрыть от правок) -
     набор про свойства, действия остаются в тулбаре и контекстном меню
   @dont Прятать в набор единственное свойство объекта - тогда кнопка тулбара
     лишний шаг, свойство ставится прямо в тулбар
   @a11y Набор - .popover, кнопка тулбара несет aria-expanded
   @sample <div class="popover objset"><div class="objset__row"><span class="objset__label">Заливка</span><button class="swatch-btn swatch-btn--sm"></button></div></div> */
.objset{padding:var(--space-2);display:flex;flex-direction:column;gap:var(--space-2);white-space:nowrap;}
.objset__row{display:flex;align-items:center;gap:var(--space-2);}
.objset__label{font:var(--text-sm)/1 var(--font-body);color:var(--color-muted);}
/* Вторая пара в строке: воздуха между парами больше, чем внутри пары - иначе
   четыре элемента подряд читаются как один ряд без смысла. */
.objset__label--far{margin-left:var(--space-4);}
/* Строка с сеткой: подпись равняется по верхней строке, а не по центру блока. */
.objset__row--top{align-items:flex-start;}
.objset__row--top .objset__label{padding-top:var(--space-2);}
/* @kit-internal сетка однотипных кнопок внутри набора (стили контура) */
.line-styles{display:grid;grid-template-columns:repeat(4,var(--space-8));gap:2px;}
/* @kit-internal список, раскрытый ИЗ набора (шрифт) - поверх родительского поповера */
.popover--nested{z-index:1001;}

.picker-grid--flat .picker-grid__item{border-color:transparent;}
.picker-grid--flat .picker-grid__item:hover{border-color:transparent;background:var(--state-hover);}
.picker-grid--flat .picker-grid__item[aria-selected="true"]{border-color:transparent;background:var(--color-accent-weak);}
/* @kit-internal иконка внутри фигуры */
.shape-ico{width:60%;height:60%;fill:none;stroke:currentColor;stroke-width:7;stroke-linejoin:round;stroke-linecap:round;}
/* @kit-internal панель «Все фигуры» */
.allshapes{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);
  box-shadow:var(--elev-2);padding:var(--space-3);min-width:calc(var(--space-16) * 3.4);max-width:320px;}
.allshapes .menu__section{margin-top:var(--space-2);}
.allshapes .menu__section:first-child{margin-top:0;}

/* @kit-internal образец в панели рисования */
.draw-color-swatch{position:relative;overflow:hidden;padding:0;cursor:pointer;}
.draw-color-swatch input[type="color"]{position:absolute;inset:0;width:100%;height:100%;
  border:none;padding:0;margin:0;cursor:pointer;opacity:0;}

/* ══════════ Домашние примитивы — дашборд и афиша (ADR-030) ══════════ */

/* ══ KPI-плитка (метрика + тренд) ══ */
/* @kit-internal ряд карточек показателей */
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-3);}
/* @kit Карточка показателя
   @group Данные
   @about Одно число, которое важно само по себе: охват, индекс, прирост. С подписью,
     что это и как менялось.
   @do Показывать динамику рядом с числом: «+12 за месяц» — число без контекста немо
   @dont Более 3–4 в ряд: показатели перестают выделяться
   @sample <div class="kpi"><b>128</b><span>Охват</span></div> */
.kpi{border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:var(--space-4);
  display:flex;flex-direction:column;gap:var(--space-1);background:var(--color-surface);}
.kpi__l{font:600 var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);}
.kpi__v{font:900 var(--text-2xl)/1 var(--font-display);color:var(--color-text);}
.kpi__v small{font:700 var(--text-md)/1 var(--font-body);color:var(--color-muted);}
.kpi__d{display:inline-flex;align-items:center;gap:var(--space-1);font:700 var(--text-xs)/1 var(--font-body);}
.kpi__d .icon{--size-icon:var(--space-3);}
.kpi__d--up{color:var(--color-success);}
.kpi__d--down{color:var(--color-danger);}
.kpi__d--flat{color:var(--color-muted);}

/* ══ Timeline (лента стадий процесса) ══ */
/* @kit Лента времени
   @group Данные
   @about Последовательность событий или этапов: жизненный цикл мероприятия, история.
   @do Отмечать текущий этап — по нему ищут «где мы сейчас»
   @do В строке списка — --compact: там лента отвечает на «далеко ли до конца»,
     а название текущей стадии подсказкой доносит точку
   @dont Ставить --compact туда, где стадия видна впервые: без подписей лента
     сообщает продвижение, но не учит названиям этапов
   @do Прерванный ход оставлять в ленте (is-cancelled на точке остановки), а
     не выносить меткой: иначе колонка говорит лентой про одни строки и
     плашкой про другие, и сравнить их взглядом уже нельзя
   @mod --compact Для строки списка: точки вдвое мельче, подписи скрыты
   @sample <div class="timeline">…</div> */
.timeline{display:flex;align-items:flex-start;}
.timeline__step{flex:1;display:flex;flex-direction:column;align-items:center;gap:var(--space-1);
  position:relative;text-align:center;}
.timeline__step::before,.timeline__step::after{content:"";position:absolute;top:var(--space-3);
  height:var(--border-thick);background:var(--color-track);z-index:0;}
.timeline__step::before{left:0;right:50%;}
.timeline__step::after{left:50%;right:0;}
.timeline__step:first-child::before{display:none;}
.timeline__step:last-child::after{display:none;}
.timeline__dot{position:relative;z-index:1;width:var(--space-6);height:var(--space-6);border-radius:var(--radius-pill);
  display:grid;place-items:center;background:var(--color-surface-2);color:var(--color-muted);
  border:var(--border-thick) solid var(--color-track);font:800 var(--text-xs)/1 var(--font-body);}
.timeline__dot .icon{--size-icon:var(--space-3);}
.timeline__label{font:600 var(--text-xs)/1.1 var(--font-body);color:var(--color-muted);}
.timeline__step.is-done .timeline__dot{background:var(--color-success);border-color:var(--color-success);color:var(--color-on-accent);}
.timeline__step.is-done::before,.timeline__step.is-done::after{background:var(--color-success);}
.timeline__step.is-current .timeline__dot{background:var(--color-accent);border-color:var(--color-accent);color:var(--color-on-accent);
  box-shadow:0 0 0 var(--space-1) var(--color-accent-weak);}
.timeline__step.is-current::before{background:var(--color-success);}
.timeline__step.is-current .timeline__label{color:var(--color-accent-active);font-weight:800;}
/* Ход прерван: точка, на которой остановились, красная — дальше ничего не
   будет. Нужна, чтобы отменённое оставалось В ЛЕНТЕ: иначе его приходится
   выносить отдельной меткой, и одна колонка начинает говорить на двух языках
   сразу — лентой для живых и плашкой для прерванных. */
.timeline__step.is-cancelled .timeline__dot{background:var(--color-danger);
  border-color:var(--color-danger);color:var(--color-on-accent);}
/* Ступени нет на этом маршруте: у объекта её не бывает вовсе, а не «пока не
   дошли». Нужна там, где лента одна на всех, а состав шагов у каждого свой -
   отклик в компании, которая не выдаёт заданий. Существующими состояниями такую
   ступень не покрасить: серая точка читается как «впереди», зелёная соврёт про
   пройденное, красная - про провал. Отсюда пунктир и прочерк: шага не было и не
   будет, и это не про успех. Подпись гасим до приглушённой - ступень не должна
   спорить за внимание с настоящими. */
.timeline__step.is-absent .timeline__dot{background:transparent;color:var(--color-muted);
  border-style:dashed;}
.timeline__step.is-absent .timeline__label{opacity:.6;}
/* Компактная — для строки списка. Подписи там не помещаются: шесть слов на
   строку удваивают её высоту, поэтому названия этапов уходят в подсказки, а
   лента отвечает на единственный вопрос строки — «далеко ли до конца».
   Соединитель едет к новому центру точки, иначе линия рвётся выше неё. */
.timeline--compact .timeline__dot{width:var(--space-3);height:var(--space-3);}
.timeline--compact .timeline__step::before,
.timeline--compact .timeline__step::after{top:calc(var(--space-3) / 2);}
.timeline--compact .timeline__label{display:none;}
.timeline--compact .timeline__step.is-current .timeline__dot{
  box-shadow:0 0 0 var(--space-05) var(--color-accent-weak);}
/* Компактная лента красится одним контуром (решение PO 2026-08-31): точка всегда
   в контуре акцента, а заливка одна называет три состояния - пройдено (сплошной
   акцент), идёт сейчас (мягкая заливка акцента), впереди (пустая, контур виден на
   белом). Одна краска вместо success/danger читается как ОДИН путь, а не смесь
   разных сигналов - в компактной ленте не о чём предупреждать, только где мы. */
.timeline--compact .timeline__dot{background:var(--color-surface);border-color:var(--color-accent);}
.timeline--compact .timeline__step::before,
.timeline--compact .timeline__step::after{background:var(--color-accent-weak);}
.timeline--compact .timeline__step.is-done .timeline__dot{background:var(--color-accent);border-color:var(--color-accent);}
.timeline--compact .timeline__step.is-done::before,
.timeline--compact .timeline__step.is-done::after{background:var(--color-accent);}
.timeline--compact .timeline__step.is-current .timeline__dot{background:var(--color-accent-weak);border-color:var(--color-accent);}

/* ══ Route (маршрут документа) ═════════════════════ */
/* @kit Маршрут
   @group Данные
   @about Путь объекта по ответственным: чертёж от Архитектора к Методологу и дальше
     к выдаче. В отличие от ленты стадий, у каждого шага есть ЧЕЛОВЕК, дата и то,
     чего шаг ждёт, - поэтому маршрут вертикальный: имена и даты в горизонтальную
     ленту не помещаются, а без них не ответить «кого толкнуть».
   @do Ставить действие шага внутрь самого шага (кнопка под пояснением)
   @do Будущему шагу писать, чего он ждёт, а не тире: пустое место не сообщает ничего
   @dont Заводить маршрут там, где ответственных нет - это лента стадий (.timeline)
   @sample <div class="route"><div class="route__step is-done"><span class="route__dot">…</span>
     <span class="route__body"><span class="route__t">Шаг</span>
     <span class="route__m">Кто и что</span></span><span class="route__when">28 июля</span></div></div> */
.route{display:flex;flex-direction:column;}
.route__step{display:grid;grid-template-columns:var(--space-6) 1fr auto;gap:var(--space-4);
  padding-bottom:var(--space-5);position:relative;}
.route__step:last-child{padding-bottom:0;}
.route__step:not(:last-child)::before{content:"";position:absolute;left:calc(var(--space-3) - 1px);
  top:var(--space-6);bottom:0;width:var(--border-thick);background:var(--color-track);}
.route__step.is-done:not(:last-child)::before{background:var(--color-success);}
.route__dot{width:var(--space-6);height:var(--space-6);border-radius:var(--radius-pill);
  display:grid;place-items:center;background:var(--color-surface-2);
  border:var(--border-thick) solid var(--color-track);color:var(--color-muted);
  font:800 var(--text-xs)/1 var(--font-body);}
.route__dot .icon{--size-icon:var(--space-3);}
.route__step.is-done .route__dot{background:var(--color-success);border-color:var(--color-success);
  color:var(--color-on-accent);}
.route__step.is-now .route__dot{background:var(--color-accent);border-color:var(--color-accent);
  color:var(--color-on-accent);}
.route__body{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-05);min-width:0;}
.route__t{font-weight:700;}
.route__step.is-now .route__t{color:var(--color-accent-active);}
.route__m{color:var(--color-muted);font-size:var(--text-sm);}
.route__act{margin-top:var(--space-2);}
.route__when{color:var(--color-muted);font-size:var(--text-xs);white-space:nowrap;}

/* @kit Сегменты маршрута
   @group Данные
   @about Тот же маршрут в строке списка, где нет места ни именам, ни подписям:
     каждый шаг - отрезок полосы, пройденные залиты. Отвечает на «сколько сделано»,
     названия шагов доносит подсказка.
   @do Задавать ширину и центрировать в своей колонке: во всю ширину полоса липнет
     к соседнему столбцу
   @do Вешать подсказку (data-tooltip) на каждый отрезок - иначе шаги безымянны
   @dont Ставить вместо маршрута на карточке объекта: там нужны ответственные
   @sample <span class="rsegs"><span class="rsegs__s is-done"></span>
     <span class="rsegs__s is-now"></span><span class="rsegs__s"></span></span> */
.rsegs{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:var(--space-1);
  width:100%;max-width:calc(var(--space-16) * 3);margin:0 auto;}
.rsegs__s{height:var(--space-2);border-radius:var(--radius-pill);background:var(--color-track);}
.rsegs__s.is-done{background:var(--color-success);}
.rsegs__s.is-now{background:var(--color-accent);}

/* ══ Data table (список с ровными столбцами) ═══════ */
/* @kit Таблица-список
   @group Данные
   @about Список однотипных записей, у которых значения должны стоять в
     столбик: имя, состояние, число, действие. Столбцы задаются ОДИН РАЗ на
     контейнере (--dtable-cols), строки наследуют их через subgrid — поэтому
     столбцы не могут разъехаться между строками.
   @do Задавать столбцы на контейнере: style="--dtable-cols: 2fr 1fr auto"
   @do Шапку помечать .dtable__head — она берёт те же столбцы
   @do Текстовую ячейку помечать .dtable__cell — длинное значение обрежется
     многоточием вместо того, чтобы растянуть столбец
   @dont Задавать текстовому столбцу auto или max-content: ширина станет
     зависеть от самой длинной записи, и одно длинное имя разъедет таблицу
   @dont Задавать grid-template-columns на КАЖДОЙ строке: строки посчитают
     ширину каждая по своему содержимому, и столбцы запляшут (ошибка макета
     маршрута чертежа 2026-08-13)
   @dont Брать <table> ради выравнивания: строка-кнопка и вложенные примитивы
     в таблицу не ложатся
   @do Много столбцов - обернуть в .dtable-scroll и задать --dtable-min: на узком
     экране таблица прокрутится целиком вместо того, чтобы смяться
   @sample <div class="dtable" style="--dtable-cols:2fr 1fr auto">
     <div class="dtable__row dtable__head"><span>Имя</span><span>Состояние</span><span></span></div>
     <button class="dtable__row"><span>Сдвиг</span><span>Готов</span><span>…</span></button></div> */
.dtable{display:grid;grid-template-columns:var(--dtable-cols, 1fr);gap:var(--space-2);}
.dtable__row{display:grid;grid-column:1/-1;grid-template-columns:subgrid;align-items:center;
  gap:var(--space-4);padding:var(--space-3) var(--space-4);text-align:left;font:inherit;color:inherit;
  background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);}
.dtable__row > *{min-width:0;}
button.dtable__row{cursor:pointer;}
button.dtable__row:hover{background:var(--state-hover);}
button.dtable__row:focus-visible{outline:none;box-shadow:var(--focus-ring);}
.dtable__head{background:transparent;border-color:transparent;padding-top:0;padding-bottom:0;
  font:700 var(--text-xs)/1 var(--font-body);color:var(--color-muted);text-transform:uppercase;
  letter-spacing:.04em;}
.dtable__head:hover{background:transparent;}
/* Узкий экран: таблица прокручивается целиком, а не сминается. Без этого колонки
   налезают друг на друга - имена перекрываются, значения садятся поверх текста
   (найдено на планшетной ширине 2026-08-19, раздел «Мои квоты»).
   Прокрутка выбрана вместо сминания намеренно: список из шести-восьми столбцов
   несет разные величины, и любая из них может быть той, ради которой человек сюда
   пришел - прятать столбцы значит решать за него.
   Ширину, ниже которой начинается прокрутка, задает потребитель через --dtable-min:
   у таблицы из трех столбцов и у таблицы из восьми она разная. */
/* @kit Имя с кнопкой действия
   @group Данные
   @about Имя человека в строке списка и действие рядом с ним: спросить, написать,
     открыть карточку.
   @do Кнопка держит место ВСЕГДА - иначе колонка дергается, когда курсор входит
     в строку и выходит
   @do Проявлять кнопку под курсором и по клавиатурному фокусу: постоянно видимая
     кнопка у каждого имени превращает список в частокол органов управления
   @sample <span class="person"><span class="who">Сергей Панов</span>
     <button class="icon-btn icon-btn--sm person__ask"><span class="icon i-message-square"></span></button></span> */
.person{display:inline-flex;align-items:center;gap:var(--space-1);min-width:0;}
.person__ask{opacity:0;transition:opacity var(--motion-fast) var(--ease-standard);flex:none;}
.person:hover .person__ask,
.person__ask:focus-visible{opacity:1;}
/* Имя в строке списка: приглушенное, в одну строку, длинное - многоточием. */
.who{font:var(--text-sm)/1.2 var(--font-body);color:var(--color-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* @kit Ячейка таблицы-списка
   @group Данные
   @about Текст в ячейке: одна строка, длинное - многоточием. Ширину столбца
     задаёт контейнер, и она не должна зависеть от того, что в ячейку попало.
   @do Помечать ЛЮБУЮ текстовую ячейку, где значение может оказаться длинным:
     имя человека, название чертежа, перечень участников
   @do Столбцы задавать долями (1fr) или точной шириной; auto и max-content
     растят столбец по самой длинной записи - от одного длинного имени таблица
     разъезжается вширь у всех строк сразу
   @dont Полагаться на то, что «имена короткие»: длинное придёт от живых данных
   @sample <span class="dtable__cell">Дмитрий Сорокин, Марина Титова</span> */
.dtable__cell{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

.dtable-scroll{overflow-x:auto;}
.dtable-scroll > .dtable{min-width:var(--dtable-min, 720px);}

/* ══ Bars (горизонтальный мини бар-чарт) ══ */
/* @kit-internal обёртка над мини-бар-чартом */
.bars{display:flex;flex-direction:column;gap:var(--space-3);}
/* @kit Мини-бар-чарт
   @group Данные
   @about Несколько величин в сравнении одной строкой: спрос по ролям, голоса, доли.
   @do Для сравнения между собой; одна доля от целого — это полоса выполнения
   @dont Больше 5–7 полос без сортировки: глазом не сравнить
   @sample <div class="bar"><span>Стратегия</span><i style="width:70%"></i></div> */
.bar{display:grid;grid-template-columns:1fr auto;gap:var(--space-05) var(--space-2);align-items:center;}
.bar__l{display:flex;align-items:center;gap:var(--space-1);min-width:0;
  font:600 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.bar__l .icon{--size-icon:var(--space-3);flex:none;}
.bar__n{font:700 var(--text-xs)/1 var(--font-body);color:var(--color-muted);white-space:nowrap;}
.bar__track{display:block;grid-column:1/-1;height:var(--space-3);background:var(--color-surface-2);border-radius:var(--radius-pill);overflow:hidden;}
.bar__fill{display:block;height:100%;border-radius:var(--radius-pill);}
.bar__fill--accent{background:var(--color-accent);}
.bar__fill--success{background:var(--color-success);}
.bar__fill--warning{background:var(--color-warning);}
.bar__fill--danger{background:var(--color-danger);}
.bar__fill--purple{background:var(--color-purple);}
.bar__fill--muted{background:var(--color-muted);}

/* ══ Radar (паутинка компетенций: свой уровень × цель) ══
   SVG строится в primitives.js → initRadars(), по data-axes="Ось:свой:цель,...".
   Координаты/viewBox генератора — неизбежная SVG-геометрия (числа без px, user units). */
/* @kit-internal обёртка над радарами */
.radars{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);}
/* @kit Радар компетенций
   @group Оценка
   @about Профиль участника по осям компетенций: свой уровень, целевой, пик.
   @do Компетенция красится цветом своего кластера — см. раздел «Кластеры»
   @do Целевой профиль — пунктиром, свой — заливкой: их постоянно сравнивают
   @dont Больше девяти осей: подписи перестают читаться
   @sample <svg class="radar">…</svg> */
.radar{display:flex;flex-direction:column;align-items:center;gap:var(--space-1);overflow:hidden;
  border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-2);background:var(--color-surface);}
.radar__title{display:flex;align-items:center;gap:var(--space-1);font:700 var(--text-xs)/1 var(--font-body);color:var(--color-text);}
.radar__title::before{content:"";width:var(--space-2);height:var(--space-2);border-radius:3px;background:currentColor;flex:none;}
.radar svg{width:100%;height:auto;display:block;overflow:visible;}
.radar .rg-ring{fill:none;stroke:var(--color-border);stroke-width:1;}
.radar .rg-axis{stroke:var(--color-border);stroke-width:1;}
.radar .rg-gap{stroke:none;}
.radar .rg-target{fill:none;stroke:currentColor;stroke-width:1.5;stroke-dasharray:4 3;stroke-linejoin:round;opacity:.9;}
.radar .rg-own{fill:currentColor;fill-opacity:.34;stroke:currentColor;stroke-width:2;stroke-linejoin:round;}
.radar .rg-dot{fill:currentColor;}
.radar .rg-tdot{fill:var(--color-surface);stroke:currentColor;stroke-width:1.3;}
/* font-size намеренно не в CSS: SVG-геометрия виджета (расстояние меток от центра) рассчитана
   под компактный кегль, заданный числом в primitives.js → initRadars() (SVG-атрибут font-size). */
.radar .rg-label{fill:var(--color-muted);font-family:var(--font-body);font-weight:600;}
.radar__legend{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--space-2);margin-top:var(--space-05);}
.radar__legend .lg{display:inline-flex;align-items:center;gap:var(--space-1);font:600 var(--text-xs)/1 var(--font-body);color:var(--color-muted);}
.radar__legend .lg::before{content:"";width:var(--space-3);height:var(--space-2);flex:none;}
.radar__legend .lg--own::before{background:currentColor;opacity:.5;border-radius:2px;}
.radar__legend .lg--tgt::before{border:1px dashed currentColor;border-radius:2px;background:transparent;}
.radar__legend .lg--gap::before{background:repeating-linear-gradient(45deg,currentColor 0 var(--space-05),transparent var(--space-05) var(--space-1));opacity:.6;border-radius:2px;}
.radar__scale{font:var(--text-xs)/1 var(--font-body);color:var(--color-muted);}

/* ══ Poster (постер-афиша мероприятия: один каркас под роль × стадия) ══
   Источник объединения: «Афиша мероприятия» (участник/ассессор, лента ЖЦ,
   расписание) + кабинет Организатора US-9 (команды с входом на доску,
   приглашение модератора по QR). Паутинные диаграммы компетенций (.radar*
   выше) используются ВНУТРИ афиши через .block, но не входят в каркас
   постера — кандидат в будущий dataviz-примитив кита, здесь не портируется. */
/* @kit Постер афиши
   @group Афиша
   @about Карточка мероприятия целиком: обложка, факты, команды, ведущие, расписание,
     приглашение. Один каркас на все роли и стадии жизненного цикла.
   @do Обложка — по типу игры: Испытание и Прокачка различаются с первого взгляда
   @dont Собирать свою вёрстку карточки под роль: каркас один, различия — состоянием
   @sample <article class="poster">…</article> */
.poster{display:flex;flex-direction:column;background:var(--color-surface);
  border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;
  box-shadow:var(--elev-2);}

.poster__cover{position:relative;padding:var(--space-5);min-height:var(--poster-cover-min-h);
  display:flex;flex-direction:column;justify-content:flex-end;overflow:hidden;color:var(--color-on-accent);}
.poster__cover--test{background:var(--poster-cover-test);}
.poster__cover--learn{background:var(--poster-cover-learn);}
.poster__wm{position:absolute;right:var(--poster-wm-offset-x);bottom:var(--poster-wm-offset-y);
  color:var(--color-on-accent);opacity:.16;pointer-events:none;}
.poster__wm .icon{--size-icon:var(--size-icon-wm);}
.poster__badges{position:absolute;top:var(--space-4);left:var(--space-5);right:var(--space-5);
  display:flex;align-items:center;gap:var(--space-2);}
.poster__badges .spacer{flex:1;}
/* @kit-internal чип участника */
.pchip{display:inline-flex;align-items:center;gap:var(--space-1);border-radius:var(--radius-pill);
  padding:var(--space-1) var(--space-3);font:700 var(--text-xs)/1 var(--font-body);
  background:var(--poster-scrim-chip);color:var(--color-text);backdrop-filter:blur(2px);}
.pchip .icon{--size-icon:var(--space-3);}
.pchip--stage{background:var(--poster-scrim-stage);color:var(--color-on-accent);}
.poster__close{margin-left:var(--space-2);width:var(--size-badge-round);height:var(--size-badge-round);
  border-radius:var(--radius-pill);border:none;background:var(--poster-scrim-chip);color:var(--color-text);
  display:grid;place-items:center;cursor:pointer;flex:none;}
.poster__close .icon{--size-icon:var(--space-4);}
.poster__title{position:relative;font:900 var(--text-xl)/1.1 var(--font-display);margin:0;
  text-shadow:var(--poster-title-shadow);}
.poster__sub{position:relative;margin-top:var(--space-1);font:var(--text-sm)/1.35 var(--font-body);
  color:var(--poster-sub-color);text-shadow:var(--poster-sub-shadow);max-width:44ch;}

.poster__body{padding:var(--space-4) var(--space-5);display:flex;flex-direction:column;gap:var(--space-4);}

/* ── лента жизненного цикла игры ── */
/* @kit-internal лента жизненного цикла афиши */
.lifecycle{display:flex;align-items:flex-start;}
/* @kit-internal шаг жизненного цикла */
.lcstep{flex:1;display:flex;flex-direction:column;align-items:center;gap:var(--space-1);position:relative;text-align:center;}
.lcstep::before,.lcstep::after{content:"";position:absolute;top:var(--space-3);height:var(--border-thick);
  background:var(--color-track);z-index:0;}
.lcstep::before{left:0;right:50%;}
.lcstep::after{left:50%;right:0;}
.lcstep:first-child::before{display:none;}
.lcstep:last-child::after{display:none;}
.lcstep__dot{position:relative;z-index:1;width:var(--size-badge-sm);height:var(--size-badge-sm);
  border-radius:var(--radius-pill);display:grid;place-items:center;background:var(--color-surface-2);
  color:var(--color-muted);border:var(--border-thick) solid var(--color-track);font:800 var(--text-xs)/1 var(--font-body);}
.lcstep__dot .icon{--size-icon:var(--space-3);}
.lcstep__label{font:600 var(--text-xs)/1.1 var(--font-body);color:var(--color-muted);}
.lcstep.is-done .lcstep__dot{background:var(--color-success);border-color:var(--color-success);color:var(--color-on-accent);}
.lcstep.is-done::before,.lcstep.is-done::after{background:var(--color-success);}
.lcstep.is-current .lcstep__dot{background:var(--color-accent);border-color:var(--color-accent);color:var(--color-on-accent);
  box-shadow:0 0 0 var(--space-1) var(--color-accent-weak);}
.lcstep.is-current::before{background:var(--color-success);}
.lcstep.is-current .lcstep__label{color:var(--color-accent-active);font-weight:800;}

/* ── честный масштаб (без содержания) ── */
/* @kit-internal лента фактов афиши */
.facts{display:flex;flex-wrap:wrap;gap:var(--space-3) var(--space-4);}
/* @kit-internal факт на афише */
.fact{display:inline-flex;align-items:center;gap:var(--space-1);font:var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.fact .icon{--size-icon:var(--space-4);color:var(--color-muted);}
.fact b{font-weight:700;}

/* ── секции афиши (заголовок-капс + содержимое) ── */
/* @kit-internal раскладочная обёртка */
.block{display:flex;flex-direction:column;gap:var(--space-2);}
.block__cap{display:flex;align-items:center;gap:var(--space-1);font:700 var(--text-xs)/1 var(--font-body);
  color:var(--color-muted);text-transform:uppercase;letter-spacing:.05em;}
.block__cap .icon{--size-icon:var(--space-3);}
.block__cap .spacer{flex:1;}
.block__cap .mini{font-weight:600;letter-spacing:0;text-transform:none;color:var(--color-muted);}

/* ── живое состояние (присутствие / прогресс) ── */
/* @kit Карта активности
   @group Данные
   @about Что происходит в сессии прямо сейчас: кто подключился, что изменилось.
   @do Для живого хода игры; итог после неё — это обычный список
   @sample <div class="live">…</div> */
.live{display:flex;flex-direction:column;gap:var(--space-2);padding:var(--space-3);
  border-radius:var(--radius-md);background:var(--color-surface-2);}
.live__row{display:flex;align-items:center;gap:var(--space-2);font:var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.live__row .icon{--size-icon:var(--space-4);color:var(--color-muted);}
.live__row b{font-weight:700;}
.live__row .spacer{flex:1;}

/* ── команды + вход на общую доску команды (US-9) ── */
/* @kit-internal список команд афиши */
.teams{display:flex;flex-direction:column;gap:var(--space-2);}
/* @kit-internal строка команды */
.team{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);
  border:1px solid var(--color-border);border-radius:var(--radius-md);}
.team > .icon{--size-icon:var(--space-4);color:var(--color-muted);flex:none;}
.team__n{font:700 var(--text-sm)/1.2 var(--font-body);}
.team__m{margin-left:auto;font:var(--text-xs)/1 var(--font-body);color:var(--color-muted);}
.team .btn{flex:none;}

/* ── ведущие (организатор / главный модератор / модератор) ── */
/* @kit-internal список ведущих афиши */
.leads{display:flex;flex-direction:column;gap:var(--space-05);}
/* @kit-internal строка ведущего */
.lead-row{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) 0;}
/* @kit-internal значок роли ведущего */
.lead-role{display:inline-grid;place-items:center;width:var(--size-badge-round);height:var(--size-badge-round);
  border-radius:var(--radius-pill);background:var(--color-accent-weak);color:var(--color-accent-active);flex:none;}
.lead-role .icon{--size-icon:var(--space-4);}
.lead-role--org{background:var(--color-warning-weak);color:var(--color-warning-strong);}
.lead-role--lead{background:var(--color-accent-weak);color:var(--color-accent-active);}
/* @kit-internal имя ведущего */
.lead-name{font:600 var(--text-sm)/1.1 var(--font-body);color:var(--color-text);}
/* @kit-internal подпись ведущего */
.lead-cap{font:var(--text-xs)/1.1 var(--font-body);color:var(--color-muted);margin-left:auto;}
.lead-row .icon-btn{margin-left:var(--space-2);}

/* ── приглашение модератора по QR-ссылке ── */
/* @kit Приглашение
   @group Данные
   @about Зов присоединиться: к доске, команде, мероприятию. Ссылка или QR.
   @do Рядом — что получит вошедший: куда именно и зачем зовут
   @sample <div class="invite">…</div> */
.invite{display:grid;grid-template-columns:auto 1fr;gap:var(--space-3);align-items:center;
  padding:var(--space-3);border:1px dashed var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-2);}
/* @kit QR-код
   @group Данные
   @about Вход по камере телефона: подключиться к сессии без ввода ссылки.
   @do Крупно и с подписью, куда ведёт: код без пояснения сканируют неохотно
   @sample <div class="qr-box"><img src="…" /></div> */
.qr-box{width:var(--size-qr-box);height:var(--size-qr-box);border-radius:var(--radius-sm);background:var(--color-surface);
  border:1px solid var(--color-border);display:grid;place-items:center;color:var(--slate-400);flex:none;}
.qr-box .icon{--size-icon:var(--size-icon-qr);}
.invite__t{font:700 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.invite__d{font:var(--text-xs)/1.4 var(--font-body);color:var(--color-muted);margin:var(--space-05) 0 var(--space-2);}
.invite__link{display:flex;gap:var(--space-2);align-items:center;}
.invite__link .field{flex:1;}

/* ── расписание дней (сеансы, без заданий) ── */
/* @kit-internal расписание афиши */
.sched{display:flex;flex-direction:column;}
/* @kit-internal строка расписания афиши */
.day{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2);
  border-radius:var(--radius-md);border:1px solid transparent;}
.day + .day{margin-top:var(--space-05);}
/* Кружок дня по ОДНОМУ правилу (решение PO 2026-08-31): всюду НОМЕР, состояние
   различается заливкой - пройденный сплошной, идущий с контуром, будущий светлый.
   Раньше пройденный день рисовался галочкой на зелёном, и глаз переключался между
   двумя разными значками, а зелёный обещал «успех», хотя пройденный день - это
   просто «уже было», без оценки результата. Галочку в разметке дня больше не
   ставят: номер отвечает на вопрос «какой это день», а состояние читается цветом. */
.day__n{width:var(--size-badge-sm);height:var(--size-badge-sm);border-radius:var(--radius-pill);display:grid;place-items:center;flex:none;
  background:var(--color-accent-weak);color:var(--color-accent-active);
  border:var(--border-thick) solid transparent;font:800 var(--text-xs)/1 var(--font-body);}
.day__n .icon{--size-icon:var(--space-3);}
.day__body{flex:1;min-width:0;}
.day__title{font:600 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.day__meta{font:var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);}
.day.is-done .day__n{background:var(--color-accent);color:var(--color-on-accent);}
.day.is-current{background:var(--color-accent-weak);border-color:color-mix(in oklab, var(--color-accent) 30%, transparent);}
.day.is-current .day__n{border-color:var(--color-accent);}
/* Будущий день не гасим целиком: заливка кружка выцветает вместе со строкой, и
   правило перестаёт читаться. Приглушаем только текст. */
.day.is-todo .day__title,.day.is-todo .day__meta{color:var(--color-muted);}

/* ── футер действия (билет: роль + одно крупное действие) ── */
.poster__action{padding:var(--space-4) var(--space-5);border-top:1px solid var(--color-border);
  background:var(--color-surface);display:flex;flex-direction:column;gap:var(--space-2);}
/* @kit-internal значок своей роли */
.myrole{display:flex;align-items:center;gap:var(--space-2);font:var(--text-sm)/1.2 var(--font-body);color:var(--color-muted);}
.myrole b{color:var(--color-text);font-weight:700;}
.myrole .rb{display:inline-grid;place-items:center;width:var(--space-6);height:var(--space-6);border-radius:var(--radius-pill);
  background:var(--color-accent-weak);color:var(--color-accent-active);flex:none;}
.myrole .rb .icon{--size-icon:var(--space-4);}
/* @kit-internal акт афиши */
.act-row{display:flex;gap:var(--space-2);align-items:center;}
.act-row .spacer{flex:1;}
/* @kit-internal акт афиши */
.act-note{font:var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);text-align:center;}

/* ══ Элементы холста: стикеры, голосование, фигуры, поток, текст, коннекторы,
   изображение, таблица. Витрина - раздел «Холст» в kit.html. ══ */
/* цвета стикера — палитра контента холста (пастельные токены, единые для холста/промо) */
.sticky--yellow{background:var(--pastel-yellow);}
.sticky--amber{background:var(--pastel-amber);}
.sticky--rose{background:var(--pastel-rose);}
.sticky--purple{background:var(--pastel-purple);}
.sticky--sky{background:var(--pastel-sky);}
.sticky--green{background:var(--pastel-green);}
.sticky--slate{background:var(--color-surface-2);}
/* формы стикера (реальные пресеты холста: квадрат по умолчанию, прямоугольник, круг) */
.sticky--rect{aspect-ratio:auto;width:calc(var(--space-16) * 2.5);min-height:calc(var(--space-16) * 1.4);}
.sticky--circle{aspect-ratio:1;border-radius:var(--radius-pill);display:grid;place-items:center;text-align:center;}
.sticky--circle::after{display:none;}
/* компактный стикер — для миниатюр слайда и промо-раскладок */
.sticky--sm{width:calc(var(--space-16) * 1.4);padding:var(--space-3);font:600 var(--text-sm)/1.35 var(--font-body);}
.sticky--sm.sticky--rect{width:calc(var(--space-16) * 1.9);min-height:calc(var(--space-12));}

/* ══ Голосование на холсте (виджет опроса — ближайшая доработка Слайдера) ══ */
.vote{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);
  box-shadow:var(--elev-2);padding:var(--space-4);width:calc(var(--space-16) * 3.6);box-sizing:border-box;
  display:flex;flex-direction:column;gap:var(--space-3);}
.vote__title{display:flex;align-items:center;gap:var(--space-2);font:700 var(--text-md)/1.2 var(--font-body);color:var(--color-text);}
.vote__title .icon{--size-icon:16px;color:var(--color-muted);}
.vote__opt{display:flex;flex-direction:column;gap:var(--space-1);}
.vote__row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);
  font:var(--text-sm)/1.2 var(--font-body);color:var(--color-text);}
.vote__pct{color:var(--color-muted);font-weight:700;flex:none;}
.vote__bar{height:var(--space-2);border-radius:var(--radius-pill);background:var(--color-surface-2);overflow:hidden;}
.vote__fill{display:block;height:100%;border-radius:var(--radius-pill);background:var(--color-accent);
  transition:width var(--motion-base) var(--ease-standard);}
.vote__fill--success{background:var(--color-success);}
.vote__fill--warning{background:var(--color-warning);}
.vote__fill--purple{background:var(--color-purple);}
.vote__opt[aria-checked="true"] .vote__row{font-weight:700;color:var(--color-accent);}
.vote__foot{display:flex;align-items:center;justify-content:space-between;
  font:var(--text-xs)/1 var(--font-body);color:var(--color-muted);padding-top:var(--space-1);}

/* ══ Фигуры холста (SHAPE_LIBRARY) — заливка + обводка на токенах ══ */
.shape{position:relative;display:grid;place-items:center;width:calc(var(--space-16) * 1.8);aspect-ratio:1;
  box-sizing:border-box;padding:var(--space-2);text-align:center;
  background:color-mix(in srgb, var(--color-accent) 10%, var(--color-surface));
  border:var(--border-thick) solid var(--color-accent);color:var(--color-text);
  font:600 var(--text-sm)/1.2 var(--font-body);box-shadow:var(--elev-1);}
.shape--slate{background:var(--color-surface);border-color:var(--slate-400);}
.shape--success{background:color-mix(in srgb,var(--color-success) 12%,var(--color-surface));border-color:var(--color-success);}
.shape--warning{background:color-mix(in srgb,var(--color-warning) 12%,var(--color-surface));border-color:var(--color-warning);}
.shape--danger{background:color-mix(in srgb,var(--color-danger) 12%,var(--color-surface));border-color:var(--color-danger);}
.shape--purple{background:color-mix(in srgb,var(--color-purple) 12%,var(--color-surface));border-color:var(--color-purple);}
/* формы с радиусами (обводка рендерится корректно) */
.shape--rect{aspect-ratio:16/10;border-radius:0;}
.shape--rounded{aspect-ratio:16/10;border-radius:var(--radius-md);}
.shape--circle{border-radius:var(--radius-pill);}
.shape--capsule{aspect-ratio:16/7;border-radius:var(--radius-pill);}
/* формы через clip-path — заливкой (обводку срезал бы clip), берут плотный цвет */
.shape--clip{border:none;background:var(--color-accent);color:var(--color-on-accent);box-shadow:none;}
.shape--clip.shape--slate{background:var(--slate-500);}
.shape--clip.shape--success{background:var(--color-success);}
.shape--clip.shape--warning{background:var(--color-warning);}
.shape--clip.shape--danger{background:var(--color-danger);}
.shape--clip.shape--purple{background:var(--color-purple);}
.shape--triangle{clip-path:polygon(50% 2%,98% 98%,2% 98%);}
.shape--diamond{clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%);}
.shape--pentagon{clip-path:polygon(50% 0,100% 38%,82% 100%,18% 100%,0 38%);}
.shape--hexagon{clip-path:polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%);}
.shape--octagon{clip-path:polygon(30% 0,70% 0,100% 30%,100% 70%,70% 100%,30% 100%,0 70%,0 30%);}
.shape--parallelogram{aspect-ratio:16/9;clip-path:polygon(24% 0,100% 0,76% 100%,0 100%);}
.shape--trapezoid{aspect-ratio:16/9;clip-path:polygon(20% 0,80% 0,100% 100%,0 100%);}
/* линия как объект-фигура */
.shape--line{aspect-ratio:auto;width:calc(var(--space-16) * 2);height:0;background:none;border:none;
  border-top:var(--border-thick) solid var(--color-text);box-shadow:none;padding:0;}

/* ══ Текстовый блок холста ══ */
.text-block{font:var(--text-lg)/1.45 var(--font-body);color:var(--color-text);
  padding:var(--space-2) var(--space-3);border:1px solid transparent;max-width:calc(var(--space-16) * 4);}
.text-block--bordered{border-color:var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);}
.text-block__ph{color:var(--color-muted);}

/* ══ Коннектор (линия-связь со стрелкой; пути curve/ortho/bezier рендерит SVG холста) ══ */
.connector{display:inline-flex;align-items:center;color:var(--color-text);}
.connector__line{width:calc(var(--space-16) * 1.6);height:0;border-top:var(--border-thick) solid currentColor;}
.connector--dashed .connector__line{border-top-style:dashed;}
.connector--dotted .connector__line{border-top-style:dotted;}
.connector--accent{color:var(--color-accent);}
.connector--danger{color:var(--color-danger);}
.connector__arrow{width:0;height:0;border-top:var(--space-1) solid transparent;border-bottom:var(--space-1) solid transparent;
  border-left:var(--space-2) solid currentColor;}

/* ══ Изображение на холсте (рамка-объект) ══ */
.canvas-image{position:relative;display:grid;place-items:center;width:calc(var(--space-16) * 2.4);aspect-ratio:16/10;
  background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--radius-sm);
  overflow:hidden;color:var(--color-muted);box-shadow:var(--elev-1);}
.canvas-image .icon{--size-icon:var(--space-6);}

/* ══ Таблица холста (мини-объект) ══ */
.canvas-table{border-collapse:collapse;border-radius:var(--radius-sm);overflow:hidden;
  background:var(--color-surface);box-shadow:var(--elev-1);font:var(--text-sm)/1.2 var(--font-body);}
.canvas-table th,.canvas-table td{border:1px solid var(--color-border);padding:var(--space-2) var(--space-3);text-align:left;}
.canvas-table th{background:var(--color-surface-2);color:var(--color-muted);font-weight:700;
  font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.3px;}
.canvas-table td{color:var(--color-text);}
.canvas-table .num{text-align:right;font-variant-numeric:tabular-nums;}
.canvas-table tr.is-top td{background:var(--color-accent-weak);color:var(--color-accent);font-weight:700;}

/* ══ Логотип-монограмма BOOG (BO/OG) — точки-спутники с кометным хвостом ══ */
.as-boog{position:relative;box-sizing:border-box;width:var(--brand-logo-size);height:var(--brand-logo-size);padding:var(--brand-logo-pad);
  display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;place-items:center;
  font-family:var(--font-display);font-weight:900;font-size:var(--brand-logo-font);line-height:.8;letter-spacing:-.3px;
  border-radius:var(--radius-pill);background:var(--color-surface);
  box-shadow:inset 0 0 0 var(--brand-logo-ring) var(--color-border);color:var(--brand-logo-ink);}
.as-boog>span{display:block;}
/* B и G — на той же орбите (.72em), что и точки-спутники, и в той же системе
   отсчета: абсолютно, от центра знака. Раскладка по ячейкам сетки задавала
   абсолютным буквам областью отсчета саму ячейку, а не знак целиком, из-за чего
   буква G уезжала вниз-вправо за круг. Синхронно с .as-boog в whiteboard-server. */
.as-boog>span:nth-child(1),.as-boog>span:nth-child(4){position:absolute;left:50%;top:50%;
  width:1em;height:1em;margin:-.5em 0 0 -.5em;display:grid;place-items:center;}
.as-boog>span:nth-child(1){transform:rotate(225deg) translate(.72em) rotate(-225deg);}
.as-boog>span:nth-child(4){transform:rotate(45deg) translate(.72em) rotate(-45deg);}
.as-boog>span:nth-child(2),.as-boog>span:nth-child(3){position:absolute;left:50%;top:50%;
  width:.71em;height:.71em;margin:-.355em 0 0 -.355em;border-radius:var(--radius-pill);color:transparent;overflow:hidden;}
.as-boog>span:nth-child(2){background:var(--brand-logo-blue);animation:as-boog-a 30s ease-in-out infinite;}
.as-boog>span:nth-child(3){background:var(--brand-logo-red);animation:as-boog-b 30s ease-in-out infinite;}
@keyframes as-boog-a{0%{transform:rotate(-45deg) translate(.72em);}10%,50%{transform:rotate(135deg) translate(.72em);}60%,100%{transform:rotate(315deg) translate(.72em);}}
@keyframes as-boog-b{0%{transform:rotate(135deg) translate(.72em);}10%,50%{transform:rotate(315deg) translate(.72em);}60%,100%{transform:rotate(495deg) translate(.72em);}}
.as-boog::before,.as-boog::after{content:'';position:absolute;inset:0;border-radius:var(--radius-pill);
  -webkit-mask:radial-gradient(closest-side,transparent 22%,black 31%,black 56%,transparent 66%);
  mask:radial-gradient(closest-side,transparent 22%,black 31%,black 56%,transparent 66%);
  filter:blur(var(--brand-logo-blur));opacity:0;pointer-events:none;}
.as-boog::before{background:conic-gradient(from 35deg,transparent,var(--brand-logo-blue) 55deg,transparent 55deg);
  animation:as-boog-rota 30s ease-in-out infinite,as-boog-trail 30s ease-in-out infinite;}
.as-boog::after{background:conic-gradient(from 35deg,transparent,var(--brand-logo-red) 55deg,transparent 55deg);
  animation:as-boog-rotb 30s ease-in-out infinite,as-boog-trail 30s ease-in-out infinite;}
@keyframes as-boog-rota{0%{rotate:-45deg}10%,50%{rotate:135deg}60%,100%{rotate:315deg}}
@keyframes as-boog-rotb{0%{rotate:135deg}10%,50%{rotate:315deg}60%,100%{rotate:495deg}}
@keyframes as-boog-trail{0%,1%{opacity:0}3%{opacity:.75}8%{opacity:.75}11%{opacity:0}50%{opacity:0}52%{opacity:.75}57%{opacity:.75}61%{opacity:0}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){.as-boog>span,.as-boog::before,.as-boog::after{animation:none!important;}.as-boog::before,.as-boog::after{display:none;}}

/* ══ Блок-схема / flow-chart (узлы процесса + стрелки-связи) ══ */
.flow{display:inline-flex;align-items:center;flex-wrap:wrap;gap:0;}
.flow__node{display:grid;place-items:center;min-width:calc(var(--space-16) * 1.1);
  padding:var(--space-2) var(--space-3);text-align:center;white-space:nowrap;
  background:color-mix(in srgb, var(--color-accent) 10%, var(--color-surface));
  border:var(--border-thick) solid var(--color-accent);border-radius:var(--radius-md);
  font:600 var(--text-sm)/1.15 var(--font-body);color:var(--color-text);box-shadow:var(--elev-1);}
.flow__node--start{border-radius:var(--radius-pill);border-color:var(--color-success);
  background:color-mix(in srgb, var(--color-success) 12%, var(--color-surface));}
.flow__node--end{border-radius:var(--radius-pill);border-color:var(--color-purple);
  background:color-mix(in srgb, var(--color-purple) 12%, var(--color-surface));}
.flow__node--decision{border-color:var(--color-warning);
  background:color-mix(in srgb, var(--color-warning) 12%, var(--color-surface));}
.flow__link{flex:none;position:relative;width:var(--space-6);height:0;
  border-top:var(--border-thick) solid var(--color-muted);}
.flow__link::after{content:"";position:absolute;right:calc(var(--space-05) * -1);top:50%;transform:translateY(-50%);
  width:0;height:0;border-top:var(--space-1) solid transparent;border-bottom:var(--space-1) solid transparent;
  border-left:var(--space-2) solid var(--color-muted);}
.flow__link--dashed{border-top-style:dashed;}
/* вертикальная ветка (напр. «нет» вниз от решения) */
.flow__link--down{width:0;height:var(--space-5);border-top:none;border-left:var(--border-thick) solid var(--color-muted);}
.flow__link--down::after{right:auto;left:50%;top:auto;bottom:calc(var(--space-05) * -1);transform:translateX(-50%);
  border-top:var(--space-2) solid var(--color-muted);border-left:var(--space-1) solid transparent;border-right:var(--space-1) solid transparent;border-bottom:none;}

/* ══ Gantt (лента периодов) ══ */
/* @kit Лента периодов
   @group Данные
   @about Календарь-лента: строка на объект, полоса - его период на шкале дней.
     Отвечает на «что идет сейчас, что горит, что уже закрыто» - очередь работ
     ассессора, набор по вакансиям, расписание мероприятий.
     Не путать с .timeline: та показывает этапы одного объекта, эта - множество
     объектов во времени.
   @do Полосу собирать из отрезков .gantt__leg - по отрезку на каждое состояние,
     тогда история читается прямо в полосе, без раскрытия карточки
   @do Состояние задавать ролями --gantt-line/--gantt-fill на самой полосе или
     отрезке: цвет здесь несет состояние, а не украшает
   @do Срок - .gantt__due (булавка), текущий день - .gantt__now
   @dont Ставить одновременно и цвет полосы, и цветную подпись рядом: подпись
     повторит то, что уже сказано цветом
   @do Ширину дня задавать токеном --gantt-day-w (по умолчанию --space-10):
     от нее зависит, сколько дней помещается и остается ли куда прокручивать
   @dont Ужимать дни под ширину экрана (1fr): тогда прокручивать нечего, и лента
     врет - показывает ровно то окно, которое влезло, а не жизнь вакансии
   @dont Заводить свою сетку дней в проекте - число колонок передается через
     grid-template-columns, разметка остается китовой
   @do Шкалу, дорожки и вертикаль «сегодня» класть в .gantt__canvas - полотно
     шириной по числу дней. Без него доли, которыми ставятся полосы, считаются
     от видимого окна, а дни занимают всю длину ленты: полосы разъезжаются со
     шкалой, и лента показывает не те даты
   @sample <div class="gantt"><div class="gantt__aside">…</div>
     <div class="gantt__days"><div class="gantt__canvas">…</div></div></div> */
.gantt{display:grid;grid-template-columns:minmax(0,var(--gantt-aside-w, calc(var(--space-16) * 3.5))) minmax(0,1fr);
  --gantt-day-w:var(--space-10);}
.gantt__aside{border-right:1px solid var(--color-border);}
/* Полоса дней прокручивается по горизонтали - в прошлое и в будущее. Левая
   колонка с названиями лежит ВНЕ этой области, поэтому остается на месте.
   Инерционную прокрутку трекпада браузер дает сам; колесо мыши переводит в
   горизонтальную initGanttScroll() из primitives.js. */
.gantt__days{position:relative;overflow-x:auto;overscroll-behavior-x:contain;
  scrollbar-width:thin;}
/* Полотно внутри прокрутки: ширина по числу дней, не по видимому окну. Полосы и
   вертикаль «сегодня» ставятся долей от полотна, поэтому без своей ширины они
   считались бы от узкой видимой области - дни уезжали бы вправо, а полосы
   оставались бы на месте, и лента показывала бы не те даты. */
.gantt__canvas{position:relative;width:max-content;min-width:100%;}
.gantt__scale,.gantt__lane{width:max-content;min-width:100%;}
.gantt__row{display:flex;flex-direction:column;justify-content:center;
  height:var(--gantt-row-h, calc(var(--space-12) + var(--space-1)));
  padding:0 var(--space-4);border-bottom:1px solid var(--color-border);}
.gantt__row:last-child{border-bottom:0;}
.gantt__name{font:600 var(--text-sm)/1.2 var(--font-body);color:var(--color-text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.gantt__sub{font:var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);}
.gantt__scale{display:grid;height:var(--space-10);background:var(--color-surface-2);
  border-bottom:1px solid var(--color-border);}
.gantt__day{display:flex;flex-direction:column;align-items:center;justify-content:center;
  border-right:1px solid var(--color-border);font:var(--text-xs)/1.2 var(--font-body);color:var(--color-muted);}
.gantt__dnum{font-weight:600;color:var(--color-text);}
.gantt__lane{position:relative;display:grid;height:var(--gantt-row-h, calc(var(--space-12) + var(--space-1)));
  border-bottom:1px solid var(--color-border);}
.gantt__lane:last-child{border-bottom:0;}
.gantt__cell{border-right:1px solid var(--color-border);}
/* Выходной или иной нерабочий день - подложкой, а не цветом текста. */
.gantt__cell--off,.gantt__day--off{background:var(--color-bg);}
.gantt__bar{position:absolute;top:var(--space-2);height:var(--space-8);display:flex;
  border-radius:var(--radius-md);overflow:hidden;border:1px solid var(--color-border);
  background:var(--color-surface);cursor:pointer;}
.gantt__bar:hover{box-shadow:var(--elev-2);}
.gantt__bar:focus-visible{outline:none;box-shadow:var(--focus-ring);}
/* Требует внимания: срок прошел, а работа не закрыта. */
.gantt__bar--alert{box-shadow:0 0 0 var(--border-thick) var(--color-danger);}
.gantt__leg{flex:1;min-width:0;background:var(--gantt-fill, var(--color-surface-2));
  box-shadow:inset 0 0 0 1px var(--gantt-line, var(--color-track));}
.gantt__leg + .gantt__leg{border-left:1px solid var(--color-surface);}
/* Прерванный отрезок - штриховкой: он в ленте, но им не гордятся. */
.gantt__leg--void{background:repeating-linear-gradient(135deg,
  var(--gantt-fill, var(--color-surface-2)), var(--gantt-fill, var(--color-surface-2)) var(--space-2),
  var(--color-surface) var(--space-2), var(--color-surface) var(--space-4));}
/* Число внутри полосы: сколько уже сделано или сколько собрано. */
.gantt__count{position:absolute;left:var(--space-2);top:50%;transform:translateY(-50%);
  display:inline-flex;align-items:center;gap:var(--space-1);
  font:600 var(--text-xs)/1 var(--font-body);color:var(--color-text);}
.gantt__count .icon{--size-icon:var(--space-3);color:var(--color-muted);}
/* Срок: булавка на шкале. */
.gantt__due{position:absolute;top:0;bottom:0;width:0;border-left:1px solid var(--color-danger);z-index:2;}
.gantt__due::before{content:"";position:absolute;left:calc(-1 * var(--space-1) - 1px);top:var(--space-1);
  width:var(--space-3);height:var(--space-3);background:var(--color-danger);
  border-radius:var(--radius-pill) var(--radius-pill) var(--space-05) var(--radius-pill);
  transform:rotate(45deg);}
/* Сегодня. */
.gantt__now{position:absolute;top:0;bottom:0;width:0;
  border-left:var(--border-thick) solid var(--color-accent);z-index:1;pointer-events:none;}

/* ── Переписка ───────────────────────────────────────────────────────────
   Лента сообщений: свои справа акцентом, чужие слева нейтралью, разделитель
   дня посередине. Облик задан перепиской со службой поддержки - решение PO
   2026-09-03 распространить его на переписку разговора.

   В кит вынесено, когда потребителей стало двое: поддержка рисовала пузыри
   своими правилами (.sa-msg), и переписка звонка повторила бы их третьим
   набором классов. Расходиться таким копиям нечем - только руками, и
   расходятся они всегда.

   Ширина пузыря в долях, а не в точках: лента живет и в широком окне
   поддержки, и в узкой панели разговора. */
.chat{display:flex;flex-direction:column;min-height:0;}
.chat__lenta{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--space-4);
  display:flex;flex-direction:column;gap:var(--space-3);}

/* Разделитель дня. Дата - не сообщение, поэтому стоит посередине и не
   притворяется пузырем. */
.chat__day{align-self:center;font:var(--text-xs)/1 var(--font-body);
  color:var(--color-muted);background:var(--color-surface-2);
  border:1px solid var(--color-border);border-radius:var(--radius-pill);
  padding:var(--space-1) var(--space-3);}

.chat__msg{max-width:74%;padding:var(--space-2) var(--space-3);
  border-radius:var(--radius-md);font:var(--text-sm)/1.45 var(--font-body);
  overflow-wrap:anywhere;}
/* Сам текст сообщения - здесь, и только здесь, переносы строк человека
   сохраняются как есть.

   ПОЧЕМУ НЕ НА ВСЕМ ПУЗЫРЕ, КАК БЫЛО. Сохранение переносов действует и на
   переводы строк В РАЗМЕТКЕ: время, набранное на следующей строке файла,
   уезжало под текст, сколько бы ни было велено держать его в строке. Пузырь
   из-за этого был почти втрое выше нужного (91 точка против 35 на короткой
   реплике, замерено 07.09.2026). */
.chat__text{white-space:pre-wrap;}
/* Свое - справа, мягкой заливкой в акцентном тоне. Угол у собственной стороны
   срезан меньше: так видно, от кого пузырь, даже когда подписи нет.

   ПОЧЕМУ НЕ НАСЫЩЕННЫЙ АКЦЕНТ. Им набрана густая плашка под каждой своей
   репликой, и в ленте из десятка сообщений переписка превращается в полосу
   яркого цвета - глазу не за что зацепиться, а насыщенный тон в ките занят
   языком действий (кнопка, фокус). Своя сторона и так опознается положением
   и срезанным углом; цвету достаточно намекнуть (правка PO 07.09.2026). */
.chat__msg--me{align-self:flex-end;background:var(--color-accent-weak);
  color:var(--color-text);border-bottom-right-radius:var(--radius-sm);}
.chat__msg--they{align-self:flex-start;background:var(--color-surface-2);
  border:1px solid var(--color-border);border-bottom-left-radius:var(--radius-sm);}

/* Имя автора - всегда отдельная строка над сообщением. `display:block` здесь
   не украшение: разметку пузыря собирают и строчным элементом (span), а у
   строчного вертикальный отступ не действует - имя прилипало к первому слову
   и читалось как часть текста («МаксимДоброе утро»). Поймано 07.09.2026 в
   переписке разговора. */
.chat__who{display:block;font:700 var(--text-xs)/1 var(--font-body);
  color:var(--color-muted);margin-bottom:var(--space-1);}
/* Время дописывается В КОНЕЦ последней строки текста, через пробел, а не
   отдельной строкой снизу: своей строкой оно прибавляло пузырю треть высоты
   ради четырех знаков, и короткая реплика выглядела крупнее, чем весит
   (правка PO 07.09.2026). `inline-block` держит его в строке независимо от
   того, каким элементом его собрали - строчным или блочным.

   Цвет - прозрачностью от текста пузыря, а не своим тоном: подложка у своей
   и чужой стороны разная, а приглушение работает на обеих. */
.chat__time{display:inline-block;font-size:var(--text-xs);opacity:.7;
  margin-left:var(--space-2);}

/* Упоминание через @ - акцентным тоном на обеих сторонах. Отдельное правило для
   своего пузыря было нужно, пока тот заливался насыщенным акцентом и упоминание
   на нем пропадало; на мягкой заливке акцент читается, и подчеркивание только
   спорит с ссылкой. */
.chat__mention{font-weight:600;color:var(--color-accent);}
/* Обратились ко мне - строка видна среди прочих. Иначе сообщение теряется
   ровно тогда, когда написано лично мне. */
.chat__msg--menya{border-left:var(--border-thick) solid var(--color-accent);}

.chat__composer{border-top:1px solid var(--color-border);
  padding:var(--space-3) var(--space-4);display:flex;flex-direction:column;
  gap:var(--space-1);}
.chat__composer-row{display:flex;gap:var(--space-2);align-items:flex-end;}
.chat__composer-row .field{flex:1 1 auto;min-width:0;}
/* @boog-ui-end */

/* ══════════════════════════════════════════════════════════════════
   МОБИЛЬНЫЙ СЛОЙ ПЛАТФОРМЫ (телефон, ≤930px)
   ══════════════════════════════════════════════════════════════════
   КАНДИДАТ В КАНОН boog-ui: блок лежит ЗА маркером @boog-ui-end, чтобы
   не расходиться с зафиксированной версией канона до штатного переноса
   (scripts/check_design_sync.sh сверяет только то, что внутри маркеров).

   ЗАЧЕМ ОТДЕЛЬНЫЙ СЛОЙ. Мобильная раскладка до сих пор делалась
   поштучно: у «Звонков» своя (720/900), у редактора Слайдера своя
   (design/surfaces/responsive.css, 930), у «Входящих» своя (820), у
   Главной своя (600/820/900/1000). Каждая страница решала одну и ту же
   задачу заново и на своей границе - платформа расползлась примерно на
   десять разных точек перелома. Здесь собрано то, что у всех общее:
   поведение ПРИМИТИВОВ кита на узком экране. Страничные особенности
   остаются на страницах, но точка перелома у всех одна.

   ПОЧЕМУ 930px, А НЕ 768px. Проверено живьём при мобильной работе над
   редактором: реальный альбомный телефон (iPhone и большинство
   современных Android - 812-932px) на границе 768px не получал НИ ОДНОГО
   мобильного правила и видел чистую десктопную раскладку. 930px
   покрывает реальные альбомные ширины (решение PO). Та же граница уже
   применяется в мобильном слое редактора и в правиле .sidenav выше -
   третьего числа платформе не заводим.

   ЧИСЛА, А НЕ ВКУС (ui-ux-pro-max, проверять измерением в браузере):
   минимум цели касания 44 точки (--size-touch), зазор между целями не
   меньше --space-2, длительность движения 150-300 мс (--motion-base). */

@media (max-width: 930px) {
  /* ── Боковая панель приложения: наплыв, а не дележ экрана ──────────
     Правило-близнец для .sidenav уже есть выше (в каноне). Оно НЕ
     срабатывало на большинстве страниц: в каркас .app-shell они кладут
     СВОЮ панель со своим классом (.home-side, .fac-side, .sidebar,
     .evt-side, .ps-sidebar, .ms-tree), а общим у них остаётся только
     структурный класс раскладки .app-shell__side. Найдено измерением на
     препроде: в «Сводке» панель 240px делила 375px экрана, рабочей
     области оставалось 135px - заголовки рвались по одному слову в
     строку, названия игр обрезались на середине буквы.

     Поэтому правило вешается на СТРУКТУРНЫЙ класс раскладки: панель -
     это роль в каркасе, а не конкретный облик. Панель выходит из потока
     и наплывает поверх содержимого, рабочая область получает всю ширину.
     Открывает её тот же язычок .sidenav-collapse-tab, что и на десктопе
     (на телефоне он работает как гамбургер, см. initSidenavCollapse);
     страницам, которые заводят механизм кита, язычок ставит сам кит
     (ensureCollapseTab) - иначе панель, ушедшая из потока, стала бы
     недостижимой.

     САМОСТРАХОВКА ЧЕРЕЗ :has(). Правило намеренно требует, чтобы язычок в
     каркасе УЖЕ был: восемь страниц с боковой панелью модуль кита вообще
     не подключают (проверено грепом: basic-settings, billing-invoices,
     events_hub, forge, operations, purchases, slider_home, wallet), и
     завести язычок им некому. Без этого условия панель у них ушла бы из
     потока, а открыть её было бы нечем - навигация раздела пропала бы
     совсем. Это хуже тесноты, которую правило лечит. Пока страница
     язычок не получила, она остаётся в прежней раскладке. */
  .app-shell:has(> .sidenav-collapse-tab) > .app-shell__side {
    position: fixed;
    top: var(--chrome-topbars, var(--topbar-height, calc(var(--space-12) + var(--space-2))));
    left: 0;
    bottom: 0;
    z-index: 400;
    width: min(85vw, var(--size-sidenav));
    box-shadow: var(--elev-4);
    transform: translateX(-100%);
    transition: transform var(--motion-base) var(--ease-standard);
  }

  .app-shell:has(> .sidenav-collapse-tab) > .app-shell__side.is-open-mobile {
    transform: translateX(0);
  }

  /* Язычок стоит у края экрана, а с открытой панелью едет к её краю - тем
     же правилом, что уже работает для .sidenav в каноне выше. */
  .app-shell > .app-shell__side.is-open-mobile ~ .sidenav-collapse-tab {
    left: min(85vw, var(--size-sidenav)) !important;
  }

  /* По вертикали язычок уходит на середину края экрана. На десктопе он стоит
     у верхнего угла панели, где рядом ничего нет; на телефоне панели в потоке
     нет вовсе, и в том же углу оказывается начало содержимого - язычок
     накрывал первую букву заголовка раздела (видно на «Уведомлениях» и
     «Сводке»). Отодвигать содержимое отступом дороже: 44 точки цели касания
     плюс зазор - это 13% ширины экрана 375, отнятые у текста на всю высоту
     страницы. Середина края - привычное место бокового ярлыка, и рука тянется
     туда же, куда при листании. */
  .app-shell > .sidenav-collapse-tab {
    position: fixed;
    top: 50%;
    transform: translateY(-50%);
    /* Язычок выступает из-за левого края, а не стоит НА содержимом. Полная
       кнопка 44 точки закрывала бы под собой колонку - в «Расписании» она
       села на первый столбец календаря и срезала подпись дня недели. Сдвиг
       уводит левую половину за экран: видно узкий выступ, а область касания
       остаётся целой (44 точки), потому что палец всё равно попадает в неё
       у самого края. Иконка прижата к видимой стороне. */
    margin-left: calc(var(--space-6) * -1);
    justify-content: flex-end;
    padding-right: var(--space-1);
    background: var(--color-surface);
    opacity: .92;
  }

  /* Открытая панель - язычок целиком на виду у её края, перекрывать ему
     нечего: под ним подложка, а не содержимое. */
  .app-shell > .app-shell__side.is-open-mobile ~ .sidenav-collapse-tab {
    margin-left: 0;
    justify-content: center;
    padding-right: 0;
    opacity: 1;
  }

  /* ── Показатели: четыре в ряд не помещаются ────────────────────────
     .kpis - жёсткая сетка repeat(4,1fr). На телефоне это 4 колонки по
     ~80 точек: крупное число (--text-2xl) в такую колонку не встаёт и
     переносится по цифрам. Два в ряд - минимальная порция, при которой
     число ещё читается целиком. */
  .kpis {
    grid-template-columns: repeat(2, 1fr);
  }

  /* ── Окно: почти во весь экран, тело прокручивается ────────────────
     На десктопе окно центрируется и растёт по содержимому. На телефоне
     содержимое почти всегда выше экрана, а .modal-overlay центрирует по
     вертикали - шапка и подвал уезжают за края, и до кнопки «Готово»
     доскроллить нельзя (подвал не в потоке страницы, а за краем окна).
     Здесь окно ограничивается высотой экрана, прокручивается ТЕЛО, а
     шапка с подвалом остаются на виду. */
  .modal-overlay,
  .dialog-overlay {
    padding: var(--space-3);
    align-items: end;
  }

  .modal-overlay .modal,
  .dialog-overlay .dialog,
  .modal-overlay .modal--sm,
  .modal-overlay .modal--md,
  .modal-overlay .modal--lg {
    max-width: 100%;
    max-height: calc(100dvh - var(--space-6));
  }

  .modal__body,
  .dialog__body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* Кнопки подвала в столбец: две-три кнопки с текстом в ряд на 375
     точках либо жмутся до нечитаемых, либо переносят слова внутри себя.
     column-reverse, а не column: в разметке главное действие идёт
     последним (порядок десктопного ряда «Отмена, Готово»), а на телефоне
     оно должно быть верхним - ближе к содержимому, к которому относится. */
  .modal__footer,
  .dialog__footer {
    flex-direction: column-reverse;
    align-items: stretch;
  }

  .modal__footer .btn,
  .dialog__footer .btn {
    width: 100%;
  }

  /* Цель касания внутри окна: окно перекрывает страницу и в него
     попадают пальцем не глядя - тот же довод, по которому в ките заведён
     .icon-btn--touch. Десктопная плотность (32) остаётся вне окон. */
  .modal .icon-btn,
  .dialog .icon-btn,
  .list-row .icon-btn {
    width: var(--size-touch);
    height: var(--size-touch);
  }

  /* ── Пункт меню: цель касания ──────────────────────────────────────
     Меню - это то, куда на телефоне точно тыкают пальцем: выбор компании,
     профиль, действия строки. Отступов --space-2 хватало на высоту 30 точек
     при принятом минимуме 44 (измерено под тестовой учётной записью: список
     компаний в переключателе контура). Ширина не трогается - пункт и так
     во всю ширину меню, а зазор между соседями даёт сам рост высоты. */
  .menu__item {
    min-height: var(--size-touch);
  }

  /* ── Вкладки и сегменты: прокрутка вместо переполнения ─────────────
     Обе полосы - ряды без переноса. На узком экране 4-6 вкладок не
     помещаются и вылезают за край экрана, раздувая документ по ширине
     (страница начинает ездить вбок целиком). Горизонтальная прокрутка -
     тот же приём, каким на телефоне живут системные ленты вкладок. */
  .tabs,
  .segmented {
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .tabs::-webkit-scrollbar,
  .segmented::-webkit-scrollbar {
    display: none;
  }

  .tab,
  .segmented__item {
    flex: none;
    white-space: nowrap;
  }

  /* ── Строка списка: действия под содержимым ────────────────────────
     .list-row - один ряд «содержимое + действия». Когда действий два-три,
     на 375 точках им остаётся полоска в полсотни точек, и содержимое
     сжимается до многоточия. Перенос отдаёт действиям свою строку. */
  .list-row {
    flex-wrap: wrap;
  }

  .list-row__body {
    flex: 1 1 100%;
  }

  .list-row__actions {
    margin-left: auto;
  }

  /* ── Топбар: плотнее и с обрезкой названия ─────────────────────────
     Зазор --space-4 между блоками топбара на 375 точках съедает
     заметную часть строки, а длинное название раздела переносится на
     две-три строки и раздувает высоту полосы. */
  .topbar {
    gap: var(--space-2);
    padding: 0 var(--space-3);
  }

  .topbar__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ── Таблица-список: прокрутка вбок вместо разъезда страницы ───────
     .dtable держит колонки на всём контейнере (--dtable-cols). На
     телефоне заданные доли колонок дают ширину больше экрана, и вбок
     едет вся страница. Обёртка .dtable-scroll в ките для этого и есть -
     здесь она включается сама, чтобы прокрутка осталась внутри таблицы,
     даже если страница обёртку не поставила. */
  .dtable {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}
