/* ============================================================
   Стили платформы. Подключаются ПОСЛЕ Bootstrap и последними:
   оформление проекта идёт раньше, чтобы каркас выигрывал
   по порядку (§9).
   Здесь только каркас страницы, список-в-карточки, формы и то,
   чего в Bootstrap нет. Всё, что его повторяет, удалено.
   ============================================================ */

/* ─── 1. ТЕМА ────────────────────────────────────────────────
   Своя тема платформы цветов не назначает: они берутся из токенов
   Bootstrap, поэтому светлая и тёмная работают без второй палитры.
   Установка вправе выбрать сторонний скин (SiteSettings.theme) — тогда
   цвета даёт он. Здесь остаётся тема-независимое: совместимость
   переменных, каркас, формы. */

/* Фон шапки — только светлая тема: в тёмной берётся из --bs-body-bg. Фон
   меню без фирменного цвета тот же, что у листа (--bs-body-bg).
   Своих цветовых переменных платформа больше не заводит. Их было четырнадцать,
   и держались они ради экранов, которые в платформу попали по недосмотру и
   отсюда убраны; каждая была вторым именем токена Bootstrap, то есть лишним
   способом сказать то же самое. */
[data-bs-theme="light"] {
  --header-bg: #ffffff;
}


/* Под фирменный цвет своей темы кнопки, кольца фокуса, вкладки и фон меню
   перекрашивает не этот файл, а правила, которые каркас собирает из
   настройки (platforma/core/themes.py): цвет — значение, а не константа
   стилей. Готовые темы красят всё сами. */

/* ─── 2. ШИРИНЫ И КАРКАС ────────────────────────────────────
   Три ширины содержимого — по назначению, не по странице.    */
:root {
  --w-form: 780px;   /* формы, профиль, справка — длина строки для чтения */
  --w-list: 1400px;  /* списки, карточки, детали                          */
  --sb: 260px;       /* сайдбар                                           */
  --hdr: 52px;       /* высота шапки                                      */
  --gutter: clamp(16px, 2vw, 32px); /* поля растут вместе с экраном        */
}
body { background: var(--bs-tertiary-bg); }

.hdr {
  position: sticky; top: 0; z-index: 1030;
  height: var(--hdr);
  background: var(--header-bg, var(--bs-body-bg));
  border-bottom: 1px solid var(--bs-border-color);
  display: flex; align-items: center; gap: .75rem;
  padding: 0 var(--gutter);
}
.app { display: grid; grid-template-columns: var(--sb) minmax(0, 1fr); align-items: start; }
/* Страницы без меню — вход и смена пароля: гостю показывать нечего. Колонка
   меню при этом не должна оставаться зарезервированной, иначе содержимое
   уезжает в неё и страница выводится шириной в меню. Условие на само
   отсутствие меню, а не класс на теле: класс можно забыть проставить на
   следующей такой странице, а забытый класс выглядит как поломка вёрстки. */
