/* ── Тема и переменные ── */
:root {
  --shadow-sm: 0 1px 3px rgba(0,0,0,.08);
  --shadow-md: 0 2px 8px rgba(0,0,0,.12);
  --border-radius: calc(.5rem * var(--radius-factor));
  --income-color: #198754;
  --expense-color: #dc3545;
  --fuel-color: #fd7e14;
  --wash-color: #0dcaf0;
  --parking-color: #6f42c1;
  --ev-color: #20c997;
  --row-hl-start: #6fcf97;
  --row-hl-end: #eafaf1;
  /* «Изменено после проверки» (2026-08-03) — розовая подсветка, отдельная
     от --bs-warning-* (Bootstrap не имеет штатного pink-семейства
     bg-*-subtle/text-*-emphasis, поэтому своя пара переменных, тот же
     принцип, что --wash-color/--parking-color выше). */
  --row-changed-bg: #f8d7e4;
  --row-changed-text: #7a1440;

  /* Легенда типов операций (календарь + KPI-карточки по всему приложению,
     2026-07-24) — единый источник цвета вместо литерального hex,
     продублированного в каждом шаблоне заново. Значения зеркалят
     app/blueprints/calendar/routes.py::_SRC_COLORS (точки календаря) —
     при изменении цвета менять оба места. НЕ путать с --income-color/
     --expense-color выше (та ось — направление денег, эта — тип
     операции) и с --fuel-color/--wash-color/--parking-color (та ось —
     подтип автотраты для метража, не весь раздел целиком). */
  --legend-shift:    #0d6efd; /* Смена */
  --legend-mileage:  #198754; /* Пробег */
  --legend-vehexp:   #fd7e14; /* Авто расходы */
  --legend-houseexp: #e05a3a; /* Расходы (хозяйственные) */
  --legend-payment:  #ffc107; /* Выплаты */
  --legend-deposit:  #20c997; /* Пополнения */
  --legend-maint:    #6f42c1; /* ТО */
  --legend-charge:   #adb5bd; /* Зарядка */

  /* KPI-карточки «Пробег общий» (vehicles/index.html + detail.html) —
     самостоятельный акцент, не входит в легенду выше и не завязан на
     --bs-primary (иначе после Фазы 2 сливался бы с обычными кнопками). */
  --mileage-total-color: #0a58ca;
}

[data-bs-theme="dark"] {
  --shadow-sm: 0 1px 3px rgba(0,0,0,.3);
  --shadow-md: 0 2px 8px rgba(0,0,0,.4);
  --row-hl-start: #2d8a5a;
  --row-hl-end: #1a3d2a;
  --row-changed-bg: #4a1f33;
  --row-changed-text: #f5a3c7;
  /* Нативные элементы браузера (попап календаря <input type="date">,
     попап <input type="color">, скроллбар) — Bootstrap не может их
     перекрасить своими CSS-переменными, это отдельная, платформенная
     палитра. color-scheme:dark говорит браузеру рендерить ИХ СОБСТВЕННЫМ
     тёмным набором — не хардкод конкретного цвета, а переключатель между
     уже готовыми палитрами браузера, тот же принцип, что и остальная
     темизация проекта (используем существующий механизм, не свой цвет).
     Без этого попап выбора даты оставался "бельмом" — светлым посреди
     тёмной страницы (2026-07-25, найдено пользователем на карточке
     карты, поле «Лимит»). */
  color-scheme: dark;
}

/* ── Акцентный цвет (панель «Настройки интерфейса») — с Фазы 2 (2026-07-24)
   влияет на ВСЁ приложение, не только на подсветку навигации: кнопки,
   ссылки, чекбоксы, прогресс-бары, пагинацию, активные пункты списков/
   вкладок/дропдаунов. --accent/--accent-rgb — единственный источник,
   остальное ниже только ссылается на них. */