.app:has(> main:only-child) { grid-template-columns: minmax(0, 1fr); }
.sb {
  position: sticky; top: var(--hdr);
  height: calc(100dvh - var(--hdr)); overflow: auto;
  background: var(--sidebar-bg, var(--bs-body-bg));
  border-right: 1px solid var(--bs-border-color);
  padding: .75rem .5rem;
  /* Стекинг-контекст выше содержимого страницы, чтобы всплывающая подпись
     свёрнутого меню не уходила под таблицу с липкими заголовками. Ниже
     выпадающих списков (1000), шапки (1030) и модальных окон (1050). */
  z-index: 100;
}
.sb .nav-link {
  padding: .3rem .75rem; font-size: .875rem; border-radius: .25rem;
  display: flex; align-items: center; gap: .6rem; color: var(--bs-body-color);
}
.sb .nav-link i { width: 1rem; text-align: center; flex: none; color: var(--bs-secondary-color); }
.sb .nav-link:hover { background: var(--bs-secondary-bg); }
.sb .nav-link.active { background: var(--bs-primary); color: #fff; }
.sb .nav-link.active i { color: #fff; }
.sb .nav-link .badge { margin-left: auto; font-size: .6875rem; }
.burger { display: none; }

/* Содержимое прижато к левому краю колонки, а не центрировано: центрирование
   при трёх разных ширинах двигало левый край на сотни пикселей при каждом
   переходе — единственное, за что цепляется глаз. Теперь у всех страниц одно
   начало, разной остаётся только правая граница. */
.page { padding: var(--gutter); margin-inline: 0; width: 100%; max-width: var(--w-list); }
body.w-form .page { max-width: var(--w-form); }

/* Отступ карточки — здесь, а не классом в разметке: в поставке набралось семь
   разных написаний одного и того же (`mb-2`, `mb-3`, `mb-4`, `mt-3`, без
   отступа), и разнобой был виден на каждом переходе между страницами.
   Карточка в сетке (`h-100`) отступа не получает: его даёт сама сетка. */
.card:not(.h-100) { margin-bottom: 1rem; }

/* Страница без меню — вход, смена пароля — центрируется: рядом с формой на
   экране нет ничего, к чему прижиматься левым краем, и прижатая форма выглядит
   не решением, а недосмотром. Условие то же, что и у сетки выше: отсутствие
   меню, а не класс на теле, — забытый класс выглядел бы поломкой вёрстки. */
.app:has(> main:only-child) .page { margin-inline: auto; }
body.w-full .page { max-width: none; }

/* 900–1199 — сайдбар сворачивается в полосу иконок */
@media (max-width: 1199.98px) {
  :root { --sb: 56px; }
  .sb { padding: .75rem .25rem; overflow: visible; }
  .sb .nav-link { justify-content: center; padding: .5rem 0; position: relative; }
  .sb .nav-link span, .sb .nav-link .badge { display: none; }
  .sb .zone { height: 0; padding: 0; overflow: hidden; }
  .sb .nav-link:hover span {
    display: block; position: absolute; left: calc(100% + 6px); top: 50%;
    transform: translateY(-50%);
    background: var(--bs-emphasis-color); color: var(--bs-body-bg);
    padding: .2rem .5rem; border-radius: .25rem;
    font-size: .8125rem; white-space: nowrap; z-index: 10;
  }
}
/* Вход в просмотр чужими глазами: список людей по ролям бывает длинным —
   прокрутка внутри меню, а не во всю страницу. */
.view-as-menu { min-width: 240px; max-height: 420px; overflow-y: auto; }

/* <900 — гамбургер + offcanvas */
@media (max-width: 899.98px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .sb { display: none; }
  .burger { display: inline-flex; }
  .page { padding: .75rem; }
}
/* Подписи в меню видны везде, где хватает ширины: ниже 1200px они уже свёрнуты
   в полосу иконок правилом выше, ниже 900px меню уезжает в выдвижную панель.
   Прежде подписи оставались только на дашборде, а на рабочих страницах меню
   сжималось в иконки — и человек, пришедший с дашборда, видел другое меню на
   той же системе. Место под 260px нашлось: списки и так ограничены 1400px. */

/* ─── 2.1 РАЗМЕРЫ, КОТОРЫХ НЕТ В НАБОРЕ СТИЛЕЙ ───────────────
   Готовые классы кончаются там, где нужна конкретная ширина: поле выбора
   файла, колонка таблицы, форма на пустой странице. Писались они числом прямо
   в разметке — пятьдесят два раза на поставку, каждый раз своим значением.
   Здесь они названы один раз; в разметке остаются только классы.            */
.w-field  { max-width: 22rem; }   /* поле выбора файла, форма на пустой странице */
.w-note   { max-width: 26rem; }   /* сообщение в две строки */
.w-logo   { max-width: 22rem; max-height: 4rem; }
.col-mark { width: 3rem; }        /* колонка отметки */
.col-value{ width: 12rem; }       /* колонка значения при сравнении */
.col-name { min-width: 16rem; }   /* колонка названия: не сжимается */
.fs-tiny  { font-size: .6875rem; }
.text-prewrap { white-space: pre-line; }   /* подсказка поля в несколько строк */
#nav      { width: 270px; }       /* выдвижное меню на узком экране */

/* ─── 3. ТАБЛИЦА СПИСКА → КАРТОЧКИ НА ТЕЛЕФОНЕ ──────────────
   Требует data-l="Заголовок" на каждой <td>
   и class="pri" на главной ячейке строки.                    */
.rt { --bs-table-bg: var(--bs-body-bg); font-size: .875rem; margin: 0; }
.rt th { font-weight: 600; font-size: .8125rem; color: var(--bs-secondary-color); white-space: nowrap; }
.rt th a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: .2rem; }
.rt th a:hover { color: var(--bs-primary); }
.rt td { vertical-align: middle; }
.num { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.sub td { font-size: .8125rem; color: var(--bs-secondary-color); background: var(--bs-tertiary-bg); border-top: 0; }

@media (max-width: 767.98px) {
  .rt thead { display: none; }
  .rt, .rt tbody, .rt tr, .rt td { display: block; width: 100%; }
  .rt tbody tr {
    border: 1px solid var(--bs-border-color); border-radius: .375rem;
    margin-bottom: .5rem; padding: .6rem .8rem; background: var(--bs-body-bg);
  }
  .rt td { border: 0; padding: .15rem 0; display: flex; justify-content: space-between; gap: 1rem; text-align: right; }
  .rt td::before { content: attr(data-l); color: var(--bs-secondary-color); font-size: .8125rem; text-align: left; flex: none; }
  .rt td.pri {
    font-size: 1rem; font-weight: 600; text-align: left;
    padding-bottom: .35rem; margin-bottom: .35rem;
    border-bottom: 1px solid var(--bs-border-color);
  }
  .rt td.pri::before { display: none; }
  .rt tr.sub { border-style: dashed; margin-top: -.35rem; }
}

/* ─── 4. ПАРЫ «ПОЛЕ — ЗНАЧЕНИЕ» (деталь) ────────────────────
   Заменяет .od-grid-2 / .od-card / fieldset+legend.          */
dl.kv { display: grid; grid-template-columns: auto 1fr; gap: .35rem 1rem; margin: 0; font-size: .875rem; }
dl.kv dt { color: var(--bs-secondary-color); font-weight: 400; white-space: nowrap; }
dl.kv dd { margin: 0; font-weight: 500; }
@media (min-width: 768px) { dl.kv { grid-template-columns: auto 1fr auto 1fr; column-gap: 1.5rem; } }

/* ─── 5. ПОКАЗАТЕЛИ И ПЛИТКИ ─────────────────────────────────
   Показатель дашборда — чип: подпись и жирное число в одну строку, шириной по
   содержимому, строкой с переносом. Квадратная плитка для числа не годится:
   она задумана ярлыком со значком и на широком экране превращала одну цифру в
   квадрат в четверть экрана. */
.indicators { display: flex; flex-wrap: wrap; gap: .5rem; }
.indicators:empty { display: none; }
.indicator {
  display: inline-flex; align-items: baseline; gap: .5rem; width: fit-content;
  padding: .375rem .875rem; border: 1px solid var(--bs-border-color); border-radius: .375rem;
  background: var(--bs-body-bg); color: var(--bs-secondary-color);
  font-size: .8125rem; white-space: nowrap; text-decoration: none;
}
a.indicator:hover { border-color: var(--bs-primary); color: var(--bs-primary); }
.indicator-value { color: var(--bs-body-color); font-weight: 600; }
.indicator-note { font-size: .75rem; }
.indicator.is-failed { border-color: var(--bs-warning-border-subtle); background: var(--bs-warning-bg-subtle); }

/* Плитка-ярлык: значок и подпись в квадрате — для страниц-перечней разделов.
   Раскладку даёт сетка, здесь только вид самой плитки. */
.tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .35rem;
  aspect-ratio: 1; background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color); border-radius: .5rem;
  text-decoration: none; color: var(--bs-body-color);
  font-size: .75rem; text-align: center; padding: .4rem; transition: .15s;
}
.tile:hover { border-color: var(--bs-primary); color: var(--bs-primary); transform: translateY(-2px); box-shadow: var(--bs-box-shadow-sm); text-decoration: none; }
.tile i { font-size: 1.35rem; color: var(--bs-secondary-color); }
.tile:hover i { color: var(--bs-primary); }

/* Вкладка «Модули» оформляется списком (.module-groups / .module-rows) ниже, а
   не плитками: плитка подсвечивается по отметке внутри себя, а в строке модуля
   отметок несколько — тумблер и права, — и подсветка срабатывала на любую. */

/* ─── 6. ПЕЧАТЬ ────────────────────────────────────────────────
   `.no-print` платформа сама не ставит: она предлагает его проекту —
   способ убрать со страницы то, что на бумаге не нужно. Это единственное
   правило здесь, объявленное не для своих страниц. */
@media print {
  .hdr, .sb, .burger, .no-print, .pagination { display: none !important; }
  .app { grid-template-columns: minmax(0, 1fr); }
  .page { max-width: none; padding: 0; }
}

/* ─── 7. ФОРМЫ — совместимость виджетов Django ──────────────
   Django рендерит {{ field }} без классов. Внутри .platform-form
   приводим базовые контролы к облику Bootstrap, чтобы не
   прописывать form-control в каждый виджет форм в Python.        */
.platform-form label { font-size: .8125rem; font-weight: 500; margin-bottom: .15rem; color: var(--bs-body-color); }
.platform-form input:not([type=checkbox]):not([type=radio]):not([type=file]):not(.btn),
.platform-form select,
.platform-form textarea {
  display: block; width: 100%;
  padding: .3rem .6rem; font-size: .875rem; line-height: 1.5;
  color: var(--bs-body-color); background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color); border-radius: .375rem;
  transition: border-color .1s, box-shadow .1s;
}
.platform-form input:focus, .platform-form select:focus, .platform-form textarea:focus {
  outline: 0; border-color: var(--bs-primary);
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .2);
}
.platform-form input[type=checkbox], .platform-form input[type=radio] {
  width: 1em; height: 1em; margin-right: .4rem; vertical-align: -.125em;
  accent-color: var(--bs-primary);
}
.platform-form .form-field { margin-bottom: .85rem; }
.platform-form .form-field small { display: block; font-size: .75rem; color: var(--bs-secondary-color); margin-top: .15rem; }
.platform-form .errorlist { list-style: none; padding: 0; margin: .2rem 0 0; color: var(--bs-danger); font-size: .8125rem; }