:root {
  --accent: #166534;
  --accent-rgb: 22, 101, 52;
  --accent-soft: #d1fae8;
  --accent-soft-dark: rgba(22, 101, 52, .25);
}
html[data-accent="blue"]   { --accent:#0d6efd; --accent-rgb: 13, 110, 253;  --accent-soft:#cfe2ff; --accent-soft-dark: rgba(13,110,253,.25); }
html[data-accent="indigo"] { --accent:#6610f2; --accent-rgb: 102, 16, 242; --accent-soft:#e0cffc; --accent-soft-dark: rgba(102,16,242,.25); }
html[data-accent="purple"] { --accent:#6f42c1; --accent-rgb: 111, 66, 193; --accent-soft:#e2d9f3; --accent-soft-dark: rgba(111,66,193,.25); }
html[data-accent="pink"]   { --accent:#d63384; --accent-rgb: 214, 51, 132; --accent-soft:#f7d6e6; --accent-soft-dark: rgba(214,51,132,.25); }
html[data-accent="red"]    { --accent:#dc3545; --accent-rgb: 220, 53, 69;  --accent-soft:#f8d7da; --accent-soft-dark: rgba(220,53,69,.25); }
html[data-accent="orange"] { --accent:#fd7e14; --accent-rgb: 253, 126, 20; --accent-soft:#ffe5d0; --accent-soft-dark: rgba(253,126,20,.25); }
html[data-accent="yellow"] { --accent:#ffc107; --accent-rgb: 255, 193, 7;  --accent-soft:#fff3cd; --accent-soft-dark: rgba(255,193,7,.25); }
html[data-accent="teal"]   { --accent:#20c997; --accent-rgb: 32, 201, 151; --accent-soft:#d2f4ea; --accent-soft-dark: rgba(32,201,151,.25); }
html[data-accent="cyan"]   { --accent:#0dcaf0; --accent-rgb: 13, 202, 240; --accent-soft:#cff4fc; --accent-soft-dark: rgba(13,202,240,.25); }

/* Bootstrap 5.3 хранит цвет кнопок/пагинации/списков/дропдаунов/вкладок
   НЕ через --bs-primary напрямую, а через свои же component-scoped
   custom properties, запечённые как статичный hex при сборке
   (например .btn-primary{--bs-btn-bg:#0d6efd}) — поэтому переопределить
   --bs-primary одного недостаточно, нужно точечно переопределить те же
   составные переменные компонентов. Утилитные классы (.text-primary/
   .bg-primary/.border-primary/.link-primary) уже сами читают
   rgba(var(--bs-primary-rgb),...) — их отдельно трогать не нужно,
   --bs-primary-rgb ниже покрывает все разом. color-mix() — тени
   наведения/нажатия без ручного подбора 6×3 дополнительных hex на
   каждый акцент (поддержан всеми браузерами, актуальными на 2026 год). */
:root {
  --bs-primary: var(--accent);
  --bs-primary-rgb: var(--accent-rgb);
  --bs-link-color: var(--accent);
  --bs-link-color-rgb: var(--accent-rgb);
  --bs-link-hover-color: color-mix(in srgb, var(--accent) 85%, black);
  --bs-focus-ring-color: rgba(var(--accent-rgb), .25);
}
.btn-primary {
  --bs-btn-bg: var(--accent);
  --bs-btn-border-color: var(--accent);
  --bs-btn-hover-bg: color-mix(in srgb, var(--accent) 85%, black);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--accent) 80%, black);
  --bs-btn-active-bg: color-mix(in srgb, var(--accent) 80%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--accent) 75%, black);
  --bs-btn-disabled-bg: var(--accent);
  --bs-btn-disabled-border-color: var(--accent);
  --bs-btn-focus-shadow-rgb: var(--accent-rgb);
}
.btn-outline-primary {
  --bs-btn-color: var(--accent);
  --bs-btn-border-color: var(--accent);
  --bs-btn-hover-bg: var(--accent);
  --bs-btn-hover-border-color: var(--accent);
  --bs-btn-active-bg: var(--accent);
  --bs-btn-active-border-color: var(--accent);
  --bs-btn-disabled-color: var(--accent);
  --bs-btn-disabled-border-color: var(--accent);
  --bs-btn-focus-shadow-rgb: var(--accent-rgb);
}
.form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}
.form-check-input:focus {
  border-color: color-mix(in srgb, var(--accent) 50%, white);
  box-shadow: 0 0 0 .25rem rgba(var(--accent-rgb), .25);
}
.form-range::-webkit-slider-thumb { background-color: var(--accent); }
.form-range::-moz-range-thumb     { background-color: var(--accent); }
.progress-bar { --bs-progress-bar-bg: var(--accent); }
.nav-pills { --bs-nav-pills-link-active-bg: var(--accent); }
.pagination {
  --bs-pagination-active-bg: var(--accent);
  --bs-pagination-active-border-color: var(--accent);
  --bs-pagination-focus-box-shadow: 0 0 0 .25rem rgba(var(--accent-rgb), .25);
}
.list-group {
  --bs-list-group-active-bg: var(--accent);
  --bs-list-group-active-border-color: var(--accent);
}
.dropdown-menu { --bs-dropdown-link-active-bg: var(--accent); }

/* Контекстные классы строк таблиц (.table-success/-danger/-warning/-info) —
   ТА ЖЕ болезнь, что у кнопок/пагинации/аккордеона: --bs-table-bg этих
   классов запечён статичным светлым hex в vendor bootstrap.min.css
   (#d1e7dd и т.п.), НЕ читает var(--bs-success-bg-subtle) живьём — в
   тёмной теме строка оставалась светлым «бельмом» (найдено пользователем
   2026-07-25 на форме карты, блок «Лимит» — активный период лимита).
   Переопределяем на уже готовые, темизированные Bootstrap-переменные
   *-bg-subtle/*-text-emphasis/*-border-subtle (эти УЖЕ правильно меняются
   под [data-bs-theme=dark] самим Bootstrap, в отличие от --bs-table-bg) —
   не хардкод нового цвета, переиспользование существующего адаптивного. */
[data-bs-theme="dark"] .table-success {
  --bs-table-bg: var(--bs-success-bg-subtle);
  --bs-table-color: var(--bs-success-text-emphasis);
  --bs-table-border-color: var(--bs-success-border-subtle);
}
[data-bs-theme="dark"] .table-danger {
  --bs-table-bg: var(--bs-danger-bg-subtle);
  --bs-table-color: var(--bs-danger-text-emphasis);
  --bs-table-border-color: var(--bs-danger-border-subtle);
}
[data-bs-theme="dark"] .table-warning {
  --bs-table-bg: var(--bs-warning-bg-subtle);
  --bs-table-color: var(--bs-warning-text-emphasis);
  --bs-table-border-color: var(--bs-warning-border-subtle);
}
[data-bs-theme="dark"] .table-info {
  --bs-table-bg: var(--bs-info-bg-subtle);
  --bs-table-color: var(--bs-info-text-emphasis);
  --bs-table-border-color: var(--bs-info-border-subtle);
}
[data-bs-theme="dark"] .table-light {
  --bs-table-bg: var(--bs-light-bg-subtle);
  --bs-table-color: var(--bs-light-text-emphasis);
  --bs-table-border-color: var(--bs-light-border-subtle);
}

/* Карточка авто — название свойства/поля отдельным (акцентным) цветом от
   значения, иначе оба сливаются в один и тот же цвет текста темы и не
   читается, что к чему относится (2026-08-23, vehicles/form.html +
   vehicles/detail.html «Паспорт»). .veh-form — обёртка формы
   добавления/редактирования авто; .spec-dt/.spec-label/.spec-value —
   карточка просмотра (dt/dd-списки и строка «Идентификация»). */
.veh-form .form-label {
  color: var(--accent);
  font-weight: 600;
}
.spec-dt, .spec-label {
  color: var(--accent);
  font-weight: 600;
}
.spec-label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .02em;
}
.spec-value {
  font-weight: 600;
}

/* Идентификация авто (vehicles/detail.html, «Паспорт») — на ПК поля
   растягиваются на всю ширину карточки равными долями, иначе при малом
   числе полей всё виснет у левого края, а правая часть карточки пустует
   (2026-08-23, живой репорт). На мобильном — как было, обычный перенос по
   содержимому (там и так вся ширина экрана уже занята, дробить нечем). */
@media (min-width: 992px) {
  .ident-specs-row { flex-wrap: nowrap; }
  .ident-specs-row > div { flex: 1 1 0; min-width: 0; }
}

/* ── Мини-шапка «на глаз» (vehicles/detail.html, вкладка «Паспорт») —
   компактная строка иконка+подпись+значение по мотивам auto.drom.ru,
   поверх уже существующего детального списка ниже (не замена, а сводка
   самого важного крупным планом, 2026-08-23). Скролл — ТОЛЬКО на
   мобильном (тот же приём, что и KPI-строка «Зарядка»); на ПК — как и
   «Идентификация» выше — колонки растягиваются равными долями на всю
   ширину карточки, без скролла и без пустого места справа. */
.hero-specs-row {
  display: flex;
  flex-wrap: nowrap;
  gap: 1.25rem;
  overflow-x: auto;
  padding: .75rem 0;
}
.hero-specs-col {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  min-width: 160px;
  flex: 1 1 160px;
}
@media (min-width: 992px) {
  .hero-specs-row { overflow-x: visible; flex-wrap: nowrap; }
  .hero-specs-col { min-width: 0; flex: 1 1 0; }
}
.hero-specs-icon {
  font-size: 1.5rem;
  color: var(--accent);
  line-height: 1;
  flex-shrink: 0;
  margin-top: .1rem;
}
.hero-specs-label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .02em;
  color: var(--bs-secondary-color);
}
.hero-specs-value {
  font-weight: 600;
  font-size: .92rem;
  line-height: 1.3;
}

/* Бейджи «Комплектация» (vehicles/detail.html) — светлый фон+рамка
   Bootstrap-класса .text-bg-light «выжигает глаза» в тёмной теме; в тёмной
   теме оставляем только светлый текст, без заливки и без рамки.
   !important ОБЯЗАТЕЛЕН на color — сам .text-bg-light задаёт цвет тоже с
   !important (Bootstrap-хелпер), без него текст оставался чёрным поверх
   прозрачного фона — нечитаемо (2026-08-23, живой баг-репорт). */
[data-bs-theme="dark"] .feat-badge {
  background-color: transparent !important;
  border-color: transparent !important;
  color: var(--bs-body-color) !important;
}

/* Активный период лимита карты (cards/form.html) — «активное состояние»
   следует акценту пользователя, а не хардкоду table-success (запечённый
   светло-зелёный фон вендорного bootstrap.min.css выглядел «бельмом» в
   обеих темах, найдено пользователем 2026-07-26). Не путать с «сегодня»
   в графике работы (work_schedule/index.html) — там, наоборот, по
   прямому требованию пользователя используется буквально жёлтый
   (bg-warning), не акцент — это не «выбранное состояние», а временная
   метка «сегодня», которая обязана быть заметна независимо от темы. */
.active-period-row {
  background-color: var(--accent-soft);
  color: var(--accent);
}
[data-bs-theme="dark"] .active-period-row {
  background-color: var(--accent-soft-dark);
}

.accordion-button {
  --bs-accordion-active-bg: var(--accent-soft);
  --bs-accordion-active-color: var(--accent);
  --bs-accordion-btn-focus-box-shadow: 0 0 0 .25rem rgba(var(--accent-rgb), .25);
}
[data-bs-theme="dark"] .accordion-button { --bs-accordion-active-bg: var(--accent-soft-dark); }

/* ── Corner Radius (панель «Настройки интерфейса», Фаза 3) — множитель
   поверх штатной шкалы Bootstrap (0.375/0.25/0.5/1/2rem), а не свои с нуля
   подобранные значения — сохраняет исходные пропорции между «мелкими»/
   «крупными» элементами при любом множителе. --bs-border-radius-pill
   (полностью круглые пилюли-бейджи/переключатели) сознательно НЕ
   масштабируется — это отдельная форма, не часть шкалы скругления. */
:root { --radius-factor: 1; }
html[data-radius="0"]   { --radius-factor: 0; }
html[data-radius="0.5"] { --radius-factor: 0.5; }
html[data-radius="1"]   { --radius-factor: 1; }
html[data-radius="1.5"] { --radius-factor: 1.5; }
html[data-radius="2"]   { --radius-factor: 2; }
:root {
  --bs-border-radius:     calc(.375rem * var(--radius-factor));
  --bs-border-radius-sm:  calc(.25rem  * var(--radius-factor));
  --bs-border-radius-lg:  calc(.5rem   * var(--radius-factor));
  --bs-border-radius-xl:  calc(1rem    * var(--radius-factor));
  --bs-border-radius-xxl: calc(2rem    * var(--radius-factor));
}

/* ── Шрифт (панель «Настройки интерфейса», Фаза 4) — ТОЛЬКО системные
   стеки, без единого внешнего файла/CDN (принцип проекта — всё
   самохостится, а тут даже хостить нечего: шрифты уже есть в ОС).
   «Sans-serif» = дефолт Bootstrap, атрибут не переопределяет ничего. */
html[data-font="serif"] { --bs-body-font-family: Georgia, "Times New Roman", Times, serif; }
html[data-font="mono"]  { --bs-body-font-family: var(--bs-font-monospace); }

/* ── Инициализация скрытых элементов (до загрузки BS) ── */
html { visibility: visible; }

/* ── Глобальные ── */
body {
  padding-bottom: 2rem;
}

/* Плейсхолдеры — курсив, светлее, мельче основного текста */
/* opacity: !important нужен, т.к. Bootstrap явно ставит opacity:1 на .form-control::placeholder */
::placeholder {
  font-style: italic !important;
  font-size: .85em !important;
  opacity: .18 !important;
}

.card {
  border-radius: var(--border-radius);
  box-shadow: var(--shadow-sm);
}

.table > :not(caption) > * > * {
  vertical-align: middle;
  padding: 0.45rem 0.75rem;
}

/* ── Шапка 1100 × 150 ── */
/* Заливка ВСЕГДА белая, независимо от темы — фото авто (header-logo)
   рассчитано на светлый фон, тёмная шапка на нём просто не задумана. */
.site-header {
  max-width: 1100px;
  width: 100%;
  height: 150px;
  margin: 6px auto 0;
  display: flex;
  align-items: center;
  background-color: #fff;
  border-radius: 0 0 .5rem .5rem;
  box-shadow: 0 2px 8px rgba(0,0,0,.15);
  overflow: hidden; /* чтобы полноразмерный header.png не вылезал за скруглённые нижние углы */
}

[data-bs-theme="dark"] .site-header {
  background-color: #fff;
}

.site-header-inner {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 0 1rem 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Фото авто текущего водителя (или header.png — см. inject_header_logo) —
   без рамок/зазоров, статично (без анимации при наведении). Два режима
   (переключаются классом header-logo--full, см. header_logo_is_generic
   в base.html): обычное фото авто — только левый край, во всю высоту
   шапки; общий header.png (нет привязанного авто) — на всю шапку
   1100×150 целиком, без единого отступа со всех четырёх сторон. */
.header-logo {
  display: inline-flex;
  align-items: center;
  max-width: 60%;
  position: relative;
  z-index: 1;
}
.header-logo img {
  height: 150px;
  max-width: 100%;
  width: auto;
  object-fit: contain;
  border: none;
  display: block;
}

.header-logo--full {
  position: absolute;
  inset: 0;
  max-width: none;
  z-index: 0;
  /* .header-logo (базовый класс) — display:inline-flex, align-items:center —
     без переопределения ниже <picture> остаётся ФЛЕКС-ЭЛЕМЕНТОМ этого же
     контейнера: default align-items:center не растягивает его по высоте,
     default flex-grow:0 не растягивает по ширине — картинка рендерилась в
     своём натуральном размере слева, остальная площадь шапки оставалась
     голым белым фоном .site-header (реальный баг, найден пользователем на
     живом скриншоте 2026-07-28, «белая дыра» — воспроизводилась в ЛЮБОМ
     режиме ширины, не только «Широкая», ширина тут была ни при чём).
     display:block снимает flex-контекст для этой (full) разновидности. */
  display: block;
}
.header-logo--full picture {
  display: block;
  width: 100%;
  height: 100%;
}
.header-logo--full img {
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}

.header-controls {
  display: flex;
  align-items: center;
  gap: .25rem;
  margin-left: auto;
  position: relative;
  z-index: 1;
}

.header-btn {
  display: inline-flex;
  align-items: center;
  color: rgba(0,0,0,.7);
  background: none;
  border: none;
  padding: .5rem .75rem;
  border-radius: .5rem;
  font-size: .95rem;
  cursor: pointer;
  transition: color .15s, background .15s;
  text-decoration: none;
}
.header-btn:hover {
  color: #000;
  background: rgba(0,0,0,.08);
}
/* Шапка всегда белая (см. .site-header) — иконки в тёмной теме были почти
   белыми и терялись на белом фоне; красим в серый, как в светлой теме. */
[data-bs-theme="dark"] .header-btn { color: rgba(0,0,0,.55); }
[data-bs-theme="dark"] .header-btn:hover { color: rgba(0,0,0,.85); background: rgba(0,0,0,.08); }

/* ── Иконочная навигация ── */
.icon-nav {
  background: var(--bs-body-bg);
  border-bottom: 1px solid var(--bs-border-color);
  box-shadow: var(--shadow-sm);
}

.icon-nav-inner {
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  padding: .5rem 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: var(--nav-icon-gap, .375rem);
}

.icon-nav-btn {
  width: var(--nav-icon-size, 52px);
  height: var(--nav-icon-size, 52px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: .5rem;
  font-size: calc(var(--nav-icon-size, 52px) * .46);
  color: var(--bs-secondary-color);
  text-decoration: none;
  transition: background .15s, color .15s;
  flex-shrink: 0;
}
.icon-nav-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
}
.icon-nav-btn.active {
  background: var(--accent);
  color: #fff;
}
[data-bs-theme="dark"] .icon-nav-btn:hover {
  background: var(--accent-soft-dark);
  color: var(--accent);
}
[data-bs-theme="dark"] .icon-nav-btn.active {
  background: var(--accent);
  color: #fff;
}

/* ── Основной контент 1100px ── */
main.site-main {
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  padding-left: 1rem;
  padding-right: 1rem;
}

/* ── KPI карточки (дашборд) ── */
.kpi-card {
  border-left: 4px solid;
  transition: transform .15s;
}
.kpi-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.kpi-card.kpi-primary { border-color: var(--bs-primary); }
.kpi-card.kpi-success { border-color: var(--bs-success); }
.kpi-card.kpi-warning { border-color: var(--bs-warning); }
.kpi-card.kpi-danger  { border-color: var(--bs-danger);  }
.kpi-card.kpi-info    { border-color: var(--bs-info);    }

.kpi-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
}

.kpi-label {
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .7;
}

/* ── KPI-строка авто (компактная, одна строка без переноса) ── */
.kpi-vehicle-row {
  display: flex;
  flex-wrap: nowrap;
  gap: .5rem;
  overflow-x: auto;
  padding-bottom: 4px; /* место под скроллбар */
}
.kpi-vehicle-col {
  flex: 1 1 0;
  min-width: 100px;
}
/* Уменьшенные шрифты внутри компактной строки */
.kpi-vehicle-row .kpi-value {
  font-size: 1.1rem;
}
.kpi-vehicle-row .kpi-label {
  font-size: .68rem;
}

/* ── Таблицы ── */
.table-hover tbody tr:hover {
  background-color: rgba(13,110,253,.05);
}

[data-bs-theme="dark"] .table-hover tbody tr:hover {
  background-color: rgba(99,155,255,.08);
}

/* ── Бейджи типов трат ── */
.badge-fuel    { background-color: var(--fuel-color);    color: #fff; }
.badge-wash    { background-color: var(--wash-color);    color: #000; }
.badge-parking { background-color: var(--parking-color); color: #fff; }
.badge-ev-charge { background-color: var(--ev-color); color: #000; }

/* ── Строки с пометкой dirty ── */
.row-dirty td:first-child {
  border-left: 3px solid var(--bs-warning);
}

/* ── Подсветка строки при переходе из другой страницы ── */
@keyframes row-hl-fade {
  0%, 30% { background-color: var(--row-hl-start); }
  100%     { background-color: var(--row-hl-end); }
}
.row-hl td { animation: row-hl-fade 2.5s ease-out forwards; }

/* ── Кнопки действий в таблице ── */
.btn-action {
  padding: .2rem .45rem;
  font-size: .8rem;
}

/* ── Единая система табличных списков (2026-07-29, ШЕСТАЯ, финальная
   версия) — заменяет ВСЕ индивидуальные `-table`/`-notes-cell` классы,
   заменяет пятую версию `.act-col` целиком.

   КОРЕНЬ всех предыдущих версий проблемы был один и тот же и не решался
   точечно: таблица имела `width: 100%` (растягивалась на весь контейнер
   принудительно), из-за чего браузеру ВСЕГДА было что распределять —
   разницу между суммой предпочтительных ширин колонок и шириной
   контейнера. КУДА уходил этот излишек — раз на раз не приходилось:
   иногда в `.act-col` (даже при явном `width`), иногда в «Примечание»
   (раздувая его сверх разумного), иногда и туда, и туда одновременно —
   поведение зависело от table-layout (auto/fixed), от того, сколько
   колонок оставались без явной ширины, и от состава ВИДИМЫХ колонок на
   конкретной странице (могло отличаться даже между вкладками ОДНОЙ и той
   же таблицы, если часть колонок скрыта условно).

   РЕШЕНИЕ — убрать саму ПРЕДПОСЫЛКУ, не подбирать реакцию на неё:
   `.dt-table` имеет `table-layout: fixed` (ширина ЛЮБОЙ колонки — всегда
   буквально то, что объявлено в `<th>`, без исключений) И `width: auto`
   (НЕ 100% — таблица не растягивается принудительно на контейнер, её
   итоговая ширина — точная СУММА объявленных колонок). Раз таблица не
   шире своего содержимого, распределять браузеру физически нечего —
   ни один сценарий catch-22 из прошлых версий больше не воспроизводим
   структурно, не только «пока опять где-то не забыли класс».

   ОБЯЗАТЕЛЬНЫЙ КОНТРАКТ для любой новой страницы со списком (см. также
   раздел «Соглашения по коду» в CLAUDE.md):
   1. `<table class="table ... dt-table">`, обёрнута в `<div class=
      "table-responsive">` (стандартный Bootstrap-класс) — если реальных
      данных на строку больше, чем помещается по ширине экрана, ЗАКОННЫЙ
      горизонтальный скролл появляется САМ, это не костыль и не баг, а
      честный сигнал «здесь много колонок»; постоянного/паразитного
      скролла из-за раздувания больше не бывает.
   2. КАЖДАЯ колонка `<th>` получает явный `width` в px, БЕЗ ИСКЛЮЧЕНИЙ —
      под `table-layout: fixed` колонка без ширины схлопывается почти до
      нуля, недопустимо ни для одной колонки.
   3. Колонка «Примечание»/«Комментарий»/«Заметка» — обычная колонка со
      СВОИМ разумным max-px (обычно 180-220px), не «безразмерная,
      поглощающая остаток» — класс `.dt-note` на `<td>` (не `<th>`) даёт
      ей `overflow:hidden;text-overflow:ellipsis` с `title=` на полный
      текст, не «резиновую» ширину.
   4. `.act-col` — `<th>`/`<td>` колонки действий, ОДНА иконка на
      колонку («1 значение — 1 колонка», см. историю правок в CLAUDE.md) —
      фиксированный `width: 29px` РАБОТАЕТ ОДИНАКОВО на любой `.dt-table`
      без исключений, т.к. режим раскладки теперь всегда один и тот же. */
/* ВОСЬМАЯ версия (2026-07-29, вечер) — попытка независимых px-ширин без
   компенсации соседа, `width:auto` — ПРИЗНАНА НЕВЕРНОЙ моделью в ТОТ ЖЕ
   день: пользователь дал буквальную Excel-спецификацию (см. ДЕВЯТУЮ версию
   ниже) — сумма ширин ВСЕХ колонок обязана РАВНЯТЬСЯ ширине блока (не
   больше — независимые px давали суммарную ширину таблицы шире контейнера
   и «законный», но НЕ желаемый пользователем горизонтальный скролл).
   Оставлено как историческая запись, кодом не используется. */
/* ДЕВЯТАЯ версия (2026-07-29, ночь) — модель Excel, дословное требование
   пользователя: (1) сумма ширин ВСЕХ колонок = ширина блока, всегда, без
   исключений — НЕ `width:auto` (восьмая версия), а `width:100%`; (2)
   перетаскивание границы между колонкой i и следующей ВИДИМОЙ i2 меняет
   ТОЛЬКО их пару (одна забирает у другой ровно то, что та отдаёт) — все
   остальные колонки не двигаются и не меняют ширину вообще; (3) хранится
   в ПРОЦЕНТАХ (не px), т.к. сумма всегда есть 100% блока. Это возврат к
   модели седьмой версии (проценты+компенсация соседа), но с исправлением
   двух причин её прошлого провала — иначе не «упрётся» и не наложится:
   пол минимальной ширины пары считается динамически из РЕАЛЬНОЙ ширины
   таблицы в момент драга (`MIN_COL_PX/tableWidthPx*100`, не «3% от суммы
   видимых» — та цифра была условной и провоцировала ложное «дальше не
   тянется»), и перенос текста на вторую строку (`.dt-table td` ниже)
   остаётся как есть — контенту всегда есть куда деться при сужении, а не
   вылезать на соседнюю ячейку. Подробности алгоритма (rescale видимых %
   на 100% при частично скрытом наборе колонок и т.п.) — см. dt_resize.js. */
.dt-table {
  table-layout: fixed;
  width: 100%;
}
/* Контент ячейки переносится на вторую строку внутри своей ширины вместо
   вылезания на соседнюю колонку (!important — перебивает text-nowrap,
   который расставлен по многим td индивидуально); overflow:hidden —
   финальная защита для контента, который в принципе не может перенестись
   (бейдж без пробелов шире колонки) — тот тихо обрежется, но НИКОГДА не
   наедет на соседнюю ячейку. Только <td> — не <th>, чтобы не обрезать
   .dt-resize-handle (у неё намеренный отрицательный right, см. ниже). */
.dt-table td {
  white-space: normal !important;
  overflow-wrap: break-word;
  word-wrap: break-word;
  overflow: hidden;
}
/* Иконка+текст заголовка — ВСЕГДА к левому краю ячейки, единообразно на
   всех колонках всех dt-table (прямое требование пользователя 2026-07-29:
   при драге границы содержимое обеих сторон должно оставаться прижатым
   к своему левому краю, а не «прыгать» между left/center/right в
   зависимости от того, забыли ли на конкретном th text-end/text-center).
   .act-col раньше был единственным исключением (центрировался) — прямо
   отвергнуто пользователем 2026-07-29 (ночь): при перетаскивании границы
   центрированное содержимое (иконка чека/редактирования/удаления) визуально
   «плывёт» не так, как левый край, — граница и левый-выровненный сосед
   двигаются согласованно, а центрированная иконка смещается лишь наполовину
   в любую сторону, создавая ощущение рассинхрона. Теперь .act-col тоже
   левый, без исключений — ВСЕ колонки ВСЕХ таблиц ведут себя одинаково. */
.dt-table th {
  text-align: left;
}
/* Отступ содержимого ячейки — статичный, слайдер-регулировка зазора
   удалена целиком (2026-07-29, ночь, см. dt_resize.js) — визуально не
   давала эффекта, признана лишней самим пользователем. `!important`
   обязателен: у Bootstrap `.table-sm > :not(caption) > * > *{padding:.25rem
   .25rem}` — ДВА классовых селектора (`.table-sm`+`:not()`) против нашего
   ОДНОГО (`.dt-table`+тип `th`/`td`) — по специфичности Bootstrap выигрывал
   бы вообще без !important, поэтому левый отступ до этой правки не
   гарантированно был именно тем, что здесь написано (расхождение,
   пойманное пользователем — «первая колонка начинается впритык к краю»).
   3px слева — «2-3 пикселя от края» дословно из требования пользователя. */
.dt-table th,
.dt-table td {
  padding-left: 10px !important;
  padding-right: 6px !important;
}
/* Anti-FOUC (2026-07-29): скрыта до готовности dt_resize.js (colgroup с
   финальными % построен) — иначе виден скачок «дефолт → сохранённое».
   visibility (не display) — элемент остаётся в layout, JS может измерить
   реальные px ширины ДО показа. .dt-ready ставит сам dt_resize.js (успех
   ИЛИ failsafe-таймер ~1.5с); если JS вообще не выполнился (отключён/
   заблокирован) — `<noscript>` в base.html форсирует visibility:visible
   отдельным правилом, таблица не остаётся скрытой навсегда. */
.dt-table[data-table-key] {
  visibility: hidden;
}
.dt-table[data-table-key].dt-ready {
  visibility: visible;
}
.act-col {
  width: 29px; /* fallback, если JS (colgroup) не выполнился — работоспособность без JS */
  white-space: nowrap;
}
.act-col:last-child {
  padding-right: 8px !important;
}
.dt-note {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: default;
}
/* Реальный баг (2026-08-05, найден пользователем и на ПК, и на мобильном
   ландшафте): `.dt-table td{white-space:normal!important}` выше (специфич-
   ность td+класс — ВЫШЕ простого класса `.dt-note`) перебивал `nowrap`
   несмотря на порядок в файле — «Примечание»/легаси `style="max-width:0"`
   всё равно переносились на вторую строку. `.dt-table td.dt-note`/
   `.dt-table td[style*="max-width:0"]` — та же пара классов+тип, что и у
   перебиваемого правила, но С добавленным классом-квалификатором — выше
   специфичность, `!important` побеждает `!important`. Действует ОДИНАКОВО
   на ПК/портрет/ландшафт — обрезка одной строкой везде, полный текст —
   только через `title=`/tooltip по наведению/тапу, не переносом. */
.dt-table td.dt-note,
.dt-table td[style*="max-width:0"] {
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis;
}
/* Тот же баг «"0 Р" рвётся на "0" и осиротевшую "Р"» (2026-08-13, см.
   мобильные блоки `.dt-table` ниже), но в ПРОСТЫХ таблицах, которые
   вообще не входят в систему `.dt-table` (сводки/аномалии «Центра
   контроля» и подобные короткие списки без `data-table-key`) — там нет
   ни `table-layout:fixed`, ни явных ширин колонок, браузерный дефолт
   `white-space:normal` точно так же разрешает разрыв по пробелу внутри
   короткого значения при недостатке места. `td.text-end` — надёжный
   маркер именно ТАКИХ ячеек по всему проекту (сумма/дата/счётчик,
   короткое право-выровненное значение), не длинного свободного текста —
   правило общее, не только мобильное: переносить сумму по пробелу не
   нужно ни на каком экране. */
.table-responsive td.text-end {
  white-space: nowrap;
}

/* ── Модалка действий строки (2026-08-05) — заменяет колонки `.act-col`
   («Редактировать»/«Удалить»/«Уточнить»/toggle-кнопки/«День в календаре» и
   т.п.) на единый клик по строке: вся построчная разметка иконок убирается
   из `<td>`, вместо неё `<tr data-actions='[...]'>` — JSON-список действий,
   строится на Python-стороне (см. app/static/js/row_actions.js). Перевод
   идёт постранично, полное удаление `.act-col` — только когда переведены
   ВСЕ таблицы (см. main.css выше, «Единая система табличных списков»). */
tr.row-actions-trigger {
  cursor: pointer;
}
/* `.dt-note`/`.exp-name-cell` были заведены ДО модалки действий строки —
   тогда это были некликабельные текстовые ячейки с обрезкой+tooltip,
   `cursor: default` был корректен. У них есть СВОЙ явный cursor на `<td>`,
   он специфичнее и локально побеждает `tr.row-actions-trigger` — курсор
   над этими ячейками оставался стрелкой, хотя клик там уже открывает
   модалку (реальный баг, найден пользователем 2026-08-09). Правило ниже
   специфичнее `.dt-table td.dt-note`/`.exp-name-cell` (та же пара
   класс+класс, но плюс тип `tr.row-actions-trigger` — выше вес). */
tr.row-actions-trigger td.dt-note,
tr.row-actions-trigger td.exp-name-cell {
  cursor: pointer;
}
.row-action-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .4rem;
  border: 0;
  background: none;
  padding: .6rem .5rem;
  min-width: 76px;
  border-radius: .5rem;
  cursor: pointer;
}
.row-action-btn:hover,
.row-action-btn:active {
  background: var(--bs-tertiary-bg, rgba(127,127,127,.12));
}
.row-action-btn i {
  font-size: 1.65rem;
  line-height: 1;
}
.row-action-btn span {
  font-size: .72rem;
  color: var(--bs-body-color);
  text-align: center;
  line-height: 1.15;
  max-width: 84px;
}
.dt-resize-handle {
  position: absolute;
  top: 0;
  right: -3px;
  bottom: 0;
  width: 6px;
  cursor: col-resize;
  z-index: 3;
}
.dt-resize-handle:hover,
.dt-resize-handle.resizing {
  background: rgba(var(--accent-rgb), .35);
}
@media (max-width: 767.98px) {
  .dt-resize-handle { display: none; }
}
/* ── Tooltip (иконка/заголовок колонки/обрезанная запись) и popover
   (пояснение по "!") — СОЗНАТЕЛЬНО РАЗНЫЕ по цвету, не путать — 2026-08-11,
   прямая правка пользователя после первой попытки. Popover уже штатно
   следует переменным страницы (--bs-body-bg/--bs-body-color, см. bootstrap.
   min.css) — тёмный фон/светлый текст в тёмной теме, светлый фон/тёмный
   текст в светлой, трогать не нужно. Tooltip штатно ИНВЕРТИРУЕТ контраст
   относительно страницы (--bs-tooltip-bg: var(--bs-emphasis-color) —
   светлый фон/тёмный текст в тёмной теме, тёмный фон/светлый текст в
   светлой) — это и есть привычный вид «как было», никакого переопределения
   не требуется, здесь нарочно НЕТ кастомных `--bs-tooltip-*`. Источник
   разнобоя был не в цвете, а в том, что `<th title="...">` (и вообще любой
   `title=`) без `data-bs-toggle="tooltip"` рядом заставляет браузер рисовать
   СВОЙ нативный тултип (мелкая квадратная рамка, системный шрифт) вместо
   штатного Bootstrap — при добавлении новой подсказки ОБЯЗАТЕЛЕН
   `data-bs-toggle="tooltip"` рядом с `title`, см. «Соглашения по коду». ── */

/* ── Квадратные icon-only кнопки в шапке страницы (подсказка по наведению) ── */
.btn-icon-square {
  width: 34px;
  height: 34px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  font-size: 1rem;
}

/* ── Попап-фото водителя при наведении («Кто сегодня работает»), 2026-08-01 ── */
.roster-hover-photo {
  display: block;
  width: 90px;
  height: 110px;
  object-fit: cover;
  border-radius: 6px;
  margin: 0 auto;
}
.roster-hover-photo--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bs-secondary-bg-subtle);
  color: var(--bs-secondary-color);
  font-size: 2.2rem;
}

/* ── Фильтры ── */
.filter-bar {
  background: var(--bs-tertiary-bg);
  border-radius: var(--border-radius);
  padding: .75rem 1rem;
  margin-bottom: 1.25rem;
}

/* ── Форма смены (полная страница) ── */
.shift-section {
  border-left: 3px solid var(--bs-secondary-border-subtle);
  padding-left: 1rem;
  margin-bottom: 1.5rem;
}

.shift-section-title {
  font-weight: 600;
  font-size: .9rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-bottom: .75rem;
  color: var(--bs-secondary);
}

/* ── Ссылки на чеки ── */
.receipt-link {
  font-size: .85rem;
  word-break: break-all;
}

/* ── Прямая загрузка чека/фото (Фаза 5) ── */
.receipt-upload-manual.receipt-upload-dimmed {
  opacity: .45;
  pointer-events: none;
}
.receipt-upload-controls .receipt-preview-img {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--bs-border-color);
}

/* ── Карточка баланса ── */
.balance-positive { color: var(--income-color); font-weight: 600; }
.balance-negative { color: var(--expense-color); font-weight: 600; }

/* ── Отчёты ── */
.chart-container {
  position: relative;
  height: 320px;
}

/* ── Навигация «хлебные крошки» и заголовок страницы ── */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
  gap: .5rem;
}

.page-header h1, .page-header h2 {
  margin: 0;
  font-size: 1.4rem;
  font-weight: 700;
}

/* ── Выходной / праздник строки смены ── */
.shift-weekend { background-color: rgba(255, 193, 7, .08) !important; }
.shift-holiday { background-color: rgba(220, 53, 69, .08) !important; }

/* ── Панель «Настройки интерфейса» (a-la Tabler Theme Settings, 2026-07-24) ── */
.ui-accent-swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.ui-accent-swatch.active {
  border-color: var(--bs-body-color);
}

/* Широкая ширина страницы — просто увеличенный max-width тех же трёх
   контейнеров, что уже центрируются на 1100px, ничего в разметке не
   меняется. */
html[data-layout="wide"] .site-header,
html[data-layout="wide"] .icon-nav-inner,
html[data-layout="wide"] main.site-main {
  max-width: 1400px;
}

/* Боковая навигация — ТОЛЬКО ПРЕДПРОСМОТР, чисто CSS-переключение без
   переделки разметки: существующий .icon-nav становится фиксированной
   левой панелью, .site-header/main.site-main сдвигаются под неё через
   padding-left на body (сами они как центрировались max-width+auto margin,
   так и продолжают — просто в уменьшенной с одной стороны области).
   Только для экрана ≥768px — на мобильном верхняя иконочная строка не
   трогается вообще, там боковая панель не имеет смысла. */
@media (min-width: 768px) {
  html[data-nav="sidebar"] body {
    padding-left: 76px;
  }
  html[data-nav="sidebar"] .icon-nav {
    position: fixed;
    top: 156px; /* высота .site-header (150px) + её верхний отступ (6px) —
                   иначе сайдбар наезжает на шапку/лого (реальный баг,
                   найден и исправлен 2026-07-24) */
    left: 0;
    bottom: 0;
    width: 76px;
    border-bottom: none;
    border-right: 1px solid var(--bs-border-color);
    overflow-y: auto;
    z-index: 1030;
  }
  html[data-nav="sidebar"] .icon-nav-inner {
    flex-direction: column;
    flex-wrap: nowrap;
    max-width: none;
    margin: 0;
    padding: 1rem .5rem;
  }
}

/* ─────────────── МОБИЛЬНЫЕ ─────────────── */
@media (max-width: 767.98px) {

  .site-header { height: 90px; border-radius: 0; }
  .header-logo:not(.header-logo--full) img { height: 90px; }

  .icon-nav-inner {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .icon-nav-inner::-webkit-scrollbar { display: none; }

  main.site-main {
    padding-left: .75rem;
    padding-right: .75rem;
  }

  /* Шрифт таблиц */
  .table { font-size: .82rem; }

  /* KPI в 2 колонки */
  .kpi-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .75rem;
  }

  .kpi-value { font-size: 1.4rem; }

  /* Скрываем лишние колонки в таблице */
  .d-mob-none { display: none !important; }

  /* Фильтр — стэк */
  .filter-bar .row > * { margin-bottom: .5rem; }

  /* Полные кнопки на мобиле — долгое нажатие перетаскивает в любое место
     экрана (fab_drag.js, 2026-08-05), позиция запоминается per-страница */
  .btn-add-mobile {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 1050;
    border-radius: 50%;
    width: 3.25rem;
    height: 3.25rem;
    font-size: 1.4rem;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 12px rgba(0,0,0,.25);
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
  }
  .btn-add-mobile.fab-dragging {
    box-shadow: 0 8px 24px rgba(0,0,0,.4);
    transform: scale(1.12);
    opacity: .92;
  }

  /* Вторая плавающая кнопка (QR-чек, my_expenses, 2026-08-12) — цвет НЕ
     хардкодится, а вычисляется как комплементарный к текущему акценту
     пользователя через hue-rotate(180deg) поверх var(--accent) (тот же
     единственный источник, что и --bs-primary-rgb) — следует за любой
     сменой темы автоматически, без JS. По умолчанию стоит стопкой НАД
     обычной кнопкой (bottom смещён на высоту кнопки+зазор), обе
     перетаскиваются независимо (data-fab-id, см. fab_drag.js). */
  .btn-qr-mobile {
    bottom: 5.5rem;
    background: var(--accent);
    filter: hue-rotate(180deg);
    color: #fff;
    border-color: transparent;
  }

  /* Форма — метки сверху */
  .form-label { font-size: .88rem; }

  /* Chart */
  .chart-container { height: 240px; }

  /* Page header стэк. Компаунд-селектор `.page-header.justify-content-
     between` (не голый `.page-header`) — сознательно: часть шаблонов
     вешает на ТОТ ЖЕ элемент бутстраповские `d-flex justify-content-
     between align-items-center` (сами с !important), эти страницы и есть
     цель фикса — без !important здесь Bootstrap выигрывал, заголовок
     оставался по центру вместо левого края (2026-08-04, sick_leaves/
     vacations/shift_swaps/route_assignments/work_schedule и другие).
     Другой, более простой паттерн шапки — «стрелка назад + заголовок»
     (`.page-header d-flex align-items-center gap-2`, БЕЗ justify-content-
     between) — НЕ содержит класс `.justify-content-between` вообще,
     поэтому этим правилом не затрагивается: там колонка сломала бы
     верстку (стрелка над заголовком вместо рядом). */
  .page-header.justify-content-between {
    flex-direction: column !important;
    align-items: flex-start !important;
  }

  /* Кнопочные ряды в шапке страницы (действия: добавить/экспорт/фильтры-
     переключатели и т.п.) — на мобильном раньше при нехватке ширины
     переносились НОВОЙ СТРОКОЙ (Bootstrap `.flex-wrap`) и, из-за
     `align-items:flex-start` на самом `.page-header`, каждая обёртка-
     строка кнопок сама по себе сжималась под контент и визуально
     «пирамидой» съезжала — набор кнопок разной ширины на разных
     перенесённых строках. Прямое требование пользователя: кнопки не
     должны «вставать друг на друга и центроваться» — вместо переноса
     строкой ряд остаётся в ОДНУ строку и скроллится пальцем по
     горизонтали (тот же приём, что уже применяется к верхней навигации
     `.icon-nav-inner`), кнопки не сжимаются (`flex-shrink:0`), поэтому
     сохраняют комфортный размер под палец независимо от их числа.
     Затрагивает дочерние `.d-flex`-обёртки `.page-header` — это
     стандартный, повсеместный паттерн проекта (84 шаблона), нестандартные
     собственные заголовки страниц (`places/index.html` и т.п.) размечены
     тем же классом `.mob-btn-row` вручную. ПК (`min-width:768px`) не
     затронут — правило целиком внутри мобильного `@media`. */
  .page-header > .d-flex,
  .mob-btn-row {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    max-width: 100%;
    /* align-self:stretch — тикеты №55/№56 (2026-09-05): без него, под
       `align-items:flex-start` родителя-`.page-header`, ЭТОТ ряд как
       flex-item сам сжимается до собственного fit-content-размера — а
       тот, эмпирически проверено, у flex-контейнера с overflow-x:auto
       получается МЕНЬШЕ суммы детей (select+button, сумма 262px, реальный
       fit-content — 236px, разница ровно с последний видимый элемент
       наполовину обрезан). Растягивание на всю ширину `.page-header`
       устраняет это самостоятельно — если контент ряда короче доступной
       ширины (обычный случай: 1-2 селекта + иконка), он теперь весь
       помещается БЕЗ горизонтального скролла; если контент шире (много
       вкладок Центра контроля и т.п.) — `overflow-x:auto` продолжает
       работать штатно, поведение НЕ меняется (stretch задаёт МИНИМУМ
       ширины, не потолок). Живой тест — оба тикета, iframe-эмуляция
       390px, до/после. */
    align-self: stretch;
  }
  .page-header > .d-flex::-webkit-scrollbar,
  .mob-btn-row::-webkit-scrollbar {
    display: none;
  }
  .page-header > .d-flex > *,
  .mob-btn-row > * {
    flex-shrink: 0;
  }

  /* Таблицы `.dt-table` на мобильном — CONTENT-BASED раскладка
     (`table-layout:auto`), НЕ `fixed`, прямое требование пользователя
     после ТРЁХ проваленных попыток той же сессии починить это через
     `fixed`+вручную угаданное число:
     1) `fixed`+`width:auto` — Chrome эмпирически КЛАМПИТ ширину таблицы к
        ширине контейнера независимо от суммы объявленных `<th>` (тот же
        баг, что уже был у Конструктора отчётов) — колонки без запаса
        сжимались ниже содержимого («Штрафы», текст рвался по одной букве).
     2) JS-фикс на это (сумма `measurePx()` по ВСЕМ `<th>`) сам оказался
        багом — считал в сумму и `.d-mob-none`-СКРЫТЫЕ колонки тоже
        (Одометр — из ~26 колонок видно всего 4+3 иконки, остальные
        скрыты) — `fixed` растягивал раздутую сумму ТОЛЬКО на видимые
        колонки, отсюда «Дата — почти половина экрана».
     3) Переход на `table-layout:auto` С `width:auto` (не `100%`) убрал
        оба класса растяжения/сжатия, НО породил третий баг — `width:auto`
        для таблицы означает shrink-to-fit (браузер даёт РОВНО столько
        ширины, сколько нужно содержимому, не больше) — если сумма
        реальных минимальных ширин видимых колонок МЕНЬШЕ ширины экрана
        (типичный случай — 3-4 узкие колонки + 2 иконки), таблица
        оставалась ýже экрана, справа висело пустое поле карточки (живой
        скриншот пользователя, `payments`/«Приход» — таблица занимала
        от силы 75% ширины).
     Правильная комбинация — `table-layout:auto` (браузер меряет РЕАЛЬНЫЙ
     контент только ВИДИМЫХ ячеек — `.d-mob-none` не участвует в расчёте
     вообще, колонку нельзя сжать ниже содержимого) `+` `width:100%`
     (наследуется от базового, НЕ media-scoped правила `.dt-table{width:
     100%}` — здесь НЕ переопределяется). Под `auto`-раскладкой (в
     отличие от `fixed`) это безопасно в ОБЕ стороны: если сумма
     минимальных ширин колонок МЕНЬШЕ 100% контейнера — браузер
     распределяет остаток МЕЖДУ колонками пропорционально их контенту
     (не оставляет пустое поле сбоку); если БОЛЬШЕ — таблица растёт за
     100% контейнера (никогда не сжимает колонку ниже содержимого),
     `.table-responsive` даёт горизонтальный скролл ровно тогда, когда он
     реально нужен. `dt_resize.js` на мобильном не трогает ширину
     таблицы вообще (ни colgroup, ни inline style) — вся работа на
     браузере, ни одного числа, угаданного в коде проекта. */
  .dt-table[data-table-key] {
    table-layout: auto;
    max-width: none;
  }
  .dt-table[data-table-key] th,
  .dt-table[data-table-key] td {
    width: auto !important;
  }
  /* Ничего НЕ переносится на вторую строку внутри ячейки на мобильном
     портрете (2026-08-13, прямая находка пользователя — «строки
     расползаются на 2-4 непонятно для чего», конкретный симптом «0 Р»
     ломалось на «0» и осиротевшую «Р» на новой строке). Причина —
     базовое `.dt-table td{white-space:normal!important}` (задумано ТОЛЬКО
     для десктопного `table-layout:fixed`, где перенос — единственный
     способ удержать длинный текст в объявленной px-ширине колонки) не
     отключается в мобильном auto-layout режиме — а под `auto` браузер
     меряет MIN-content ширину колонки по САМОМУ длинному НЕРАЗРЫВНОМУ
     слову; с `white-space:normal` пробел — легальная точка разрыва,
     поэтому «0 Р» умеет ужаться до одной «Р» ценой переноса «0» на
     отдельную строку при любом дефиците места — а на мобильном экране с
     10+ колонками дефицит есть всегда. `nowrap` возвращает auto-layout
     его честную семантику «min-content = ширина ЦЕЛОЙ ячейки одной
     строкой»: если сумма таких ширин больше экрана — таблица просто
     становится шире контейнера, законный горизонтальный скролл внутри
     уже имеющегося `.table-responsive` (прямо то, что запросил
     пользователь — скролл пальцем это ОК, разъезжание строки на
     несколько линий — нет). `!important` + `[data-table-key]`-атрибут в
     селекторе — та же техника перебивки специфичности, что уже
     применяется для `.dt-table td.dt-note` выше (базовое правило тоже на
     `!important`, побеждает более специфичный селектор). */
  .dt-table[data-table-key] td {
    white-space: nowrap !important;
  }
  /* Иконки действий (`.act-col`) — тесная колонка без лишнего воздуха:
     обычный паддинг 10px/6px (см. `.dt-table th,td` выше) рассчитан на
     текстовые колонки, для одной кнопки-иконки это чистый балласт. */
  .dt-table[data-table-key] .act-col {
    padding-left: 3px !important;
    padding-right: 3px !important;
  }
  /* Truncate-колонки без table-layout:fixed теряют смысл overflow:
     ellipsis (нечему обрезать, если ширина ничем не ограничена) — на
     мобильном нужен явный потолок. Покрывает и текущую `.dt-note`, и
     легаси-приём `style="max-width:0"` (percent-колонки до системы
     .dt-note — payments/vehicle_expenses/maintenance/control/
     my_expenses/service_places/shift_swaps). */
  .dt-table[data-table-key] .dt-note,
  .dt-table[data-table-key] td[style*="max-width:0"] {
    max-width: 130px !important;
  }
}

/* Телефон, повёрнутый БОКОМ (ландшафт) — попадает в диапазон ШИРИНЫ
   десктопа/планшета (768-991px), поэтому предыдущий блок его не ловит:
   получал `table-layout:fixed` + процентные ширины `dt_resize.js`,
   рассчитанные на куда более широкий экран — колонки схлопывались,
   ячейка «Дата» с 2-3 доп. иконками (история изменений/заморозка/
   «изменено после проверки») переставала помещаться в одну строку
   (2026-08-05, найдено пользователем на «Сменах»). `(max-height:500px)
   and (orientation:landscape)` — стандартный эвристический трюк отличить
   именно ТЕЛЕФОН боком (типичная высота 350-430px) от десктопного/
   планшетного окна ТОЙ ЖЕ ширины (высота почти всегда заметно больше) —
   без user-agent sniffing, тот же класс эвристики, что используется
   везде в вебе для «мобильный ландшафт».

   В отличие от портретного блока выше, здесь СОЗНАТЕЛЬНО НЕ скрываются
   `.d-mob-none`-колонки — вширь есть место показать больше, чем в
   портретном режиме (прямое требование пользователя: «теперь нет
   колонок [действий], можно всё разместить как надо пропорционально»).
   Общий класс `.dt-table` — правило действует на ЛЮБОЙ странице с этой
   системой (тот же принцип, что и у портретного блока: один общий
   компонент, не per-страничная копия), проверено и настроено пока
   только на «Сменах» — остальные страницы унаследуют его автоматически
   при переводе на систему `.dt-table` (все уже переведены), донастройка
   по месту — по факту жалобы, как и здесь. */
@media (min-width: 768px) and (max-width: 991.98px) and (max-height: 500px) and (orientation: landscape) {
  .dt-resize-handle { display: none; }
  .dt-table[data-table-key] {
    table-layout: auto;
    max-width: none;
  }
  .dt-table[data-table-key] th,
  .dt-table[data-table-key] td {
    width: auto !important;
  }
  .dt-table[data-table-key] col {
    width: auto !important;
  }
  /* Тот же баг и тот же фикс, что в портретном блоке выше (2026-08-13,
     см. развёрнутый комментарий там) — auto-layout здесь ТОЖЕ активен,
     базовое `.dt-table td{white-space:normal!important}` ТАК ЖЕ ломает
     короткие значения по пробелу («0 Р» → «0» и осиротевшая «Р» на новой
     строке) при нехватке места. */
  .dt-table[data-table-key] td {
    white-space: nowrap !important;
  }
  .dt-table[data-table-key] .act-col {
    padding-left: 4px !important;
    padding-right: 4px !important;
  }
  .dt-table[data-table-key] .dt-note,
  .dt-table[data-table-key] td[style*="max-width:0"] {
    max-width: 220px !important;
  }
  /* «Зеркальные» колонки (`data-mirror-index`, см. dt_resize.js::
     syncMirrors) — на десктопе их ширина уравнивается JS-ом (общий pct).
     На мобильном JS ширину таблицы вообще не трогает — `table-layout:
     auto` сам считает ширину КАЖДОЙ колонки НЕЗАВИСИМО, по её собственным
     ячейкам (th+td, все строки) — Топливо(факт) и Расход(норма) физически
     РАЗНЫЕ колонки в разметке (никак не связаны для браузера, `data-
     mirror-index` — атрибут, который знает только НАШ JS, самому браузеру
     он ничего не говорит), поэтому при разной длине реального текста в
     ячейках (напр. отрицательное значение нормы с минусом) их ширины
     расходились НЕЗАВИСИМО друг от друга — и, так как `width:100%` на
     таблице заставляет браузер распределять разницу между суммой
     предпочтительных ширин колонок и шириной контейнера ПРОПОРЦИОНАЛЬНО
     между ВСЕМИ колонками (не только изменившимися), сдвиг «прыгал» даже
     на совершенно не связанные «Начало дня»/«Конец дня»/«Пробег»
     (2026-08-06, найдено и подтверждено пользователем дважды подряд).
     ДВЕ предыдущие попытки не сработали:
     1) `min-width` — только нижняя граница, реальная (auto-вычисленная)
        ширина по-прежнему могла расти выше нее по-разному у факта/нормы.
     2) `width` (без `max-width`) ТОЛЬКО на `<th>` — задаёт лишь
        ПРЕДПОЧТИТЕЛЬНУЮ ширину заголовка, а под auto-layout ширина
        колонки — максимум preferred-width ВСЕХ её ячеек, включая `<td>`;
        `<td>` этих колонок не имели `data-mirror-index` вообще (только
        `<th>`) и продолжали жить на голом `width:auto` — их РЕАЛЬНЫЙ
        текст по-прежнему раздувал колонку по-разному у факта/нормы.
     Настоящий фикс — ДВА действия разом: (а) `data-mirror-index`
     проставлен теперь и на соответствующих `<td>` тоже (`odometer/
     index.html`, tbody, 2026-08-06), не только на `<th>`; (б) ширина
     фиксируется ЖЁСТКО — `width` + `max-width` ОДНИМ и тем же числом
     (не только `min-width`/только `width`) — под auto-layout explicit
     `width` без `max-width` всё ещё позволяет колонке ВЫРАСТИ шире, если
     контент требует больше места; `max-width` этого не допускает, вместе
     с `overflow:hidden`+`text-overflow:ellipsis` (контент этих ячеек —
     короткие числа, обрезка практически никогда не сработает, но
     гарантирует отсутствие визуального вылезания за границу, если
     когда-нибудь текст окажется длиннее). Селектор — БЕЗ ограничения по
     тегу (`[data-mirror-index=...]`, не `th[data-mirror-index=...]`) —
     матчит и `<th>`, и `<td>` одним правилом. */
  .dt-table[data-table-key] [data-mirror-index="odo0"] {
    width: 70px !important;
    max-width: 70px !important;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .dt-table[data-table-key] [data-mirror-index="odo1"],
  .dt-table[data-table-key] [data-mirror-index="odo2"],
  .dt-table[data-table-key] [data-mirror-index="odo3"],
  .dt-table[data-table-key] [data-mirror-index="odo4"] {
    width: 52px !important;
    max-width: 52px !important;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

@media (min-width: 768px) {
  .btn-add-mobile { display: none !important; }
}

/* Старые `-table`/`-notes-cell`/`.pay-driver-cell` классы (odo/shift/vexp/
   exp/pay/dep/ev/myexp) удалены 2026-07-29 — заменены единой `.dt-table`/
   `.dt-note`, см. блок выше «Единая система табличных списков». */

.exp-name-cell {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: default;
}
/* Та же специфичность-дыра, что и у `.dt-note` (см. блок выше, 2026-08-05) —
   `.dt-table td{white-space:normal!important}` перебивает голый класс. */
.dt-table td.exp-name-cell {
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis;
}

/* ── Бейджи карт (С = личная, А = работодателя) ── */
.badge-card-self,
.badge-card-employer {
  display: inline-block;
  padding: .2em .5em;
  border-radius: .375rem;
  font-size: .8rem;
  font-weight: 600;
  background: transparent;
  line-height: 1;
}

.badge-card-self {
  border: 2px solid var(--income-color);
  color: var(--income-color);
}

.badge-card-employer {
  border: 2px solid var(--expense-color);
  color: var(--expense-color);
}

/* ── Гос. номер авто — рамка в стиле реального номерного знака РФ ── */
.ru-plate {
  display: inline-flex;
  align-items: stretch;
  height: 1.7em;
  border: 2px solid #000;
  border-radius: .25rem;
  background: #fff;
  font-family: "Arial Narrow", Arial, sans-serif;
  font-weight: 700;
  line-height: 1;
  overflow: hidden;
  vertical-align: middle;
}

.ru-plate__body {
  display: flex;
  align-items: center;
  padding: 0 .35em;
  color: #000;
  letter-spacing: .02em;
}

.ru-plate__main {
  font-size: 1em;
}

.ru-plate__region {
  font-size: .8em;
  margin-left: .2em;
  align-self: center;
}

.ru-plate__flag {
  display: flex;
  flex-direction: column;
  width: 1.05em;
  border-left: 2px solid #000;
}

.ru-plate__flag-stripes {
  flex: 1 1 auto;
  background: linear-gradient(
    to bottom,
    #fff 0%, #fff 33.33%,
    #0039a6 33.33%, #0039a6 66.66%,
    #d52b1e 66.66%, #d52b1e 100%
  );
}

/* Тёмная тема — сама табличка всегда остаётся "белой", как настоящий
   номерной знак (иначе теряется весь смысл стилизации); только внешняя
   рамка чуть светлеет, чтобь не сливаться с тёмным фоном страницы. */
[data-bs-theme="dark"] .ru-plate {
  border-color: #555;
}
[data-bs-theme="dark"] .ru-plate__flag {
  border-left-color: #555;
}

/* Первая колонка "sticky" в широких таблицах-графиках (Учёт рабочего
   времени/Отпуска) — остаётся на месте при горизонтальном скролле дней,
   список водителей не теряется из виду. Общий класс, переиспользуется
   несколькими страницами (work_schedule, vacations/timeline). */
.sticky-col {
  position: sticky;
  left: 0;
  z-index: 5;
  background-color: var(--bs-body-bg);
  /* Собственный compositing-слой — известный класс багов sticky-элементов
     на очень широких скроллящихся контейнерах (Отпуска/График работы,
     трек может быть шире 15000px при макс. зуме) — без этого в некоторых
     браузерах колонка визуально «уплывает»/перекрывается содержимым трека
     на большом горизонтальном скролле (найдено пользователем 2026-07-28). */
  transform: translateZ(0);
}
.sticky-col-head {
  z-index: 3;
}

/* «Изменено после проверки» (2026-08-03, розовый — было жёлтым/bs-warning
   до правки в тот же день) — подсветка строки self-report списка,
   изменившейся ПОСЛЕ последней ЯВНОЙ проверки этим зрителем (кнопка
   «Отметить проверенным», см. app/utils/list_view.py — просто открыть
   страницу больше НЕ гасит подсветку, см. mark_reviewed()). Bootstrap не
   имеет штатного pink bg-*-subtle/text-*-emphasis, поэтому свои
   --row-changed-bg/--row-changed-text (theme-aware пара, см. :root/
   [data-bs-theme=dark] выше), не .table-warning/.bg-warning-subtle. */
.row-changed > td,
.visit-card.row-changed {
  background-color: var(--row-changed-bg) !important;
}
.row-changed-badge {
  color: var(--row-changed-text);
}

/* Ссылка в раздел, отключённый категории Смотрящего — обезврежена
   after_request'ом (app/utils/nav_access.py::guard_html_links): убран
   href/hx-*, добавлен data-nav-blocked. Не прячем (сломало бы ряды
   кнопок / ячейки таблиц), а гасим визуально и по кликам. */
a[data-nav-blocked] {
  opacity: .4;
  pointer-events: none;
  cursor: default;
  text-decoration: none;
}

/* Заморозка периода (control/driver_detail.html, settings/user_profile.html)
   — история закрытых периодов не должна раздувать высоту карточки по мере
   накопления записей (2026-08-04, прямой запрос пользователя) — фикс.
   высота ~6 строк table-sm (строка ~31px + шапка ~33px), дальше свой
   внутренний overflow-y:auto; шапка sticky-top внутри этого контейнера,
   не всей страницы. */
.frz-history {
  max-height: 219px;
  overflow-y: auto;
}

/* Блок bulk-заморозки (control/index.html) — на мобильном Примечание на
   своей строке, обе кнопки вместе строкой ниже (было 3-строчной пирамидой,
   2026-08-04). */
@media (max-width: 767.98px) {
  .frz-bulk-note { flex: 1 1 100%; }
  .frz-bulk-note input { width: 100% !important; }
}

/* Вкладки-иконки Центра контроля — счётчики-бейджи (position-absolute
   top-0 start-100 translate-middle) обрезались сверху overflow-x:auto
   у .mob-btn-row (тот же non-visible-overflow клипает обе оси) и налезали
   на соседнюю кнопку при узком gap-1 (2026-08-04). */
@media (max-width: 767.98px) {
  .ctrl-tabs-row {
    padding-top: 8px;
    gap: .5rem !important;
  }
}

/* Тот же клип-баг (см. выше .ctrl-tabs-row), общий класс для ЛЮБОГО ряда
   кнопок в .page-header с position-absolute-бейджем (top-0 start-100
   translate-middle) — сейчас: «Импорт из календаря» на /route_assignments/
   (2026-08-15, тикет #22). Сознательно НЕ применяется ко ВСЕМ `.page-
   header > .d-flex` рядам разом — узкий класс на конкретный ряд, как
   .ctrl-tabs-row, не бланкет-фикс (не все ряды кнопок несут бейдж). */
@media (max-width: 767.98px) {
  .header-badge-row {
    padding-top: 8px;
  }
}

/* ПК — иконки вкладок Центра контроля крупнее (х2) + больший промежуток
   между кнопками, чтобы счётчики-бейджи соседних вкладок не сливались
   (2026-08-11, прямое требование пользователя; мобильная раскладка выше
   не трогается — там уже свой фикс от 2026-08-04). Кнопка экспорта
   (#ctrlExportBtn) в этом же ряду — не «закладка», остаётся штатного
   размера .btn-icon-square. */
@media (min-width: 768px) {
  .ctrl-tabs-row {
    gap: 1rem !important;
  }
  .ctrl-tabs-row .ctrl-tab-btn {
    width: 68px;
    height: 68px;
    font-size: 1.8rem;
  }
  .ctrl-tabs-row .ctrl-tab-btn .badge {
    font-size: .75rem;
    padding: .4em .55em;
  }
}
/* МГ (мобильный ЛАНДШАФТ, телефон боком) — ширина попадает в тот же
   `min-width:768px`, что и настоящий ПК/планшет, поэтому наследовала
   десктопные 68px-иконки выше — на реальной ширине телефона боком
   (обычно 650-930px) 10 вкладок + кнопка экспорта в таком размере не
   помещаются в одну строку (2026-08-13, прямая находка пользователя —
   «огромные иконки как на ПК», нужен размер, при котором строка не
   рвётся). `(max-height:500px) and (orientation:landscape)` — тот же
   эвристический приём, что уже используется для `.dt-table` в этом же
   файле (отличить именно ТЕЛЕФОН боком от десктопного/планшетного окна
   той же ШИРИНЫ — у тех высота почти всегда заметно больше 500px).
   Специфичность идентична блоку выше, побеждает порядком (идёт следом в
   файле) — `!important` добавлен явно, чтобы не зависеть от порядка при
   будущих правках. Размер — 40px (не откат к голым 34px `.btn-icon-square`
   портрета: на 650+px ширины есть небольшой запас, чуть крупнее — удобнее
   пальцем), считано так, чтобы 11 квадратных иконок с небольшим gap
   гарантированно умещались даже на самых узких landscape-телефонах. */
@media (min-width: 768px) and (max-width: 991.98px) and (max-height: 500px) and (orientation: landscape) {
  .ctrl-tabs-row {
    gap: .4rem !important;
  }
  .ctrl-tabs-row .ctrl-tab-btn {
    width: 40px !important;
    height: 40px !important;
    font-size: 1.05rem !important;
  }
}

/* Центр контроля/Изменения — колонка «Что изменилось»: до 3 строк видимых,
   остальное — внутренний вертикальный скролл ячейки, не раздувает строку
   таблицы (тот же приём, что .frz-history, 2026-08-04). */
.diff-cell {
  max-height: calc(3 * 1.5em);
  overflow-y: auto;
}

/* Новости — лента карточек (news/index.html) + тело поста (news/detail.html).
   .news-card — вся карточка кликабельна (обёрнута в <a>), лёгкий hover-
   акцент вместо стандартного подчёркивания ссылки. .news-preview — обрезка
   HTML-превью до 3 строк (WebKit line-clamp, поддержан во всех целевых
   браузерах проекта). .news-body — контент из Quill: изображения не должны
   вылезать за карточку, iframe-видео — 16:9 через aspect-ratio. */
.news-card {
  transition: box-shadow .15s ease, border-color .15s ease;
}
.news-card:hover {
  border-color: var(--accent);
  box-shadow: 0 .125rem .5rem rgba(0, 0, 0, .08);
}
.news-preview {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.news-card-thumb {
  width: 100%;
  height: 100%;
  min-height: 110px;
  object-fit: cover;
}
@media (max-width: 575.98px) {
  .news-card-thumb { min-height: 90px; }
}
.news-body img {
  max-width: 100%;
  height: auto;
  border-radius: .375rem;
}
.news-body iframe.ql-video {
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 0;
  border-radius: .375rem;
}
.news-body blockquote {
  border-left: 3px solid var(--accent);
  padding-left: 1rem;
  color: var(--bs-secondary-color);
}
.news-body video.ql-video-file {
  max-width: 100%;
  border-radius: .375rem;
}
.news-body hr {
  border-top: 1px solid var(--bs-border-color);
  margin: 1.25rem 0;
}

/* Ссылки в тексте новости — явный акцентный цвет+подчёркивание вместо
   дефолтного (едва заметного, реальная жалоба пользователя 2026-08-11:
   «на ПК даже намёка нет, что в тексте есть ссылка»), не полагаемся на
   унаследованный --bs-link-color. */
.news-body a {
  color: var(--accent);
  text-decoration: underline;
}

/* Классы Quill-редактора (выравнивание/отступ/размер шрифта) — общие
   правила ОБЯЗАНЫ применяться и на опубликованной странице (.news-body),
   и в самом редакторе (.ql-editor, news/form.html) — реальная находка
   пользователя 2026-08-11: страницы просмотра новостей понятия не имели
   об этих классах (quill.snow.css с их CSS загружен ТОЛЬКО на странице
   редактирования), поэтому выравнивание/размер текста/центрирование
   картинки пропадали при публикации. Значения — 1:1 с quill.snow.css
   (Quill 1.3.7), чтобы редактор реально совпадал с итоговым видом. */
.news-body .ql-align-center, .ql-editor .ql-align-center { text-align: center; }
.news-body .ql-align-right, .ql-editor .ql-align-right { text-align: right; }
.news-body .ql-align-justify, .ql-editor .ql-align-justify { text-align: justify; }
.news-body .ql-indent-1, .ql-editor li.ql-indent-1:not(.ql-direction-rtl) { padding-left: 3em; }
.news-body .ql-indent-2, .ql-editor li.ql-indent-2:not(.ql-direction-rtl) { padding-left: 6em; }
.news-body .ql-indent-3, .ql-editor li.ql-indent-3:not(.ql-direction-rtl) { padding-left: 9em; }
.news-body p.ql-indent-1, .news-body p.ql-indent-2, .news-body p.ql-indent-3 { padding-left: inherit; margin-left: 3em; }
.news-body .ql-size-small, .ql-editor .ql-size-small { font-size: .75em; }
.news-body .ql-size-large, .ql-editor .ql-size-large { font-size: 1.5em; }
.news-body .ql-size-huge, .ql-editor .ql-size-huge { font-size: 2.5em; }

/* Позиционирование/обтекание картинки-карты-видео (2026-08-11, свой
   мини-модуль редактора — см. news/form.html) — общие правила для
   `.ql-editor` и `.news-body`, та же логика WYSIWYG, что и выше. */
.news-body .ql-pos-left, .ql-editor .ql-pos-left { float: left; margin: 0 1rem .5rem 0; max-width: 50%; }
.news-body .ql-pos-right, .ql-editor .ql-pos-right { float: right; margin: 0 0 .5rem 1rem; max-width: 50%; }
.news-body .ql-pos-center, .ql-editor .ql-pos-center { display: block; margin: 0 auto .5rem; float: none; }
.news-body .ql-pos-full, .ql-editor .ql-pos-full { display: block; width: 100% !important; float: none; margin: 0 0 .5rem; }
.news-body .ql-wrap-left, .ql-editor .ql-wrap-left { float: left; margin: 0 1rem .5rem 0; }
.news-body .ql-wrap-right, .ql-editor .ql-wrap-right { float: right; margin: 0 0 .5rem 1rem; }
.news-body iframe.ql-pos-left, .news-body iframe.ql-pos-right, .news-body iframe.ql-pos-center,
.ql-editor iframe.ql-pos-left, .ql-editor iframe.ql-pos-right, .ql-editor iframe.ql-pos-center {
  width: 60%; aspect-ratio: 16 / 9; height: auto;
}
.news-body iframe.ql-wrap-left, .news-body iframe.ql-wrap-right,
.ql-editor iframe.ql-wrap-left, .ql-editor iframe.ql-wrap-right {
  width: 45%; aspect-ratio: 16 / 9; height: auto;
}

/* Мини-модуль редактора — плавающая панель выравнивания + ручка ресайза
   (только в редакторе, на опубликованной странице не появляются). */
.news-float-toolbar {
  position: absolute; z-index: 20; display: flex; gap: 2px;
  background: var(--bs-body-bg); border: 1px solid var(--bs-border-color);
  border-radius: .375rem; box-shadow: var(--shadow-sm); padding: 2px;
}
.news-float-toolbar button {
  width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; border-radius: .25rem; cursor: pointer; color: var(--bs-body-color);
}
.news-float-toolbar button:hover, .news-float-toolbar button.active {
  background: var(--accent); color: #fff;
}
.news-resize-handle {
  position: absolute; width: 14px; height: 14px; background: var(--accent);
  border: 2px solid #fff; border-radius: 50%; cursor: nwse-resize; z-index: 20;
}