/* Статья справки и базы знаний. Тело статьи приходит разметкой — заголовками,
   списками, таблицами, — и без правил ниже они выводятся браузерными
   умолчаниями: строка вплотную к строке, заголовок неотличим от текста. Правила
   одни на обе статьи: второй набор под другим классом расходился бы с первым
   при первой правке. */
.help-article { max-width: 42rem; line-height: 1.6; }
.help-article > *:first-child { margin-top: 0; }
.help-article h2 { font-size: 1.15rem; font-weight: 600; margin: 1.6rem 0 .5rem; }
.help-article h3 {
  font-size: 1rem; font-weight: 600; margin: 1.5rem 0 .4rem;
  padding-bottom: .25rem; border-bottom: 1px solid var(--bs-border-color);
}
.help-article h4 { font-size: .95rem; font-weight: 600; margin: 1.1rem 0 .3rem; }
.help-article p { margin-bottom: .7rem; }
.help-article ul, .help-article ol { margin-bottom: .9rem; padding-left: 1.4rem; }
.help-article li { margin-bottom: .3rem; }
.help-article li > ul, .help-article li > ol { margin: .3rem 0 .2rem; }
.help-article blockquote {
  margin: .9rem 0; padding: .5rem .9rem;
  border-left: 3px solid var(--bs-border-color);
  color: var(--bs-secondary-color);
}
.help-article code {
  font-size: .875em; padding: .1em .3em; border-radius: 3px;
  background: var(--bs-secondary-bg); color: var(--bs-body-color);
}
.help-article pre {
  padding: .7rem .9rem; border-radius: 6px; overflow-x: auto;
  background: var(--bs-secondary-bg);
}
.help-article pre code { background: none; padding: 0; }
/* Таблица в статье приходит без классов: их вырезает очистка разметки, потому
   что произвольный class в тексте статьи оформлением не является. */
.help-article table {
  width: 100%; margin: .9rem 0; border-collapse: collapse; font-size: .9375rem;
}
.help-article th, .help-article td {
  padding: .4rem .6rem; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--bs-border-color);
}
.help-article th { font-weight: 600; background: var(--bs-tertiary-bg); }
.help-article img { max-width: 100%; height: auto; border-radius: 6px; }
.help-article hr { margin: 1.4rem 0; }

/* Состояния, которые ставят сценарии. Сценарий меняет класс, а не число в
   `style`: размер и длительность живут здесь, рядом с остальным оформлением. */
.ac-menu { max-height: 260px; }              /* список подсказок поиска      */
.alert.is-fading { transition: opacity .4s; opacity: 0; } /* уход сообщения */

/* Заголовок зоны меню: чьи это разделы — прикладного модуля или платформы.
   Крупнее и заметнее подписи группы: иначе два уровня читаются как один
   список, ради чего верхний уровень и заводился.

   В полосе иконок подпись прячется, а черта остаётся: слово вроде «База
   знаний» переносится там по слогам и занимает больше места, чем сами пункты
   раздела. Группировку держит разделительная черта — она и есть граница
   зоны. */
.zone {
  font-size: .8125rem; font-weight: 600; letter-spacing: .02em;
  padding: .75rem .75rem .25rem;
  color: var(--bs-body-color);
  border-top: 1px solid var(--bs-border-color);
  margin-top: .5rem;
}
.sb > .zone:first-child, .offcanvas-body > .zone:first-child {
  border-top: 0; margin-top: 0; padding-top: .25rem;
}

/* Широкая страница (`body.w-full`: матрица прав, канбаны, график) — меню в
   полосу значков на любой ширине окна: там ширина — рабочий ресурс, и 260px под
   подписи не окупаются. Решает содержимое, а не человек. Те же правила, что у
   окна 900–1199 выше, — и заголовок зоны прячется так же: без этого подписи
   групп выходили из узкой колонки поверх страницы. Правила стоят после
   базового `.zone`, чтобы исход не зависел от порядка. */
@media (min-width: 1200px) {
  body.w-full { --sb: 56px; }
  body.w-full .sb { padding: .75rem .25rem; overflow: visible; }
  body.w-full .sb .nav-link { justify-content: center; padding: .5rem 0; position: relative; }
  body.w-full .sb .nav-link span, body.w-full .sb .nav-link .badge { display: none; }
  body.w-full .sb .zone { height: 0; padding: 0; overflow: hidden; }
  body.w-full .sb .nav-link:hover span {
    display: block; position: absolute; left: calc(100% + 6px); top: 50%;
    transform: translateY(-50%);
    background: var(--bs-emphasis-color); color: var(--bs-body-bg);
    padding: .2rem .5rem; border-radius: .25rem;
    font-size: .8125rem; white-space: nowrap; z-index: 10;
  }
}
/* В полосе значков первая зона тоже без отступа: иначе её верхний отступ
   открывает полоску подписи, которую зона прячет. */
@media (max-width: 1199.98px) { .sb > .zone:first-child { padding: 0; } }
@media (min-width: 1200px) { body.w-full .sb > .zone:first-child { padding: 0; } }

/* Строка формсета, помеченная к удалению. Она остаётся на экране намеренно:
   пометку надо отправить на сервер, а исчезнувшая строка выглядит как уже
   удалённая — и человек уходит со страницы, не сохранив форму. */
.formset-row.is-removed {
  opacity: .45;
  text-decoration: line-through;
}
