::selection {
  background-color:var(--sportosphere-primary);
  color:#fff
}

/* --------------------------- */
/* Базовые настройки элементов */
/* --------------------------- */

label {
  display: inline-block;
  font-size:.8333333333rem;
  font-weight:500;
  margin-bottom:.5rem;
  letter-spacing:.02em
}

/* .formGroup {
  padding:.25rem .25rem;
  min-width: 50px;
} */

.align-top {
  vertical-align: top !important;
}

/* =========================================================================
   Раздел: Универсальный компонент — кнопка (.button) — 2026-08-26.

   Собственный класс кнопки взамен связки Bootstrap-Falcon `.btn` +
   `.btn-sportosphere-{вариант}` (theme.min.css) — устраняет зависимость
   JS-генерируемой разметки инструментов библиотеки от классов Bootstrap
   (архитектурный инвариант №8, openspec/context/03-architecture.md), не
   меняя визуал: значения ниже — точный перенос вычисленных правил
   `.btn`/`.btn-sm`/`.btn-lg`/`.btn-sportosphere-*` из theme.min.css
   (rules.md, п. 7 — переносить точные значения).

   Класс физически лежит здесь, а не в sportosphereTheme.css (целевом файле
   для новых стилей проекта, rules.md п. 5), по прямому исключению из того
   же пункта правил: стили, которые использует JS-генерируемая разметка
   самой библиотеки инструментов (smartTable/smartForm/smartConstructor/
   smartCalendar и т. д. — все уже сегодня рисуют кнопки через
   `.btn.btn-sportosphere-*`), остаются в sportosphereTools.css. Это и
   единственное место, гарантированно подключённое там, где вообще
   используются инструменты библиотеки, включая `_management.html`
   (подключает sportosphereTools.css, но не sportosphereTheme.css) —
   обратное не выполняется (sportosphereTheme.css нигде не подключён без
   sportosphereTools.css рядом), см. раздел про кнопки в sportosphereTheme
   .css.

   Разметка:
     <button type="button" class="button button--primary">Сохранить</button>
     <a href="..." class="button button--default button--sm">Отмена</a>
     <button type="button" class="button button--danger" disabled>...</button>

   Модификаторы:
   - цвет — button--default/--primary/--success/--info/--warning/--danger
     (ровно те 6, для которых в theme.min.css реально есть правило
     `.btn-sportosphere-*` — токены `--sportosphere-btn-sportosphere-
     {secondary|light|dark}-*` в theme.min.css объявлены, но НИ ОДНОГО
     правила `.btn-sportosphere-{secondary|light|dark}` в файле нет, т. е.
     скопировать в разработанном виде нечего — не заведены);
   - размер — button--sm/--lg (без модификатора — базовый размер);
   - переключённое/выбранное состояние (аналог Bootstrap `.active` —
     например текущая страница пагинации) — доп. класс button--active;
   - принудительно отключена (для <a>/<span>, где нет нативного :disabled)
     — доп. класс button--disabled; для <button> — обычный атрибут disabled.

   Источник значений — theme.min.css:
   - структура/базовые размеры — блок `.btn{...}` (~строка 4127) и
     `.btn-sm`/`.btn-lg` (~4763/4745). Внутри `.btn{...}` Bootstrap объявляет
     часть переменных (--sportosphere-btn-padding-x/-y, -font-size,
     -border-radius и т. д.) ЛОКАЛЬНО на самом `.btn`, не в :root — при
     отказе от класса `.btn` они не резолвятся, поэтому ниже используются
     их буквальные значения (проверено чтением исходного правила), а не
     var(--sportosphere-btn-*);
   - фон/тень (одинаковые для всех 6 вариантов — единственное, что меняется
     по варианту, это ЦВЕТ ТЕКСТА и фон toggle-состояния) и цвета вариантов
     — токены `--sportosphere-btn-sportosphere-*`, объявленные ГЛОБАЛЬНО в
     `:root,[data-bs-theme=light]`/`[data-bs-theme=dark]` (theme.min.css,
     ~229–269/521–579) — используются как var(...), автоматически
     переключаются вместе с темой;
   - box-shadow на :active/.button--active — !important сохранён намеренно
     (как в источнике, ~21674 и аналогичные): без него более специфичное
     правило :hover перекрывает box-shadow при одновременном hover+active
     (мышь зажата над наведённой кнопкой).
   - :active/.button--active задают ТОЛЬКО фон (-active-background) и
     border/box-shadow (общие на .button, см. выше) — намеренно НЕ задают
     color. Проверено через getComputedStyle (Playwright, реально зажатая
     кнопка): в theme.min.css цвет текста при настоящем нажатии мышью
     (когда :hover и :active действуют одновременно — обычный клик) в
     итоге определяет более специфичное правило `:hover:not(.disabled):not
     (:disabled)` (~21668, 4 уровня специфичности) — оно "перебивает" даже
     более специфичный, чем просто :hover, составной селектор
     `:first-child:active`/`:not(.btn-check)+...:active` (~21679-21686,
     3 уровня), который сам, в свою очередь, перебивает простое
     `:active{color:...-active-color}` (~21674, 1 доп. уровень). Итог для
     обычной (не входящей в Bootstrap-паттерн `.btn-check`-переключателей)
     кнопки: наведена+нажата → hover-color; нажата без наведения
     (клавиатура) или просто .active без наведения → базовый -color;
     токены `-active-color` в реальном рендере ни для одного варианта не
     проявляются. Ниже это воспроизведено тем, что цвет в :active/
     .button--active не переопределяется вовсе — он естественно наследуется
     из уже существующих правил `.button--X`/`.button--X:hover` по их
     обычной специфичности, без ручного дублирования каскада.
   ========================================================================= */

.button {
  display: inline-block;
  padding: .3125rem 1rem;
  font-family: var(--sportosphere-font-sans-serif);
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.5;
  text-align: center;
  vertical-align: middle;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  border: 1px solid var(--sportosphere-btn-sportosphere-background);
  border-radius: var(--sportosphere-border-radius);
  background-color: var(--sportosphere-btn-sportosphere-background);
  box-shadow: var(--sportosphere-btn-sportosphere-box-shadow);
  transition: color .15s ease-in-out, background-color .15s ease-in-out,
    border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}

.button:hover,
.button:focus {
  text-decoration: none;
  background-color: var(--sportosphere-btn-sportosphere-background);
  border-color: var(--sportosphere-btn-sportosphere-background);
}

.button:hover:not(:disabled):not(.button--disabled),
.button:focus:not(:disabled):not(.button--disabled) {
  box-shadow: var(--sportosphere-btn-sportosphere-hover-box-shadow);
}

.button:focus-visible {
  outline: 0;
}

/* :active и переключённое .button--active дают одинаковый фон/рамку/тень —
   подробный разбор каскада (откуда именно "none" и почему color здесь не
   задаётся) см. в комментарии-заголовке раздела выше */
.button:active,
.button.button--active {
  box-shadow: none !important;
  border-color: var(--sportosphere-btn-sportosphere-background);
}

.button:disabled,
.button.button--disabled {
  pointer-events: none;
  opacity: .5;
  border-color: transparent;
  background-color: var(--sportosphere-btn-sportosphere-background);
  box-shadow: var(--sportosphere-btn-sportosphere-box-shadow) !important;
}

.button--sm {
  padding: .1875rem .75rem;
  font-size: .875rem;
  border-radius: var(--sportosphere-border-radius-sm);
}

.button--lg {
  padding: .375rem 1.25rem;
  font-size: 1.2rem;
  border-radius: var(--sportosphere-border-radius-lg);
}

/* ---- Вариант: default ---- */
.button--default {
  color: var(--sportosphere-btn-sportosphere-default-color);
}
.button--default:hover,
.button--default:focus {
  color: var(--sportosphere-btn-sportosphere-default-hover-color);
}
.button--default:active,
.button--default.button--active {
  background-color: var(--sportosphere-btn-sportosphere-default-active-background);
}
.button--default:disabled,
.button--default.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: primary ---- */
.button--primary {
  color: var(--sportosphere-btn-sportosphere-primary-color);
}
.button--primary:hover,
.button--primary:focus {
  color: var(--sportosphere-btn-sportosphere-primary-hover-color);
}
.button--primary:active,
.button--primary.button--active {
  background-color: var(--sportosphere-btn-sportosphere-primary-active-background);
}
.button--primary:disabled,
.button--primary.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: success ---- */
.button--success {
  color: var(--sportosphere-btn-sportosphere-success-color);
}
.button--success:hover,
.button--success:focus {
  color: var(--sportosphere-btn-sportosphere-success-hover-color);
}
.button--success:active,
.button--success.button--active {
  background-color: var(--sportosphere-btn-sportosphere-success-active-background);
}
.button--success:disabled,
.button--success.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: info ---- */
.button--info {
  color: var(--sportosphere-btn-sportosphere-info-color);
}
.button--info:hover,
.button--info:focus {
  color: var(--sportosphere-btn-sportosphere-info-hover-color);
}
.button--info:active,
.button--info.button--active {
  background-color: var(--sportosphere-btn-sportosphere-info-active-background);
}
.button--info:disabled,
.button--info.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: warning ---- */
.button--warning {
  color: var(--sportosphere-btn-sportosphere-warning-color);
}
.button--warning:hover,
.button--warning:focus {
  color: var(--sportosphere-btn-sportosphere-warning-hover-color);
}
.button--warning:active,
.button--warning.button--active {
  background-color: var(--sportosphere-btn-sportosphere-warning-active-background);
}
.button--warning:disabled,
.button--warning.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: danger ---- */
.button--danger {
  color: var(--sportosphere-btn-sportosphere-danger-color);
}
.button--danger:hover,
.button--danger:focus {
  color: var(--sportosphere-btn-sportosphere-danger-hover-color);
}
.button--danger:active,
.button--danger.button--active {
  background-color: var(--sportosphere-btn-sportosphere-danger-active-background);
}
.button--danger:disabled,
.button--danger.button--disabled {
  color: var(--sportosphere-btn-disabled-color);
}

/* ---- Вариант: outline (обводка, не заливка — другая природа, чем у
   остальных 6 вариантов, которые делят общий светлый фон
   --sportosphere-btn-sportosphere-background и различаются только цветом
   текста; перенос .btn-outline-secondary из theme.min.css, полностью
   переопределяет фон/рамку на каждом состоянии, а не только цвет) ---- */
.button--outline {
  color: var(--sportosphere-gray-600);
  background-color: transparent;
  border-color: var(--sportosphere-gray-600);
}
.button--outline:hover,
.button--outline:focus,
.button--outline:active,
.button--outline.button--active {
  color: #fff;
  background-color: var(--sportosphere-gray-600);
  border-color: var(--sportosphere-gray-600);
}
.button--outline:disabled,
.button--outline.button--disabled {
  color: var(--sportosphere-gray-600);
  background-color: transparent;
  border-color: var(--sportosphere-gray-600);
}

/* Стиль таблиц */

/* =========================================================================
   Раздел: Сетка (.row/.g-3/.col-*) — 2026-09-04 (smartform-native-styles).

   Собственные правила взамен Bootstrap-сетки (theme.min.css) — точный
   перенос вычисленных значений (rules.md, п. 5 — переносить точные
   значения), под ТЕМИ ЖЕ именами классов: значения `cssClass` в конфигах
   smartForm.js по всему проекту заданы буквенно как 'col-sm-6' и т. п. на
   десятках страниц — переименование их всех дало бы кратно больший объём
   правки без функциональной пользы (см. openspec/changes/archive/
   *-smartform-native-styles/design.md, Decision 1, если change уже
   заархивирован — иначе openspec/changes/smartform-native-styles/design.md).
   Набор ограничен вариантами, реально используемыми хотя бы в одном
   cssClass-конфиге проекта на дату аудита (полный grep по всем *.js/*.html) —
   не общий Bootstrap-грид на все брейкпоинты/доли.

   Дополнено 2026-09-04 (openspec/changes/smartcalendar-native-styles) —
   добавлены варианты, используемые непосредственно в разметке
   smartCalendar.js (.col-6/.col-auto/.gx-0/.g-1/.col-md-auto/.col-md-5/
   .col-md-7/.order-md-*) — тот же принцип: переопределение тех же имён,
   не общий грид на все брейкпоинты/доли.

   Дополнено 2026-09-04 (openspec/changes/smartview-native-styles) —
   добавлены .col-sm-9/.col-sm-auto/.col-lg-8/.g-0, используемые в
   2-колоночных детальных представлениях smartView.js
   (workoutTemplateView/programTemplateView/assignedProgramView).

   Дополнено 2026-09-06 (найдено при анализе вёрстки place_member_
   subscriptions.html) — добавлен `.gy-2`, используемый в ШАПКЕ ЛЮБОЙ
   таблицы smartTable.js (`row flex-between-center gy-2 px-x1`,
   initializeViewStructure(), безусловная часть разметки, а не
   cssClass-конфиг — этот файл (smartTable.js) не входил ни в один из
   трёх предыдущих аудитов выше, только smartForm/smartCalendar/
   smartView). Тот же класс дефекта, что `CSS-7`: `.gy-2` (theme.min.css)
   задаёт `--sportosphere-gutter-y: 0.5rem` БЕЗ `!important` (в отличие от
   утилит из раздела «Утилиты» ниже — гаттеры сетки в реальном Bootstrap
   `!important` не используют) — при равной специфичности с `.row` этого
   файла (тоже безусловно выставляет `--sportosphere-gutter-y: 0`) и более
   позднем подключении sportosphereTools.css это `0.5rem` всегда проигрывало
   `0`. Эффект невидим, пока в шапке ровно один дочерний `.col-auto`
   (нет второго элемента, значит и переноса на новую строку, которому
   нужен вертикальный гаттер, не бывает) — таблица «Абонементы клиента» на
   `place_member_subscriptions.html` именно такая (заголовок таблицы без
   `textFilteringEnable`), поэтому баг не был заметен на глаз, но
   становится видимым (нулевой, а не 0.5rem, зазор между перенесённой на
   новую строку формой поиска и заголовком) на любой другой странице,
   где у smartTable одновременно включён `tableHeader` и
   `textFilteringEnable` на узком экране.

   НЕ включена в этот же проход отдельная, более серьёзная находка того
   же файла — ветка `if (config.fieldFilteringEnable)` (~строки 784-791)
   строит `row gx-3` > `col-xxl-10 col-xl-9` + `col-xxl-2 col-xl-3`
   (раскладка «таблица + офканвас-сайдбар фильтра»): ни `gx-3`, ни любой
   из четырёх `col-*` там не входит в белый список, `col-xxl-*` не имеет
   даже частичной защиты через `flex:1 0 0%` (в отличие от `col-lg`
   в `CSS-7`) — раскладка ломается сильнее и на большем диапазоне
   ширин. Эта ветка не выполняется ни на одной из проверенных сегодня
   страниц (`memberSubscriptions` не задаёт `fieldFilteringEnable`) — вне
   объёма текущей точечной правки; заведено отдельным пунктом в
   `docs/known-issues.md` (`CSS-7`, зона `smartTable.js`).
   ========================================================================= */

.row {
  --sportosphere-gutter-x: 2rem;
  --sportosphere-gutter-y: 0;
  display: flex;
  flex-wrap: wrap;
  margin-top: calc(-1 * var(--sportosphere-gutter-y));
  margin-right: calc(-0.5 * var(--sportosphere-gutter-x));
  margin-left: calc(-0.5 * var(--sportosphere-gutter-x));
}
.row > * {
  flex-shrink: 0;
  width: 100%;
  max-width: 100%;
  padding-right: calc(var(--sportosphere-gutter-x) * .5);
  padding-left: calc(var(--sportosphere-gutter-x) * .5);
  margin-top: var(--sportosphere-gutter-y);
}
.g-3 {
  --sportosphere-gutter-x: 1rem;
  --sportosphere-gutter-y: 1rem;
}
.gx-0 {
  --sportosphere-gutter-x: 0;
}
.g-1 {
  --sportosphere-gutter-x: 0.25rem;
  --sportosphere-gutter-y: 0.25rem;
}
.g-0 {
  --sportosphere-gutter-x: 0;
  --sportosphere-gutter-y: 0;
}
.gy-2 {
  --sportosphere-gutter-y: 0.5rem;
}
.col-12 { flex: 0 0 auto; width: 100%; }
.col-6 { flex: 0 0 auto; width: 50%; }
.col-auto { flex: 0 0 auto; width: auto; }
@media (min-width: 576px) {
  .col-sm-2 { flex: 0 0 auto; width: 16.66666667%; }
  .col-sm-3 { flex: 0 0 auto; width: 25%; }
  .col-sm-4 { flex: 0 0 auto; width: 33.33333333%; }
  .col-sm-6 { flex: 0 0 auto; width: 50%; }
  .col-sm-9 { flex: 0 0 auto; width: 75%; }
  .col-sm-12 { flex: 0 0 auto; width: 100%; }
  .col-sm-auto { flex: 0 0 auto; width: auto; }
}
@media (min-width: 768px) {
  .col-md-4 { flex: 0 0 auto; width: 33.33333333%; }
  .col-md-5 { flex: 0 0 auto; width: 41.66666667%; }
  .col-md-6 { flex: 0 0 auto; width: 50%; }
  .col-md-7 { flex: 0 0 auto; width: 58.33333333%; }
  .col-md-auto { flex: 0 0 auto; width: auto; }
  .order-md-0 { order: 0; }
  .order-md-1 { order: 1; }
  .order-md-2 { order: 2; }
  .order-md-3 { order: 3; }
}
@media (min-width: 992px) {
  .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; }
  .col-lg-6 { flex: 0 0 auto; width: 50%; }
  .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; }
}
@media (min-width: 1200px) {
  .col-xl-3 { flex: 0 0 auto; width: 25%; }
}

/* Контейнеры smartNavBars.js (setStretch(), переключатель "Растянуть" в
   панели настроек) — было classList.toggle('container'/'container-fluid',
   ...) на внешнем элементе страницы (openspec/changes/
   smartnavbars-native-styles). В отличие от .row/.col-* выше, НЕ
   переопределяем сами имена `container`/`container-fluid` — это гораздо
   более общие, вездесущие Bootstrap-имена, потенциально используемые где-то
   ещё независимо от smartNavBars; переопределение изменило бы поведение
   чужой разметки на любой странице проекта. Точные значения — theme.min.css. */
.smartNavBars-container,
.smartNavBars-container-fluid {
  --sportosphere-gutter-x: 2rem;
  --sportosphere-gutter-y: 0;
  width: 100%;
  padding-right: calc(var(--sportosphere-gutter-x) * .5);
  padding-left: calc(var(--sportosphere-gutter-x) * .5);
  margin-right: auto;
  margin-left: auto;
  position: relative;
}
@media (min-width: 576px) {
  .smartNavBars-container { max-width: 540px; }
}
@media (min-width: 768px) {
  .smartNavBars-container { max-width: 720px; }
}
@media (min-width: 992px) {
  .smartNavBars-container { max-width: 960px; }
}
@media (min-width: 1200px) {
  .smartNavBars-container { max-width: 1140px; }
}
@media (min-width: 1540px) {
  .smartNavBars-container { max-width: 1480px; }
}

/* =========================================================================
   Раздел: Утилиты — 2026-09-04 (openspec/changes/smartcalendar-native-styles).

   Собственные правила взамен литеральных Bootstrap-утилит, используемых
   непосредственно в разметке smartCalendar.js (тот же принцип, что и у
   сетки выше — переопределение тех же имён под точными значениями
   theme.min.css, rules.md п. 5). Набор ограничен вариантами, реально
   встречающимися в разметке этого инструмента на дату аудита.

   `!important` — не своеволие, а точный перенос: в Bootstrap ВСЕ эти
   утилиты (в отличие от сетки/`.row`/`.col-*` выше, без `!important`)
   объявлены с `!important`, и это не случайность — без него, например,
   `.widget-body` (sportosphereTheme.css, загружается ПОСЛЕ
   sportosphereTools.css в _main.html) со своим литеральным
   `padding: 1.25rem` побеждал бы `.p-0` при равной специфичности только
   по порядку файлов, а не по намерению разметки («убрать паддинг»)
   — сохраняем `!important`, чтобы утилита оставалась утилитой независимо
   от порядка подключения файлов, как и в оригинале. */
.d-flex { display: flex !important; }
@media (min-width: 768px) {
  .d-md-none { display: none !important; }
}
/* Дополнено 2026-09-06 (найдено по сообщению пользователя — задублированные
   заголовок/кнопка виджета «Задачи» на /students/<id>/progress/,
   docs/known-issues.md, CSS-9): брейкпоинт-версии `.d-xl-none`/
   `.d-xl-flex` отсутствовали в этом файле, хотя базовый `.d-flex` выше уже
   был переопределён с `!important`. Поскольку этот файл грузится ПОСЛЕ
   theme.min.css целиком (включая его `@media(min-width:1200px)`-блок),
   безусловный `.d-flex` из этого файла побеждал `.d-xl-none`/`.d-xl-flex`
   из theme.min.css на любом элементе, где они использовались вместе
   (studentProfile.html, шапка встроенного/офканвас-варианта виджета
   «Задачи») — оба варианта шапки отображались одновременно на всех
   ширинах. Тот же класс проблемы, что и `CSS-7` (переопределение базового
   имени класса в файле, загружаемом позже theme.min.css, ломает ЛЮБОЙ его
   брейкпоинт-вариант, если сам вариант остаётся только в theme.min.css) —
   но для утилит displaya, не сетки. Исправление — добавить сюда же
   брейкпоинт-варианты, реально используемые в живой разметке (см.
   методологию раздела выше: набор не исчерпывающий, только то, что
   встретилось). */
@media (min-width: 1200px) {
  .d-xl-none { display: none !important; }
  .d-xl-flex { display: flex !important; }
}
.justify-content-end { justify-content: flex-end !important; }
.justify-content-between { justify-content: space-between !important; }
.align-items-center { align-items: center !important; }
.text-500 { color: var(--sportosphere-gray-500) !important; }
.text-600 { color: var(--sportosphere-gray-600) !important; }
.fs-9 { font-size: 1rem !important; }
.fs-10 { font-size: 0.8333333333rem !important; }
@media (min-width: 576px) {
  .fs-sm-8 { font-size: 1.2rem !important; }
  .px-sm-4 { padding-right: 1.8rem !important; padding-left: 1.8rem !important; }
}
@media (min-width: 992px) {
  .fs-lg-7 { font-size: 1.44rem !important; }
  .me-lg-2 { margin-right: 0.5rem !important; }
}
@media (min-width: 768px) {
  .ms-md-2 { margin-left: 0.5rem !important; }
}
.mb-0 { margin-bottom: 0 !important; }
.mb-3 { margin-bottom: 1rem !important; }
.me-1 { margin-right: 0.25rem !important; }
.me-2 { margin-right: 0.5rem !important; }
.ms-1 { margin-left: 0.25rem !important; }
.ms-2 { margin-left: 0.5rem !important; }
.p-0 { padding: 0 !important; }
.p-3 { padding: 1rem !important; }
.p-x1 { padding: 1.25rem !important; }
.px-3 { padding-right: 1rem !important; padding-left: 1rem !important; }
.py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; }
.py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
.h-100 { height: 100% !important; }
.border { border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color) !important; }
.border-0 { border: 0 !important; }
.font-sans-serif { font-family: var(--sportosphere-font-sans-serif) !important; }

/* Дополнено 2026-09-04 (openspec/changes/smartnavbars-native-styles) —
   недостающие варианты, используемые в разметке smartNavBars.js. */
.z-1 { z-index: 1 !important; }
.py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; }
.mb-1 { margin-bottom: 0.25rem !important; }
.mt-0 { margin-top: 0 !important; }
.text-white { color: rgba(var(--sportosphere-white-rgb), 1) !important; }
.opacity-75 { opacity: 0.75 !important; }
.px-x1 { padding-right: 1.25rem !important; padding-left: 1.25rem !important; }
.position-relative { position: relative !important; }
.position-absolute { position: absolute !important; }
.rounded-start { border-bottom-left-radius: var(--sportosphere-border-radius) !important; border-top-left-radius: var(--sportosphere-border-radius) !important; }
.rounded-end { border-top-right-radius: var(--sportosphere-border-radius) !important; border-bottom-right-radius: var(--sportosphere-border-radius) !important; }

/* Дополнено 2026-09-04 (openspec/changes/smartview-native-styles) —
   недостающие варианты, используемые в разметке smartView.js. */
.mb-2 { margin-bottom: 0.5rem !important; }
.text-900 { color: var(--sportosphere-gray-900) !important; }
.fw-normal { font-weight: 400 !important; }
.text-800 { color: var(--sportosphere-gray-800) !important; }
.rounded-1 { border-radius: var(--sportosphere-border-radius-sm) !important; }
.text-danger { color: var(--sportosphere-danger) !important; }
.text-primary { color: var(--sportosphere-primary) !important; }
.shadow-none { box-shadow: none !important; }
/* Дополнено 2026-09-06 (найдено по сообщению пользователя — у виджета
   «Задачи» на /students/<id>/progress/ пропала тень): `shadow-show-xl`
   уже использовался в разметке (studentProfile.html, `.widget shadow-none
   shadow-show-xl` — «без тени по умолчанию (внутри офканвас-панели своя
   тень), с тенью на ≥1200px, где виджет встраивается в сайдбар как
   обычная карточка»), но сам класс никогда не был объявлен ни в одном
   CSS-файле — `shadow-none` действовал безусловно, восстановления на xl+
   не происходило. */
@media (min-width: 1200px) {
  .shadow-show-xl { box-shadow: var(--sportosphere-box-shadow) !important; }
}
.bg-white { background-color: rgba(var(--sportosphere-white-rgb), 1) !important; }
.overflow-hidden { overflow: hidden !important; }
@media (min-width: 992px) {
  .pe-lg-2 { padding-right: 0.5rem !important; }
  .ps-lg-2 { padding-left: 0.5rem !important; }
  .mb-lg-0 { margin-bottom: 0 !important; }
}
.rounded-pill { border-radius: var(--sportosphere-border-radius-pill) !important; }

/*----------------*/
/*   Стиль форм   */
/*----------------*/

.formGroup {
  display: block;
  width: 100%;
  padding: 0.3125rem 1rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sportosphere-gray-900);
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-color: var(--sportosphere-quaternary-bg);
  background-clip: padding-box;
  border: var(--sportosphere-border-width) solid var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius);
  -webkit-box-shadow: var(--sportosphere-box-shadow-inset);
  box-shadow: var(--sportosphere-box-shadow-inset);
  -webkit-transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .formGroup {
    -webkit-transition: none;
    transition: none;
  }
}
.formGroup[type=file] {
  overflow: hidden;
}
.formGroup[type=file]:not(:disabled):not([readonly]) {
  cursor: pointer;
}
.formGroup:focus {
  color: var(--sportosphere-gray-900);
  background-color: var(--sportosphere-quaternary-bg);
  border-color: var(--sportosphere-input-focus-border-color-global);
  outline: 0;
  -webkit-box-shadow: var(--sportosphere-box-shadow-inset), 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
  box-shadow: var(--sportosphere-box-shadow-inset), 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}
.formGroup::-webkit-date-and-time-value {
  min-width: 85px;
  height: 1.5em;
  margin: 0;
}
.formGroup::-webkit-datetime-edit {
  display: block;
  padding: 0;
}
.formGroup::-webkit-input-placeholder {
  color: var(--sportosphere-input-placeholder-color-global);
  opacity: 1;
}
.formGroup::-moz-placeholder {
  color: var(--sportosphere-input-placeholder-color-global);
  opacity: 1;
}
.formGroup:-ms-input-placeholder {
  color: var(--sportosphere-input-placeholder-color-global);
  opacity: 1;
}
.formGroup::-ms-input-placeholder {
  color: var(--sportosphere-input-placeholder-color-global);
  opacity: 1;
}
.formGroup::placeholder {
  color: var(--sportosphere-input-placeholder-color-global);
  opacity: 1;
}
.formGroup:disabled {
  background-color: var(--sportosphere-gray-200);
  opacity: 1;
}
.formGroup::file-selector-button {
  padding: 0.3125rem 1rem;
  margin: -0.3125rem -1rem;
  -webkit-margin-end: 1rem;
  margin-inline-end: 1rem;
  color: #d8e2ef;
  background-color: #344050;
  pointer-events: none;
  border-color: inherit;
  border-style: solid;
  border-width: 0;
  border-inline-end-width: var(--sportosphere-border-width);
  border-radius: 0;
  -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .formGroup::file-selector-button {
    -webkit-transition: none;
    transition: none;
  }
}
.formGroup:hover:not(:disabled):not([readonly])::file-selector-button {
  background-color: #344050;
}

.formGroup-plaintext {
  display: block;
  width: 100%;
  padding: 0.3125rem 0;
  margin-bottom: 0;
  line-height: 1.5;
  color: #5e6e82;
  background-color: transparent;
  border: solid transparent;
  border-width: var(--sportosphere-border-width) 0;
}
.formGroup-plaintext:focus {
  outline: 0;
}
.formGroup-plaintext.formGroup-sm, .formGroup-plaintext.formGroup-lg {
  padding-right: 0;
  padding-left: 0;
}

.formGroup-sm {
  min-height: calc(1.5em + 0.375rem + calc(var(--sportosphere-border-width) * 2));
  padding: 0.1875rem 0.75rem;
  font-size: 0.875rem;
  border-radius: var(--sportosphere-border-radius-sm);
}
.formGroup-sm::file-selector-button {
  padding: 0.1875rem 0.75rem;
  margin: -0.1875rem -0.75rem;
  -webkit-margin-end: 0.75rem;
  margin-inline-end: 0.75rem;
}

.formGroup-lg {
  min-height: calc(1.5em + 0.75rem + calc(var(--sportosphere-border-width) * 2));
  padding: 0.375rem 1.25rem;
  font-size: 1.2rem;
  border-radius: var(--sportosphere-border-radius-lg);
}
.formGroup-lg::file-selector-button {
  padding: 0.375rem 1.25rem;
  margin: -0.375rem -1.25rem;
  -webkit-margin-end: 1.25rem;
  margin-inline-end: 1.25rem;
}
textarea.formGroup {
  min-height: calc(1.5em + 0.625rem + calc(var(--sportosphere-border-width) * 2));
}
textarea.formGroup-sm {
  min-height: calc(1.5em + 0.375rem + calc(var(--sportosphere-border-width) * 2));
}
textarea.formGroup-lg {
  min-height: calc(1.5em + 0.75rem + calc(var(--sportosphere-border-width) * 2));
}

.formGroup-color {
  width: 3rem;
  height: calc(1.5em + 0.625rem + calc(var(--sportosphere-border-width) * 2));
  padding: 0.3125rem;
}
.formGroup-color:not(:disabled):not([readonly]) {
  cursor: pointer;
}
.formGroup-color::-moz-color-swatch {
  border: 0 !important;
  border-radius: var(--sportosphere-border-radius);
}
.formGroup-color::-webkit-color-swatch {
  border: 0 !important;
  border-radius: var(--sportosphere-border-radius);
}
.formGroup-color.formGroup-sm {
  height: calc(1.5em + 0.375rem + calc(var(--sportosphere-border-width) * 2));
}
.formGroup-color.formGroup-lg {
  height: calc(1.5em + 0.75rem + calc(var(--sportosphere-border-width) * 2));
}

/* Стиль для select */

.formSelect {
  --sportosphere-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%234d5969' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
  display: block;
  width: 100%;
  padding: 0.3125rem 3rem 0.3125rem 1rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sportosphere-gray-900);
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-color: var(--sportosphere-quaternary-bg);
  background-image: var(--sportosphere-form-select-bg-img), var(--sportosphere-form-select-bg-icon, none);
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 16px 12px;
  border: var(--sportosphere-border-width) solid var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius);
  -webkit-box-shadow: var(--sportosphere-box-shadow-inset);
  box-shadow: var(--sportosphere-box-shadow-inset);
  -webkit-transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
}
@media (prefers-reduced-motion: reduce) {
  .formSelect {
    -webkit-transition: none;
    transition: none;
  }
}
.formSelect:focus {
  border-color: var(--sportosphere-input-focus-border-color-global);
  outline: 0;
  -webkit-box-shadow: var(--sportosphere-box-shadow-inset), 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
  box-shadow: var(--sportosphere-box-shadow-inset), 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}
.formSelect[multiple], .formSelect[size]:not([size="1"]) {
  padding-right: 1rem;
  background-image: none;
}
.formSelect:disabled {
  background-color: var(--sportosphere-gray-200);
}
.formSelect:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 var(--sportosphere-gray-900);
}

.formSelect-sm {
  padding-top: 0.1875rem;
  padding-bottom: 0.1875rem;
  padding-left: 0.75rem;
  font-size: 0.875rem;
  border-radius: var(--sportosphere-border-radius-sm);
}

.formSelect-lg {
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
  padding-left: 1.25rem;
  font-size: 1.2rem;
  border-radius: var(--sportosphere-border-radius-lg);
}

/* Стиль для типов checkbox и radio */

/* Cтиль для родительского контейнера одного элемента */
.formCheck {
  display: block;
  min-height: 1.5rem;
  padding-left: 1.5em;
  margin-bottom: 0.34375rem;
}
/* Размещение контейнеров в ряд */
.formCheck-inline {
  display: inline-block;
  margin-right: 1rem;
}
/* Стиль для поля ввода */
.formCheck .formCheck-input {
  float: left;
  margin-left: -1.5em;
}

.formCheck-input {
  --sportosphere-form-check-bg: transparent;
  -ms-flex-negative: 0;
  flex-shrink: 0;
  width: 1em;
  height: 1em;
  margin-top: 0.25em;
  vertical-align: top;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-color: var(--sportosphere-form-check-bg);
  background-image: var(--sportosphere-form-check-bg-image);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  border: 1px solid var(--sportosphere-gray-400);
  print-color-adjust: exact;
}
/* Стиль для выбранного поля ввода */
.formCheck-input:checked {
  background-color: #2c7be5;
  border-color: #2c7be5;
}
.formCheck-input:checked[type=checkbox] {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
.formCheck-input:checked[type=radio] {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
/* Стиль для фокусировки на выбранном поле ввода */
.formCheck-input:focus {
  border-color: var(--sportosphere-input-focus-border-color-global);
  outline: 0;
  -webkit-box-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
  box-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}
.formCheck-input:disabled {
  pointer-events: none;
  -webkit-filter: none;
  filter: none;
  opacity: 0.5;
}
.formCheck-input[disabled] ~ .formCheck-label, .formCheck-input:disabled ~ .formCheck-label {
  cursor: default;
  opacity: 0.5;
}

/* Стиль для поля ввода для типа radio */
.formCheck-input[type="radio"] {
  border-radius: 50%;
}
/* Стиль для поля ввода для типа checkbox */
.formCheck-input[type="checkbox"] {
  border-radius: .25em;
}
/* Стиль для поля ввода для типа label */
.formCheck-label {
  line-height: 1.45rem;
}

/*----------------------*/
/* Стиль модальных окон */
/*----------------------*/

.isModal {
  --isModal-zindex: 1055;
  --isModal-width: 500px;
  --isModal-padding: 1rem;
  --isModal-margin: 0.5rem;
  --isModal-color: var(--sportosphere-body-color);
  --isModal-bg: var(--sportosphere-emphasis-bg);
  --isModal-border-color: var(--sportosphere-border-color-translucent);
  --isModal-border-width: var(--sportosphere-border-width);
  --isModal-border-radius: 0.375rem;
  --isModal-box-shadow: var(--sportosphere-box-shadow-sm);
  --isModal-inner-border-radius: calc(0.375rem - (var(--sportosphere-border-width)));
  --isModal-header-padding-x: 1rem;
  --isModal-header-padding-y: 1rem;
  --isModal-header-padding: 1rem 1rem;
  --isModal-header-border-color: var(--sportosphere-border-color);
  --isModal-header-border-width: var(--sportosphere-border-width);
  --isModal-title-line-height: 1.5;
  --isModal-footer-gap: 0.5rem;
  --isModal-footer-bg: ;
  --isModal-footer-border-color: var(--sportosphere-border-color);
  --isModal-footer-border-width: var(--sportosphere-border-width);
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--isModal-zindex);
  display: none;
  width: 100%;
  height: 100%;
  overflow-x: hidden;
  overflow-y: auto;
  outline: 0;
}

.isModal-dialog {
  position: relative;
  width: auto;
  margin: var(--isModal-margin);
  pointer-events: none;
}
.isModal.fade .isModal-dialog {
  -webkit-transform: translate(0, -50px);
  -ms-transform: translate(0, -50px);
  transform: translate(0, -50px);
  -webkit-transition: -webkit-transform 0.3s ease-out;
  transition: -webkit-transform 0.3s ease-out;
  transition: transform 0.3s ease-out;
  transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .isModal.fade .isModal-dialog {
    -webkit-transition: none;
    transition: none;
  }
}
.isModal.active .isModal-dialog {
  -webkit-transform: none;
  -ms-transform: none;
  transform: none;
}
.isModal.isModal-static .isModal-dialog {
  -webkit-transform: scale(1.02);
  -ms-transform: scale(1.02);
  transform: scale(1.02);
}

.isModal-dialog-scrollable {
  height: calc(100% - var(--isModal-margin) * 2);
}
.isModal-dialog-scrollable .isModal-content {
  max-height: 100%;
  overflow: hidden;
}
.isModal-dialog-scrollable .isModal-body {
  overflow-y: auto;
}

.isModal-dialog-centered {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  min-height: calc(100% - var(--isModal-margin) * 2);
}

.isModal-content {
  position: relative;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
  -ms-flex-direction: column;
  flex-direction: column;
  width: 100%;
  color: var(--isModal-color);
  pointer-events: auto;
  background-color: var(--isModal-bg);
  background-clip: padding-box;
  border: var(--isModal-border-width) solid var(--isModal-border-color);
  border-radius: var(--isModal-border-radius);
  -webkit-box-shadow: var(--isModal-box-shadow);
  box-shadow: var(--isModal-box-shadow);
  outline: 0;
}

.isModal-backdrop {
  --sportosphere-backdrop-zindex: 1050;
  --sportosphere-backdrop-bg: #000;
  --sportosphere-backdrop-opacity: 0.5;
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--sportosphere-backdrop-zindex);
  width: 100vw;
  height: 100vh;
  background-color: var(--sportosphere-backdrop-bg);
  opacity: var(--sportosphere-backdrop-opacity);
}
/* .isModal-backdrop.fade {
  opacity: 0;
}
.isModalal-backdrop .active {
  opacity: var(--sportosphere-backdrop-opacity);
} */

.isModal-header {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-negative: 0;
  flex-shrink: 0;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  padding: var(--isModal-header-padding);
  border-bottom: var(--isModal-header-border-width) solid var(--isModal-header-border-color);
  border-top-left-radius: var(--isModal-inner-border-radius);
  border-top-right-radius: var(--isModal-inner-border-radius);
}
.isModal-header .btn-close {
  padding: calc(var(--isModal-header-padding-y) * 0.5) calc(var(--isModal-header-padding-x) * 0.5);
  margin-top: calc(-0.5 * var(--isModal-header-padding-y));
  margin-right: calc(-0.5 * var(--isModal-header-padding-x));
  margin-bottom: calc(-0.5 * var(--isModal-header-padding-y));
  margin-left: auto;
}

.isModal-title {
  margin-bottom: 0;
  line-height: var(--isModal-title-line-height);
}

.isModal-body {
  position: relative;
  -webkit-box-flex: 1;
  -ms-flex: 1 1 auto;
  flex: 1 1 auto;
  padding: var(--isModal-padding);
}

.isModal-footer {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-negative: 0;
  flex-shrink: 0;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: end;
  -ms-flex-pack: end;
  justify-content: flex-end;
  padding: calc(var(--isModal-padding) - var(--isModal-footer-gap) * 0.5);
  background-color: var(--isModal-footer-bg);
  border-top: var(--isModal-footer-border-width) solid var(--isModal-footer-border-color);
  border-bottom-right-radius: var(--isModal-inner-border-radius);
  border-bottom-left-radius: var(--isModal-inner-border-radius);
}
.isModal-footer > * {
  margin: calc(var(--isModal-footer-gap) * 0.5);
}

@media (min-width: 576px) {
  .isModal {
    --isModal-margin: 1.75rem;
    --isModal-box-shadow: var(--sportosphere-box-shadow);
  }
  .isModal-dialog {
    max-width: var(--isModal-width);
    margin-right: auto;
    margin-left: auto;
  }
  .isModal-sm {
    --isModal-width: 300px;
  }
}
@media (min-width: 992px) {
  .isModal-lg,
  .isModal-xl {
    --isModal-width: 800px;
  }
}
@media (min-width: 1200px) {
  .isModal-xl {
    --isModal-width: 1140px;
  }
}

/*--------------------------*/
/* Стиль offcanvas-панелей  */
/*--------------------------*/
/* Собственная замена offcanvas-компонента Bootstrap/theme.min.css
   (.offcanvas/.offcanvas-end/.offcanvas-header/.offcanvas-body/
   .offcanvas-backdrop), по аналогии с .isModal выше. Открытие/закрытие —
   класс .show, переключаемый smartNavBars.js (openOffcanvasPanel()/
   closeOffcanvasPanel(), см. smart-navbars/offcanvas-panel), а не JS
   Bootstrap (которого в проекте нет). Первый потребитель —
   #studentTasksOffcanvas (виджет «Задачи» на странице «Прогресс ученика»,
   student-tasks-offcanvas-own-classes). Карточное содержимое внутри
   панели — обычный .widget/.widget-header/.widget-body/.widget-footer
   (см. "Универсальный компонент «виджет»" в sportosphereTheme.css), а не
   отдельный набор классов — здесь только механика самой раздвижной
   панели. */

.isOffcanvas {
  /* Значения — литералы из локального блока переменных .offcanvas в
     theme.min.css (--sportosphere-offcanvas-*): эти переменные заданы НЕ
     глобально на :root, а прямо на самом классе .offcanvas — раз этот
     класс на элементе больше не используется, var(--sportosphere-offcanvas-*)
     здесь всегда резолвился бы в невалидное значение (обнаружено при
     Playwright-проверке: z-index панели/подложки оказался "auto"). Те же
     литералы/глобальные токены, что использует theme.min.css, только
     под собственными именами — как и --isModal-* выше. */
  --isOffcanvas-zindex: 1045;
  --isOffcanvas-width: 400px;
  --isOffcanvas-color: var(--sportosphere-body-color);
  --isOffcanvas-bg: var(--sportosphere-quaternary-bg);
  --isOffcanvas-border-color: var(--sportosphere-border-color-translucent);
  --isOffcanvas-border-width: var(--sportosphere-border-width);
  --isOffcanvas-box-shadow: var(--sportosphere-box-shadow-sm);
  --isOffcanvas-transition: transform 0.3s ease-in-out;
  --isOffcanvas-padding-x: 1rem;
  --isOffcanvas-padding-y: 1rem;
  position: fixed;
  bottom: 0;
  z-index: var(--isOffcanvas-zindex);
  display: flex;
  flex-direction: column;
  max-width: 100%;
  color: var(--isOffcanvas-color);
  visibility: hidden;
  background-color: var(--isOffcanvas-bg);
  background-clip: padding-box;
  outline: 0;
  box-shadow: var(--isOffcanvas-box-shadow);
  transition: var(--isOffcanvas-transition);
}

.isOffcanvas-end {
  top: 0;
  right: 0;
  width: var(--isOffcanvas-width);
  border-left: var(--isOffcanvas-border-width) solid var(--isOffcanvas-border-color);
  transform: translateX(100%);
}

.isOffcanvas.show:not(.hiding) {
  transform: none;
}
.isOffcanvas.show {
  visibility: visible;
}

.isOffcanvas-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1040;
  width: 100vw;
  height: 100vh;
  background-color: #000;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s linear;
}
.isOffcanvas-backdrop.show {
  opacity: 0.5;
  visibility: visible;
}

.isOffcanvas-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--isOffcanvas-padding-y) var(--isOffcanvas-padding-x);
}
.isOffcanvas-header .btn-close {
  padding: calc(var(--isOffcanvas-padding-y) * 0.5) calc(var(--isOffcanvas-padding-x) * 0.5);
  margin-top: calc(-0.5 * var(--isOffcanvas-padding-y));
  margin-right: calc(-0.5 * var(--isOffcanvas-padding-x));
  margin-bottom: calc(-0.5 * var(--isOffcanvas-padding-y));
}

.isOffcanvas-body {
  flex-grow: 1;
  padding: var(--isOffcanvas-padding-y) var(--isOffcanvas-padding-x);
  overflow-y: auto;
}

/* Роль модификатора — «панель-виджет постоянно на месте в сайдбаре», а не
   буквально CSS position:sticky на самой панели: настоящее прилипание при
   прокрутке уже обеспечивает предок .position-xl-sticky (см.
   studentProfile.html — тот же контейнер держит и соседние карточки
   сайдбара); сама панель на ≥1200px становится обычным position:static
   блоком в потоке — иначе предку было бы нечего "прилеплять". Перенос
   эффекта модификатора .offcanvas-contact-info (theme.min.css): та же
   спецификация правил там на деле разрешается в position:static, а не
   sticky (второе, более специфичное правило .offcanvas-contact-info.
   offcanvas-contact-info { position: static } переопределяет первое).

   Дополнено 2026-09-06 (найдено по сообщению пользователя — виджет
   «Задачи» на /students/<id>/progress/ отображался уже соседних карточек
   сайдбара): `width: 100%` добавлен явно. Без него `.isOffcanvas-end`'s
   `width: var(--isOffcanvas-width)` (400px, нужен для мобильной
   выдвижной панели) продолжал действовать и на ≥1200px — совпадение с
   шириной соседних `.widget` при этом чисто случайное: панель либо
   упиралась в `max-width: 100%` и становилась той же ширины, что и
   контейнер сайдбара (когда он уже 400px), либо, если контейнер шире,
   оставалась буквально 400px — заметно уже соседей. `width: 100%` делает
   панель равной ширине контейнера в любом случае, как и остальные
   `.widget`. */
@media (min-width: 1200px) {
  .isOffcanvas--sidebar-sticky {
    position: static;
    height: auto;
    visibility: visible;
    background-color: transparent;
    box-shadow: none;
    border: 0;
    transform: none;
    width: 100%;
  }
}
@media (max-width: 1199.98px) {
  html[data-bs-theme='dark'] .isOffcanvas--sidebar-sticky {
    background-color: #121e2d;
  }
}

/* Стиль для отображения хода выполнения тренировки */

.setProgress .nav-pills .nav-link.active {
  color: #fff !important;
}
.setProgress .nav-link {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  padding-left: 0;
  padding-right: 0;
  width: 100%;
  text-align: center;
  color: var(--sportosphere-gray-600);
  display: block;
}
.setProgress .nav-link .nav-item-circle {
  color: var(--sportosphere-gray-500);
  background-color: var(--sportosphere-setProgress-nav-item-circle-bg);
  border: 2px solid var(--sportosphere-gray-300);
  height: 2.5rem;
  width: 2.5rem;
  border-radius: 50%;
  line-height: 2.25rem;
  padding: 0;
  text-align: center;
  -ms-flex-item-align: center;
  align-self: center;
  display: block;
  -webkit-transition: all 0.2s ease-in-out;
  transition: all 0.2s ease-in-out;
}
.setProgress .nav-link.active {
  color: var(--sportosphere-primary) !important;
}
.setProgress .nav-link.active .nav-item-circle {
  background-color: var(--sportosphere-primary) !important;
  color: #fff;
  border-color: var(--sportosphere-primary);
}
.setProgress .nav-link.done {
  color: var(--sportosphere-success);
}
.setProgress .nav-link.done .nav-item-circle {
  background-color: var(--sportosphere-success) !important;
  color: #fff;
  border-color: var(--sportosphere-success);
}
.setProgress .nav-link.done .nav-item-circle .check-icon {
  display: inline-block;
}
.setProgress .nav-link.done .nav-item-circle-parent:before {
  background-color: var(--sportosphere-success);
}
.setProgress .nav-link .nav-item-circle .check-icon {
  display: none;
}
.setProgress .nav-link .nav-item-circle-parent {
  display: block;
}
.setProgress .nav-link .nav-item-circle-parent:before {
  content: "";
  height: 2px;
  width: 101%;
  position: absolute;
  left: 51%;
  top: 50%;
  -webkit-transform: translate3d(-50%, -50%, 0);
  transform: translate3d(-50%, -50%, 0);
  background: var(--sportosphere-gray-300);
}
.setProgress .nav-item:not(:first-child) .active .nav-item-circle-parent:after {
  content: "";
  width: 50%;
  position: absolute;
  left: -1px;
  top: 50%;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  height: 2px;
  background-color: var(--sportosphere-success);
}
.setProgress .nav-item-circle-parent {
  position: relative;
  padding: 0 1rem;
}
.setProgress .nav-item-circle {
  margin: 0 auto;
  z-index: 1;
  position: relative;
}
.setProgress .nav-item {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-preferred-size: auto;
  flex-basis: auto;
  -webkit-box-flex: 1;
  -ms-flex-positive: 1;
  flex-grow: 1;
  max-width: 100%;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
}
.setProgress .nav-item:first-child .nav-item-circle-parent:before, .setProgress .nav-item:last-child .nav-item-circle-parent:before {
  width: 50%;
}
.setProgress .nav-item:first-child .nav-item-circle-parent:before {
  -webkit-transform: translate3d(0, -50%, 0);
  transform: translate3d(0, -50%, 0);
}
.setProgress .nav-item:last-child .nav-item-circle-parent:before {
  left: 0;
  -webkit-transform: translate3d(0, -50%, 0);
  transform: translate3d(0, -50%, 0);
}
.setProgress .pager {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
}
.setProgress .wizard-lottie-wrapper {
  height: 1px;
  position: relative;
  padding-bottom: 11.875rem;
  overflow: hidden;
}
.setProgress .wizard-lottie-wrapper .wizard-lottie {
  width: 21.25rem;
  height: auto;
  -webkit-transform: translateY(-73%);
  -ms-transform: translateY(-73%);
  transform: translateY(-73%);
}

.validation-success {
  display:none;
  width:100%;
  margin-top:.25rem;
  font-size:75%;
  color:var(--sportosphere-form-valid-color)
}

.validation-error {
  display:none;
  width:100%;
  margin-top:.25rem;
  font-size:75%;
  color:var(--sportosphere-form-invalid-color)
}

.validation-failed {
  border-color:var(--sportosphere-form-invalid-border-color);
  padding-right:calc(1.5em + 0.625rem);
  /* box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); */
  box-shadow: 0 0 5px var(--sportosphere-form-invalid-border-color);
  background-image: url('/static/icons/triangle-exclamation-regular-full.svg');
  background-repeat: no-repeat;
  background-position: right calc(0.375em + 0.15625rem) center;
  background-size: calc(0.75em + 0.3125rem) calc(0.75em + 0.3125rem);
}


/******************
 Стиль календаря
*******************/

/* Базовыек настройки */

.smartCalendar table,
.smartCalendarSimple table {
  width: 100%;
  height: 100%;
  table-layout: auto; /* Автоматическое определение ширины */
  font-size: 1em;
}

.smartCalendar table thead tr,
.smartCalendarSimple table thead tr {
  height: 1px;
}

.smartCalendar table thead th,
.smartCalendarSimple table thead th {
  text-align: center;
  font-weight: 600;
}

.smartCalendar table thead th {
  background-color: var(--sportosphere-gray-100);
  border: 1px solid var(--sportosphere-gray-200);
}

.smartCalendarSimple table th {
  background-color: var(--sportosphere-gray-100);
  border: none;
}

.smartCalendar td { /* Внутренние границы для каждой ячейки */
  border-bottom: 1px solid var(--sportosphere-gray-200); /* Горизонтальная граница снизу каждой строки */
  border-right: 1px solid var(--sportosphere-gray-200); /* Добавляем вертикальную границу справа */
}

.smartCalendar td,
.smartCalendarSimple td { /* Внутренние границы для каждой ячейки */
  text-align: right; /* Выравниваем содержимое слева */
  position: relative;
}

.smartCalendarSimple td { /* Внутренние границы для каждой ячейки */
  padding-top: .5rem;
}

.calendar-events-list {
  overflow: auto !important;
  scrollbar-color: var(--sportosphere-scrollbar-bg) transparent;
  scrollbar-width: thin;
  height: 20rem;
  margin-top: 1rem !important;
  padding-left: 0;
  list-style: none;
}

.calendar-events-list li {
  margin-bottom: 1rem !important;
  border-top: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color) !important;
  cursor: pointer !important;
  padding-bottom: 0.25rem !important;
  padding-top: 1rem !important;
  margin-bottom: 1rem !important;
}

.calendar-events-list li div {
  --sportosphere-border-opacity: 1;
  border-color: rgba(var(--sportosphere-primary-rgb), var(--sportosphere-border-opacity)) !important;
  padding-left: 1rem !important;
  margin-top: 0.25rem !important;
  --sportosphere-border-width: 3px;
  border-left: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color) !important;
}

.calendar-events-list li div h6 {
  --sportosphere-text-opacity: 1;
  color: rgba(var(--sportosphere-gray-700-rgb), var(--sportosphere-text-opacity)) !important;
  font-weight: 600 !important;
  margin-bottom: 0.25rem !important;
  font-size: 0.8333333333rem;
  margin-top: 0;
  font-family: var(--sportosphere-font-sans-serif);
  line-height: 1.2;
}

.calendar-events-list li div p {
  --sportosphere-text-opacity: 1;
  color: rgba(var(--sportosphere-gray-600-rgb), var(--sportosphere-text-opacity)) !important;
  font-size: 0.6944444444rem !important;
  margin-bottom: 0 !important;
  margin-top: 0;
}


/* .smartCalendarSimple table a {
  display: inline-block;
  padding: .5rem;
} */

.smartCalendar table th:last-child, td:last-child, 
.smartCalendarSimple table th:last-child, td:last-child {
  border-right: none;
}

.smartCalendar table th:first-child, td:first-child,
.smartCalendarSimple table th:first-child, td:first-child {
  border-left: none;
}

/* Дневное представление */

/* Стили первой колонки (подпись часа) дневного вида объединены с
   недельным видом — см. .smartCalendar.week-view th:first-child /
   td:first-child ниже в файле (2026-08-21). Здесь остаётся только то, что
   специфично для дневного вида (всего 2 колонки, вторая — события). */
.smartCalendar.day-view td:last-child {
  width: 100%; /* Растягиваем на оставшуюся ширину */
}

.smartCalendar .day-frame {
  padding: .5rem;
  position: absolute; /* Абсолютное позиционирование */
  top: 0; /* Привязываем верхний край к верхнему краю ячейки */
  left: 0; /* Привязываем левый край к левому краю ячейки */
  right: 0; /* Растягиваем вправо */
  bottom: 0; /* Растягиваем вниз */
  /* top+bottom:0 при абсолютном позиционировании задают .day-frame
     определённую (не auto) высоту, равную высоте ячейки дня — это и есть
     "дневное окно". overflow: hidden здесь (а не только на
     .day-frame-events ниже, у которой высота auto и поэтому обрезать
     нечего) не даёт содержимому (число дня + события) визуально выходить
     за пределы ячейки в соседние строки месячной сетки. */
  overflow: hidden;
}

.today-highlight {
  border: 1px solid var(--sportosphere-primary) !important;
}

.smartCalendar .day-frame .day-frame-date,
.smartCalendarSimple .day-frame .day-frame-date {
  margin-bottom: .5rem;
}

.smartCalendar .day-frame .day-frame-date,
.smartCalendarSimple .day-frame .day-frame-date {
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  display: flex;
  flex-direction: row-reverse;
}

.smartCalendar .day-frame .day-frame-date a,
.smartCalendarSimple .day-frame .day-frame-date a {
  z-index: 9;
  position: relative;
  width: 1.875rem;
  height: 1.875rem;
  background-color: var(--sportosphere-gray-100);
  text-align: center;
  text-decoration: none !important;
  border-radius: 50%;
  line-height: 1.875rem;
  padding: 0 !important;
  font-size: 0.8333333333rem;
  -webkit-transition: all 0.2s ease-in-out;
  transition: all 0.2s ease-in-out;
  color: var(--sportosphere-gray-500-rgb)
}

.today-a-highlight {
  background-color: var(--sportosphere-primary) !important;
  color: #fff !important;
}

.selected-day-highlight {
  border: 2px solid var(--sportosphere-success) !important;
  border-radius: 50% !important;
  width: 30px !important;
  height: 30px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
}

.smartCalendarSimple .next-month a,
.smartCalendarSimple .previous-month a { /* Внутренние границы для каждой ячейки */
  --sportosphere-text-opacity: 0.5;
  color: rgba(var(--sportosphere-gray-500-rgb), var(--sportosphere-text-opacity)) !important;
}

.smartCalendar .day-event {
  position: relative;
  z-index: 10;
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  margin-bottom: .5rem;
  font-size: 0.875rem;
  color: var(--sportosphere-success-text-emphasis);
  background-color: var(--sportosphere-success-bg-subtle);
  overflow: hidden; /* Скрываем лишние символы */
  white-space: nowrap; /* Запрещаем перенос текста */
  text-overflow: ellipsis; /* Показываем многоточие при обрезании */
  cursor: move;
}

.smartCalendar .day-frame .day-frame-bg {
  z-index: 8;
  position: absolute;     /* Абсолютное позиционирование */
  top: 0;                 /* Привязываем верхний край к верхнему краю ячейки */
  left: 0;                /* Привязываем левый край к левому краю ячейки */
  right: 0;               /* Растягиваем вправо */
  bottom: 0; 
}

.smartCalendar .hour-frame .hour-frame-bg {
  z-index: 8;
  position: absolute;     /* Абсолютное позиционирование */
  top: 0;                 /* Привязываем верхний край к верхнему краю ячейки */
  left: 0;                /* Привязываем левый край к левому краю ячейки */
  right: 0;               /* Растягиваем вправо */
  bottom: 0; 
}

.smartCalendar .day-frame-events {
  overflow: hidden;
}

.droppable-over{
  background-color: #ebfff7;
}

.dragging {
  opacity: 0.5;
}

.smartCalendar.day-view {
  width: auto; /* Автоширина */
  border-collapse: collapse;
  table-layout: auto; /* Автоматическое определение ширины */
}

.smartCalendar.day-view tbody tr {
  height: 4rem;
}

.smartCalendar.day-view .hour-frame-time {
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  margin-bottom: .5rem;
  font-size: 0.875rem;
  white-space: nowrap;
}

.smartCalendar.day-view .hour-frame-events {
  -webkit-box-orient: horizontal !important;
  -webkit-box-direction: normal !important;
  -ms-flex-direction: row !important;
  flex-direction: row !important;
  display: flex !important;
}

.smartCalendar .hour-event {
  position: relative;
  z-index: 10;
  width: 100%;
  box-sizing: border-box;
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  border-top: 2px solid var(--sportosphere-success-text-emphasis);
  /* box-shadow: 0 0 0 1px #fff; */
  font-size: 0.875rem;
  color: var(--sportosphere-success-text-emphasis);
  background-color: var(--sportosphere-success-bg-subtle);
  overflow: hidden; /* Скрываем лишние символы */
  white-space: nowrap; /* Запрещаем перенос текста */
  text-overflow: ellipsis; /* Показываем многоточие при обрезании */
  cursor: move;
}

/*
* Стили для просмотра фотографий модуля smartView.js
*/

.imageViewer-container {
  display: none; /* Скрыто по умолчанию */
  position: fixed; /* Фиксированное положение */
  z-index: 9990; /* Сидит поверх всего содержимого страницы */
  left: 0;
  top: 0;
  width: 100%; /* Полностью закрывает экран */
  height: 100%;
  overflow: auto; /* Если контент больше экрана появляется прокрутка */
  background-color: rgba(0,0,0,0.8); /* Полупрозрачный фон */
}
.imageViewer-content {
  width: 100%; /* Полностью закрывает экран */
  height: 100%;
  display: flex;
  justify-content: center; /* Горизонтальное выравнивание */
  align-items: center;
  z-index: 9991;
}
.imageViewer-image {
    max-width: 80%; /* Ограничиваем ширину до 80% */
    max-height: 80vh; /* Ограничиваем высоту до 80% видимой области (view height) */
    width: auto;     /* Автоматически устанавливаем ширину исходя из пропорций */
    height: auto;    /* Автоматически устанавливаем высоту исходя из пропорций */
}
.imageViewer-prev,
.imageViewer-next,
.imageViewer-close {
  z-index: 9999;
  width: 35px;
  height: 35px;
  position: absolute;
  cursor: pointer;
  -webkit-box-sizing: content-box;
  box-sizing: content-box;
  padding: 0.25em 0.25em;
  color: gray;
  border: 0;
  border-radius: 50%;
  font-size: 1.5rem;
}
.imageViewer-close {
  top: 20px;
  right: 20px;
}
.imageViewer-prev {
  left: 30px;
  display: none;
}
.imageViewer-next {
  right: 30px;
  display: none;
}
.imageViewer-prev:hover,
.imageViewer-next:hover,
.imageViewer-close:hover {
  background-color: gray; /* Светлый серый фон при наведении */
  color: white;               /* Черный цвет иконки при наведении */
}

@media (min-width:992px) {
  .imageViewer-close {
    right:20px
  }
}

/*
* Стили для модуля загрузки фотографий smartUploader.js
*/

.filesDropzone {
  position: relative;
  border-radius: .375rem;
  min-height: 150px;
  box-sizing: border-box;
}
.filesDropzone.fdz-clickable {
  cursor: pointer;
}
.filesDropzone.fdz-multiple {
  padding: 0;
  border: 0;
  background: rgba(0,0,0,0);
}
.filesDropzone.fdz-multiple .fdz-message {
  padding: 4rem 2rem;
  border: 2px dashed var(--sportosphere-border-color);
  border-radius: .375rem;
}
.filesDropzone .fdz-message {
  position:relative;
  margin:0;
  display: flex;
  justify-content: center;
  align-items: center;
}
.filesDropzone.fdz-multiple .fdz-image-preview {
  padding-bottom: 1rem !important;
  margin-bottom: 1rem !important;
}
.filesDropzone.fdz-multiple .fdz-image-preview:first-child {
  margin-top: 1.8rem;
}
.filesDropzone .fdz-image {
  border-radius: .25rem;
  height: 2.5rem;
  width: 2.5rem;
  -o-object-fit: cover;
  object-fit: cover;
  margin-right: 1rem;
  border: 1px solid #d8e2ef;
}
.fdz-image-preview {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--sportosphere-border-color);
}
.fdz-image-preview-body {
  flex: 1;
  /* Без display:flex здесь justify-content/align-items из
     .flex-between-center (theme.min.css, применяется вместе на этом же
     элементе разметки) не действуют вовсе — родительский div не был
     flex-контейнером, кнопка удаления съезжала под текст вместо
     выравнивания по правому краю строки (найдено визуально). */
  display: flex;
}
.fdz-image-preview-meta {
  display: flex;
  align-items: center;
}
.fdz-image-preview-size {
  margin-bottom: 0;
  font-size: 0.8333333333rem;
  color: var(--sportosphere-gray-400);
  line-height: 1;
}
.fdz-remove-btn {
  flex: 0 0 auto;
}
.smartUploader-fields {
  margin-bottom: 1rem;
}
.smartUploader-field-label {
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sportosphere-gray-600);
  font-weight: 600;
  margin-bottom: 0;
}
.smartUploader-dropzone-icon {
  margin-right: 0.5rem;
  font-size: xx-large;
}
.fdz-preview {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.smartUploader-submit-btn {
  margin-right: 0.5rem;
  margin-top: 1rem;
}
.smartUploader-submit-btn-label {
  display: none;
  margin-left: 0.25rem;
}
@media (min-width: 1200px) {
  .smartUploader-submit-btn-label {
    display: inline-block;
  }
}

/* Панель выбора фото формы создания поста ленты (change
   feed-post-photo-button) монтирует smartUploader ДО публикации поста,
   когда его apiEndpoint — заведомый плейсхолдер (id поста ещё не
   известен) — собственная кнопка отправки smartUploader здесь скрыта,
   отправку запускает feed.js программно после успешного создания поста
   (docs/feed-spec.md, раздел 5; static/scripts/pages/feed.js,
   togglePhotoPicker()). Селектор специфичен только для контейнера этой
   панели — остальные ~9 мест использования smartUploader в проекте не
   затрагиваются (docs/smartUploader-spec.md, раздел 9). */
#feed-postCreate-uploader .smartUploader-submit-btn {
  display: none;
}
/* Отступ панели от ряда кнопок формы создания поста — без него нижний
   край кнопок вплотную примыкает к верхнему краю зоны загрузки при
   открытии панели (найдено визуально при проверке change
   feed-post-photo-button). */
#feed-postCreate-uploader:not([hidden]) {
  margin-top: 1rem;
}

/*
* Стили для модуля создания вкладок (Tab) smartTabs.js
*/

.tabsBarHeader {
  border-bottom:var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color) !important
}

.menu {
  --sportosphere-nav-link-padding-x: 1rem;
  --sportosphere-nav-link-padding-y: 0.5rem;
  --sportosphere-nav-link-font-weight: ;
  --sportosphere-nav-link-color: var(--sportosphere-link-color);
  --sportosphere-nav-link-hover-color: var(--sportosphere-link-hover-color);
  --sportosphere-nav-link-disabled-color: var(--sportosphere-gray-600);
  display:-webkit-box;
  display:-ms-flexbox;
  display:flex;
  -ms-flex-wrap:wrap;
  flex-wrap:wrap;
  padding-left:0;
  margin-bottom:0;
  list-style:none
}
.menu-tabs {
  gap: 10px;
  padding: 0;
}
.menu-tabs li {
    margin: 0;
}
.tab-item {
  white-space: nowrap;
}
.tab-link {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 1rem 1.25rem;
  border: var(--sportosphere-border-color);
  border-radius: 0;
  /* text-decoration: none; */
  /* Тот же цвет, что у .tab-link-title — иконка (fa-duotone) без
     собственного color наследует currentColor именно отсюда */
  color: var(--sportosphere-gray-600);
}
.tab-link-title {
  margin-bottom: 0;
  color: var(--sportosphere-gray-600);
}
.tab-link:hover {
  --sportosphere-link-color-rgb: var(--sportosphere-link-hover-color-rgb);
  text-decoration: none;
}
.tab-pane {
  display: none;
}
.tab-pane.active {
  display: block;
}
/* Дополнено 2026-08-30 (задача set-categories): раньше вместо этого класса
   на панель вкладки навешивались литеральные bootstrap-классы
   `card-body`/`bg-body-tertiary` (нарушение правила «своя визуальная
   система, без сторонних зависимостей» — 03-architecture.md, п. 8) — они
   резолвили padding/цвет/фон через переменные `--sportosphere-card-*`
   /`--sportosphere-tertiary-bg-rgb`, которые `.card` в theme.min.css
   объявляет ЛОКАЛЬНО на себе; вне настоящего `.card` (например, внутри
   `.widget`, см. sportosphereTheme.css, раздел «Универсальный компонент
   «виджет»») эти переменные не резолвились и панель вкладки рендерилась
   без padding (обнаружено на management_set_list.html). Ниже — те же
   самые вычисленные значения (padding 1.25rem = --sportosphere-card-
   spacer-y/x; фон rgba(249,250,253,1)/rgba(22,34,49,1) = то же, что даёт
   bg-body-tertiary через ГЛОБАЛЬНУЮ --sportosphere-tertiary-bg-rgb,
   объявленную в :root в обеих темах — значит, в отличие от прежнего
   `--sportosphere-card-*`-варианта, дополнительно корректно работает и в
   тёмной теме; color не задаётся намеренно — у `.card-body` он тоже был
   пустым, т.е. это было наследование цвета от родителя, ровно то же
   поведение сохраняется без явного свойства), собственным классом
   `.tab-pane-body`, не зависящим от того, что именно снаружи — `.card`
   или `.widget`. */
.tab-pane-body {
  flex: 1 1 auto;
  padding: 1.25rem;
  background-color: rgba(var(--sportosphere-tertiary-bg-rgb), 1);
}

/* Дополнено 2026-08-30: разделитель между несколькими плагинами
   (importPlugin[]) одной вкладки — smartTabs.js вставляет
   <hr class="tab-pane-divider"> между соседними content-плагинами
   (fetchPluginsForTab()), когда на вкладке их больше одного (например,
   smartForm создания записи + smartTable списка на одной панели). */
.tab-pane-divider {
  margin: 1.25rem 0;
  border: 0;
  border-top: 1px dashed var(--sportosphere-border-color);
}

.menu-tabs .tab-link.active {
  color:var(--sportosphere-primary);
  background-color:rgba(0,0,0,0);
  border-bottom:2px solid var(--sportosphere-primary)
}

.menu-tabs .tab-link.active span {
  color:var(--sportosphere-primary) !important
}

.menu-tabs .tab-link.active h6 {
  color:var(--sportosphere-primary) !important
}
/*
* Стили для модуля smartToasts.js
*/

.smartToast-container {
  width: -webkit-max-content;
  width: -moz-max-content;
  width: max-content;
  max-width: 100%;
  pointer-events: none;
  position: absolute;
  position: fixed !important;
  bottom: 0 !important;
  right: 0 !important;
  padding: 1rem !important;
  /* Верхний слой матрицы z-index проекта (см. docs/z-index-layers.md) —
     обязан быть выше любого другого слоя (smartCalendar, isModal,
     smartNavBars, imageViewer и т. д.), иначе позиционированные элементы
     этих инструментов с z-index:auto у контейнера рисуются поверх тоста
     независимо от порядка в DOM. До этой правки z-index у контейнера не
     был задан вовсе (была неиспользуемая переменная в .smartToast). */
  --sportosphere-toast-zindex: 10000;
  z-index: var(--sportosphere-toast-zindex);
}

.smartToast-container > :not(:last-child) {
  margin-bottom: 0.25rem;
}

.smartToast {
  width: 350px;
  max-width: 100%;
  font-size: 0.875rem;
  pointer-events: auto;
  background-color: var(--sportosphere-toast-bg);
  border: 1px solid rgba(0, 0, 0, 0.1);
  -webkit-box-shadow: var(--sportosphere-box-shadow);
  box-shadow: var(--sportosphere-box-shadow);
  border-radius: 0.25rem;
  --sportosphere-toast-padding-x: 0.75rem;
  --sportosphere-toast-padding-y: 0.5rem;
  --sportosphere-toast-spacing: 2rem;
  --sportosphere-toast-max-width: 350px;
  --sportosphere-toast-font-size: 0.875rem;
  --sportosphere-toast-color: var(--sportosphere-gray-600);
  --sportosphere-toast-bg: var(--sportosphere-toast-bg-global);
  --sportosphere-toast-border-width: var(--sportosphere-border-width);
  --sportosphere-toast-border-color: var(--sportosphere-border-color-translucent);
  --sportosphere-toast-border-radius: var(--sportosphere-border-radius);
  --sportosphere-toast-box-shadow: var(--sportosphere-box-shadow);
  --sportosphere-toast-header-color: var(--sportosphere-gray-600);
  --sportosphere-toast-header-bg: var(--sportosphere-toast-bg-global);
  --sportosphere-toast-header-border-color: rgba(0, 0, 0, 0.05);
  width: var(--sportosphere-toast-max-width);
  max-width: 100%;
  font-size: var(--sportosphere-toast-font-size);
  color: var(--sportosphere-toast-color);
  pointer-events: auto;
  background-color: var(--sportosphere-toast-bg);
  background-clip: padding-box;
  border: var(--sportosphere-toast-border-width) solid var(--sportosphere-toast-border-color);
  -webkit-box-shadow: var(--sportosphere-toast-box-shadow);
  box-shadow: var(--sportosphere-toast-box-shadow);
  border-radius: var(--sportosphere-toast-border-radius);
}

.smartToast-header {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  padding: 0.5rem 0.75rem;
  color: rgba(var(--sportosphere-white-rgb), var(--sportosphere-text-opacity)) !important;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  border-top-left-radius: calc(0.25rem - 1px);
  border-top-right-radius: calc(0.25rem - 1px);
}

.smartToast-btn-close {
  /* 1. Сделать фон прозрачным и убрать рамку */
  background-color: transparent;
  border: none;
  cursor: pointer;
  margin-right: -0.375rem;
  margin-left: 0.75rem;
  
  /* 2. Размеры кнопки */
  width: 20px;
  height: 20px;
  
  /* 3. Центрирование крестика */
  display: flex;
  align-items: center;
  justify-content: center;
  
  /* 4. Стилизация крестика */
  font-size: 24px;
  color: var(--sportosphere-btn-close-color);
  padding: 0;
}

/* Эффект при наведении */
.smartToast-btn-close:hover {
  /* color: #000; */
  /* background-color: rgba(0,0,0,0.1); */
  /* border-radius: 50%; */
  color: #ff0000;
}

/* .smartToast-btn-close {
  margin-right: -0.375rem;
  margin-left: 0.75rem;
  --sportosphere-btn-close-color: #000;
  --sportosphere-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");
  --sportosphere-btn-close-opacity: 0.5;
  --sportosphere-btn-close-hover-opacity: 0.75;
  --sportosphere-btn-close-focus-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
  --sportosphere-btn-close-focus-opacity: 1;
  --sportosphere-btn-close-disabled-opacity: 0.25;
  -webkit-box-sizing: content-box;
  box-sizing: content-box;
  width: 1em;
  height: 1em;
  padding: 0.25em 0.25em;
  color: var(--sportosphere-btn-close-color);
  background: transparent var(--sportosphere-btn-close-bg) center/1em auto no-repeat;
  -webkit-filter: var(--sportosphere-btn-close-white-filter);
  filter: var(--sportosphere-btn-close-white-filter);
  border: 0;
  border-radius: 0.25rem;
  opacity: var(--sportosphere-btn-close-opacity);
} */

.smartToast-body {
  padding: 0.75rem;
  word-wrap: break-word;
  opacity: 1;
}

.smartToast-header.success {
  --sportosphere-text-opacity: 1;
  background-color: rgba(var(--sportosphere-success-rgb), var(--sportosphere-text-opacity)) !important;
}

.smartToast-header.info {
  --sportosphere-text-opacity: 1;
  background-color: rgba(var(--sportosphere-info-rgb), var(--sportosphere-text-opacity)) !important;
}

.smartToast-header.warning {
  --sportosphere-text-opacity: 1;
  background-color: rgba(var(--sportosphere-warning-rgb), var(--sportosphere-text-opacity)) !important;
}

.smartToast-header.error,
.smartToast-header.danger {
  --sportosphere-text-opacity: 1;
  background-color: rgba(var(--sportosphere-danger-rgb), var(--sportosphere-text-opacity)) !important;
}

#smartCalendar {
  position: relative;
  /* Изолирующий контекст наложения (CSS-3, known-issues.md) — без него
     локальные z-index инструмента (8-25) сравниваются напрямую с
     позиционированными элементами верхнего уровня страницы вместо того,
     чтобы конкурировать только между собой внутри #smartCalendar */
  isolation: isolate;
}

/* Кнопки тулбара "вперёд"/"назад" (data-event="prev"/"next",
   smartCalendar.js buildUI(), calendarType: 'fullCalendar') — класс
   .icon-item (theme.min.css) красит фон и на :hover, и на :focus
   одинаково, а мышиный клик по <button> оставляет на нём фокус, поэтому
   подсветка "зависает" после нажатия, даже когда курсор уже увёл мышь с
   кнопки. Возвращаем фон к дефолтному, когда кнопка в фокусе, но не под
   курсором — подсветка остаётся только по наведению. */
#smartCalendar [data-event="prev"].icon-item:focus:not(:hover),
#smartCalendar [data-event="next"].icon-item:focus:not(:hover) {
  background-color: var(--sportosphere-quaternary-bg);
}

/* Основные настройки таблицы */
.smartCalendar table {
    width: 100%;
    height: 100%;
    border-collapse: separate; /* Чтобы границы оставались раздельными */
    border-spacing: 0;          /* Без промежутков между границами */
    font-size: 1em;
}

.smartCalendar table thead tr {
    height: 1px;
    font-weight: 600;
        
}

.smartCalendar table thead th {
    text-align: center;
    border: none;
    background-color: var(--sportosphere-gray-100);
    border: 1px solid var(--sportosphere-gray-200);
}

/* Внутренние границы для каждой ячейки */
.smartCalendar td {
  text-align: left; /* Выравниваем содержимое слева */
  position: relative;
  border-bottom: 1px solid var(--sportosphere-gray-200); /* Горизонтальная граница снизу каждой строки */
  border-right: 1px solid var(--sportosphere-gray-200); /* Добавляем вертикальную границу справа */
}

/* Убираем внешние границы таблицы */
.smartCalendar tr {
    border: none;               /* Внешняя граница отсутствует */
}

.smartCalendar table th:last-child, td:last-child {
  border-right: none;
}

.smartCalendar .day-frame {
  padding: .5rem;
  position: absolute;     /* Абсолютное позиционирование */
  top: 0;                 /* Привязываем верхний край к верхнему краю ячейки */
  left: 0;                /* Привязываем левый край к левому краю ячейки */
  right: 0;               /* Растягиваем вправо */
  bottom: 0;              /* Растягиваем вниз */
  /* См. идентичное правило и комментарий выше (~строка 883) — тот же
     дублирующийся блок, см. smartCalendar-spec.md, раздел 10. */
  overflow: hidden;
}

.today-highlight {
  border: 1px solid var(--sportosphere-primary) !important;;
}

.smartCalendar .day-frame .day-frame-date {
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  margin-bottom: .5rem;
  display: flex;
  flex-direction: row-reverse;
}

.smartCalendar .day-frame .day-frame-date a {
  z-index: 9;
  position: relative;
  width: 1.875rem;
  height: 1.875rem;
  background-color: var(--sportosphere-gray-100);
  text-align: center;
  text-decoration: none !important;
  border-radius: 50%;
  line-height: 1.875rem;
  padding: 0 !important;
  font-size: 0.8333333333rem;
  -webkit-transition: all 0.2s ease-in-out;
  transition: all 0.2s ease-in-out;
}

.smartCalendar .day-event {
  position: relative;
  z-index: 10;
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  margin-bottom: .5rem;
  font-size: 0.875rem;
  color: var(--sportosphere-success-text-emphasis);
  background-color: var(--sportosphere-success-bg-subtle);
  overflow: hidden; /* Скрываем лишние символы */
  white-space: nowrap; /* Запрещаем перенос текста */
  text-overflow: ellipsis; /* Показываем многоточие при обрезании */
  cursor: move;
  transition: filter 0.15s ease-in-out;
}

/* Подсказка кликабельности для записей, открывающих редактирование */
.smartCalendar .day-event:hover {
  filter: brightness(0.95);
}

.smartCalendar .day-frame .day-frame-bg {
  z-index: 8;
  position: absolute;     /* Абсолютное позиционирование */
  top: 0;                 /* Привязываем верхний край к верхнему краю ячейки */
  left: 0;                /* Привязываем левый край к левому краю ячейки */
  right: 0;               /* Растягиваем вправо */
  bottom: 0;
}

.smartCalendar .hour-frame .hour-frame-bg {
  z-index: 8;
  position: absolute;     /* Абсолютное позиционирование */
  top: 0;                 /* Привязываем верхний край к верхнему краю ячейки */
  left: 0;                /* Привязываем левый край к левому краю ячейки */
  right: 0;               /* Растягиваем вправо */
  bottom: 0;
}

.smartCalendar .day-frame-events {
  overflow: hidden;
}

/* Блок "Ещё N" при переполнении списка событий дня */
.smartCalendar .day-event.day-event-more {
  color: var(--sportosphere-gray-700);
  background-color: var(--sportosphere-gray-200);
  text-align: center;
  cursor: pointer;
}

/* Popover со всеми событиями дня. Позиция (top/left) вычисляется в JS
   по левому верхнему углу ячейки с датой и задаётся инлайн-стилем. */
.smartCalendar .day-events-popover {
  position: absolute;
  z-index: 20;
  width: 220px;
  background-color: var(--sportosphere-body-bg);
  border: 1px solid var(--sportosphere-gray-300);
  border-radius: 5px;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  padding: 0.5rem;
  max-height: 220px;
  overflow-y: auto;
}

.smartCalendar .day-events-popover-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.8125rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.smartCalendar .day-events-popover-body .day-event {
  cursor: default;
}

.droppable-over{
  background-color: #ebfff7;
}

.dragging {
  opacity: 0.5;
}

/* .day-View */

.smartCalendar.day-view th:first-child {
  width: auto;
}

.smartCalendar.day-view tbody tr {
  height: 4rem;
}

.smartCalendar.day-view .hour-frame-time {
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  margin-bottom: .5rem;
  font-size: 0.875rem;
  white-space: nowrap;
}

.smartCalendar.day-view .hour-frame-events {
  -webkit-box-orient: horizontal !important;
  -webkit-box-direction: normal !important;
  -ms-flex-direction: row !important;
  flex-direction: row !important;
  display: flex !important;
}

/* Скоуп только на дневной вид — иначе (тот же класс .hour-event) правило
   перебивает position/margin недельного вида общим селектором */
.smartCalendar.day-view .hour-event {
  position: absolute;
  z-index: 10;
  padding: 0.25rem 0.5rem !important;
  border-radius: 5px;
  margin-bottom: .5rem;
  font-size: 0.875rem;
  color: var(--sportosphere-success-text-emphasis);
  background-color: var(--sportosphere-success-bg-subtle);
  overflow: hidden; /* Скрываем лишние символы */
  white-space: nowrap; /* Запрещаем перенос текста */
  text-overflow: ellipsis; /* Показываем многоточие при обрезании */
  cursor: move;
}

/* Недельный вид: часовая ячейка должна расти под все свои события, а не
   обрезать их (по прямому запросу — события идут одно под другим в
   порядке заведения, ячейка масштабируется по их числу). .day-frame в
   общем правиле выше (.smartCalendar .day-frame) — position: absolute с
   top/left/right/bottom: 0, из потока, поэтому не передаёт свою высоту
   <td>/<tr>: без переопределения строка получала бы фиксированную долю
   высоты таблицы независимо от числа событий, а reset ниже (overflow:
   hidden) обрезал бы лишние. Здесь — обратное: .day-frame возвращён в
   поток (position: relative вместо absolute, без top/left/right/bottom),
   тогда его реальная высота (следовательно, высота <td>/<tr>) определяется
   содержимым .day-frame-events. Таблица (.smartCalendar table { height:
   100% }, раздел 10) — не потолок, а минимум (стандартное поведение CSS
   для table height): строки без событий получают свою долю от
   распределения свободной высоты, а строка с несколькими событиями
   вырастает сверх неё; итоговое превышение общей высоты таблицы над
   #smartCalendar скрывается прокруткой .calendar-body (уже .scrollbar →
   overflow: auto, theme.min.css). Скоуп только на недельный вид — в
   месячном .day-frame обязан оставаться абсолютным (число дня + события в
   общей ячейке дня, раздел 6). */
.smartCalendar.week-view .day-frame {
  position: relative;
  /* overflow: hidden из общего правила .day-frame (месячный вид, "дневное
     окно") здесь безвреден — без top/bottom:0 высота .day-frame снова auto
     по содержимому, обрезать нечего, — но переопределяем явно, чтобы это
     не стало сюрпризом при будущей правке недельного вида. */
  overflow: visible;
}

.smartCalendar.week-view .day-frame-events {
  overflow: visible;
}

/* .day-frame-bg в недельном виде — ПРЯМОЙ потомок <td> (col.appendChild
   в createCalendar(), не dayFrame.appendChild, в отличие от общего правила
   выше, .smartCalendar .day-frame .day-frame-bg, — оно её больше не
   затрагивает, т.к. требует вложенности именно в .day-frame). Причина: у
   .day-frame здесь (см. правило прямо выше) высота по контенту, не
   гарантированно равна фактической высоте строки — если высоту строки
   форсирует высокой СОСЕДНЯЯ ячейка того же часа с несколькими событиями,
   .day-frame ячеек с одним/без событий остаётся заметно меньше видимой
   высоты строки. <td> (уже position: relative, общее правило
   .smartCalendar td выше) такой неопределённости не имеет — его высоту
   вычисляет сама таблица. position: absolute; inset: 0 относительно <td> —
   обычное, не квирковое поведение CSS, надёжно работает одинаково во всех
   браузерах (в отличие от опробованной и отброшенной
   альтернативы — min-height: 100% на прямом потомке ячейки таблицы:
   сработало в Chromium, не сработало в Firefox). Именно на этом элементе,
   не на dayFrame, стоит draggable=true (см. smartCalendar.js, dayFrameBG) —
   он и определяет фактическую область захвата события мышью на пустом
   месте ячейки. z-index не задаём — ниже .hour-event (10) и без него: ни
   .day-frame-bg (тут), ни .day-frame (общее правило выше, тоже без
   z-index) не создают собственный контекст наложения (z-index: auto), она
   сравнивается с чипами в общем контексте предка — чипы всё равно
   оказываются выше per их собственный z-index: 10 */
.smartCalendar.week-view .day-frame-bg {
  position: absolute;
  inset: 0;
}

/* Подсказка курсора: draggable="true" ставится JS на dayFrameBG (см. выше),
   не только на сам чип события — курсор move должен быть виден везде в
   ячейке, а не только над чипом */
.smartCalendar.week-view .day-frame-bg[draggable="true"] {
  cursor: move;
}

/* Небольшой отступ между несколькими событиями одного часа, когда они
   идут друг под другом — общее правило .hour-event отступа не задаёт
   (в отличие от дневного вида выше, margin-bottom: .5rem, но это отдельный
   скоуп .smartCalendar.day-view .hour-event, недельного не касается) */
.smartCalendar.week-view .hour-event {
  margin-bottom: 2px;
}

.smartCalendar.week-view .hour-event:last-child {
  margin-bottom: 0;
}

/* Шапка недельного вида (Пн..Вс + даты) остаётся видимой при прокрутке
   .calendar-body — реальный внутренний скролл этого блока (не всей
   страницы) обеспечивает #smartCalendar с высотой, выставленной инлайн из
   JS страницы (fitCalendarHeight() в calendar.html/place_scheduler.html/
   visitsite_place_scheduler.html) + .calendar-body { overflow: auto } из
   .scrollbar (theme.min.css): .calendar-body — flex-item внутри
   Bootstrap-.card (#smartCalendar), сжимается до остатка высоты после
   .card-header, а не растягивает карточку. sticky — на th, не на thead
   (надёжнее кроссбраузерно). z-index выше .hour-event (10, строка ~1020)
   и .day-frame-bg/.day-frame-date (8-9) недельного вида — иначе события
   при скролле оказывались бы поверх шапки. Фон у th уже непрозрачный
   (var(--sportosphere-gray-100) / --sportosphere-highlight-bg у
   .today-column, см. выше) — плашка не просвечивает. */
.smartCalendar.week-view thead th {
  position: sticky;
  top: 0;
  z-index: 15;
}

/* Строка "весь день" недельного вида — контейнер полос многодневных личных
   записей (createCalendar(), case 'week', .week-all-day-row; рендер полос —
   renderWeekMultiDayEventBar()/measureWeekAllDayBarGeometry()), раздел
   "Недельный вид — многодневная запись одной полосой" smartCalendar-spec.md.
   Ячейки — <td>, не <th> (см. комментарий у JS), поэтому не подпадают под
   общее правило .smartCalendar.week-view thead th выше — sticky-позиция
   задаётся здесь отдельно, top проставляется инлайн из JS (измеренная
   высота headerRowWeek, а не константа — см. createCalendar()). z-index
   ниже шапки дней (15), но выше обычных ячеек/событий сетки (.hour-event —
   10), чтобы полоса оставалась поверх скроллящейся почасовой сетки. Высота
   строки — инлайн из JS (лестница дорожек, умноженная на высоту одной
   полосы) — сама по себе ячейка не растёт под absolute-позиционированные
   полосы (см. комментарий в JS), поэтому её нельзя оставлять auto. Padding
   ячейки — единственный источник отступа полосы от края (paddingLeft
   измеряется в JS как sideInsetPx, paddingTop/Bottom — как часть высоты
   строки), в отличие от месячного вида, где отступ брался от вложенного
   .day-frame. background-color — тот же цвет и по той же причине, что у
   thead th чуть выше (.smartCalendar table thead th) — sticky-ячейка без
   собственного непрозрачного фона просвечивала бы скроллящимися часовыми
   строками, проходящими "сквозь" её зафиксированную экранную позицию. */
.smartCalendar.week-view .week-all-day-row td {
  position: sticky;
  z-index: 14;
  padding: 0.25rem 0.375rem;
  background-color: var(--sportosphere-gray-100);
}

/* Колонка времени (первая колонка) недельного и дневного видов — подпись
   часа ("00:00"..."23:00", timeCell.textContent/hour-frame-time,
   smartCalendar.js). Общее правило для обоих видов (2026-08-21, по
   прямому запросу — стили дневного вида должны повторять недельный,
   переиспользованы буквально те же правила, не задублированы отдельным
   блоком) — раньше у .day-view была своя, более слабая версия (без
   центрирования/vertical-align, ширина колонки от min-width: max-content
   вместо фиксированной формулы), теперь заменена этой.
   Ширина — на th:first-child (в недельном виде пустая угловая ячейка
   шапки; в дневном — ячейка с текстом "Время", тоже первая строка):
   у недельного вида таблица — table-layout: fixed (createCalendar(),
   case 'week' — иначе длинная подпись события растягивала бы свою колонку
   дня), а при fixed-раскладке ширину колонки задаёт именно ячейка ПЕРВОЙ
   строки (thead th), не последующие td — по стандартному алгоритму
   фиксированной раскладки. У дневного вида table-layout остаётся auto
   (не менялся этой правкой — только 2 колонки, вторая и так тянется на
   100% по отдельному правилу td:last-child ниже), но auto-алгоритм тоже
   учитывает width на th как сильную подсказку по колонке — тот же
   calc(5ch + 1rem) даёт тот же результат без переключения раскладки.
   calc(5ch + 1rem) — 5 символов подписи часа ("00:00") + по 0.5rem
   отступа слева/справа (padding ниже), а не наугад подобранное значение.
   text-align/vertical-align/padding/white-space дублируются на
   td:first-child, потому что именно там реальный текст часа —
   th:first-child в недельном виде всегда пустой (weekHeadersText[0] ===
   ''), в дневном — статичный текст "Время", в обоих случаях сам не
   несёт стилей текста, только ширину колонки. */
.smartCalendar.week-view th:first-child,
.smartCalendar.day-view th:first-child {
  width: calc(5ch + 1rem);
}

.smartCalendar.week-view td:first-child,
.smartCalendar.day-view td:first-child {
  text-align: center;
  vertical-align: top;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  white-space: nowrap;
}

/* Подсветка нерабочих часов недельного вида серым — по графику работы
   заведения (place.WorkingHours), опциональный config.workingHoursSource
   (smartCalendar.js::isNonWorkingHour()/fetchWorkingHours()). Класс
   добавляется только часовым ячейкам (td), не шапке дня — у шапки нет
   понятия "час", в отличие от today-column ниже. Без данных о графике
   (workingHoursSource не задан, или график для дня ещё не сохранён)
   класс не появляется вовсе — календарь выглядит как раньше. Правило
   объявлено раньше today-column (совпадающая специфичность) намеренно:
   в ячейке, которая одновременно "сегодня" и "нерабочий час", побеждает
   today-column — приоритет "сегодня" как более заметного ориентира. */
.smartCalendar.week-view td.non-working-hour {
  background-color: var(--sportosphere-gray-200);
}

/* Подсветка колонки текущей даты в недельном виде — заливка фона (в
   отличие от today-highlight в месячном виде выше, там просто рамка
   вокруг .day-frame-bg одной ячейки, а не целой колонки). Класс
   today-column добавляется в JS (createCalendar(), case 'week') и шапке
   (th), и каждой часовой ячейке (td) дня, чья дата совпадает с реальной
   сегодняшней датой (this.today) — не с выбранной/отображаемой неделей,
   поэтому при навигации на другую неделю подсветки нигде нет. */
.smartCalendar.week-view th.today-column,
.smartCalendar.week-view td.today-column {
  background-color: var(--sportosphere-highlight-bg);
}

/* Стили для выделения дней недели */

.smartCalendar table thead th a,
.smartCalendarSimple table thead th a {
  text-decoration: none;
  color: inherit;
  display: block;
  padding: 0.5rem;
  border-radius: 0.25rem;
  transition: background-color 0.2s ease;
}

.smartCalendar table thead th a:hover {
  background-color: var(--sportosphere-gray-200);
}

/* Выделение текущего дня недели */
.smartCalendar table thead th.today-weekday a,
.smartCalendarSimple table thead th.today-weekday a {
  background-color: var(--sportosphere-primary);
  color: #fff;
}

/* Выделение выходных дней (Суббота и Воскресенье) */
.smartCalendar table thead th:nth-child(6) a,
.smartCalendar table thead th:nth-child(7) a,
.smartCalendarSimple table thead th:nth-child(6) a,
.smartCalendarSimple table thead th:nth-child(7) a,
.smartCalendar table thead th:nth-child(6) a:hover,
.smartCalendar table thead th:nth-child(7) a:hover,
.smartCalendarSimple table thead th:nth-child(6) a:hover,
.smartCalendarSimple table thead th:nth-child(7) a:hover {
  color: var(--sportosphere-danger);
}

/* Подсветка при наведении на ячейку дня */
.smartCalendar td:hover {
  background-color: rgba(0, 0, 0, 0.02);
}

/*
* Стили для виджетов модуля smartView.js
* (widgetType: workoutView, simpleCard, fullCard, visitCard, contactCard,
* businessCard, clubCard). Классы-компоненты Bootstrap/темы (card, dropdown,
* btn, alert, table, avatar и т.п.) переиспользуются как есть — здесь только
* собственная вёрстка/раскладка каждого виджета.
*/

/* --- Общее для всех виджетов --- */

/* Дополнено 2026-09-04 (openspec/changes/smartview-native-styles) —
   визуальные свойства .alert/.alert-info (theme.min.css) добавлены прямо в
   этот уже существующий маркерный класс — одна правка закрывает все 13
   мест разметки, где он использовался рядом с литеральными alert/alert-info. */
.smartView-emptyMessage {
  width: 100%;
  margin: auto;
  margin-bottom: 1rem;
  padding: 1rem;
  text-align: center;
  color: var(--sportosphere-info-text-emphasis);
  background-color: var(--sportosphere-info-bg-subtle);
  border: var(--sportosphere-border-width) solid var(--sportosphere-info-border-subtle);
  border-radius: var(--sportosphere-border-radius);
}

/* Замена bg-body-tertiary (theme.min.css) внутри карточек smartView.js —
   тот же приём, что уже сделан для smartTable-tertiary-bg/
   smartCalendar-tertiary-bg: глобальная --sportosphere-tertiary-bg-rgb,
   корректно работает в обеих темах. */
.smartView-tertiary-bg {
  background-color: rgba(var(--sportosphere-tertiary-bg-rgb), 1);
}

/* Замена rounded-circle/avatar avatar-4xl (theme.min.css) — используется
   не самим smartView.js, а внешними шаблонами, передающими Bootstrap-
   классы через конфиг cssClass_img/cssClass_img_div (fullCard/visitCard,
   openspec/changes/smartview-native-styles, design.md Decision 11). */
.smartView-configAvatar {
  position: relative;
  display: inline-block;
}
.smartView-configAvatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.smartView-configAvatar--4xl {
  height: 6.125rem;
  width: 6.125rem;
}

/* --- workoutView --- */

.smartView-workout-main {
  padding-right: 0;
}
@media (min-width: 992px) {
  .smartView-workout-main {
    max-width: 66.66667%;
    padding-right: 0.5rem;
  }
}
.smartView-workout-card {
  margin-bottom: 1rem;
}
.smartView-workout-title {
  margin-bottom: 0;
}
.smartView-workout-field {
  margin-bottom: 1rem;
}
.smartView-workout-field-label h6,
.smartView-workout-field-value h6 {
  margin-bottom: 0;
  color: var(--sportosphere-gray-600);
}
@media (min-width: 576px) {
  .smartView-workout-field {
    display: flex;
  }
  .smartView-workout-field-label {
    flex: 0 0 25%;
    max-width: 25%;
  }
  .smartView-workout-field-value {
    flex: 1 1 auto;
  }
}
.smartView-workout-set-title {
  margin-bottom: 0;
  font-size: 1rem;
  font-weight: 400;
  color: var(--sportosphere-gray-800);
}
/* Дополнено 2026-09-04 (smartview-native-styles) — базовые визуальные
   свойства .table/.table-borderless (theme.min.css) добавлены напрямую:
   width/vertical-align на самом классе, паддинг ячеек и обнуление нижней
   границы (table-borderless) — на дочерних ячейках/шапке. */
.smartView-workout-table {
  width: 100%;
  vertical-align: top;
  margin-bottom: 0;
  font-size: 0.8333333333rem;
  border-color: var(--sportosphere-gray-200);
  overflow: hidden;
}
.smartView-workout-table > :not(caption) > * > * {
  padding: 0.75rem 0.75rem;
  border-bottom-width: 0;
}
.smartView-workout-table > thead {
  vertical-align: bottom;
}
.smartView-workout-table thead th:first-child {
  color: var(--sportosphere-gray-900);
}

/* Общий "dashboard"-вид таблицы для 2-колоночных детальных представлений
   (workoutTemplateView/programTemplateView/assignedProgramView) — тот же
   набор итоговых значений, что и у smartView-workout-table выше, заведён
   отдельным именем: разные методы/разные вызывающие контексты (тот же
   принцип самодостаточности, что у smartCalendar-nav-icon-btn/-nav-btn). */
.smartView-detail-table {
  width: 100%;
  vertical-align: top;
  margin-bottom: 0;
  font-size: 0.8333333333rem;
  border-color: var(--sportosphere-gray-200);
  overflow: hidden;
}
.smartView-detail-table > :not(caption) > * > * {
  padding: 0.75rem 0.75rem;
  border-bottom-width: 0;
}
.smartView-detail-table > thead {
  vertical-align: bottom;
}

/* Обёртка разминки/заминки/сета в workoutTemplateView (card-body border
   rounded-1 mb-2, без окружающего .card/.card-header — card-body
   использовался только ради паддинга, самостоятельным компонентом; тот же
   случай, что и smartView-workout-set у workoutView, здесь маркерного
   класса ещё не было). */
.smartView-detail-set {
  padding: 1.25rem;
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color);
  border-radius: var(--sportosphere-border-radius-sm);
  margin-bottom: 0.5rem;
}

/* Замена .sticky-sidebar (theme.min.css) — тот же рецепт, что уже
   перенесён как .smartConstructor-sticky-sidebar (одна и та же
   позиция/z-index/отступ, отдельный класс на каждый инструмент —
   самодостаточность, не общая утилита). */
.smartView-sticky-sidebar {
  position: sticky;
  z-index: 1015;
  top: calc(var(--sportosphere-top-nav-height) + 1rem);
}

/* Замена .scrollbar (theme.min.css) — тот же рецепт, что уже перенесён как
   .smartCalendar-scrollbar. */
.smartView-scrollbar {
  overflow: auto;
  scrollbar-color: var(--sportosphere-scrollbar-bg) transparent;
  scrollbar-width: thin;
}
/* Дополнено 2026-09-04 (smartview-native-styles) — свойства card-body
   (padding) + border (theme.min.css), было "card-body border
   smartView-workout-set" (без окружающего .card/.card-header — здесь
   card-body использовался только ради паддинга, самостоятельным
   компонентом). */
.smartView-workout-set {
  padding: 1.25rem;
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color);
  margin-bottom: 0.5rem;
  border-radius: 0.2rem;
}

/* --- simpleCard --- */

.smartView-simpleCard-label {
  display: block;
  margin-bottom: 0.5rem;
  font-size: 0.875rem;
}
.smartView-simpleCard-row {
  display: flex;
  align-items: center;
  position: relative;
}
.smartView-simpleCard-info {
  flex: 1;
  margin-left: 1rem;
}
.smartView-simpleCard-name {
  margin-bottom: 0;
  font-weight: 600;
}
.smartView-simpleCard-name a {
  color: var(--sportosphere-gray-900);
}
/* Замена stretched-link (theme.min.css) — кликабельная зона на всю
   карточку, единственный потребитель, отдельный класс не заводился. */
.smartView-simpleCard-name a:after {
  position: absolute;
  content: "";
  inset: 0;
  z-index: 1;
}

/* Замена avatar/avatar-2xl/rounded-circle + динамически собираемого
   status-online (theme.min.css) — базовые свойства .avatar (position/
   display) + размер .avatar-2xl (2.5rem) + статус-точка только при
   модификаторе --online (единственный реально используемый статус —
   status-offline/-away/-do-not-disturb нигде не задействованы). */
.smartView-simpleCard-avatar {
  position: relative;
  display: inline-block;
  height: 2.5rem;
  width: 2.5rem;
}
.smartView-simpleCard-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}
.smartView-simpleCard-avatar--online:before {
  content: "";
  position: absolute;
  border-radius: 100%;
  z-index: 1;
  border: 2px solid var(--sportosphere-avatar-status-border-color);
  height: 16px;
  width: 16px;
  right: -3px;
  bottom: -3px;
  background-color: var(--sportosphere-success);
}
.smartView-simpleCard-text {
  margin-bottom: 0;
  font-size: 0.6944444444rem;
  color: var(--sportosphere-gray-500);
}

/* --- fullCard --- */

.smartView-fullCard-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
}
.smartView-fullCard-avatar,
.smartView-fullCard-body {
  flex: 0 0 100%;
  max-width: 100%;
  text-align: center;
}
@media (min-width: 768px) {
  .smartView-fullCard-avatar,
  .smartView-fullCard-body {
    flex: 0 0 50%;
    max-width: 50%;
  }
}
@media (min-width: 992px) {
  .smartView-fullCard-avatar,
  .smartView-fullCard-body {
    flex: 0 0 100%;
    max-width: 100%;
  }
}
.smartView-fullCard-avatar-img {
  width: 100%;
  height: 20rem;
  -o-object-fit: cover;
  object-fit: cover;
  border-radius: 0.375rem;
}

/* --- visitCard --- */

.smartView-visitCard-row {
  display: flex;
}
.smartView-visitCard-info {
  flex: 1;
  position: relative;
  padding-left: 1rem;
}
.smartView-visitCard-divider {
  margin-top: 1rem;
  margin-bottom: 1rem;
  border-bottom: 1px dashed var(--sportosphere-border-color);
}

/* --- contactCard --- */

.smartView-contactCard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
.smartView-contactCard-title {
  margin-bottom: 0;
}
/* Дополнено 2026-09-04 (smartview-native-styles) — меню "⋮" переносится
   ЧИСТО ВИЗУАЛЬНО, без добавления рабочей логики (design.md, Decision 8):
   на всех реально достижимых страницах Bootstrap JS отключён, меню и
   сегодня никогда не открывается ни у одного пользователя — сохраняем
   ровно это (виден только триггер + hover-эффект тени, само меню
   display:none всегда). */
.smartView-contactCard-menu-wrapper {
  position: relative;
}
.smartView-contactCard-menu-btn {
  color: var(--sportosphere-gray-600);
  background: transparent;
  border: 0;
  padding: 0.25rem 0.5rem;
  border-radius: var(--sportosphere-border-radius-sm);
  cursor: pointer;
  box-shadow: none;
}
.smartView-contactCard-menu-wrapper:hover .smartView-contactCard-menu-btn,
.smartView-contactCard-menu-wrapper:focus-within .smartView-contactCard-menu-btn {
  box-shadow: var(--sportosphere-btn-reveal-hover-shadow);
}
.smartView-contactCard-menu-icon {
  font-size: 0.6944444444rem;
}
.smartView-contactCard-menu {
  display: none;
  position: absolute;
  z-index: 1000;
  min-width: 10rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  background-color: var(--sportosphere-emphasis-bg);
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-radius: var(--sportosphere-border-radius);
  box-shadow: var(--sportosphere-box-shadow);
}
.smartView-contactCard-menu-item {
  display: block;
  width: 100%;
  padding: 0.25rem 1rem;
  clear: both;
  font-weight: 400;
  color: var(--sportosphere-gray-900);
  text-align: inherit;
  text-decoration: none;
  white-space: nowrap;
  background-color: transparent;
  border: 0;
}
.smartView-contactCard-menu-divider {
  height: 0;
  margin: 0.5rem 0;
  overflow: hidden;
  border-top: 1px solid var(--sportosphere-border-color);
}
.smartView-contactCard-ticket {
  position: relative;
  border: 1px solid var(--sportosphere-border-color);
  border-radius: 0.375rem;
  background-color: rgba(var(--sportosphere-white-rgb), 1);
  box-shadow: none;
}
[data-bs-theme=dark] .smartView-contactCard-ticket {
  background-color: #232e3c;
}
/* Замена bg-holder/bg-card (theme.min.css). */
.smartView-contactCard-ticket-bg {
  display: none;
  position: absolute;
  width: 100%;
  min-height: 100%;
  top: 0;
  left: 0;
  overflow: hidden;
  background-size: contain;
  background-position: right;
}
@media (min-width: 576px) {
  .smartView-contactCard-ticket-bg {
    display: block;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-ticket-bg {
    display: none;
  }
}
.smartView-contactCard-ticket-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  z-index: 1;
}
@media (min-width: 576px) {
  .smartView-contactCard-ticket-body {
    flex-direction: row;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-ticket-body {
    flex-direction: column;
  }
}
.smartView-contactCard-avatar-wrap {
  flex: 0 0 auto;
  text-align: center;
}
@media (min-width: 576px) {
  .smartView-contactCard-avatar-wrap {
    margin-right: 1.25rem;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-avatar-wrap {
    margin-right: 0;
  }
}
.smartView-contactCard-name-wrap {
  flex: 0 0 100%;
  max-width: 100%;
}
@media (min-width: 576px) {
  .smartView-contactCard-name-wrap {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
}
@media (min-width: 768px) {
  .smartView-contactCard-name-wrap {
    flex: 0 0 50%;
    max-width: 50%;
  }
}
@media (min-width: 992px) {
  .smartView-contactCard-name-wrap {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-name-wrap {
    flex: 0 0 100%;
    max-width: 100%;
  }
}
.smartView-contactCard-name {
  margin-top: 1rem;
  margin-bottom: 1rem;
  font-weight: 600;
  color: var(--sportosphere-gray-800);
  text-align: center;
}
@media (min-width: 576px) {
  .smartView-contactCard-name {
    margin-top: 0;
    text-align: left;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-name {
    margin-top: 1rem;
    text-align: center;
  }
}
.smartView-contactCard-fields {
  margin-top: 1rem;
  padding: 1.25rem;
  border: 1px solid var(--sportosphere-border-color);
  border-radius: 0.375rem;
  background-color: rgba(var(--sportosphere-white-rgb), 1);
}
[data-bs-theme=dark] .smartView-contactCard-fields {
  background-color: #232e3c;
}
/* Замена ticket-preview-avatar (theme.min.css). */
.smartView-contactCard-ticket-avatar {
  width: 5.625rem;
  height: 5.625rem;
  border-radius: 50%;
}
.smartView-contactCard-fields-inner {
  flex: 0 0 100%;
  max-width: 100%;
}
@media (min-width: 768px) {
  .smartView-contactCard-fields-inner {
    flex: 0 0 50%;
    max-width: 50%;
    padding-right: 1.8rem;
  }
}
@media (min-width: 1200px) {
  .smartView-contactCard-fields-inner {
    flex: 0 0 100%;
    max-width: 100%;
    padding-right: 0;
  }
}
.smartView-contactCard-field {
  margin-bottom: 1.8rem;
}
.smartView-contactCard-field-label {
  margin-bottom: 0.25rem;
}
.smartView-contactCard-field-value {
  font-size: 0.8333333333rem;
}

/* --- businessCard --- */

.smartView-businessCard-title {
  margin-bottom: 1rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.smartView-businessCard-field {
  display: flex;
  margin-bottom: 0;
}
.smartView-businessCard-field-label {
  flex: 0 0 41.66667%;
  max-width: 41.66667%;
  margin-bottom: 0.25rem;
  font-weight: 600;
}
.smartView-businessCard-field-value {
  flex: 1 1 auto;
}

/* --- clubCard --- */

/* Ряд карточки клубной карты (визитка + "Действия с картой") переиспользует
   общий сеточный контейнер `.widgets-layout` (sportosphereTheme.css, rules.md,
   п. 5) вместо собственного дублирующего flex-контейнера — `.smartView-
   clubCard` больше не задаёт display/flex-wrap/gap сам, остаётся только
   маркером для остальных `.smartView-clubCard-*` потомков (см. smartView.js,
   populateClubCardWithData()). `.smartView-clubCard-visual-col`/`-actions-col`
   — модификаторы пропорции прямо на `.widget` (тот же приём, что
   `.myday-widget--workouts`/`--tasks`), не обёрточные div-колонки. */
.smartView-clubCard-visual-col,
.smartView-clubCard-actions-col {
  flex: 0 0 100%;
  max-width: 100%;
}
@media (min-width: 992px) {
  /* Пропорция 1:2 через flex-grow (flex-basis 0%), а не фиксированные
     проценты — с фиксированными max-width: 33.333%/66.667% сумма ширин двух
     виджетов плюс `gap: 1rem` контейнера `.widgets-layout` всегда чуть
     превышала 100% его ширины, и оба виджета переносились на отдельные
     строки вместо ряда (проверено Playwright — при 33.333%/66.667% ряд
     складывался в 259px/518px друг НАД другом, не рядом, на 1400px viewport).
     `flex-grow`-пропорция (тот же приём, что `.myday-widget--workouts`
     `flex: 2 1 0%`/`--tasks` `flex: 1 1 0%`) корректно учитывает gap. */
  .smartView-clubCard-visual-col {
    flex: 1 1 0%;
    min-width: 0;
  }
  .smartView-clubCard-actions-col {
    flex: 2 1 0%;
    min-width: 0;
  }
}
.smartView-clubCard-card {
  height: 100%;
}
.smartView-clubCard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
.smartView-clubCard-header-title {
  margin-bottom: 0;
  white-space: nowrap;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}
@media (min-width: 1200px) {
  .smartView-clubCard-header-title {
    padding-top: 0;
    padding-bottom: 0;
  }
}
.smartView-clubCard-inner {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  /* height: 100%; */
}
@media (min-width: 576px) {
  .smartView-clubCard-inner {
    flex-direction: row;
  }
  .smartView-clubCard-visual-wrap,
  .smartView-clubCard-table-wrap {
    flex: 0 0 50%;
    max-width: 50%;
  }
}
@media (min-width: 992px) {
  .smartView-clubCard-inner {
    flex-direction: column;
  }
  .smartView-clubCard-visual-wrap,
  .smartView-clubCard-table-wrap {
    flex: 0 0 100%;
    max-width: 100%;
  }
}
/* Дополнено 2026-09-04 (smartview-native-styles) — визуальные свойства
   .card (резолвленные значения, тот же набор, что и у .widget) добавлены
   напрямую вместо второго класса на том же элементе.
   Дополнено 2026-09-06 (change member-club-card-modal) — `aspect-ratio`
   вместо content-driven высоты: пропорция стандартной пластиковой карты
   ISO/IEC 7810 ID-1 (85.6×53.98 мм, ≈1.5858:1), см. design.md решение 1.
   `.smartView-clubCard-visual-body` переведён на `position: absolute` —
   без этого при фиксированной через aspect-ratio высоте контента
   (номер/срок/владелец) не хватало бы места и он раздвигал бы блок сверх
   зафиксированной пропорции. */
.smartView-clubCard-visual {
  position: relative;
  aspect-ratio: 85.6 / 53.98;
  overflow: hidden;
  background-color: var(--sportosphere-emphasis-bg);
  border-radius: var(--sportosphere-border-radius-lg);
  box-shadow: var(--sportosphere-box-shadow);
}
/* Замена bg-holder/bg-card (theme.min.css) — декоративный фон визитки,
   раньше был без собственного класса вовсе. */
.smartView-clubCard-visual-bg {
  position: absolute;
  width: 100%;
  min-height: 100%;
  top: 0;
  left: 0;
  overflow: hidden;
  background-size: contain;
  background-position: right;
}
.smartView-clubCard-visual-body {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 1rem;
  padding-top: 3rem;
}
@media (min-width: 1400px) {
  .smartView-clubCard-visual-body {
    padding-top: 1.8rem;
  }
}

/* Плейсхолдер пустого состояния клубной карты (у клиента ещё нет карты)
   — 2026-09-06, change member-club-card-modal, уточнено по прямому
   сообщению пользователя: разметка вокруг плейсхолдера — та же, что у
   заполненной карты (`.widget.smartView-clubCard-card` >
   `.widget-header` с `cardTitle` > `.widget-body` >
   `.smartView-clubCard-inner`, см. `populateClubCardWithData()`), в этой
   структуре заменяется только сам `.smartView-clubCard-visual-wrap` —
   на `.smartView-clubCard-empty` (без `.smartView-clubCard-table-wrap`/
   `-actions-col`-соседей — им нечего показывать без данных экземпляра).
   Та же пропорция, что и заполненная карта выше (visual), тот же приём
   пунктирной рамки/без тени, что уже использует фрейм загрузки фото
   (`.filesDropzone .fdz-message`, см. выше в этом файле) — не тот же
   класс, т.к. этот плейсхолдер не связан с загрузкой файлов, только
   визуально похож. `width: 100%` — единственный потомок
   `.smartView-clubCard-inner` в этом случае (нет `-table-wrap`-соседа,
   с которым обычно делится 50%/100% по брейкпоинтам), должен сам
   занимать всю доступную ширину. */
.smartView-clubCard-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 85.6 / 53.98;
  padding: 1rem;
  border: 2px dashed var(--sportosphere-border-color);
  border-radius: var(--sportosphere-border-radius-lg);
  box-shadow: none;
  color: var(--sportosphere-gray-600);
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}
.smartView-clubCard-number {
  margin-bottom: 0.25rem;
  line-height: 1;
  color: var(--sportosphere-primary);
  font-family: var(--sportosphere-font-base);
}
.smartView-clubCard-expiry {
  margin-bottom: 1rem;
  font-size: 0.6944444444rem;
  font-weight: 600;
  color: rgba(var(--sportosphere-facebook-rgb), 1);
}
.smartView-clubCard-owner {
  margin-bottom: 0;
  color: rgba(var(--sportosphere-facebook-rgb), 1);
}
/* Замена table/table-borderless/font-sans-serif (theme.min.css). */
.smartView-clubCard-table {
  width: 100%;
  vertical-align: top;
  margin-bottom: 0.5rem;
  font-size: 0.8333333333rem;
  font-weight: 500;
  font-family: var(--sportosphere-font-sans-serif);
}
.smartView-clubCard-table > :not(caption) > * > * {
  border-bottom-width: 0;
}
.smartView-clubCard-table-label,
.smartView-clubCard-table-value {
  padding: 0.25rem;
}
.smartView-clubCard-table-label {
  width: 35%;
}
.smartView-clubCard-table-value {
  color: var(--sportosphere-gray-600);
}
.smartView-clubCard-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 1rem;
}
/* ≥992px — то же ширина, при которой у `.smartView-clubCard-actions-col`
   (выше) появляется место для полноразмерного столбца (66.67%): кнопки
   действий выстраиваются по одной в строку вместо компактного ряда иконок,
   рядом с иконкой становится виден текст описания действия
   (`.smartView-clubCard-action-text`, ниже). На узких экранах поведение не
   меняется — тот же ряд иконок без текста, что и раньше; описание при этом
   доступно как всплывающая подсказка (data-smart-tooltip*, smartTooltips.js
   — см. renderActionButtons() в smartView.js). */
@media (min-width: 992px) {
  .smartView-clubCard-actions {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0;
  }
}
/* Обёртка кнопки + текста описания (renderActionButtons(), smartView.js) —
   текст выводится РЯДОМ с кнопкой, а не внутри неё. На узком экране —
   просто держит кнопку (текст display:none, ширины не добавляет), ряды
   компактно переносятся как раньше. На ≥992px строки становятся
   полноразмерными и разделяются пунктирной линией (кроме последней). */
.smartView-clubCard-action {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
@media (min-width: 992px) {
  .smartView-clubCard-action {
    width: 100%;
    padding: 0.75rem 0;
  }
  .smartView-clubCard-action:not(:last-child) {
    border-bottom: 1px dashed var(--sportosphere-border-color);
  }
}
.smartView-clubCard-action-text {
  display: none;
}
@media (min-width: 992px) {
  .smartView-clubCard-action-text {
    display: inline;
  }
}

/* Стили для карточек задач (widgetType: 'taskCard', tasks-widgets-compliance)
   — значения перенесены дословно из прежних .myday-widget__* и
   .tasks-page-* (sportosphereTheme.css, разделы «Мой день»/«Все задачи») —
   единый
   библиотечный компонент вместо двух дублирующих друг друга наборов классов
   на разных страницах (docs/tasks-spec.md, было в разделе 7 как известное
   ограничение). Заголовок виджета (`.myday-widget__title`/`.tasks-page-title`)
   и пропорции виджета на странице (`.myday-widget--tasks`/`.tasks-page-widget`)
   остаются в sportosphereTheme.css как модификаторы `.widget`, специфичные
   для конкретной страницы (rules.md, п. 5) — сюда не переносились.

   Кнопки действий карточки (статус/редактировать/удалить) переведены на
   единый компонент `.button`/`.button--*` (см. раздел «Универсальный
   компонент — кнопка» в начале файла) — 2026-08-26. Прежний собственный
   класс `.smartView-taskCard-action` (сплошная пилюля, всегда primary-синяя,
   без различия по типу действия) убран из разметки (`smartView.js`,
   renderStatusActions()/renderEditDeleteActions()) и из этого файла — вместо
   него у каждой кнопки свой цветовой вариант по смыслу действия (по
   аналогии с уже принятой в проекте конвенцией — ср. smartConstructor.js:
   danger = удаление, success = позитивное завершение, default = нейтральное
   действие, primary = основное действие-продвижение):
   - "Взять в работу" (data-status="in_progress") — button--primary;
   - "Отметить выполненной" (data-status="done") — button--success;
   - "Редактировать" (editToggle) — button--default;
   - "Удалить" (delete) — button--danger.
   Классы-«крючки» для обработчиков (`.smartView-taskCard-statusAction`/
   `-editToggle`/`-delete`, taskListWidget.js) и data-атрибуты не менялись —
   меняется только визуал. */
.smartView-taskCard-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.smartView-taskCard-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--sportosphere-border-color);
}
.smartView-taskCard-item:last-child {
  border-bottom: none;
}
.smartView-taskCard-main {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.smartView-taskCard-title {
  font-weight: 600;
  color: var(--sportosphere-body-color);
}
.smartView-taskCard-meta {
  font-size: 0.85rem;
  color: var(--sportosphere-gray-600);
}
.smartView-taskCard-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.smartView-taskCard-status {
  font-size: 0.85rem;
  color: var(--sportosphere-gray-600);
}
.smartView-taskCard-status--done {
  color: var(--sportosphere-success);
}
.smartView-taskCard-editSlot {
  width: 100%;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--sportosphere-border-color);
}

/* Стили для карточек заметок тренера об ученике (widgetType:
   'coachNoteCard', coach-student-progress-notes) — по образцу taskCard
   выше (список без таблицы/пагинации, инлайн-редактирование через слот
   формы), переменные и структура классов те же самые, набор действий у
   карточки заметки проще (нет статуса) — только "Редактировать"
   (button--default) и "Удалить" (button--danger), см. смысловую
   конвенцию у taskCard. Классы-«крючки» для обработчиков
   (`.smartView-coachNoteCard-editToggle`/`-delete`,
   static/scripts/pages/coachNoteWidget.js) и data-атрибуты
   (`data-note-id`) — в smartView.js. */
.smartView-coachNoteCard-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.smartView-coachNoteCard-item {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--sportosphere-border-color);
}
.smartView-coachNoteCard-item:last-child {
  border-bottom: none;
}
.smartView-coachNoteCard-main {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.smartView-coachNoteCard-text {
  margin: 0;
  color: var(--sportosphere-body-color);
  white-space: pre-wrap;
}
.smartView-coachNoteCard-meta {
  font-size: 0.85rem;
  color: var(--sportosphere-gray-600);
}
.smartView-coachNoteCard-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.smartView-coachNoteCard-editSlot {
  width: 100%;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--sportosphere-border-color);
}

/* Стили для карточки антропометрии (widgetType: 'anthropometryCard',
   progress-anthropometry-widget) — текущие/целевые значения 9 параметров
   списком. Заголовок виджета с кнопками "Внести замер"/"Целевые
   показатели" (smartView-anthropometryCard-header/-headerActions) строит
   не сама карточка, а static/scripts/pages/anthropometryWidget.js —
   действия кнопок доменные (открыть/закрыть инлайн-форму), а не часть
   рендера populateAnthropometryCardWithData(). */
.smartView-anthropometryCard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.smartView-anthropometryCard-title {
  font-weight: 600;
}
.smartView-anthropometryCard-headerActions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.smartView-anthropometryCard-date {
  font-size: 0.85rem;
  color: var(--sportosphere-gray-600);
  margin-bottom: 0.5rem;
}
.smartView-anthropometryCard-date--empty {
  font-style: italic;
}
.smartView-anthropometryCard-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.smartView-anthropometryCard-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--sportosphere-border-color);
}
.smartView-anthropometryCard-item:last-child {
  border-bottom: none;
}
.smartView-anthropometryCard-label {
  flex: 1 1 auto;
  color: var(--sportosphere-body-color);
}
.smartView-anthropometryCard-current {
  flex: 0 0 auto;
  font-weight: 600;
  color: var(--sportosphere-body-color);
}
.smartView-anthropometryCard-target {
  flex: 0 0 auto;
  font-size: 0.85rem;
  color: var(--sportosphere-primary);
  min-width: 6rem;
  text-align: right;
}
.smartView-anthropometryCard-emptyMessage {
  margin: 0;
  color: var(--sportosphere-gray-600);
}

/* Стили для карточек поста ленты (widgetType: 'postCard',
   feed-architecture-migration) — самостоятельно стилизованный компонент
   (rules.md, п. 5): каждая карточка — свой блок с рамкой/скруглением,
   не .widget (та обёртка — для контентных блоков страницы вокруг
   готовых инструментов, не для повторяющихся элементов списка,
   которые строит сам smartView, см. уже принятый прецедент
   .smartView-taskCard-item/.smartView-coachNoteCard-item выше — они
   тоже не .widget). Кнопка лайка — единый компонент .button/.button--*
   (см. «Универсальный компонент — кнопка» в начале файла) с
   переключаемым визуальным состоянием через [data-liked="true"], по
   той же конвенции цвета действия, что и .smartView-taskCard-status
   выше (primary — активное/подтверждённое состояние). */
.smartView-postCard-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.smartView-postCard-item {
  border: 1px solid var(--sportosphere-border-color);
  border-radius: 0.5rem;
  background-color: var(--sportosphere-emphasis-bg);
  overflow: hidden;
}
.smartView-postCard-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem;
  border-bottom: 1px solid var(--sportosphere-border-color);
}
.smartView-postCard-avatar {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  overflow: hidden;
  background-color: var(--sportosphere-gray-200);
}
.smartView-postCard-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.smartView-postCard-authorInfo {
  display: flex;
  flex-direction: column;
}
.smartView-postCard-authorName {
  font-weight: 600;
  color: var(--sportosphere-body-color);
}
.smartView-postCard-date {
  font-size: 0.8rem;
  color: var(--sportosphere-gray-600);
}
.smartView-postCard-body {
  padding: 1rem;
}
.smartView-postCard-text {
  margin: 0 0 0.75rem;
  color: var(--sportosphere-body-color);
  white-space: pre-wrap;
}
.smartView-postCard-text:last-child {
  margin-bottom: 0;
}
.smartView-postCard-images {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.smartView-postCard-image {
  width: 8rem;
  height: 8rem;
  object-fit: cover;
  border-radius: 0.375rem;
  border: 1px solid var(--sportosphere-border-color);
}
.smartView-postCard-footer {
  padding: 1rem;
  border-top: 1px solid var(--sportosphere-border-color);
}
.smartView-postCard-stats {
  display: flex;
  gap: 1rem;
  margin-bottom: 0.75rem;
  font-size: 0.85rem;
  color: var(--sportosphere-gray-600);
}
.smartView-postCard-likeButton {
  margin-bottom: 0.75rem;
}
.smartView-postCard-likeButton[data-liked="true"] {
  color: var(--sportosphere-white);
  background-color: var(--sportosphere-primary);
  border-color: var(--sportosphere-primary);
}
.smartView-postCard-comments {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.smartView-postCard-comment {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  background-color: var(--sportosphere-gray-100);
  font-size: 0.9rem;
}
.smartView-postCard-commentAuthor {
  font-weight: 600;
  color: var(--sportosphere-body-color);
}
.smartView-postCard-commentText {
  color: var(--sportosphere-body-color);
}
.smartView-postCard-commentDate {
  flex: 0 0 100%;
  font-size: 0.75rem;
  color: var(--sportosphere-gray-600);
}
.smartView-postCard-commentForm {
  display: flex;
  gap: 0.5rem;
}
.smartView-postCard-commentInput {
  flex: 1 1 auto;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--sportosphere-border-color);
  border-radius: 999px;
  color: var(--sportosphere-body-color);
  background-color: var(--sportosphere-emphasis-bg);
}
.smartView-postCard-commentInput:focus {
  outline: none;
  border-color: var(--sportosphere-primary);
}

/*
* Стили для модуля smartForm.js
*/

.smartForm-block-title {
  margin-bottom: 0;
  font-weight: 600;
}
.smartForm-actions {
  margin-top: 1rem;
}
/* Модификатор — только при заданном config.secondaryButton (change
   feed-post-photo-button, docs/smartForm-spec.md, раздел 2/5) — без этой
   конфигурации класс не добавляется, .smartForm-actions для всех
   остальных существующих мест использования smartForm не меняется. */
.smartForm-actions--split {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.smartForm-cancel-link {
  margin-left: 0.5rem;
}
/* .formLabel — маркерный класс без собственных свойств: визуальное
   оформление подписей полей (margin-bottom/font-size/font-weight/
   letter-spacing/display) уже даёт глобальный тег-селектор `label` в самом
   начале этого файла — добавлять те же значения повторно под классом
   избыточно (проверено getComputedStyle, см. design.md соответствующего
   change, Decision 3). Класс сохраняется ради единообразия именования с
   уже принятым в smartCalendar.js. */
.formText {
  margin-top: 0.25rem;
  font-size: 75%;
  color: #9da9bb;
}
.smartForm-label-block {
  display: block;
}
.smartForm-search-card {
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color);
}
.smartForm-search-card > .smartForm-block-title {
  margin-bottom: 0.75rem;
}
.smartForm-search-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.5rem;
}
.smartForm-search-field {
  flex: 0 0 100%;
  max-width: 100%;
}
.smartForm-search-result {
  flex: 0 0 100%;
  max-width: 100%;
}
@media (min-width: 576px) {
  .smartForm-search-field {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .smartForm-search-result {
    flex: 0 0 50%;
    max-width: 50%;
  }
}
@media (min-width: 768px) {
  .smartForm-search-field {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
  .smartForm-search-result {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
  }
}
.smartForm-search-preview {
  display: flex;
  align-items: center;
  padding: 0.5rem;
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-border-color);
  border-radius: var(--sportosphere-border-radius);
}
.smartForm-search-avatar {
  margin-right: 1rem;
  border-radius: 50%;
}
.smartForm-search-input-group {
  display: flex;
  align-items: stretch;
  width: 100%;
}
.smartForm-search-input-group > .formGroup {
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.smartForm-search-input-group > .button {
  margin-left: calc(var(--sportosphere-border-width) * -1);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
/* search.inputType: 'phone' — .smartForm-phone-group (флаг + код страны +
   поле) переиспользуется БЕЗ модификаций, тем же классом, что у
   fields[].type: 'phone' (см. ниже) — выглядит идентично полю формы входа.
   Кнопка поиска рядом — обычным отступом, не сшита с полем в одну рамку,
   как для .smartForm-search-input-group выше (там сшивается сам input). */
.smartForm-search-phone-row {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  width: 100%;
}
.smartForm-search-phone-row > .smartForm-phone-group {
  flex: 1 1 auto;
  min-width: 0;
}
.smartForm-search-preview-name {
  font-weight: 600;
}
.smartForm-search-hint {
  color: var(--sportosphere-gray-600);
}
.smartForm-phone-group {
  display: flex;
  align-items: stretch;
}
.smartForm-phone-prefix {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4375rem 0.75rem;
  border: 1px solid var(--sportosphere-gray-300);
  border-right: none;
  border-radius: var(--sportosphere-border-radius) 0 0 var(--sportosphere-border-radius);
  background-color: var(--sportosphere-gray-100);
  color: var(--sportosphere-gray-600);
  white-space: nowrap;
}
.smartForm-phone-input {
  border-radius: 0 var(--sportosphere-border-radius) var(--sportosphere-border-radius) 0;
}

/*
* Стили для модуля smartConstructor.js
*
* Раздел переработан 2026-08-31 (см. sportosphere/docs/known-issues.md,
* CSS-4/CSS-5) — вся генерируемая JS-разметка модуля переведена с классов
* Bootstrap/theme.min.css (.card/.card-header/.card-body/.card-footer/
* .bg-body-tertiary, .btn/.btn-sportosphere-*, .modal*, .btn-close/.btn-circle,
* .input-group*, .form-control/.form-select/.form-check*, .form-label,
* .sticky-sidebar и утилит отступов .mb-*, .ms-*, .me-*, .mt-*) на собственные
* классы — переиспользованы уже существующие компоненты библиотеки
* (.button/.button--*, .formGroup/.formGroup-sm, .formSelect,
* .formCheck/.formCheck-input/.formCheck-label, .isModal/.isModal-* — модалка
* пикера открывается/закрывается через openModal()/closeModal() из
* smartModal.js) и заведены новые там, где готового аналога не было (карточки,
* input-group, крестик закрытия модалки, sticky-сайдбар) — точные вычисленные
* значения перенесены из theme.min.css (rules.md, п. 7), не переизобретены
* "на глаз". Для двух select'ов типа сета/времени отдыха и раньше (в
* Bootstrap-версии) использовался класс form-control, а не form-select —
* сохранено как есть (formGroup, не formSelect) ради идентичного визуала.
*
* Обновлено 2026-09-01 — собственные .smartConstructor-card/-card-header/
* -card-body[--tertiary]/-card-footer/-sidebar-card (заведены правкой выше,
* 2026-08-31) убраны: блоки «Общие сведения»/«Сеты»-«Мини-циклы»/«Информация»
* переведены на единый компонент проекта .widget/.widget-header/.widget-body
* (sportosphereTheme.css, rules.md, п. 5) — см. smartConstructor-spec.md,
* раздел 8а.
*/

/* ---- Карточки блоков «Общие сведения»/«Сеты»-«Мини-циклы»/«Информация» —
   с 2026-09-01 не собственный класс, а единый компонент проекта .widget/
   .widget-header/.widget-body (sportosphereTheme.css, rules.md, п. 5):
   BaseTemplateConstructor.buildWidgetHTML() строит эту разметку один раз
   для обоих классов-конструкторов вместо дублирования (эти три блока были
   идентичны в WorkoutTemplateConstructor и ProgramTemplateConstructor) и
   вместо более раннего собственного .smartConstructor-card/-card-header/
   -card-body/-card-footer (2026-08-31, CSS-5) — тот дублировал стили уже
   существующего .widget, установленного тем же rules.md ещё 2026-08-24, до
   этой правки. Заголовок виджета — по образцу уже принятого в проекте
   паттерна (.sets-page-title/.myday-widget__title и т. п.) — свой класс
   заголовка, не голый h5. */
.smartConstructor-widget-title {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--sportosphere-heading-color);
}
/* Отступ между двумя виджетами, стоящими друг под другом в form-col
   («Общие сведения» + «Сеты»/«Мини-циклы») — сам .widget его не задаёт,
   раскладкой (не gap .widgets-layout) управляет form-col. */
.smartConstructor-form-col .widget + .widget {
  margin-top: 1rem;
}
.smartConstructor-sub-block {
  flex: 1 1 auto;
  padding: 1.25rem;
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-radius: 0.2rem;
  margin-bottom: 0.5rem;
}

/* ---- Модальное окно пикера записи — на классах .isModal/.isModal-*
   (см. выше в этом файле) и openModal()/closeModal() из smartModal.js;
   с 2026-09-02 — настоящие .isModal-header/.isModal-title (тот же
   канонический контракт .isModal, что уже верно используют другие
   потребители, например smartCalendar.js), не самодельная замена внутри
   .isModal-body. Собственная разметка — только кнопка-крестик
   (готового 1:1 аналога SVG-крестика .btn-close/.btn-circle в
   библиотеке нет). На корневой .isModal намеренно не навешивается класс
   .fade — .isModal переключается классом .active, а не .show, и оставшееся
   правило Bootstrap .fade:not(.show){opacity:0} в этом случае держит
   модалку невидимой (см. sportosphereTools.css, раздел isModal). ---- */
.smartConstructor-modal-dialog {
  margin-bottom: 1rem;
}
.smartConstructor-modal-close-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: #edf2f9;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 1em auto;
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  opacity: 0.5;
  cursor: pointer;
  transition: all 0.2s ease-in-out;
}
.smartConstructor-modal-close-btn:hover,
.smartConstructor-modal-close-btn:focus {
  opacity: 1;
  background-color: #d8e2ef;
}

.smartConstructor-main {
  display: flex;
  flex-wrap: wrap;
}
.smartConstructor-form-col {
  flex: 0 0 100%;
  max-width: 100%;
}
.smartConstructor-sidebar-col {
  flex: 0 0 100%;
  max-width: 100%;
  margin-bottom: 1rem;
}
@media (min-width: 992px) {
  .smartConstructor-form-col {
    flex: 0 0 66.66667%;
    max-width: 66.66667%;
    padding-right: 0.5rem;
  }
  .smartConstructor-sidebar-col {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
    padding-left: 0.5rem;
  }
}
/* Замена .sticky-sidebar (theme.min.css) — та же позиция/z-index/отступ */
.smartConstructor-sticky-sidebar {
  position: sticky;
  z-index: 1015;
  top: calc(var(--sportosphere-top-nav-height) + 1rem);
}
.smartConstructor-input-group {
  margin-bottom: 1rem;
}

/* ---- Input-group — замена .input-group/.input-group-sm/.input-group-text;
   готового аналога в библиотеке нет. Во всех местах smartConstructor.js
   использовался только маленький размер — отдельного модификатора "sm" не
   заводим, .smartConstructor-input-group всегда маленького размера.
   Слияние границ соседних элементов и обнуление радиуса воспроизводит
   поведение оригинальных бутстраповских правил
   .input-group>:not(:first-child)/:not(:last-child) (theme.min.css). ---- */
.smartConstructor-input-group {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}
.smartConstructor-input-group > * {
  position: relative;
}
.smartConstructor-input-group > *:not(:first-child) {
  margin-left: calc(var(--sportosphere-border-width) * -1);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
.smartConstructor-input-group > *:not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.smartConstructor-input-group .formGroup,
.smartConstructor-input-group .formSelect {
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
}
/* Вложенный .smartConstructor-input-group (поле выбора рядом с кнопками
   в общем ряду, buildWorkoutHTML()) иначе наследует width:100% от общего
   правила выше и всегда занимает всю строку целиком — соседний
   .smartConstructor-block-actions при этом переносится на новую строку
   при любой ширине экрана, не только на узких. */
.smartConstructor-input-group > .smartConstructor-input-group {
  width: auto;
  flex: 1 1 auto;
  min-width: 0;
}
/* .smartConstructor-block-actions рядом с полем ввода (buildWorkoutHTML())
   — самостоятельный кластер кнопок, не часть визуально слитого
   input-group, поэтому вместо отрицательного margin-left/обнуления
   радиуса из общего правила выше (рассчитанного на слияние поле+кнопка
   в одну "таблетку") — обычный отступ, тот же, что между кнопками
   внутри самого .smartConstructor-block-actions. */
.smartConstructor-input-group > .smartConstructor-block-actions {
  margin-left: 0.5rem;
}
.smartConstructor-input-group .formGroup:focus,
.smartConstructor-input-group .formSelect:focus {
  z-index: 5;
}
.smartConstructor-input-group .button {
  position: relative;
  z-index: 2;
}
.smartConstructor-input-group-text {
  display: flex;
  align-items: center;
  padding: 0.1875rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--sportosphere-gray-900);
  text-align: center;
  white-space: nowrap;
  background-color: var(--sportosphere-gray-200);
  border: var(--sportosphere-border-width) solid var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius-sm);
}

.smartConstructor-row {
  display: flex;
  flex-wrap: wrap;
  column-gap: 0.5rem;
}
.smartConstructor-col-12 {
  flex: 0 0 100%;
  max-width: 100%;
  margin-bottom: 1rem;
}
.smartConstructor-col-4 {
  flex: 0 0 100%;
  max-width: 100%;
  margin-bottom: 1rem;
}
@media (min-width: 576px) {
  .smartConstructor-col-4 {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
}
.smartConstructor-subtitle {
  font-size: 1rem;
}
.smartConstructor-check-label {
  margin-bottom: 0;
}
/* Замена ms-2 между кнопками "переместить/удалить" сета и мини-цикла —
   тот же интервал (0.5rem) между всеми кнопками, кроме первой */
.smartConstructor-block-actions > .button:not(:first-child) {
  margin-left: 0.5rem;
}
/* Замена mt-2 на кнопках "Добавить сет"/"Добавить мини-цикл" */
.smartConstructor-add-set-btn {
  margin-top: 0.5rem;
}
/* Замена me-1/mb-1 на кнопках "Добавить упражнение"/"Добавить тренировку" */
.smartConstructor-add-item-btn {
  margin-right: 0.25rem;
  margin-bottom: 0.25rem;
}
/* Замена me-1 у иконок перед текстом кнопки/заголовка */
.smartConstructor-inline-icon {
  margin-right: 0.25rem;
}
.smartConstructor-exercises-hint {
  font-size: 0.8rem;
  color: var(--sportosphere-gray-600);
  margin-bottom: 0.5rem;
}
.smartConstructor-exercises-hint:empty {
  display: none;
}
.smartConstructor-settings-info {
  background-color: var(--sportosphere-info-bg-subtle);
  border: 1px solid var(--sportosphere-info-border-subtle);
  border-radius: 0.2rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.75rem;
}
.smartConstructor-settings-info-title {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--sportosphere-info-text-emphasis);
  margin-bottom: 0.25rem;
}
.smartConstructor-settings-info-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.8rem;
  color: var(--sportosphere-info-text-emphasis);
}
.smartConstructor-settings-info-label {
  font-weight: 600;
}

/*
* Стили для модуля smartTable.js
*/

.smartTable-cell {
  vertical-align: middle !important;
}
.smartTable-cell-row {
  display: flex;
  align-items: center;
  position: relative;
}
.smartTable-cell-info {
  flex: 1;
  margin-left: 1rem;
}
.smartTable-cell-title {
  margin-bottom: 0;
  font-weight: 600;
}
.smartTable-cell-title-tight {
  margin-bottom: 0.25rem;
}
.smartTable-subtitle {
  margin-bottom: 0;
  font-weight: 600;
  color: var(--sportosphere-gray-500);
}
.smartTable-btn-label {
  display: none;
  margin-right: 0.25rem;
}
@media (min-width: 576px) {
  .smartTable-btn-label {
    display: inline-block;
  }
}
@media (min-width: 1200px) {
  .smartTable-btn-label {
    display: none;
  }
}
@media (min-width: 1400px) {
  .smartTable-btn-label {
    display: inline-block;
  }
}
.smartTable-modal-close {
  position: absolute;
  top: 0;
  right: 0;
  margin-top: 0.5rem;
  margin-right: 0.5rem;
  z-index: 1;
}
.smartTable-control-divider {
  display: none;
  width: 1px;
  height: 29px;
  margin-left: 1rem;
  margin-right: 1rem;
  background-color: var(--sportosphere-gray-300);
}
@media (min-width: 992px) {
  .smartTable-control-divider {
    display: block;
  }
}
@media (min-width: 1200px) {
  .smartTable-control-divider {
    display: none;
  }
}
.smartTable-cell-checkbox {
  width: 1%;
  white-space: nowrap;
  text-align: center;
}
.smartTable-checkbox {
  cursor: pointer;
}
.smartTable-responsive {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* Дополнено 2026-08-30 (known-issues.md, ST-11): раньше пагинация
   (buildPaginationHTML()) использовала литеральные bootstrap-классы
   `.pagination`/`.page-item`/`.page`. В theme.min.css их промежутки между
   кнопками и подсветка активной страницы завязаны на селекторы
   `[data-list] .page`/`.table-list .page` (margin) и
   `[data-list] li.active .page`/`.table-list li.active .page`
   (background/color) — оба требуют, чтобы предок нёс атрибут `data-list`
   или класс `.table-list`, которого у smartTable никогда не было: правила
   существовали в теме, но физически не могли примениться. Ниже —
   собственные классы с тем же эффектом, не завязанные на конкретного
   предка. Кнопки внутри (`.btn.btn-sm.btn-sportosphere-default`) — не
   bootstrap, общая система кнопок проекта, не тронута. */
.smartTable-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding-left: 0;
  margin: 0;
  list-style: none;
}
.smartTable-pagination-item.active .btn {
  background-color: rgba(0, 0, 0, 0);
  color: var(--sportosphere-primary) !important;
}

/* Дополнено 2026-09-02 (known-issues.md, CSS-5) — литеральные
   bootstrap-классы .card/.card-header/.card-body/.card-footer/
   .bg-body-tertiary (initializeViewStructure()) заменены на единый
   компонент проекта .widget/.widget-header/.widget-body/.widget-footer
   (sportosphereTheme.css, rules.md, п. 5) — та же обёртка (карточка
   таблицы, модалка с вложенной таблицей, вложенная карточка
   offcanvas-фильтра). .smartTable-tertiary-bg — та же замена
   bg-body-tertiary, что уже сделана для .tab-pane-body (TABS-3/CSS-5):
   та же глобальная переменная --sportosphere-tertiary-bg-rgb, тот же
   приём (корректно работает в обеих темах, в отличие от локальной
   --sportosphere-card-* на исходном .card). Остальные bootstrap-классы,
   ранее стоявшие рядом (border-bottom/border-200/px-0/p-0 и т. п.), —
   не тронуты, вне объёма этой правки (см. known-issues.md, CSS-5). */
.smartTable-tertiary-bg {
  background-color: rgba(var(--sportosphere-tertiary-bg-rgb), 1);
}

/*
* Стили для модуля smartCalendar.js
*
* Дополнено 2026-09-04 (openspec/changes/smartcalendar-native-styles,
* known-issues.md CSS-5) — вся генерируемая JS-разметка переведена с
* классов Bootstrap/theme.min.css на собственные семантические классы.
* Крестик закрытия .isModal, кнопки навигации, tertiary-bg, scrollbar,
* меню переключателя вида — новые классы ниже (готовых 1:1 аналогов не
* было — либо были для другого визуального паттерна, см. design.md того
* change'а). Кнопки btn-primary/btn-secondary/btn-outline-danger в 5
* модалках намеренно унифицированы на уже существующие .button--primary/
* .button--default/.button--danger (см. docs/known-issues.md, CSS-5 →
* «Решение (smartCalendar.js)» — единственное намеренное визуальное
* изменение, явно одобренное пользователем).
*/

/* Крестик закрытия .isModal — не .btn-circle-паттерн (тот уже сделан для
   другой модалки, smartConstructor-modal-close-btn), а точный перенос
   базового .btn-close (обычный, встроенный в строку .isModal-header,
   позиционируется её собственным правилом .isModal-header .btn-close,
   которое не трогаем — им пользуется нетронутый .btn-close в
   smartTable.js). Самодостаточен: margin/padding продублированы, а не
   унаследованы от чужого селектора. */
.smartCalendar-modal-close-btn {
  box-sizing: content-box;
  width: 1em;
  height: 1em;
  padding: calc(var(--isModal-header-padding-y) * 0.5) calc(var(--isModal-header-padding-x) * 0.5);
  margin-top: calc(-0.5 * var(--isModal-header-padding-y));
  margin-right: calc(-0.5 * var(--isModal-header-padding-x));
  margin-bottom: calc(-0.5 * var(--isModal-header-padding-y));
  margin-left: auto;
  color: #000;
  background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center / 1em auto no-repeat;
  border: 0;
  border-radius: 0.25rem;
  opacity: 0.5;
  cursor: pointer;
}
.smartCalendar-modal-close-btn:hover,
.smartCalendar-modal-close-btn:focus {
  opacity: 0.75;
}
.smartCalendar-modal-close-btn:focus {
  outline: 0;
  box-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}

/* Кнопки навигации prev/сегодня/next — два разных сегодняшних визуальных
   паттерна (fullCalendar/simpleCalendar), сохранены каждый как есть, без
   унификации между собой (design.md, Decision 3). */
.smartCalendar-nav-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.875rem;
  height: 1.875rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: var(--sportosphere-gray-700);
  background-color: var(--sportosphere-quaternary-bg);
  cursor: pointer;
  transition: background-color 0.2s ease-in-out;
}
.smartCalendar-nav-icon-btn:hover,
.smartCalendar-nav-icon-btn:focus {
  background-color: var(--sportosphere-gray-200);
}
.smartCalendar-nav-btn {
  padding: 0.1875rem 0.75rem;
  font-size: 0.875rem;
  color: var(--sportosphere-gray-700);
  background-color: transparent;
  border: var(--sportosphere-border-width) solid var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius);
  box-shadow: var(--sportosphere-box-shadow-sm);
  cursor: pointer;
}
.smartCalendar-nav-btn--today {
  color: var(--sportosphere-gray-600);
  padding-left: 1.8rem;
  padding-right: 1.8rem;
}

/* bg-body-tertiary — тот же приём, что .smartTable-tertiary-bg (TABS-3/
   CSS-5): глобальная --sportosphere-tertiary-bg-rgb, работает в обеих
   темах. */
.smartCalendar-tertiary-bg {
  background-color: rgba(var(--sportosphere-tertiary-bg-rgb), 1);
}

/* .scrollbar (theme.min.css) — overflow:auto + кастомная полоса прокрутки
   через уже существующий --sportosphere-scrollbar-bg. */
.smartCalendar-scrollbar {
  overflow: auto;
  scrollbar-color: var(--sportosphere-scrollbar-bg) transparent;
  scrollbar-width: thin;
}

/* Меню переключателя вида (Месяц/Неделя/День) — открытие/закрытие целиком
   свой JS (bindViewSwitchToggle(), .classList.toggle('show')), не
   Bootstrap JS/data-bs-toggle; data-bs-popper выставляется вручную только
   чтобы включить позиционирование ниже. Точные значения — theme.min.css
   .dropdown-menu/.dropdown-item, через уже объявленные на :root
   var(--sportosphere-dropdown-*)/var(--sportosphere-gray-*) токены. */
.smartCalendar-view-menu {
  position: absolute;
  z-index: 1000;
  display: none;
  min-width: 10rem;
  padding: 1rem 0;
  margin: 0;
  font-size: var(--sportosphere-dropdown-font-size);
  color: var(--sportosphere-dropdown-color);
  text-align: left;
  list-style: none;
  background-color: var(--sportosphere-dropdown-bg-global);
  background-clip: padding-box;
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-radius: var(--sportosphere-border-radius);
  box-shadow: var(--sportosphere-box-shadow);
}
.smartCalendar-view-menu[data-bs-popper] {
  top: 100%;
  margin-top: 0.125rem;
}
.smartCalendar-view-menu--end {
  left: auto;
  right: -0.5625rem;
}
.smartCalendar-view-menu.show {
  display: block;
}
.smartCalendar-view-item {
  display: block;
  width: 100%;
  padding: 0.25rem 1rem;
  clear: both;
  font-weight: 400;
  color: var(--sportosphere-gray-900);
  text-align: inherit;
  white-space: nowrap;
  background-color: transparent;
  border: 0;
}
.smartCalendar-view-item:hover,
.smartCalendar-view-item:focus {
  color: var(--sportosphere-dropdown-link-hover-color-global);
  text-decoration: none;
  background-color: var(--sportosphere-dropdown-link-hover-bg-global);
}
.smartCalendar-view-item.active {
  color: #fff;
  background-color: #2c7be5;
}
.smartCalendar-view-toggle::after,
.smartCalendar-view-toggle::before {
  display: none;
}
/* Обёртка триггер+меню — .dropdown (theme.min.css) даёт position:relative
   через групповой селектор `.dropup,.dropend,.dropdown,...{position:
   relative}`, необходимый контекст позиционирования для
   .smartCalendar-view-menu (position: absolute). Без него меню
   позиционировалось бы от следующего действительно спозиционированного
   предка выше по DOM, а не от самого триггера. */
.smartCalendar-view-menu-wrapper {
  position: relative;
}

/* Вкладки "Рабочие"/"Личные" в модалке добавления записи (buildModal()) —
   обнаружено при построчной проверке разметки после первого прохода
   аудита (nav/nav-tabs/nav-item/nav-link пропущены исходным grep по
   классам). Переключение вкладок — свой JS (setupTabButtons(),
   classList.toggle('active') на кнопках, инлайновый style.display на
   панелях — панели не используют bootstrap-классы вовсе, не трогаются).
   Точные значения — getComputedStyle на живой странице (this theme
   переопределяет базовый Bootstrap .nav-tabs .nav-link своим, более
   специфичным правилом — считать итоговый эффект надёжнее, чем
   вычислять каскад вручную). */
.smartCalendar-event-tabs {
  display: flex;
  list-style: none;
  padding-left: 0;
  margin-bottom: 0;
  border-bottom: var(--sportosphere-border-width) solid var(--sportosphere-gray-300);
}
.smartCalendar-event-tab-btn {
  display: block;
  padding: 0.5rem 1rem;
  margin-bottom: -1px;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--sportosphere-gray-800);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.smartCalendar-event-tab-btn.active {
  color: var(--sportosphere-primary);
  border-bottom-color: var(--sportosphere-primary);
}

/* Крестик закрытия попапа "все события дня" (showDayEventsPopover(),
   createElement + classList.add — не шаблонная строка, найден отдельно от
   основного аудита class="..."). Другой контекст, чем .isModal — обычный
   .btn-close без header-специфичных отрицательных margin (те были нужны
   только чтобы компенсировать padding .isModal-header, здесь и без того
   .day-events-popover-header уже justify-content:space-between и сам
   прижимает кнопку к правому краю). */
.smartCalendar-popover-close-btn {
  box-sizing: content-box;
  width: 1em;
  height: 1em;
  padding: 0.25em;
  color: #000;
  background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center / 1em auto no-repeat;
  border: 0;
  border-radius: 0.25rem;
  opacity: 0.5;
  cursor: pointer;
}
.smartCalendar-popover-close-btn:hover,
.smartCalendar-popover-close-btn:focus {
  opacity: 0.75;
}
.smartCalendar-popover-close-btn:focus {
  outline: 0;
  box-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}

.smartCalendar-input-narrow {
  width: 100px;
}
.smartCalendar-schedule-col {
  flex: 0 0 100%;
  max-width: 100%;
  padding-right: 1rem;
  padding-left: 1rem;
}
@media (min-width: 768px) {
  .smartCalendar-schedule-col {
    flex: 0 0 33.33333%;
    max-width: 33.33333%;
  }
}
.smartCalendar-schedule-col-full {
  flex: 0 0 100%;
  max-width: 100%;
  padding-right: 1rem;
  padding-left: 1rem;
}
/* Вариант .smartCalendar-schedule-col без колонки длительности (2 колонки
   на строку дня вместо 3) — используется формой «Добавить тренировку»
   place_scheduler.html, где длительность одна общая на весь план, а не
   у каждого дня отдельно */
.smartCalendar-schedule-col-half {
  flex: 0 0 100%;
  max-width: 100%;
  padding-right: 1rem;
  padding-left: 1rem;
}
@media (min-width: 768px) {
  .smartCalendar-schedule-col-half {
    flex: 0 0 50%;
    max-width: 50%;
  }
}
.smartCalendar-field {
  margin-bottom: 1rem;
}
.smartCalendar-edit-modal-footer {
  display: flex;
  justify-content: space-between;
  width: 100%;
}

/*
* Цветовая схема записей учеников в общем календаре тренера (fullCalendar)
* Каждому ученику по его ID стабильно назначается один из 4 цветов (см.
* getStudentColorClass() в smartCalendar.js: studentId % 4; 5-й ученик
* повторяет цвет 1-го и т.д. -- текстовая подпись (имя ученика) всегда
* показывается рядом, поэтому повтор цвета не теряет идентичность).
*
* ПОЧЕМУ ИМЕННО 4, А НЕ 8: проверено собственным валидатором палитры
* (OKLCH-конверсия + симуляция дальтонизма по модели
* Machado-Oliveira-Fernandes 2009, severity=1.0, протанопия и дейтеранопия +
* WCAG-контраст; без внешних библиотек). На 8 цветах даже у оттенков текста
* (не то что у светлого фона) худшая пара не проходит порог обычного
* (не дальтонического) зрения -- ΔE 7-9 при пороге >=15. На 4 цветах
* текстовый слой проходит оба порога с запасом: худшая пара
* ΔE(дальтонизм)=9.2 (>=8), ΔE(обычное зрение)=15.0 (>=15), в обоих
* режимах темы. Светлый фон (bg) в эту проверку целенаправленно НЕ входит:
* он слишком светлый/малонасыщенный, чтобы быть надёжным носителем
* идентичности при любом количестве цветов (доказано тем же валидатором) --
* поэтому идентичность несёт ТЕКСТ (--...-text, всегда контраст >= 4.5:1 к
* своему фону), а фон -- лишь вспомогательный, приятный глазу акцент.
* Первый цвет -- как и раньше, уже используемый в проекте "success"
* (#d9f8eb / #003f25), остальные 3 получены той же формулой OKLCH
* (оттенок повёрнут на равные 90°).
*/

:root,
[data-bs-theme=light] {
  --sportosphere-student-color-1-bg: #d9f8eb;
  --sportosphere-student-color-1-text: #007c5e;
  --sportosphere-student-color-2-bg: #e6f0ff;
  --sportosphere-student-color-2-text: #0061d6;
  --sportosphere-student-color-3-bg: #ffe8f3;
  --sportosphere-student-color-3-text: #b9007e;
  --sportosphere-student-color-4-bg: #fdedd4;
  --sportosphere-student-color-4-text: #8a6000;
}

[data-bs-theme=dark] {
  --sportosphere-student-color-1-bg: #003f25;
  --sportosphere-student-color-1-text: #00bd91;
  --sportosphere-student-color-2-bg: #1a335a;
  --sportosphere-student-color-2-text: #63a1ff;
  --sportosphere-student-color-3-bg: #4f223a;
  --sportosphere-student-color-3-text: #f763b6;
  --sportosphere-student-color-4-bg: #462f00;
  --sportosphere-student-color-4-text: #d39400;
}

.smartCalendar .day-event.student-color-1,
.smartCalendar .hour-event.student-color-1 {
  background-color: var(--sportosphere-student-color-1-bg);
  color: var(--sportosphere-student-color-1-text);
}
.smartCalendar .day-event.student-color-2,
.smartCalendar .hour-event.student-color-2 {
  background-color: var(--sportosphere-student-color-2-bg);
  color: var(--sportosphere-student-color-2-text);
}
.smartCalendar .day-event.student-color-3,
.smartCalendar .hour-event.student-color-3 {
  background-color: var(--sportosphere-student-color-3-bg);
  color: var(--sportosphere-student-color-3-text);
}
.smartCalendar .day-event.student-color-4,
.smartCalendar .hour-event.student-color-4 {
  background-color: var(--sportosphere-student-color-4-bg);
  color: var(--sportosphere-student-color-4-text);
}

/*
* Личные записи и отпуска в общем календаре тренера — визуально отдельно от
* цветов учеников (те кодируют "с каким учеником", это — "тип записи").
* Переиспользует уже существующие в проекте токены темы (theme.min.css,
* пары *-bg-subtle/*-text-emphasis с автоматической светлой/тёмной версией),
* а не заводит новые цвета: "secondary" (нейтральный) для обычной личной
* записи, "warning" (акцентный, статус "нет на месте") для отпуска.
*/

.smartCalendar .day-event.personal-event,
.smartCalendar .hour-event.personal-event {
  background-color: var(--sportosphere-secondary-bg-subtle);
  color: var(--sportosphere-secondary-text-emphasis);
}

.smartCalendar .day-event.vacation-event,
.smartCalendar .hour-event.vacation-event {
  background-color: var(--sportosphere-warning-bg-subtle);
  color: var(--sportosphere-warning-text-emphasis);
}

/*
* Многодневная личная запись/отпуск в месячном виде — одна полоса на все
* занятые дни вместо отдельного чипа в каждой ячейке (см. addEventsToCalendar()/
* renderMultiDayEventBars() в smartCalendar.js). Сиблинг .day-frame внутри
* <td>, не её потомок — .day-frame имеет намеренный overflow: hidden (см.
* правило .smartCalendar .day-frame выше), а полосе, наоборот, нужно выходить
* за границы одной ячейки в соседние. position/left/top/width — per-instance
* geometry, проставляются в JS (измерены по факту отрисованной таблицы, тот
* же приём, что и у .day-event-more/chipStep выше).
*/

.smartCalendar .day-multi-event-bar {
  z-index: 10;
  padding: 0.25rem 0.5rem;
  margin-bottom: 0.25rem;
  font-size: 0.875rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: filter 0.15s ease-in-out;
}

.smartCalendar .day-multi-event-bar:hover {
  filter: brightness(0.95);
}

/* Скругление только со стороны, совпадающей с фактическим началом/концом
   записи — плоский край сигнализирует, что запись продолжается в соседнюю
   строку сетки/за пределы отображаемого месяца (без отдельной графики) */
.smartCalendar .day-multi-event-bar.day-multi-event-bar--start {
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
}

.smartCalendar .day-multi-event-bar.day-multi-event-bar--end {
  border-top-right-radius: 5px;
  border-bottom-right-radius: 5px;
}

.smartCalendar .day-multi-event-bar.personal-event {
  background-color: var(--sportosphere-secondary-bg-subtle);
  color: var(--sportosphere-secondary-text-emphasis);
}

.smartCalendar .day-multi-event-bar.vacation-event {
  background-color: var(--sportosphere-warning-bg-subtle);
  color: var(--sportosphere-warning-text-emphasis);
}

/*
* Расписание заведения (PlaceWorkout, kind: 'workout') — публичная
* read-only страница /visitsite_places/.../scheduler/. Отдельный от
* палитры student-color-N и от personal-event/vacation-event акцент
* ("info" — ранее не занят в этом разделе), т.к. это не тренер-центричный
* календарь (нет ни ученика, ни личных записей — только тренировки заведения).
*/

.smartCalendar .day-event.workout-event,
.smartCalendar .hour-event.workout-event {
  background-color: var(--sportosphere-info-bg-subtle);
  color: var(--sportosphere-info-text-emphasis);
}

/*
* Еженедельный шаблон дежурства заведения (schedule.PlaceDutyShift,
* kind: 'duty') — place_duty_schedule.html, openspec/changes/
* place-duty-schedule. Отдельный акцент ("success" — ранее не занят в
* этом разделе), недельный вид (.hour-event) — единственное место
* рендера (design.md, Non-Goals: месячный/дневной вид не реализованы).
*/

.smartCalendar .hour-event.duty-event {
  background-color: var(--sportosphere-success-bg-subtle);
  color: var(--sportosphere-success-text-emphasis);
}

/*
* Дни с событиями в simpleCalendar (виджет профиля ученика) — фон кружка с
* числом дня (.day-frame-date a, тот же элемент, что и у today-a-highlight/
* selected-day-highlight) подсвечивается, чтобы было видно на какие дни
* месяца уже что-то назначено, не открывая каждый день по отдельности (см.
* addEventsToCalendar() в smartCalendar.js, ветка 'simpleCalendar').
*/

/* =========================================================================
   smartCalendar — расширенный чип и модалка деталей события расписания
   заведения (kind: 'workout', config.showWorkoutDetails)
   =========================================================================
   Активно только там, где явно включено конфигом (сейчас — place_scheduler.html).
   Внешняя обёртка модалки использует существующие .isModal-*; ниже — только
   новая внутренняя разметка (чип, шапка, карточка тренера, вкладки),
   собственные классы без Bootstrap (rules.md, п. 5).
*/

.smartCalendar-workout-chip {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  overflow: hidden;
}
.smartCalendar-workout-chip-title {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.smartCalendar-workout-chip-meta {
  font-size: 0.75em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0.85;
}

.smartCalendar-workout-details-header-row {
  display: flex;
  flex: 1 1 auto;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  min-width: 0;
}
.smartCalendar-workout-details-header-title {
  font-weight: 600;
  font-size: 1.1rem;
  line-height: var(--isModal-title-line-height);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.smartCalendar-workout-details-header-time {
  font-weight: 600;
  flex-shrink: 0;
}

.smartCalendar-workout-details-area {
  display: flex;
  gap: 0.375rem;
  margin-bottom: 1rem;
}
.smartCalendar-workout-details-area-label {
  color: var(--sportosphere-secondary-color);
}

.smartCalendar-workout-details-trainer {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem;
  margin: 0 -0.5rem 1rem;
  border-radius: var(--isModal-border-radius);
}
.smartCalendar-workout-details-trainer-linked {
  cursor: pointer;
}
.smartCalendar-workout-details-trainer-linked:hover,
.smartCalendar-workout-details-trainer-linked:focus-visible {
  background-color: var(--sportosphere-tertiary-bg);
}
.smartCalendar-workout-details-trainer-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  overflow: hidden;
  background-color: var(--sportosphere-tertiary-bg);
  color: var(--sportosphere-secondary-color);
}
.smartCalendar-workout-details-trainer-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.smartCalendar-workout-details-trainer-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.smartCalendar-workout-details-trainer-name {
  font-weight: 600;
}
.smartCalendar-workout-details-trainer-role {
  font-size: 0.85em;
  color: var(--sportosphere-secondary-color);
}

.smartCalendar-workout-details-tabs {
  display: flex;
  gap: 1rem;
  border-bottom: var(--isModal-header-border-width) solid var(--isModal-header-border-color);
  margin-bottom: 1rem;
}
.smartCalendar-workout-details-tab-btn {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 0.5rem 0;
  color: var(--sportosphere-secondary-color);
  cursor: pointer;
}
.smartCalendar-workout-details-tab-btn.active {
  color: var(--sportosphere-body-color);
  border-bottom-color: var(--sportosphere-primary);
  font-weight: 600;
}
.smartCalendar-workout-details-tab-panel {
  display: none;
}
.smartCalendar-workout-details-tab-panel.active {
  display: block;
}
.smartCalendar-workout-details-specializations-list {
  margin: 0;
  padding-left: 1.25rem;
}

/* =========================================================================
   smartCalendar — контекстное меню события (правая кнопка мыши).
   Изначально (2026-08-19) только для расписания заведения (kind:
   'workout', config.allowWorkoutEditing — в недельном виде всегда, в
   месячном с 2026-08-21); с 2026-08-21 тем же классом/билдером
   (showEventContextMenu(), smartCalendar.js) пользуется и тренировочная
   сессия (kind: 'session', showSessionContextMenu() — без конфиг-флага,
   доступно везде, где и раньше было доступно редактирование по левому
   клику, раздел 5 spec). Класс переименован из
   .smartCalendar-workout-context-menu* в .smartCalendar-context-menu* при
   обобщении. Позиционируется скриптом через position: absolute
   относительно #smartCalendar (уже position: relative — см. выше).
   Модалка редактирования (#smartCalendarWorkoutEditModal/
   #smartCalendarEditEventModal) переиспользует существующие isModal-,
   smartCalendar-field, formGroup, smartCalendar-edit-modal-footer —
   собственных классов для неё не требуется.
   =========================================================================
*/

.smartCalendar-context-menu {
  position: absolute;
  z-index: 25;
  min-width: 180px;
  display: flex;
  flex-direction: column;
  background-color: var(--sportosphere-body-bg);
  border: 1px solid var(--sportosphere-gray-300);
  border-radius: 5px;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  padding: 0.25rem;
}

.smartCalendar-context-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  color: var(--sportosphere-gray-900);
  border-radius: 4px;
  cursor: pointer;
}

.smartCalendar-context-menu-item:hover,
.smartCalendar-context-menu-item:focus-visible {
  background-color: var(--sportosphere-gray-200);
}

.smartCalendar-context-menu-item-danger {
  color: var(--sportosphere-danger);
}

/* =========================================================================
   smartNavBars — навигационные панели (smartNavBars.js)
   =========================================================================
   Инструмент пересобирается с нуля пошагово (см. СТАТУС в шапке
   smartNavBars.js и docs/smart-navbars-spec.md за целевой версией). Ниже —
   ТОЛЬКО то, что реально построено сейчас: панель настроек (offcanvas),
   каркас верхней панели (логотип + пустая обёртка двухстрочного варианта)
   и локальная панель навигации раздела.

   ПЕРЕСМОТРЕНО 2026-08-07 (по прямому запросу пользователя, в соответствии
   с rules.md §2 — "собственные семантические классы, не переиспользующие
   классы существующих компонентов и не полагающиеся на классы/JS
   Bootstrap"): раньше верхняя панель/логотип/локальная панель строились
   РОДНЫМИ классами темы (.navbar/.navbar-top/.navbar-glass/.navbar-brand/
   .card/.btn-sportosphere-* и т.д. — визуально повторяя
   _navbar_top_basic/_navbar_top_double/образец локальной панели), чтобы
   получать стилизацию из theme.min.css "бесплатно". Это противоречило
   правилу изоляции — заменено на полностью СОБСТВЕННЫЕ классы
   smartNavBars-* (панель настроек уже была построена по этому правилу с
   самого начала, см. её раздел ниже, — теперь верхняя и локальная панели
   ему тоже соответствуют). Визуальный результат должен остаться тем же:
   значения ниже не придуманы заново, а перенесены из соответствующих
   родных правил theme.min.css (в т.ч. через те же CSS-переменные
   --sportosphere-*, где это осмысленно — так тёмная тема продолжает
   работать без дублирования палитры). Не покрыты намеренно узкие Bootstrap
   состояния, которых у наших конкретных объектов не бывает (disabled,
   RTL, reduced-motion и т.п.) — упрощение сознательное, не "на глаз".

   Ранее здесь же лежали правила для вертикальной панели (ещё не
   построена), мобильного триггера/кнопки настроек в верхнем меню (их
   строители удалены из smartNavBars.js при пересборке с нуля) и старого
   flex-wrap/order-механизма двухстрочной раскладки (несовместим с текущей
   .w-100-структурой, см. buildTopNavRows()) — не переносить обратно без
   проверки актуальности относительно текущего кода.

   ИСПРАВЛЕНО 2026-08-07: часть перенесённых значений ссылалась на
   var(--sportosphere-navbar-*), var(--sportosphere-card-*) и
   var(--sportosphere-dropdown-*) — в theme.min.css это НЕ глобальные
   токены, а ЛОКАЛЬНЫЕ алиасы: в отличие от настоящих глобальных
   var(--sportosphere-*), объявленных на :root/[data-bs-theme], они
   ЗАДАЮТСЯ ВНУТРИ конкретных компонент-классов (`.navbar {...}`,
   `.card {...}`, `.dropdown-menu {...}`). Раз элементы smartNavBars эти
   классы не носят (правило изоляции выше), var() резолвился в initial/
   unset — визуально это давало прозрачный фон дропдауна и т.п. Исправлено
   заменой на (а) глобальные "-global"-версии, где они существуют
   (--sportosphere-dropdown-bg-global и т.п., объявлены в
   :root/[data-bs-theme]), либо (б) прямое вычисленное выражение через
   истинно глобальный базовый токен (--sportosphere-emphasis-color-rgb,
   --sportosphere-emphasis-bg, --sportosphere-gray-300, --sportosphere-
   border-color, --sportosphere-box-shadow — все объявлены в :root/
   [data-bs-theme], проверено построчно). При добавлении новых
   var(--sportosphere-*) в этот раздел ОБЯЗАТЕЛЬНО проверять не только факт
   существования имени (grep недостаточен), а именно SELECTOR SCOPE
   объявления в theme.min.css. */

/* Базовое требование ко ВСЕМ <a> во всех панелях этого инструмента: без
   декорации (подчёркивания) при наведении/фокусе. Родное правило
   theme.min.css — a:hover{text-decoration:underline} — специфичность
   (0,1,1) (1 псевдокласс + 1 тег), и это БОЛЬШЕ, чем просто class-селектор
   вида .smartNavBars-link{text-decoration:none} (0,1,0) — поэтому точечных
   text-decoration:none на самих компонентах недостаточно (часть компонентов
   и так дублирует его в своих :hover, часть — нет, см. историю в разделе 11
   docs/smart-navbars-spec.md). Одно общее правило, специфичность (0,2,1),
   гарантированно перебивает родное вне зависимости от порядка и класса
   конкретной ссылки — включая ссылки, которые появятся в компоненте позже */

.smartNavBars-app a:hover,
.smartNavBars-app a:focus {
  text-decoration: none;
}

.smartNavBars-app {
  /* Ширина свёрнутой панели (см. "Свёрнутое-в-иконки состояние" ниже) —
     ИСПРАВЛЕНО (третья итерация): "по правой границе .smartNavBars-
     vertical-toggle-wrapper" (со 2-й итерации, margin-box целиком, включая
     margin-right) на практике давало заметный пустой хвост между кнопкой и
     правым краем панели — margin-right у wrapper (1.5rem) больше самой
     кнопки (2.5rem). Пользователь по факту отверг этот вариант и явно
     указал на 2 ориентира: видимая часть .smartNavBars-vertical-toggle-
     wrapper и ширина самой .smartNavBars-vertical-toggle — попросив выбрать
     оптимальный вариант.

     Ни то, ни другое ЧИСЛО само по себе не подходит буквально: margin-left
     wrapper — уже не 0 (после унификации отступов между стилями — .25rem),
     то есть кнопка реально рендерится смещённой от левого края панели, а
     не от x=0 — взять только "ширину кнопки" (2.5rem) без этого сдвига
     обрезало бы её правый край (кнопка бы вылезала за границу свёрнутой
     панели). Правильное сочетание обоих ориентиров — ПОЗИЦИЯ правого края
     видимого рендер-бокса кнопки (без margin-right, он и есть тот самый
     лишний хвост) с учётом реального сдвига margin-left: margin-left
     (.25rem) + padding-left wrapper (.125rem) + ширина кнопки (2.5rem) =
     2.875rem — кнопка вплотную к правому краю панели, без хвоста и без
     обрезки */
  --smartNavBars-vertical-collapsed-width: 2.875rem;
}

/* ---- Верхняя панель — <nav class="smartNavBars-top" data-smart-navbar=
   "top"> строит buildTopNav(). Значения — родной .navbar-top в
   theme.min.css (position/z-index/min-height/шрифт) + .navbar (сам
   flex-контейнер) + .navbar-glass (фон). Показ/скрытие по раскладке —
   целиком на стороне JS (вставка/удаление из DOM в updateTopNav()), CSS
   тут ни при чём ---- */

/* ИСПРАВЛЕНО: вертикальные отступы унифицированы с эталоном
   .smartNavBars-vertical-toggle-row (тоже строка с логотипом) — там
   ЖЁСТКАЯ height: var(--sportosphere-top-nav-height) и НУЛЕВОЙ вертикальный
   padding (см. её блок ниже), центрирование содержимого — целиком на
   align-items:center. Раньше здесь было min-height + padding: .5rem 1rem
   (вертикальный .5rem с обеих сторон) — при том же значении переменной для
   высоты это давало РАЗНУЮ геометрию двух формально аналогичных строк:
   .top мог растягиваться выше 4.3125rem, если контент строки не помещался
   в 4.3125rem - 1rem паддинга, тогда как .toggle-row — никогда (там
   height, не min-height). Теперь оба ряда ведут себя одинаково —
   фиксированная высота, без вертикального паддинга. Горизонтальный
   1rem оставлен (у toggle-row нет аналога — это отступ от края всей
   полосы меню, не связан с центрированием по вертикали).

   ИСПРАВЛЕНО ещё раз: <nav> сам больше не flex-контейнер — по тому же
   образцу .smartNavBars-vertical (position:fixed, БЕЗ display:flex) +
   .smartNavBars-vertical-toggle-row (отдельный flex-ребёнок, см. ниже)
   flex/align-items:center переехали во вложенный div.smartNavBars-
   vertical-top-row (строит buildTopNav(), см. smartNavBars.js) — логотип
   и содержимое строки 1/2 вставляются уже в него (updateTopNavBrand()/
   updateTopNavContent()), а не прямым потомком <nav> */

.smartNavBars-top {
  position: sticky;
  top: 0;
  z-index: 1020;
  height: var(--sportosphere-top-nav-height);
  font-family: var(--sportosphere-font-sans-serif);
  font-size: .8333333333rem;
  font-weight: 600;
  /* --smartNavBars-visitsite-accent-bg — точка расширения под кастомизацию
     навбара сайта-визитки индивидуально по БА. По умолчанию НЕ объявлена
     здесь (это правило) — значение задаёт [data-smart-navbar-type="visitsite"]
     ниже (тёмный фон вёрстки-образца _visitsite_pages_navbar_top, прямой
     запрос пользователя); в админке переменная по-прежнему нигде не
     объявлена, фолбэк резолвится в исходное --sportosphere-bg-navbar-glass,
     нулевое визуальное изменение. Когда появится поле цвета БА в БД,
     подключается инлайн-стилем на rootId визитки (`style="--smartNavBars-
     visitsite-accent-bg: ..."`) — data-smart-navbar-type стоит на ТОМ ЖЕ
     элементе (this.root), поэтому инлайн выигрывает cascade и
     переопределяет тёмный дефолт без правок JS/CSS. */
  background-color: var(--smartNavBars-visitsite-accent-bg, var(--sportosphere-bg-navbar-glass));
  transition: padding-left .2s ease;
}

/* Точка расширения под НЕЗАВИСИМО стилизуемые "типы" меню, общая для любого
   числа потребителей — data-smart-navbar-type на rootId (config.type в
   smartNavBars.js, конструктор): [data-smart-navbar-type="<тип>"]
   { --свои-переменные }, атрибут переносится между сколькими угодно
   разными rootId разных страниц, в отличие от ID-правила на конкретный
   rootId (id уникален только внутри одной страницы). Два практических
   потребителя:
   - 'standard' (главная страница портала, раздел 10
     docs/smartNavbars-spec.md) — пока БЕЗ собственных значений (вид не
     отличается от дефолта .smartNavBars-top выше), правило для него не
     объявлено, добавляется по факту, когда появятся конкретные значения.
   - 'visitsite' (сайт-визитка БА) — тёмная вёрстка верхней панели по
     образцу _visitsite_pages_navbar_top/_visitsite_landing_navbar_top.html
     (прямой запрос пользователя, 2026-08-16), конкретные значения ниже. До
     2026-08-17 значения были объявлены ID-правилом #smartNavBarsVisitsiteApp
     — перенесено на этот общий механизм по прямому запросу пользователя,
     без изменений в составе/вёрстке самих пунктов меню и без потери задела
     на будущий инлайн-стиль (data-smart-navbar-type стоит на том же
     rootId, что получил бы инлайн-стиль, — инлайн выигрывает cascade
     независимо от типа правила-конкурента: ID/атрибут/класс). */
[data-smart-navbar-type="visitsite"] {
  --smartNavBars-visitsite-accent-bg: rgb(11, 23, 39);
  --smartNavBars-visitsite-accent-link: rgba(255, 255, 255, .55);
  --smartNavBars-visitsite-accent-link-hover: rgba(255, 255, 255, .75);
  /* Компенсация высоты — см. блок .smartNavBars-top ниже: панель уходит из
     потока (position:fixed), rootId (этот элемент) сам держит место
     нужной высоты, чтобы контент, который раньше рисовался внутри
     [data-smart-navbar-content] (сейчас пустой — см. ниже), не наезжал на
     панель сверху. */
  height: var(--sportosphere-top-nav-height);
}

/* Панель визитки — position:fixed вместо общего position:sticky у
   .smartNavBars-top выше, по прямому запросу пользователя (во всю ширину
   экрана + всегда видна при прокрутке): страницы визитки после отказа от
   вложенных шаблонов (`{% block sections %}` в _visitsite_main.html)
   держат реальное содержимое страницы СНАРУЖИ [data-smart-navbar-content]
   (соседним блоком, не внутри) — у sticky-элемента для "прилипания" при
   прокрутке нужен предок высотой во всю страницу, а здесь предок
   ([data-smart-navbar-content]) остаётся пустым (пустой бокс нулевой
   высоты), поэтому sticky не успевает "прилипнуть" — панель уезжает вместе
   со страницей уже на первом пикселе прокрутки. fixed не зависит от
   высоты предка вообще — panel всегда закреплена относительно viewport,
   ТОЧНО как в вёрстке-образце (`fixed-top` у исходного Bootstrap-навбара,
   см. `_visitsite_pages_navbar_top`). left/right:0 одновременно даёт
   ширину во весь экран — .container/.container-fluid у rootId (см.
   setStretch()) на эту раскладку больше не влияет, поэтому добавлен
   padding-left/-right тем же отступом, что и у .container
   (--sportosphere-gutter-x, унаследован от класса .container/-fluid на
   самом rootId — см. theme.min.css), чтобы логотип/меню/телефон не
   упирались в самый край экрана. */
[data-smart-navbar-type="visitsite"] .smartNavBars-top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  width: auto;
  max-width: none;
  padding-left: calc(var(--sportosphere-gutter-x, 2rem) * .5);
  padding-right: calc(var(--sportosphere-gutter-x, 2rem) * .5);
}

/* .smartNavBars-bottom-link — ОБЩИЙ класс для пунктов и верхнего
   горизонтального меню, и мобильной вертикальной панели-подмены
   (isVerticalVisible(), узкий экран < 992px). Без сброса ниже переменные
   выше (белый цвет ссылок для тёмного .smartNavBars-top) утекли бы через
   обычное наследование CSS-переменных и в .smartNavBars-vertical — там
   фон светлый (тема), белый текст был бы нечитаем. initial возвращает
   переменную в необъявленное состояние — var(..., фолбэк) у
   .smartNavBars-bottom-link снова резолвится в исходный фолбэк, как в
   админке. */
[data-smart-navbar-type="visitsite"] .smartNavBars-vertical {
  --smartNavBars-visitsite-accent-link: initial;
  --smartNavBars-visitsite-accent-link-hover: initial;
}

/* Баннер (первая секция лендинга визитки, когда включена, {% if
   banner.active %} в visitsite_page_landing.html) — под панелью, а не под
   ней с отступом, по прямому запросу пользователя. Чистый CSS, БЕЗ
   зависимости от data-smart-navbar-* атрибутов (в отличие от остального в
   этом разделе) — #banner уже есть в исходной серверной разметке, а эти
   атрибуты появляются только после выполнения скрипта; будь правило
   завязано на них, баннер сначала отрисовался бы в обычном (не
   перекрывающемся панелью) положении и "прыгнул" бы вверх спустя
   мгновение после загрузки скрипта — эффект мигания. #banner — прямой
   next-sibling rootId (первый элемент {% block sections %} в
   _visitsite_main.html, тот же уровень вложенности, что и сам rootId —
   см. шаблон), margin-top тем же значением, что и height у rootId выше —
   компенсирует её, "утапливая" баннер обратно под панель. На страницах
   без баннера (`visitsite_employee_list.html`/`visitsite_workout_list.html`)
   селектор просто ничего не находит — без эффекта. */
#smartNavBarsVisitsiteApp + #banner {
  margin-top: calc(-1 * var(--sportosphere-top-nav-height));
}

/* Единственная активная секция страницы визитки — забирает всё доступное
   видимое пространство экрана (по прямому запросу пользователя: секция
   должна доходить до нижнего края экрана, а футер — уходить под сгиб,
   оставаясь снаружи первого экрана, а не делить с ним место на одном
   экране). Затрагивает как "монолитные" страницы, у которых
   {% block sections %} в _visitsite_main.html всегда рендерит ровно одну
   <section> (visitsite_employee_list.html, visitsite_workout_list.html,
   visitsite_place_scheduler.html), так и visitsite_page_landing.html,
   когда у БА активна только одна из 7 секций ({% if X.active %} в самом
   шаблоне) — правило чисто структурное, не завязано на конкретную
   страницу/секцию.

   :only-of-type — единственная <section> среди детей main.visitsite-main
   (футер, #visitsite-footer, теперь <footer>, НЕ <section> — намеренно
   другой тег, в счёт не идёт): при 2+ активных секциях :only-of-type не
   совпадает ни с одной из них — они, как и раньше, идут одна за другой по
   высоте своего контента, без растяжения.

   ПЕРВАЯ ПОПЫТКА (flex:1 внутри main.visitsite-main{min-height:100vh},
   паттерн "прилипающий футер") была ОШИБОЧНОЙ — flex делит 100vh МЕЖДУ
   секцией и футером (footer сохраняет свою высоту по контенту, секция
   растягивается лишь на остаток ДО него), из-за чего футер оставался
   виден на первом экране целиком, а не уходил за сгиб — визуально
   разницы с исходным поведением почти не было (проверено скриншотом
   пользователя). min-height на самой секции, отсчитанный от высоты
   панели, а не от места, оставшегося после футера, — единственный
   способ гарантировать нижний край именно секции ровно на нижнем крае
   экрана независимо от высоты футера. margin/padding самой секции и
   вложенных .container/.row/.col не переопределяются, растягивается
   только box секции. */
main.visitsite-main > section:only-of-type {
  min-height: calc(100vh - var(--sportosphere-top-nav-height));
}

/* #banner — единственная секция, чей box визуально начинается не под
   панелью (y = высота панели), а с самого верха экрана (y = 0): его
   утапливает под панель отрицательный margin-top, см. правило
   #smartNavBarsVisitsiteApp + #banner выше. Поэтому для него, в отличие
   от остальных секций, "высота до низа экрана" — это полные 100vh, а не
   100vh минус высота панели; иначе (при активном только баннере) низ
   секции оказался бы на 4.3125rem выше нижнего края экрана. */
#banner:only-of-type {
  min-height: 100vh;
}

/* Прозрачная до прокрутки — только когда сервер явно передал
   transparentTop: true (см. "transparentTop" в шапке smartNavBars.js — для
   визитки это {% if banner.active %}, задаётся в _visitsite_main.html) —
   герой-баннер виден сквозь панель, пока страница не прокручена; после —
   обычный тёмный фон типа 'visitsite' выше, ПЛАВНО (transition), а не
   скачком. data-smart-navbar-scrolled — общее, не завязанное на
   transparentTop состояние прокрутки (updateScrolledState() в
   smartNavBars.js, ставится всегда, для любого потребителя) — здесь
   используется только на страницах с transparentTop. */
[data-smart-navbar-type="visitsite"][data-smart-navbar-transparent-top] .smartNavBars-top {
  transition: background-color .3s ease;
}

[data-smart-navbar-type="visitsite"][data-smart-navbar-transparent-top]:not([data-smart-navbar-scrolled]) {
  --smartNavBars-visitsite-accent-bg: transparent;
}

/* ИСПРАВЛЕНО: у "Двойное верхнее меню" (layout "double-top", единственная
   раскладка с topVariant "double") содержимое — ДВЕ строки (поиск+
   виджеты, разделитель, меню навигации), а .smartNavBars-top заимствует
   у всех раскладок одну и ту же ФИКСИРОВАННУЮ высоту var(--sportosphere-
   top-nav-height), рассчитанную под ОДНУ строку. Внутри —
   .smartNavBars-vertical-top-row{height:100%;align-items:center}
   центрировал в этой слишком тесной высоте двухстрочное содержимое,
   которое реально выше контейнера — верхняя строка вылезала выше
   верхней границы экрана (сообщил пользователь). height:auto здесь даёт
   контейнеру вырасти под настоящую высоту содержимого (см. также
   .smartNavBars-vertical-top-row{height:100%} ниже — со auto-родителем
   проценты тоже резолвятся в auto, а не в 0) — align-items:center
   перестаёт что-либо обрезать не по месту, верхняя строка встаёт ровно
   там же (у самого верха), где была бы у однострочных раскладок */

[data-smart-navbar-layout="double-top"] .smartNavBars-top {
  height: auto;
}

/* ИСПРАВЛЕНО: при сворачивании вертикальной панели margin-left у
   [data-smart-navbar-content] (а значит, и позиция .smartNavBars-top
   внутри него — строка поиска первой стоит в .smartNavBars-top-row-top,
   прижата к левому краю) уменьшается вместе с шириной панели — строка
   поиска визуально "уезжала" влево вместе с этим. По прямому запросу
   пользователя компенсируем это увеличением padding-left самого
   .smartNavBars-top ровно на столько, на сколько уменьшился margin-left
   content — итоговая позиция строки поиска остаётся неизменной что в
   развёрнутом, что в свёрнутом виде. transition выше синхронизирован по
   длительности с [data-smart-navbar-content]{transition:margin-left .2s
   ease} (см. ниже), чтобы оставаться неподвижной ВЕСЬ переход, а не
   только в начале/конце.

   Расчёт: обычный margin-left content — 20.625rem, свёрнутый —
   calc(collapsed-width + 1rem) (см. .smartNavBars-app/margin-left
   content ниже). padding-left = 20.625rem − collapsed-margin =
   20.625rem − collapsed-width − 1rem = 19.625rem − collapsed-width.
   Ссылается на ту же переменную --smartNavBars-vertical-collapsed-width,
   что и content — при новом изменении её значения оба места пересчитаются
   синхронно сами. Раскладко-зависимо (base/combo — единственные, где
   margin-left content вообще завязан на ширину вертикальной панели).

   ДОБАВЛЕНО: обёрнуто в @media(min-width:1200px) — свёрнутое-в-иконки
   состояние (this.collapsed) актуально только на десктопе (раздел 3.5), на
   мобильном (< 1200px) это же самое место (кнопка+логотип, встроенные в
   полосу .smartNavBars-top) занимает ВСЕГДА одну и ту же ширину независимо
   от [data-smart-navbar-collapsed]/[data-smart-navbar-mobile-open] — там
   своя, не зависящая от состояния компенсация padding-left, см. блок
   "Мобильное поведение вертикального меню" в конце файла */

@media (min-width: 1200px) {
  [data-smart-navbar-collapsed][data-smart-navbar-layout="base"] .smartNavBars-top,
  [data-smart-navbar-collapsed][data-smart-navbar-layout="combo"] .smartNavBars-top {
    padding-left: calc(19.625rem - var(--smartNavBars-vertical-collapsed-width));
  }
}

/* ИСПРАВЛЕНО: flex-wrap:wrap (перенесён дословно из родного .navbar, там
   он тоже есть) в паре с ЖЁСТКОЙ height (см. .smartNavBars-top выше) даёт
   известный эффект — при переносе строк align-items:center больше не
   центрирует единственную строку по всей высоте контейнера сам по себе:
   этим управляет align-content (по умолчанию normal/flex-start для
   flex-wrap-контейнера с одной строкой), и она прижимается к началу
   (верху), а не к центру. У родного .navbar эта разница не проявлялась —
   там нет собственной жёсткой высоты, контейнер растягивается точно под
   контент, лишнего места для смещения попросту нет. Подтверждено визуально
   пользователем (без wrap логотип встаёт по центру) — flex-wrap убран, у
   нас и так нет ещё узкоэкранного поведения (тогглер верхней панели для
   мобильных не реализован, см. статус-комментарий в шапке smartNavBars.js),
   которое могло бы полагаться на перенос строк */

.smartNavBars-vertical-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
}

/* ---- Логотип — <a class="smartNavBars-brand"><div class="smartNavBars-
   brand-inner">{иконка}{текст}</div></a> строит buildBrand(). Значения —
   родной .navbar-brand (шрифт/цвет/отступ) + утилиты me-1/me-sm-3
   (отступ у самого логотипа) и me-2 (между иконкой и текстом), как в
   _navbar_top_basic/_navbar_top_double ---- */

.smartNavBars-brand {
  display: inline-block;
  margin-right: .25rem;
  font-size: 1.728rem;
  font-weight: 800;
  color: rgba(var(--sportosphere-emphasis-color-rgb), .9);
  white-space: nowrap;
  text-decoration: none;
}

@media (min-width: 576px) {
  .smartNavBars-brand {
    margin-right: 1rem;
  }
}

.smartNavBars-brand-inner {
  display: flex;
  align-items: center;
}

.smartNavBars-brand-icon,
.smartNavBars-brand-text {
  margin-right: .5rem;
}

/* ---- Инлайн-SVG логотип (config.svg, buildBrand()) — та же роль, что у
   пары .smartNavBars-brand-icon/-text выше (картинки бизнес-логотипа для
   _visitsite_main.html, не тронуты), но для собственного бренда
   СПОРТОСФЕРА: значок + текст в одном SVG, высота зафиксирована как у
   прежней иконки (40px), ширина — по intrinsic-соотношению самого SVG
   (viewBox 1500x266.39), чтобы сохранить прежний габарит связки
   icon(40)+gap+text(180) ---- */

.smartNavBars-brand-logo {
  display: inline-flex;
  align-items: center;
}

.smartNavBars-brand-logo svg {
  height: 40px;
  width: auto;
}

/* ---- Цвет самого SVG-логотипа (.sportosphere-logo, шаблон logo_name) —
   общий для любого места встраивания (buildBrand() выше и прямые
   {% include 'logo_name' %} на 500/404/лендинге): текст — через
   --sportosphere-emphasis-color (уже #000 в светлой/#fff в тёмной теме,
   sportosphereVariables.css), значок — фирменный красный в светлой теме,
   белый в тёмной ---- */

.sportosphere-logo-text path {
  fill: var(--sportosphere-emphasis-color);
}

.sportosphere-logo-icon {
  fill: #E30613;
}

[data-bs-theme="dark"] .sportosphere-logo-icon {
  fill: var(--sportosphere-emphasis-color);
}

/* Логотип строится в двух экземплярах — this.brand (верхняя панель,
   buildTopNav()) и this.verticalBrand (вертикальная панель,
   buildVerticalToggle()), см. smartNavBars.js. Ровно один виден
   одновременно — при раскладках с вертикальной панелью ("base"/"combo",
   а с раздела 3.5.1 — и у "top"/"double-top" при ширине экрана < 992px,
   isVerticalVisible()) this.brand ВООБЩЕ НЕ ВСТАВЛЯЕТСЯ в DOM (см.
   updateTopNavBrand() — ИСПРАВЛЕНО с CSS visibility:hidden на невставку в
   разметку, по прямому запросу пользователя: невидимый, но присутствующий
   в DOM элемент не имеет смысла), поэтому здесь для этого отдельного
   CSS-правила больше не нужно — this.topNav просто не содержит
   .smartNavBars-brand в этом состоянии */

/* ---- Содержимое верхней панели — три topVariant раздела 3.3 docs/
   smart-navbars-spec.md, строятся в buildTopNav(), переставляются между
   строками updateTopNavContent(). .smartNavBars-top-row-top — строка 1
   (прямой потомок <nav> у вариантов 'search'/'menu', первый потомок
   .smartNavBars-top-rows у 'double'; flex-basis:100%/flex-grow:1 —
   растягивается на всю строку в обоих случаях, как родной .navbar-collapse).
   .smartNavBars-top-rows — обёртка двухстрочного варианта ("Двойное
   верхнее меню"), только у 'double'. Значения строки поиска/виджетов —
   родной .navbar-nav (flex-раскладка списка) + navbar-nav-icons/flex-row/
   ms-auto (виджеты в ряд, прижаты вправо) + d-none/d-lg-block (поиск и
   разделитель скрыты ниже lg, как в _navbar_top_double); нижняя строка —
   родной .navbar-top .navbar-collapse (скролл/отступ) ---- */

.smartNavBars-top-rows {
  width: 100%;
}

/* ИСПРАВЛЕНО: height задан явно и безусловно (не только через
   .smartNavBars-top{height:var(--sportosphere-top-nav-height)}/
   .smartNavBars-vertical-top-row{height:100%} снаружи) — по прямому
   запросу пользователя. В однострочных раскладках строка и так
   визуально получала эту высоту (флекс-центрирование внутри уже
   var(--sportosphere-top-nav-height)-высокого .smartNavBars-top), но
   после ИСПРАВЛЕНО у "Двойное верхнее меню" выше (.smartNavBars-top
   {height:auto}) внешняя высота стала считаться ОТ содержимого, а не
   наоборот — без собственной явной высоты строка 1 сжималась до своего
   минимального контента и оказывалась НИЖЕ, чем та же строка в
   однострочных раскладках. Явная высота делает строку 1 одинаковой в
   обоих случаях независимо от того, что именно задаёт высоту снаружи */

.smartNavBars-top-row-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-basis: 100%;
  flex-grow: 1;
  height: var(--sportosphere-top-nav-height);
}

.smartNavBars-top-search,
.smartNavBars-top-widgets {
  display: flex;
  padding-left: 0;
  margin-bottom: 0;
  list-style: none;
}

.smartNavBars-top-search {
  align-items: center;
}

.smartNavBars-top-widgets {
  flex-direction: row;
  align-items: center;
  margin-left: auto;
}

@media (max-width: 991.98px) {
  .smartNavBars-top-search {
    display: none;
  }
}

.smartNavBars-top-divider {
  display: block !important;
  margin-top: .5rem !important;
  margin-bottom: .5rem !important;
  border-top: var(--sportosphere-border-width) solid;
  color: var(--sportosphere-border-color);
}

@media (max-width: 991.98px) {
  .smartNavBars-top-divider {
    display: none !important;
  }
}

/* ---- Строка поиска — buildTopNavSearchItem() в smartNavBars.js.
   Значения — родной .search-box/.search-box-icon/.search-input в
   theme.min.css. Панель результатов (.smartNavBars-search-dropdown) —
   пустая структурная заготовка, см. JSDoc над buildTopNavSearchItem() ---- */

.smartNavBars-top-search-item {
  display: flex;
  align-items: center;
}

.smartNavBars-search {
  position: relative;
  width: 21rem;
  font-size: .8333333333rem;
}

.smartNavBars-search-form {
  position: relative;
}

.smartNavBars-search-input {
  display: block;
  width: 100%;
  padding: .375rem 2rem;
  font-size: .8333333333rem;
  font-weight: 400;
  line-height: 1.7;
  color: var(--sportosphere-body-color);
  background-color: var(--sportosphere-body-bg);
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-radius: 50rem;
  box-shadow: none;
  appearance: none;
}

.smartNavBars-search-input::-webkit-search-cancel-button {
  display: none;
}

.smartNavBars-search-icon {
  position: absolute;
  color: var(--sportosphere-gray-400);
  top: 50%;
  left: .9rem;
  transform: translateY(-48%);
}

.smartNavBars-search-close {
  display: none;
  position: absolute;
  top: 50%;
  right: .5rem;
  transform: translateY(-50%);
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: var(--sportosphere-emphasis-bg);
  color: var(--sportosphere-gray-600);
  font-size: .625rem;
  cursor: pointer;
}

.smartNavBars-search.show .smartNavBars-search-close {
  display: flex;
  align-items: center;
  justify-content: center;
}

.smartNavBars-search-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1000;
  width: 100%;
  margin-top: .5rem;
  padding: 1rem;
  background-color: var(--sportosphere-dropdown-bg-global);
  border-radius: .375rem;
  box-shadow: var(--sportosphere-box-shadow);
  min-height: 3rem;
}

.smartNavBars-search.show .smartNavBars-search-dropdown {
  display: block;
}

/* ---- Общая обёртка "иконка-тогглер + выпадающее окно" для темы/
   уведомлений/приложений/профиля — buildWidgetDropdown() в
   smartNavBars.js. Позиционирование/уголок-указатель — родной .navbar
   .dropdown-menu.dropdown-caret(.dropdown-menu-end) в theme.min.css (тот
   же приём, что и у .smartNavBars-bottom-dropdown, но END-выровненный —
   виджеты у правого края панели, а не у левого) ---- */

.smartNavBars-top-widget-item {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0 .375rem;
}

/* Виджет "phone" (buildPhoneWidget() в smartNavBars.js) — статичный текст,
   вёрстка-образец сайта-визитки (_visitsite_pages_navbar_top): жёлтый
   номер телефона у правого края тёмной верхней панели */
.smartNavBars-top-phone {
  color: #ffef00;
  font-weight: 700;
  white-space: nowrap;
}

/* Виджет "links" (buildLinksWidget() в smartNavBars.js) — простые ссылки в
   ряд (например "Войти"/"Регистрация" для неавторизованных посетителей),
   как и phone выше — не выпадающее окно, просто содержимое в общей
   обёртке .smartNavBars-top-widget-item (уже flex, gap здесь только
   раздвигает несколько <a> внутри). В отличие от phone (жёлтый цвет по
   образцу визитки) цвет НЕ привязан к конкретному потребителю — тот же
   нейтральный rgba(var(--sportosphere-emphasis-color-rgb), .55/.7), что и
   у .smartNavBars-widget-toggle ниже */
.smartNavBars-top-links {
  gap: .75rem;
}

.smartNavBars-top-link {
  color: rgba(var(--sportosphere-emphasis-color-rgb), .55);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.smartNavBars-top-link:hover,
.smartNavBars-top-link:focus {
  color: rgba(var(--sportosphere-emphasis-color-rgb), .7);
}

/* Цвет — родные --sportosphere-navbar-color/-hover-color (см. .navbar
   в theme.min.css): rgba(var(--sportosphere-emphasis-color-rgb), 0.55/0.7)
   — те же значения, что уже верно применены у .smartNavBars-bottom-link
   выше; раньше здесь стояли неточные .7/.9 */

.smartNavBars-widget-toggle {
  display: flex;
  align-items: center;
  color: rgba(var(--sportosphere-emphasis-color-rgb), .55);
  cursor: pointer;
  text-decoration: none;
}

.smartNavBars-widget-toggle:hover {
  color: rgba(var(--sportosphere-emphasis-color-rgb), .7);
}

/* БЕЗ собственного z-index (найдено через DevTools пользователя) — со
   своим z-index:1000 этот блок создаёт СОБСТВЕННЫЙ контекст наложения, и
   внутри него z-index:-1 у :after-треугольника (см. ниже) перестаёт
   уходить под содержимое меню — треугольник рисуется поверх текста/кнопок
   вместо того чтобы прятаться позади них. Без явного z-index блок остаётся
   обычным position:absolute-потомком .smartNavBars-top (z-index:1020) — от
   родительского контекста этого достаточно, чтобы быть выше обычного
   содержимого страницы; отдельный высокий z-index тут не нужен */

.smartNavBars-widget-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  right: -.5625rem;
  left: auto;
  min-width: 10rem;
  margin: .75rem 0 0;
  overflow: visible;
  font-size: .8333333333rem;
  background-color: var(--sportosphere-dropdown-bg-global);
  border: 0;
  border-radius: .375rem;
  box-shadow: var(--sportosphere-box-shadow);
}

.smartNavBars-widget-dropdown.show {
  display: block;
}

/* right сдвинут с .95rem до 1.25rem (влево, по запросу пользователя) —
   ближе к горизонтальному центру .smartNavBars-widget-toggle. Точный центр
   зависит от реальной ширины конкретной иконки (глиф FontAweome — не
   постоянная величина, замерить без браузера нельзя), значение —
   приближение по геометрии (padding .smartNavBars-top-widget-item + офсет
   right:-.5625rem у самого блока меню), при необходимости точечно
   подправить после визуальной проверки */

.smartNavBars-widget-dropdown:after {
  content: '';
  position: absolute;
  z-index: -1;
  top: -.375rem;
  right: 1.25rem;
  left: auto;
  width: 1rem;
  height: 1rem;
  background-color: var(--sportosphere-dropdown-bg-global);
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-width: 1px 0 0 1px;
  border-radius: .125rem;
  transform: rotate(45deg);
  box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
}

/* Модификатор "card" (dropdown-menu-card) — фон/уголок-указатель уходят на
   само выпадающее окно без изменений (см. выше), внутренняя "карточка"
   (уведомления/приложения) добавляет только собственные скругление/паддинг,
   без своей рамки/тени — их даёт .smartNavBars-widget-dropdown */

.smartNavBars-widget-dropdown-card {
  padding: 0;
}

.smartNavBars-widget-dropdown-inner {
  padding: .5rem 0;
}

/* ---- "Кнопка переключения режима" — buildThemeWidget(). Активная иконка
   и галочка выбранного варианта — чистый CSS от [data-bs-theme] на
   <html> (см. setTheme() в smartNavBars.js), без ручной синхронизации ---- */

.smartNavBars-theme-icon {
  font-size: 1.2rem;
}

.smartNavBars-theme-icon[data-smart-navbar-theme-icon="dark"] {
  display: none;
}

html[data-bs-theme="dark"] .smartNavBars-theme-icon[data-smart-navbar-theme-icon="light"] {
  display: none;
}

html[data-bs-theme="dark"] .smartNavBars-theme-icon[data-smart-navbar-theme-icon="dark"] {
  display: inline-block;
}

.smartNavBars-theme-option {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .25rem 1rem;
  border: 0;
  background-color: transparent;
  color: var(--sportosphere-gray-800);
  font-size: .8333333333rem;
  text-align: left;
  cursor: pointer;
}

.smartNavBars-theme-option:hover,
.smartNavBars-theme-option:focus {
  color: var(--sportosphere-gray-link-hover-600);
  background-color: var(--sportosphere-dropdown-link-hover-bg-global);
}

.smartNavBars-theme-check {
  margin-left: auto;
  color: var(--sportosphere-gray-600);
  visibility: hidden;
}

html:not([data-bs-theme="dark"]) .smartNavBars-theme-option[value="light"] .smartNavBars-theme-check,
html[data-bs-theme="dark"] .smartNavBars-theme-option[value="dark"] .smartNavBars-theme-check {
  visibility: visible;
}

/* ---- "Значок уведомлений" — buildNotificationsWidget()/
   buildNotificationItem(). Значения — .notification/.notification-body/
   .notification-time/.list-group в theme.min.css ---- */

.smartNavBars-notifications-icon {
  font-size: 1.2rem;
}

/* Обёртка иконки+бейджа (buildNotificationsWidget()) — сама, а не
   .smartNavBars-top-widget-item, служит точкой отсчёта для
   position:absolute бейджа ниже, чтобы бейдж лежал точно у угла иконки, а
   не у угла всего пункта виджета (с его паддингами) */
.smartNavBars-notifications-toggle-content {
  position: relative;
  display: inline-flex;
  align-items: center;
}

/* Бейдж числа непрочитанных — показывается/скрывается через [hidden] в
   JS (renderNotificationsBadge()). Цвет — var(--sportosphere-danger), тот
   же токен "внимание", что уже используется для .smartNavBars-badge-danger
   (вертикальное меню) */
.smartNavBars-notifications-badge {
  position: absolute;
  top: -.3rem;
  right: -.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 .25rem;
  border-radius: 999px;
  background-color: var(--sportosphere-danger);
  color: #fff;
  font-size: .625rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

.smartNavBars-notifications {
  width: 21rem;
}

.smartNavBars-notifications-header {
  padding: .75rem 1rem;
  border-bottom: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
}

.smartNavBars-notifications-title {
  margin-bottom: 0;
  font-size: .8333333333rem;
  font-weight: 700;
}

.smartNavBars-notifications-list {
  display: flex;
  flex-direction: column;
  max-height: 19rem;
  overflow-y: auto;
}

.smartNavBars-notification-item {
  display: flex;
  padding: .5rem 1rem;
  color: var(--sportosphere-gray-1000);
  text-decoration: none;
  background-color: var(--sportosphere-emphasis-bg);
  border-bottom: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  transition: background-color .2s ease-in-out;
}

.smartNavBars-notification-item:last-child {
  border-bottom: 0;
}

.smartNavBars-notification-item:hover,
.smartNavBars-notification-item:focus {
  color: var(--sportosphere-gray-1100);
  text-decoration: none;
}

.smartNavBars-notification-unread {
  background-color: var(--sportosphere-gray-200) !important;
}

.smartNavBars-notification-avatar {
  flex: 0 0 auto;
  margin-right: .75rem;
}

.smartNavBars-notification-body {
  min-width: 0;
}

.smartNavBars-notification-text {
  margin-bottom: .25rem;
  font-size: .8333333333rem;
}

.smartNavBars-notification-time {
  color: var(--sportosphere-gray-600);
  font-size: .8333333333rem;
}

/* Пустой список / ошибка загрузки (renderNotificationsList()/
   renderNotificationsError() в smartNavBars.js) — один и тот же нейтральный
   текстовый вид для обоих случаев, различается только сообщением */
.smartNavBars-notifications-empty {
  margin: 0;
  padding: 1rem;
  color: var(--sportosphere-gray-600);
  font-size: .8333333333rem;
  text-align: center;
}

/* Ссылка "Показать все" внизу дропдауна — виден только в живом режиме
   (apiEndpoint задан, buildNotificationsWidget()) */
.smartNavBars-notifications-footer {
  display: block;
  padding: .625rem 1rem;
  color: var(--sportosphere-primary);
  font-size: .8333333333rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  border-top: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
}

.smartNavBars-notifications-footer:hover,
.smartNavBars-notifications-footer:focus {
  color: var(--sportosphere-primary);
  text-decoration: underline;
}

/* Аватар — <img> или инициалы-заглушка (buildAvatar() в smartNavBars.js,
   общий и для уведомлений, и для профиля). Размер задаётся местом
   использования (см. .smartNavBars-notification-avatar img/-user-avatar
   img ниже) — сам блок только про форму/фон инициалов, аналог родного
   .avatar/.avatar-name */

.smartNavBars-notification-avatar img,
.smartNavBars-notification-avatar .smartNavBars-avatar-name {
  display: block;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
}

.smartNavBars-avatar-name {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--sportosphere-avatar-name-bg);
  color: #fff;
  font-weight: 700;
  font-size: .8333333333rem;
  text-transform: uppercase;
}

/* ---- "Выпадающий список внешних приложений" — buildAppsWidget().
   Значения — .row/.col-4/.avatar-2xl/.hover-bg-200 в theme.min.css ---- */

.smartNavBars-apps-icon {
  font-size: 1.2rem;
}

.smartNavBars-apps-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .25rem;
  width: 16rem;
  padding: .75rem;
}

.smartNavBars-apps-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25rem;
  padding: .5rem;
  border-radius: .375rem;
  color: var(--sportosphere-gray-800);
  text-decoration: none;
  text-align: center;
  transition: background-color .2s ease-in-out;
}

.smartNavBars-apps-item:hover,
.smartNavBars-apps-item:focus {
  color: var(--sportosphere-gray-800);
  text-decoration: none;
  background-color: var(--sportosphere-gray-200);
}

.smartNavBars-apps-item-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
}

.smartNavBars-apps-item-label {
  margin-bottom: 0;
  font-size: .6944444444rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* ---- "Выпадающий список для управления аккаунтом" — buildUserWidget().
   Значения — .avatar-xl/.dropdown-item/.dropdown-divider в theme.min.css ---- */

.smartNavBars-user-avatar img,
.smartNavBars-user-avatar .smartNavBars-avatar-name {
  display: block;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
}

.smartNavBars-dropdown-link {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .25rem 1rem;
  color: var(--sportosphere-gray-800);
  font-size: .8333333333rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.smartNavBars-dropdown-link:hover,
.smartNavBars-dropdown-link:focus {
  color: var(--sportosphere-gray-link-hover-600);
  background-color: var(--sportosphere-dropdown-link-hover-bg-global);
  text-decoration: none;
}

.smartNavBars-dropdown-link-icon {
  flex: 0 0 auto;
}

.smartNavBars-dropdown-link-warning {
  color: var(--sportosphere-warning);
  font-weight: 700;
}

.smartNavBars-dropdown-link-warning:hover,
.smartNavBars-dropdown-link-warning:focus {
  color: var(--sportosphere-warning);
}

.smartNavBars-dropdown-divider {
  height: 0;
  margin: .5rem 0;
  border-top: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  opacity: 1;
}

/* overflow/max-height здесь намеренно НЕ заданы (в отличие от родного
   .navbar-top .navbar-collapse) — оригинал безопасно сочетает их с
   position:absolute-дропдаунами только благодаря Bootstrap JS/Popper
   (тот при необходимости выносит .dropdown-menu в position:fixed
   относительно viewport, минуя обрезание). У нас нет Bootstrap JS —
   наши .smartNavBars-bottom-dropdown лежат ВНУТРИ этого блока, и
   overflow:auto/hidden на предке обрезает их по DOM-иерархии независимо
   от z-index. overflow/max-height понадобятся вернуть (в media-запросе
   для мобильных, где список схлопывается вертикально и нужен скролл) при
   реализации мобильного тоггла — десктопная строка и так умещается */
.smartNavBars-top-row-bottom {
  padding: .5rem 0;
}

/* ---- Меню навигации в нижней строке двухстрочного варианта —
   buildMenuList()/buildMenuItem(). Значения — по тем же --sportosphere-*
   токенам, что и остальные пункты инструмента (nav-link цвета —
   --sportosphere-navbar-color/-hover-color, как у родного .navbar-nav
   .nav-link) ---- */

.smartNavBars-bottom-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  padding-left: 0;
  margin: 0;
  list-style: none;
}

.smartNavBars-bottom-item {
  position: relative;
}

.smartNavBars-bottom-link {
  display: flex;
  align-items: center;
  padding: .5rem;
  /* Тот же приём, что у --smartNavBars-visitsite-accent-bg выше — фолбэк
     на исходное значение (везде, кроме сайта-визитки, где переменная
     объявлена через [data-smart-navbar-type="visitsite"] — см. её
     комментарий; в .smartNavBars-vertical там же явно сброшена обратно в
     initial, чтобы не задеть мобильную подмену меню) */
  color: var(--smartNavBars-visitsite-accent-link, rgba(var(--sportosphere-emphasis-color-rgb), .55));
  text-decoration: none;
  cursor: pointer;
}

.smartNavBars-bottom-link:hover {
  color: var(--smartNavBars-visitsite-accent-link-hover, rgba(var(--sportosphere-emphasis-color-rgb), .7));
  text-decoration: none;
}

.smartNavBars-bottom-link-label {
  margin-left: .25rem;
}

/* ---- Всплывающее окно "Основного раздела меню" — buildMenuDropdown(), см.
   раздел 3.3 docs/smart-navbars-spec.md ("Заголовки"/"Приложения").
   Значения — родной .dropdown-menu.dropdown-caret.dropdown-menu-card
   (фон/рамка/тень/скругление/уголок-указатель) + .bg-white.rounded-3.py-2
   (внутренний паддинг/скругление) из примера в sample.html ---- */

/* Без собственного z-index — та же причина, что у .smartNavBars-widget-
   dropdown (см. её блок): свой z-index создаёт отдельный контекст
   наложения, в котором z-index:-1 у :after-треугольника перестаёт уходить
   под содержимое. Наследуемого приоритета от .smartNavBars-top
   (z-index:1020) достаточно */

.smartNavBars-bottom-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: -.5625rem;
  box-sizing: border-box;
  min-width: 10rem;
  margin: 0;
  padding: 0;
  overflow: visible;
  text-align: left;
  font-family: var(--sportosphere-font-sans-serif);
  font-size: .8333333333rem;
  font-weight: 600;
  color: var(--sportosphere-gray-300);
  background-color: var(--sportosphere-dropdown-bg-global);
  background-clip: padding-box;
  border: 0;
  border-radius: .375rem;
  box-shadow: var(--sportosphere-box-shadow);
}

.smartNavBars-bottom-dropdown.show {
  display: block;
}

/* Уголок-указатель ("caret") сверху окна — тот же приём, что у родного
   .dropdown-caret: повёрнутый на 45° квадрат под основным фоном */

.smartNavBars-bottom-dropdown:after {
  content: '';
  position: absolute;
  z-index: -1;
  top: -.375rem;
  left: 1.8rem;
  width: 1rem;
  height: 1rem;
  background: var(--sportosphere-dropdown-bg-global);
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-width: 1px 0 0 1px;
  border-radius: .125rem;
  transform: rotate(45deg);
  box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
}

.smartNavBars-bottom-dropdown-inner {
  padding: .5rem 0;
  background-color: var(--sportosphere-dropdown-bg-global);
  border-radius: var(--sportosphere-border-radius-lg);
}

/* "Заголовок" (topic) — нередактируемый жирный подзаголовок над группой
   ссылок внутри всплывающего окна */

.smartNavBars-bottom-topic {
  padding: .25rem 1rem;
  margin-bottom: 0;
  font-size: .8333333333rem;
  font-weight: 700;
  color: rgba(var(--sportosphere-gray-700-rgb), 1);
}

/* "Приложение" (item) — обычная кликабельная ссылка внутри всплывающего
   окна, и под заголовком-topic, и в простом плоском списке без заголовков */

.smartNavBars-bottom-dropdown-link {
  display: block;
  padding: .25rem 1rem;
  font-size: .8333333333rem;
  font-weight: 500;
  color: rgba(var(--sportosphere-gray-600-rgb), 1);
  text-decoration: none;
}

.smartNavBars-bottom-dropdown-link:hover {
  color: var(--sportosphere-gray-link-hover-600);
  background-color: var(--sportosphere-dropdown-link-hover-bg-global);
  text-decoration: none;
}

/* ---- Контент страницы — точка вставки верхней панели (и опциональной
   локальной панели раздела, см. buildLocalNav()). margin-left тут пока не
   нужен ни в одном состоянии — он появится вместе с вертикальной панелью
   (см. историю выше) ---- */

[data-smart-navbar-content] {
  min-height: 100vh;
  transition: margin-left .2s ease;
}

/* ---- Локальная панель навигации раздела — buildLocalNav()/
   buildLocalNavLink(). Значения — родные .card/.card-header (фон/тень/
   скругление/паддинг шапки) и .btn-sportosphere-default/-primary (цвета
   ссылок-вкладок, активная — через *-primary-* токены вместо *-default-*),
   перенесённые в собственные классы ---- */

/* position:relative + явный низкий z-index — БЕЗ этого панель (обычный
   непозиционированный блок) перекрывала раскрытые выпадающие окна верхней
   панели (.smartNavBars-widget-dropdown/.smartNavBars-bottom-dropdown,
   z-index:1000 внутри своего .smartNavBars-top, z-index:1020), хотя по
   идее позиционированный сосед со своим z-index должен рисоваться поверх
   непозиционированного. Тот же приём, что уже применён для аналогичного
   конфликта .smartNavBars-vertical/.smartNavBars-top (см. её блок) —
   не полагаться на неявное сравнение "без z-index против z-index", а
   зафиксировать порядок явно */

.smartNavBars-local {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  margin-bottom: 1rem;
  background-color: var(--sportosphere-emphasis-bg);
  border-radius: .375rem;
  box-shadow: var(--sportosphere-box-shadow);
}

.smartNavBars-local-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  background-color: transparent;
  border-bottom: 0px solid rgba(var(--sportosphere-emphasis-color-rgb), .125);
}

/* justify-content: flex-end — прижимает ряд вкладок к правому краю;
   flex: 1 1 0%/min-width: 0 держат tabs.clientWidth равным реально
   доступной ширине (не размеру собственного контента) — от этого зависит
   точность распределения "сколько вкладок влезает" в
   observeLocalNavOverflow() (smartNavBars.js). overflow: hidden здесь
   НАМЕРЕННО не задан — обрезает box-shadow кнопок (тень выходит за
   пределы border-box на несколько px), а не только их переполнение */
.smartNavBars-local-tabs {
  display: flex;
  flex: 1 1 0%;
  flex-wrap: nowrap;
  justify-content: flex-end;
  min-width: 0;
}

.smartNavBars-local-back,
.smartNavBars-local-link {
  display: block;
  box-sizing: border-box;
  padding: .1875rem .75rem;
  margin-right: .5rem;
  font-family: var(--sportosphere-font-sans-serif);
  font-size: .875rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--sportosphere-btn-sportosphere-default-color);
  text-align: center;
  white-space: nowrap;
  text-decoration: none;
  background-color: var(--sportosphere-btn-sportosphere-background);
  border: var(--sportosphere-border-width) solid var(--sportosphere-btn-sportosphere-background);
  border-radius: var(--sportosphere-border-radius);
  box-shadow: var(--sportosphere-btn-sportosphere-box-shadow);
  cursor: pointer;
  transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}

.smartNavBars-local-back {
  margin-right: 0;
}

.smartNavBars-local-back:hover,
.smartNavBars-local-link:hover {
  color: var(--sportosphere-btn-sportosphere-default-hover-color);
}

.smartNavBars-local-link-active {
  color: var(--sportosphere-btn-sportosphere-primary-color);
}

.smartNavBars-local-link-active:hover {
  color: var(--sportosphere-btn-sportosphere-primary-hover-color);
}

.smartNavBars-local-link-label {
  margin-left: .25rem;
}

/* ---- Приоритетная навигация локальной панели, три уровня (см.
   observeLocalNavOverflow() в smartNavBars.js): (1) весь ряд с подписями;
   (2) весь ряд без подписей (класс .smartNavBars-local-tabs-compact на
   .smartNavBars-local-tabs — скрывает подписи и у обычных вкладок, и у
   кнопки "Ещё", но НЕ у пунктов внутри уже открытого выпадающего списка —
   те вложены на 4 уровня глубже прямого потомка tabs, ни один из двух
   селекторов ниже до них не дотягивается); (3) столько иконок, сколько
   влезает, а остальные (в исходном порядке) — в выпадающий список за
   кнопкой "Ещё" (buildLocalNavMore()). Кнопка "Ещё" — та же разметка/
   стили обычной вкладки (buildLocalNavLink()), с модификатором
   -more-toggle; список — те же принципы, что и у .smartNavBars-bottom-
   dropdown (показ/скрытие через .show, фон/тень/скругление — те же
   глобальные токены) ---- */

.smartNavBars-local-tabs-compact > .smartNavBars-local-link .smartNavBars-local-link-label,
.smartNavBars-local-tabs-compact > .smartNavBars-local-more > .smartNavBars-local-more-toggle .smartNavBars-local-link-label {
  display: none;
}

.smartNavBars-local-more {
  position: relative;
}

.smartNavBars-local-more-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 1000;
  margin-top: .5rem;
  min-width: 10rem;
  padding: .5rem 0;
  background-color: var(--sportosphere-dropdown-bg-global);
  border-radius: .375rem;
  box-shadow: var(--sportosphere-box-shadow);
}

.smartNavBars-local-more-dropdown.show {
  display: block;
}

.smartNavBars-local-more-dropdown-inner {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding: 0 .5rem;
}

/* Пункты внутри списка "Ещё" — плоские (не кнопки/пилюли, как сама
   кнопка-тоггл и обычные вкладки ряда): фон/рамка/тень сброшены, вместо
   них — подсветка фона при наведении, тот же приём и та же переменная,
   что уже использует .smartNavBars-dropdown-link (выпадающий список
   аккаунта, см. выше) — по прямой правке пользователя, 2026-08-26. */

.smartNavBars-local-more-dropdown-inner .smartNavBars-local-link {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  margin-right: 0;
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
}

.smartNavBars-local-more-dropdown-inner .smartNavBars-local-link:hover {
  background-color: var(--sportosphere-dropdown-link-hover-bg-global);
}

/* ---- Вертикальная панель (сайдбар) — buildVerticalNav()/buildVerticalList()/
   buildVerticalItem(). Реализован только стиль "transparent" (по
   умолчанию) — значения перенесены из .navbar-vertical/.navbar-nav/
   .dropdown-indicator/.navbar-vertical-label* в theme.min.css (десктопный
   .navbar-expand-xl-вариант, @media(min-width:1200px)), не изобретены.

   ВРЕМЕННОЕ УПРОЩЕНИЕ (см. статус-комментарий над buildVerticalNav() в
   smartNavBars.js): у панели пока нет собственного логотипа, поэтому
   top задан равным var(--sportosphere-top-nav-height) (а не 0, как в
   оригинале, где это пространство наверху занимает лого) — понадобится
   пересмотреть вместе с логотипом. Свёрнутое-в-иконки состояние (this.
   collapsed), варианты оформления card/vibrant/inverted (this.style) и
   мобильная выезжающая панель (< 1200px) — НЕ реализованы: панель
   отображается одинаково на всех разрешениях экрана и только в стиле
   transparent, независимо от этих двух настроек ---- */

/* ИСПРАВЛЕНО: max-width унифицирован по образцу "card" (эталон, по прямому
   запросу пользователя — все 4 варианта оформления должны совпадать по
   вёрстке/расположению, отличаясь только цветом) — раньше тут было
   узкое 12.625rem только для "transparent" (родное значение "голого"
   .navbar-vertical), из-за чего при этом стиле (и при "inverted"/"vibrant",
   для которых своего CSS вообще не было — визуально они ничем не
   отличались от transparent) между правым краем панели и content
   образовывался зазор ~8rem (воспринимался как "меню сдвинуто влево") —
   у "card" (20.625rem) того же зазора не было, т.к. margin-left content
   (см. ниже) и без того всегда был 20.625rem. Теперь у всех стилей
   одна и та же геометрия — .smartNavBars-vertical-card/-inverted/-vibrant
   ниже содержат ТОЛЬКО цвет/тень, без повторного изменения размеров */

/* ИСПРАВЛЕНО: max-width панели больше НЕ уменьшается в свёрнутом
   состоянии (см. [data-smart-navbar-collapsed] ниже — там теперь сужается
   только .smartNavBars-vertical-collapse/-content) — по прямому запросу
   пользователя, чтобы .smartNavBars-vertical-toggle-row (кнопка+логотип)
   всегда оставалась на полной ширине и никогда не нуждалась в скрытии
   логотипа. Обратная сторона: у самого <nav> теперь всегда широкий бокс,
   у которого справа от узкой видимой .smartNavBars-vertical-collapse в
   свёрнутом состоянии остаётся невидимая (без фона) область — именно в
   неё "заезжает" расширившийся content. pointer-events:none тут и
   pointer-events:auto на двух реальных дочерних блоках ниже — чтобы эта
   невидимая область не перехватывала клики по content под ней.

   ИСПРАВЛЕНО: z-index поднят с 1019 до 1021 (выше .smartNavBars-top,
   1020, см. её блок) — по прямому запросу пользователя. Из-за того же
   сдвига content при сворачивании .smartNavBars-vertical-toggle-row
   (кнопка+логотип, y от 0 до var(--sportosphere-top-nav-height)) стала
   геометрически пересекаться с .smartNavBars-top — та рисовалась поверх
   и перекрывала логотип. z-index — это ЦЕЛЫЙ <nav>, а не только
   toggle-row: поднять z-index только потомку бессмысленно, он всё равно
   ограничен стековым контекстом родителя (.smartNavBars-vertical и
   .smartNavBars-top — отдельные стековые контексты, оба position:fixed/
   sticky + z-index, а ранжируются между собой только на уровне этих двух
   контекстов целиком, не глубже). Риск: если у выпадающих окон верхней
   панели (поиск/виджеты/разделы меню) когда-нибудь появится расширение
   вниз в ту же зону — они окажутся под вертикальной панелью, а не
   поверх; сейчас там пусто, актуального конфликта нет */

/* ИСТОРИЯ: left/position тут пытались чинить несколько раз подряд для
   мобильного поведения (безусловный left:0 — ломал десктоп; left:0 только
   в мобильном медиазапросе — ломал .smartNavBars-vertical-toggle-row;
   -100vw у translateX — работало, но пользователь отказался от самого
   варианта "выезжающая шторка"; затем — попытка сделать панель обычным
   поточным блоком на мобильном — сломала позицию .smartNavBars-top, см.
   блок "Мобильное поведение вертикального меню" в конце файла).
   Подробности всех отменённых веток больше не актуальны — position:fixed
   ниже действует БЕЗУСЛОВНО, на всех разрешениях экрана одинаково: и на
   десктопе, и на мобильном кнопка+логотип "встроены" в полосу
   .smartNavBars-top одним и тем же способом, различается только то, что
   раскрывается по клику (свёрнутое-в-иконки на десктопе или список поверх
   content на мобильном, см. блок в конце файла) */

.smartNavBars-vertical {
  position: fixed;
  top: var(--sportosphere-top-nav-height);
  height: calc(100vh - var(--sportosphere-top-nav-height));
  display: inline-block;
  width: 100%;
  max-width: 20.625rem;
  z-index: 1021;
  padding: 0;
  pointer-events: none;
}

/* Кнопка сворачивания/разворачивания — buildVerticalToggle() в
   smartNavBars.js, дословно перенесённый образец из _navbar_vertical
   (rules.md §5). display:flex у самой кнопки задан безусловно (а не
   только в @media(min-width:1200px), как в оригинале, где ниже этой
   ширины показывается другой, мобильный тогглер) — кнопка (в отличие от
   родного разделения на два разных тогглера) везде одна и та же, меняется
   только её обработчик клика (buildVerticalToggle() в smartNavBars.js) и
   то, что она раскрывает — см. isMobileViewport()/setMobileNavOpen() там
   же и блок "Мобильное поведение вертикального меню" в конце файла.
   .toggle-icon-wrapper без
   модификатора стиля — из .navbar-vertical .toggle-icon-wrapper
   (действует всегда); override для "card" — у .smartNavBars-vertical-card
   ниже, из .navbar-vertical.navbar-expand-xl.navbar-card .toggle-icon-
   wrapper.

   ИСПРАВЛЕНО: .smartNavBars-vertical-toggle-row строился первым ребёнком
   .smartNavBars-vertical В ОБЫЧНОМ ПОТОКЕ, перед .smartNavBars-vertical-
   collapse — из-за этого сдвигал видимую часть панели (список пунктов)
   вниз на свою высоту, ломая выравнивание её верхнего края с верхним
   краем [data-smart-navbar-content]/.smartNavBars-local (обе точки
   отсчёта используют один и тот же var(--sportosphere-top-nav-height),
   см. п. 8 docs/smart-navbars-spec.md). В оригинале этого сдвига нет,
   т.к. там у .navbar-vertical top:0 (панель растянута от самого верха
   экрана, а верхнюю строку целиком занимает лого — места, которого у
   нас пока нет, см. статус-комментарий над buildVerticalNav()), и
   .d-flex.align-items-center с тогглером — часть той же полной высоты
   панели, а не надстройка над ней.

   Раз своего лого у панели ещё нет, а верхний край видимой части должен
   остаться на var(--sportosphere-top-nav-height) (см. запрос
   пользователя), кнопка выведена из потока (position:absolute;
   bottom:100%) и встаёт в пространство НАД .smartNavBars-vertical (т.е.
   ровно в промежуток 0..var(--sportosphere-top-nav-height) слева от
   верхней панели, где сейчас и так пусто — верхняя панель занимает
   только область [data-smart-navbar-content], см. margin-left у неё
   ниже), не влияя на положение .smartNavBars-vertical-collapse */

.smartNavBars-vertical-toggle-row {
  position: absolute;
  bottom: 100%;
  left: 0;
  width: 100%;
  height: var(--sportosphere-top-nav-height);
  display: flex;
  align-items: center;
  pointer-events: auto;
}

/* ИСПРАВЛЕНО: margin-left/margin-right унифицированы по образцу "card" для
   ВСЕХ стилей (было -.75rem/1.25rem — родное значение "голого"
   .navbar-vertical, действовавшее только для transparent, тогда как
   card/inverted/vibrant уже переопределяли его на .25rem/1.5rem, см. ниже,
   удалено как избыточное) — при общей для всех стилей ширине панели (см.
   .smartNavBars-vertical выше) разные margin означали разное положение
   этого блока (кнопка+логотип) при transparent относительно остальных
   трёх стилей — пользователь заметил и указал на это отдельно. Значение
   --smartNavBars-vertical-collapsed-width (см. .smartNavBars-app выше)
   зависело от старых чисел — пересчитано вместе с этой правкой */

.smartNavBars-vertical-toggle-wrapper {
  margin-left: .25rem;
  padding-left: .125rem;
  margin-right: 1.5rem;
}

.smartNavBars-vertical-toggle {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 2.5rem;
  width: 2.5rem;
  padding: .3125rem;
  border: 0;
  border-radius: 50%;
  background-color: transparent;
  box-shadow: none;
  cursor: pointer;
}

.smartNavBars-vertical-toggle:hover,
.smartNavBars-vertical-toggle:focus {
  background-color: var(--sportosphere-gray-300) !important;
}

.smartNavBars-vertical-toggle-icon {
  position: relative;
  height: .125rem;
  width: 1.25rem;
  display: block;
  top: -.3125rem;
  padding-right: 0;
  transition: all .2s ease-in-out;
}

.smartNavBars-vertical-toggle-line {
  display: block;
  height: 100%;
  border-radius: .25rem;
  background-color: var(--sportosphere-gray-700);
  transition: all .2s ease-in-out;
}

.smartNavBars-vertical-toggle-icon:before,
.smartNavBars-vertical-toggle-icon:after {
  content: "";
  position: absolute;
  height: .125rem;
  width: 100%;
  left: 0;
  border-radius: .25rem;
  background-color: var(--sportosphere-gray-700);
  transition: all .2s ease-in-out;
}

.smartNavBars-vertical-toggle-icon:before {
  top: .3125rem;
}

.smartNavBars-vertical-toggle-icon:after {
  top: .625rem;
}

/* Собственная визуальная обратная связь кнопки при [data-smart-navbar-
   collapsed] на this.root (setCollapsed() в smartNavBars.js) — три
   полоски сужаются, как в оригинале (.navbar-vertical-collapsed
   .navbar-toggle-icon/:after/:before).

   ДОБАВЛЕНО: та же обратная связь — и при [data-smart-navbar-mobile-open]
   (setMobileNavOpen() в smartNavBars.js, мобильное раскрытие < 1200px, см.
   блок "Мобильное поведение вертикального меню" в конце файла) — состояния разные
   ([data-smart-navbar-collapsed] десктопный персистентный, -mobile-open
   мобильный сессионный, не персистится), но кнопка та же самая физически
   и визуальная реакция на её нажатие уместна в обоих случаях одинаково */

[data-smart-navbar-collapsed] .smartNavBars-vertical-toggle-icon,
[data-smart-navbar-mobile-open] .smartNavBars-vertical-toggle-icon {
  padding-right: .3125rem;
}

[data-smart-navbar-collapsed] .smartNavBars-vertical-toggle-icon:after,
[data-smart-navbar-mobile-open] .smartNavBars-vertical-toggle-icon:after {
  width: 75%;
}

[data-smart-navbar-collapsed] .smartNavBars-vertical-toggle-icon:before,
[data-smart-navbar-mobile-open] .smartNavBars-vertical-toggle-icon:before {
  width: 50%;
}

/* ИСПРАВЛЕНО и УБРАНО: логотип в .smartNavBars-vertical-toggle-row раньше
   скрывался при [data-smart-navbar-collapsed], т.к. сама строка была
   width:100% от УЖЕ СУЖЕННОЙ панели и логотип в ней переполнял видимую
   область. Теперь (по прямому запросу пользователя) в свёрнутом состоянии
   сужается только .smartNavBars-vertical-collapse/-content — сам <nav> и,
   соответственно, .smartNavBars-vertical-toggle-row остаются на полной
   ширине всегда (см. .smartNavBars-vertical выше), поэтому логотипу
   больше некуда переполняться и прятать его не нужно: положение и
   отображение кнопки+логотипа теперь одинаковы в развёрнутом и свёрнутом
   состоянии */

.smartNavBars-vertical-collapse {
  width: 19.625rem;
  height: 100%;
  overflow: hidden;
  border-radius: .375rem;
  background-color: var(--sportosphere-navbar-vertical-default-bg-color);
  pointer-events: auto;
}

.smartNavBars-vertical-content {
  width: 19.625rem;
  height: 100%;
  padding: .7rem .625rem 0 1rem;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  scrollbar-width: thin;
  scrollbar-color: var(--sportosphere-navbar-vertical-default-scrollbar-color) transparent;
}

.smartNavBars-vertical-collapse,
.smartNavBars-vertical-content {
  max-height: calc(100vh - var(--sportosphere-top-nav-height) - 1rem);
}

.smartNavBars-vertical-content::-webkit-scrollbar-thumb {
  background-color: var(--sportosphere-navbar-vertical-default-scrollbar-color);
}

.smartNavBars-vertical-list {
  display: flex;
  flex-direction: column;
  padding-left: 0;
  margin: 0 0 1rem;
  list-style: none;
  font-size: .875rem;
  font-weight: 500;
}

/* Заголовок раздела (type:'label') — двухколоночная строка: подпись
   фиксированной ширины + горизонтальная линия, занимающая остаток */

.smartNavBars-vertical-label-row {
  display: flex;
  align-items: center;
  min-height: 1.125rem;
  margin: 1rem 0 .5rem;
}

.smartNavBars-vertical-label {
  flex: 0 0 auto;
  font-size: .75rem;
  color: var(--sportosphere-navbar-vertical-default-label-color);
  /* В оригинале отступ до разделителя даёт правый паддинг Bootstrap-
     колонки .col-auto (сетка .row > .col-auto + .col.ps-0), т.е.
     padding-right: calc(var(--sportosphere-gutter-x)*.5) — но
     --sportosphere-gutter-x объявлена ЛОКАЛЬНО внутри .container/.row/
     .g-* в theme.min.css, а не на :root (rules.md §3), и у наших classes
     недоступна — дословный var() тут резолвился бы в 0. Значение взято
     вычисленным: 2rem (--sportosphere-gutter-x на .row) * .5 = 1rem */
  padding-right: 1rem;
}

.smartNavBars-vertical-label-line {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 0 !important;
}

.smartNavBars-vertical-divider {
  /* margin-top:.5625rem у оригинального .navbar-vertical-divider был
     рассчитан под родную Bootstrap-сетку (.row/.col) — в нашей flex-строке
     (.smartNavBars-vertical-label-row{align-items:center}) он, наоборот,
     смещал линию вниз от центра строки. У flex align-items:center все
     элементы центрируются по margin-box одной и той же линии — обнулив
     margin, линия (тонкий border-top) центрируется наравне с текстом
     .smartNavBars-vertical-label, без ручного расчёта отступа */
  margin: 0;
  border: 0;
  border-top: var(--sportosphere-border-width) solid;
  opacity: var(--sportosphere-hr-opacity);
  color: var(--sportosphere-navbar-vertical-default-hr-color);
}

.smartNavBars-link {
  display: block;
  position: relative;
  padding: .35rem 0;
  color: var(--sportosphere-navbar-vertical-default-link-color);
  text-decoration: none;
  cursor: pointer;
  word-break: break-word;
  transition: all .2s ease-in-out;
}

.smartNavBars-link:hover,
.smartNavBars-link:focus {
  color: var(--sportosphere-navbar-vertical-default-link-hover-color);
}

.smartNavBars-link-active {
  color: var(--sportosphere-navbar-vertical-default-link-active-color);
}

.smartNavBars-link-body {
  display: flex;
  align-items: center;
}

.smartNavBars-link-icon {
  width: 1.5rem;
  min-width: 1.5rem;
  font-size: .875rem;
}

.smartNavBars-link-text {
  padding-left: .25rem;
}

/* Уголок-индикатор раскрытия — тот же приём, что у родного
   .dropdown-indicator: повёрнутый на 45° квадрат из двух границ,
   разворачивается на 225° при [aria-expanded=true] */

.smartNavBars-link-toggle:after {
  content: '';
  display: block;
  position: absolute;
  right: 5px;
  top: 50%;
  width: .4rem;
  height: .4rem;
  border-right: 1px solid var(--sportosphere-navbar-vertical-default-link-color);
  border-bottom: 1px solid var(--sportosphere-navbar-vertical-default-link-color);
  transform: translateY(-50%) rotate(45deg);
  transform-origin: center;
  transition: transform .2s ease-in-out, border-color .2s ease-in-out;
}

.smartNavBars-link-toggle:hover:after,
.smartNavBars-link-toggle:focus:after {
  border-color: var(--sportosphere-navbar-vertical-default-link-hover-color);
}

.smartNavBars-link-toggle[aria-expanded="true"]:after {
  transform: translateY(-50%) rotate(225deg);
}

.smartNavBars-badge {
  display: inline-block;
  margin-left: .5rem;
  padding: .355555em .711111em;
  font-size: .75em;
  font-weight: 600;
  line-height: 1;
  color: #fff;
  text-align: center;
  white-space: nowrap;
  vertical-align: baseline;
  border-radius: var(--sportosphere-border-radius-pill);
}

.smartNavBars-badge-primary { background-color: var(--sportosphere-primary); }
.smartNavBars-badge-secondary { background-color: var(--sportosphere-secondary); }
.smartNavBars-badge-success { background-color: var(--sportosphere-success); }
.smartNavBars-badge-danger { background-color: var(--sportosphere-danger); }
.smartNavBars-badge-warning { background-color: var(--sportosphere-warning); }
.smartNavBars-badge-info { background-color: var(--sportosphere-info); }

/* Вложенный список (children) — свой .show по клику (toggleVerticalSubmenu()
   в smartNavBars.js), без Bootstrap JS. Отступ пункта растёт с глубиной —
   те же 4 уровня, что и в оригинале (.navbar-vertical .navbar-nav
   .nav[ .nav[ .nav[ .nav ]]] .nav-item .nav-link), читаются по числу
   предков .smartNavBars-vertical-submenu, а не JS-параметром */

.smartNavBars-vertical-submenu {
  display: none;
  flex-direction: column;
  padding-left: 0;
  margin: 0;
  list-style: none;
  font-size: .8333333333rem;
}

.smartNavBars-vertical-submenu.show {
  display: flex;
}

.smartNavBars-vertical-submenu > .smartNavBars-vertical-item > .smartNavBars-link {
  padding: .2rem .2rem .2rem 1.5rem;
}

.smartNavBars-vertical-submenu > .smartNavBars-vertical-item:last-child {
  margin-bottom: .35rem;
}

.smartNavBars-vertical-submenu .smartNavBars-vertical-submenu > .smartNavBars-vertical-item > .smartNavBars-link {
  padding-left: 2.625rem;
}

.smartNavBars-vertical-submenu .smartNavBars-vertical-submenu .smartNavBars-vertical-submenu > .smartNavBars-vertical-item > .smartNavBars-link {
  padding-left: 4.125rem;
}

.smartNavBars-vertical-submenu .smartNavBars-vertical-submenu .smartNavBars-vertical-submenu .smartNavBars-vertical-submenu > .smartNavBars-vertical-item > .smartNavBars-link {
  padding-left: 5.625rem;
}

/* ---- Варианты оформления "card"/"inverted"/"vibrant" (this.style,
   модификатор smartNavBars-vertical-<style> ставит setStyle()) — размеры/
   паддинги/радиус/max-height ОБЩИЕ для всех ЧЕТЫРЁХ стилей (см.
   .smartNavBars-vertical/-collapse/-content выше, включая margin-left/
   margin-right у .smartNavBars-vertical-toggle-wrapper — ИСПРАВЛЕНО,
   раньше было отдельным исключением именно для transparent, см. её
   комментарий выше). Три блока ниже меняют ТОЛЬКО цвет/фон/тень.

   Значения — .navbar-vertical.navbar-card/.navbar-vibrant/.navbar-inverted
   и их .navbar-collapse в theme.min.css, не изобретены. У "inverted" и
   "vibrant" НЕТ box-shadow (родное — он есть только у card); у "vibrant"
   — background-image (фото+градиент), а не background-color ---- */

.smartNavBars-vertical-card .smartNavBars-vertical-collapse {
  background-color: var(--sportosphere-navbar-vertical-card-bg-color);
  box-shadow: 0 7px 14px 0 rgba(65, 69, 88, .1), 0 3px 6px 0 rgba(0, 0, 0, .07);
}

.smartNavBars-vertical-card .smartNavBars-vertical-content::-webkit-scrollbar-thumb {
  background-color: var(--sportosphere-navbar-vertical-card-scrollbar-color);
}

.smartNavBars-vertical-card .smartNavBars-vertical-label {
  color: var(--sportosphere-navbar-vertical-card-label-color);
}

.smartNavBars-vertical-card .smartNavBars-vertical-divider {
  color: var(--sportosphere-navbar-vertical-card-hr-color);
}

.smartNavBars-vertical-card .smartNavBars-link {
  color: var(--sportosphere-navbar-vertical-card-link-color);
}

.smartNavBars-vertical-card .smartNavBars-link:hover,
.smartNavBars-vertical-card .smartNavBars-link:focus {
  color: var(--sportosphere-navbar-vertical-card-link-hover-color);
}

.smartNavBars-vertical-card .smartNavBars-link-active {
  color: var(--sportosphere-navbar-vertical-card-link-active-color);
}

.smartNavBars-vertical-card .smartNavBars-link-toggle:after {
  border-color: var(--sportosphere-navbar-vertical-card-link-color);
}

.smartNavBars-vertical-card .smartNavBars-link-toggle:hover:after,
.smartNavBars-vertical-card .smartNavBars-link-toggle:focus:after {
  border-color: var(--sportosphere-navbar-vertical-card-link-hover-color);
}

.smartNavBars-vertical-inverted .smartNavBars-vertical-collapse {
  background-color: var(--sportosphere-navbar-vertical-inverted-bg-color);
}

.smartNavBars-vertical-inverted .smartNavBars-vertical-content::-webkit-scrollbar-thumb {
  background-color: var(--sportosphere-navbar-vertical-inverted-scrollbar-color);
}

.smartNavBars-vertical-inverted .smartNavBars-vertical-label {
  color: var(--sportosphere-navbar-vertical-inverted-label-color);
}

.smartNavBars-vertical-inverted .smartNavBars-vertical-divider {
  color: var(--sportosphere-navbar-vertical-inverted-hr-color);
}

.smartNavBars-vertical-inverted .smartNavBars-link {
  color: var(--sportosphere-navbar-vertical-inverted-link-color);
}

.smartNavBars-vertical-inverted .smartNavBars-link:hover,
.smartNavBars-vertical-inverted .smartNavBars-link:focus {
  color: var(--sportosphere-navbar-vertical-inverted-link-hover-color);
}

.smartNavBars-vertical-inverted .smartNavBars-link-active {
  color: var(--sportosphere-navbar-vertical-inverted-link-active-color);
}

.smartNavBars-vertical-inverted .smartNavBars-link-toggle:after {
  border-color: var(--sportosphere-navbar-vertical-inverted-link-color);
}

.smartNavBars-vertical-inverted .smartNavBars-link-toggle:hover:after,
.smartNavBars-vertical-inverted .smartNavBars-link-toggle:focus:after {
  border-color: var(--sportosphere-navbar-vertical-inverted-link-hover-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-vertical-collapse {
  background-image: var(--sportosphere-navbar-vertical-vibrant-bg-image);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  backface-visibility: hidden;
}

.smartNavBars-vertical-vibrant .smartNavBars-vertical-content::-webkit-scrollbar-thumb {
  background-color: var(--sportosphere-navbar-vertical-vibrant-scrollbar-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-vertical-label {
  color: var(--sportosphere-navbar-vertical-vibrant-label-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-vertical-divider {
  color: var(--sportosphere-navbar-vertical-vibrant-hr-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-link {
  color: var(--sportosphere-navbar-vertical-vibrant-link-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-link:hover,
.smartNavBars-vertical-vibrant .smartNavBars-link:focus {
  color: var(--sportosphere-navbar-vertical-vibrant-link-hover-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-link-active {
  color: var(--sportosphere-navbar-vertical-vibrant-link-active-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-link-toggle:after {
  border-color: var(--sportosphere-navbar-vertical-vibrant-link-color);
}

.smartNavBars-vertical-vibrant .smartNavBars-link-toggle:hover:after,
.smartNavBars-vertical-vibrant .smartNavBars-link-toggle:focus:after {
  border-color: var(--sportosphere-navbar-vertical-vibrant-link-hover-color);
}

/* ---- Свёрнутое-в-иконки состояние — [data-smart-navbar-collapsed] на
   this.root, ставит/убирает setCollapsed() (см. buildVerticalToggle() в
   smartNavBars.js). Скрываются подписи разделов, текст пунктов и
   уголок-индикатор раскрытия подменю (сама иконка пункта и бейджи не
   тронуты — этого не просили; при необходимости — отдельным шагом).

   ИСПРАВЛЕНО: сужается только .smartNavBars-vertical-collapse/-content
   (до --smartNavBars-vertical-collapsed-width, см. .smartNavBars-app
   выше) — САМ <nav class="smartNavBars-vertical"> больше не сужается
   (раньше здесь было ещё [data-smart-navbar-collapsed] .smartNavBars-
   vertical{max-width:...}, убрано по прямому запросу пользователя: цель —
   .smartNavBars-vertical-toggle-row с кнопкой и логотипом остаётся на
   полной ширине всегда, см. .smartNavBars-vertical выше про
   pointer-events). Специфичность у следующего правила (0,2,0) совпадает с
   .smartNavBars-vertical-card .smartNavBars-vertical-collapse/-content
   выше — побеждает за счёт порядка (идёт позже в файле), поэтому блок
   должен оставаться ПОСЛЕ стилей card, а не до.

   ДОБАВЛЕНО: весь блок (вместе с разворотом по наведению ниже) обёрнут в
   @media(min-width:1200px) — по прямому запросу пользователя свёрнутое-в-
   иконки состояние актуально только на десктопе (раздел 3.5). Без этой
   обёртки при персистентном this.collapsed=true (сохранён с прошлого
   визита на десктопе) на мобильном раскрытая по клику панель (см. блок
   "Мобильное поведение вертикального меню" в конце файла) отображалась бы
   на полную ширину (100%), но подписи/текст пунктов всё равно скрывались
   бы этим правилом как в свёрнутом виде — противоречие, панель выглядела
   бы сломанной (широкая, но без текста ссылок) ---- */

@media (min-width: 1200px) {
  [data-smart-navbar-collapsed] .smartNavBars-vertical-label,
  [data-smart-navbar-collapsed] .smartNavBars-link-text,
  [data-smart-navbar-collapsed] .smartNavBars-link-toggle:after {
    display: none;
  }

  [data-smart-navbar-collapsed] .smartNavBars-vertical-collapse,
  [data-smart-navbar-collapsed] .smartNavBars-vertical-content {
    width: var(--smartNavBars-vertical-collapsed-width);
  }

  /* ---- Разворот поверх контента по наведению — аналог родного
     .navbar-vertical-collapsed-hover. data-smart-navbar-collapsed-hover
     ставится/убирается по mouseenter/mouseleave на .smartNavBars-vertical-
     collapse (см. buildVerticalNav() в smartNavBars.js), а не на весь <nav> —
     наведение на саму кнопку-тогглер (она вне .smartNavBars-vertical-collapse,
     см. buildVerticalToggle()) временный разворот не запускает. Content
     (margin-left) НЕ трогаем — панель просто ложится поверх него
     (position: fixed уже есть у .smartNavBars-vertical, z-index: 1019).

     Ширина .smartNavBars-vertical-collapse/-content на наведении — тот же
     литерал, что и в обычном (не свёрнутом) состоянии, один и тот же для
     всех стилей (см. .smartNavBars-vertical-card/-inverted/-vibrant выше).
     Сам <nav> ЗДЕСЬ переопределять больше не нужно — его max-width теперь
     всегда 20.625rem, свёрнутое состояние его не трогает (см. .smartNavBars-
     vertical и комментарий над [data-smart-navbar-collapsed] .smartNavBars-
     vertical-collapse выше). Тень у .smartNavBars-vertical-collapse —
     родное значение (.navbar-vertical-collapsed-hover .navbar-collapse) —
     только для стилей БЕЗ своей постоянной тени (:not(card) — у card она
     есть всегда, независимо от состояния, добавлять поверх не нужно и даже
     вредно: специфичность этого правила выше, переопределила бы
     собственную тень card на меньшую) */

  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-vertical-collapse,
  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-vertical-content {
    width: 19.625rem;
  }

  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-vertical:not(.smartNavBars-vertical-card) .smartNavBars-vertical-collapse {
    box-shadow: .625rem 0 .625rem -.5625rem rgba(0, 0, 0, .2);
  }

  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-vertical-label,
  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-link-text {
    display: block;
  }

  [data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover] .smartNavBars-link-toggle:after {
    display: block;
  }
}

/* Отступ под сайдбар — только для раскладок, где он показан (см.
   SMART_NAVBAR_LAYOUTS в smartNavBars.js: base/combo — vertical:true).
   data-smart-navbar-layout стоит на rootId — предке [data-smart-navbar-
   content], а не на нём самом.

   ИСПРАВЛЕНО: значение НЕ зависит от оформления (this.style) — проверено
   по родному .navbar-vertical.navbar-expand-xl+.content в theme.min.css:
   там ОДНА и та же margin-left:20.625rem что для голого .navbar-vertical
   (ширина панели 12.625rem), что для .navbar-card/.navbar-vibrant/
   .navbar-inverted (внешний max-width уже 20.625rem, внутренняя карточка
   19.625rem — с ней margin-left ровно совпадает с внешним краем панели,
   без зазора). Раньше здесь стояло 12.625rem для transparent, а card
   переопределялся отдельным составным [data-smart-navbar-style="card"]-
   селектором на 19.625rem (ширину ВНУТРЕННЕЙ карточки, а не внешнего
   <nav>) — из-за этого у card-варианта пропадал зазор между правым краем
   карточки и content (сообщил пользователь, скриншот). Правильное общее
   значение — 20.625rem всегда, независимо от style; отдельный
   стиль-зависимый селектор и связанный с ним data-smart-navbar-style
   (были нужны только для этого) убраны как избыточные.

   С тех пор ширина панели унифицирована по образцу card для ВСЕХ стилей
   (см. .smartNavBars-vertical выше) — то есть теперь у 20.625rem margin-
   left отсутствие зазора справедливо для всех четырёх стилей, а не
   только для card, как раньше */

[data-smart-navbar-layout="base"] [data-smart-navbar-content],
[data-smart-navbar-layout="combo"] [data-smart-navbar-content] {
  margin-left: 20.625rem;
}

/* Тот же отступ в свёрнутом состоянии — без переопределения тут остался
   бы огромный зазор до content (20.625rem), т.к. правило выше не знает о
   [data-smart-navbar-collapsed]. Специфичность выше (два атрибута на
   одном элементе rootId + один у content = (0,3,0) против (0,2,0) у
   правила выше) — побеждает без учёта порядка в файле.

   ИСПРАВЛЕНО: изначально margin-left был РАВЕН ширине панели
   (--smartNavBars-vertical-collapsed-width, без зазора) — по образцу
   родного .navbar-vertical-collapsed ...+.content в theme.min.css, где
   margin-left действительно точно совпадает с шириной свёрнутой панели
   (3.125rem/4.125rem — зазора там нет вообще, в отличие от развёрнутого
   состояния). На практике это визуально "склеивало" правый край
   свёрнутой панели с content — пользователь отметил пропажу зазора,
   который был в развёрнутом виде. Добавлен тот же зазор, что уже
   используется в этой же панели для "card" (см. комментарий выше:
   20.625rem margin-left − 19.625rem ширина карточки = 1rem между правым
   краем видимой панели и content) — то есть здесь сознательно ОТХОДИМ от
   родного 1-в-1 поведения (там в свёрнутом виде зазора нет) в пользу
   зрительной консистентности с уже принятым в этом же компоненте
   решением.

   ДОБАВЛЕНО: обёрнуто в @media(min-width:1200px) — по той же причине, что
   и у остальных правил свёрнутого-в-иконки состояния выше (актуально
   только на десктопе, раздел 3.5). На мобильном margin-left вообще не
   должен зависеть от [data-smart-navbar-collapsed] — там своё, не
   зависящее от состояния 0 (панель, что свёрнута, что раскрыта по клику,
   всегда ложится поверх content, никогда его не раздвигает), см. блок
   "Мобильное поведение вертикального меню" в конце файла */

@media (min-width: 1200px) {
  [data-smart-navbar-collapsed][data-smart-navbar-layout="base"] [data-smart-navbar-content],
  [data-smart-navbar-collapsed][data-smart-navbar-layout="combo"] [data-smart-navbar-content] {
    margin-left: calc(var(--smartNavBars-vertical-collapsed-width) + 1rem);
  }
}

/* ---- Панель настроек: скелет ----
   Offcanvas-обёртка и шапка строились в smartNavBars.js (buildSettingsPanel()/
   buildSettingsBackdrop()/buildSettingsToggle()) на настоящих классах
   Bootstrap/theme.min.css (.offcanvas/.offcanvas-end/.settings-panel/
   .settings-panel-header/.bg-shape/.setting-toggle/.settings-popover и т.д.)
   — перенесено на собственные smartNavBars-settings-* классы (CSS-5,
   known-issues.md; openspec/changes/smartnavbars-native-styles), последний
   из 4 инструментов библиотеки с такой зависимостью. Значения — дословно
   (rules.md §5) из theme.min.css (единственный реально подключаемый файл —
   theme.css нигде не используется, не источник правды), включая всю
   механику, которую раньше давали настоящие Bootstrap-классы бесплатно
   (позиционирование/z-index/transform/transition анимации выезда — здесь
   нет ни Bootstrap JS, ни классов .showing/.hiding, только собственный
   classList.toggle('show', ...), см. заголовок smartNavBars.js).

   Атрибуты data-smart-navbar-settings/-backdrop (использовались как
   CSS-скоупинг, чтобы не задеть прежний живой widget_settingsOffcanvas на
   других страницах портала) убраны вместе с классами — тот виджет
   физически удалён задачей VS-15 (known-issues.md), новые классы уникальны
   сами по себе и в скоупинге не нуждаются.

   Содержимое панели (4 блока настроек) в этот "скелет" не входит — его
   строит smartNavBars.js отдельно, см. следующий раздел. */

.smartNavBars-settings-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0; /* .offcanvas — без этого панель сжимается по контенту вместо растяжения на всю высоту */
  z-index: 1045; /* --sportosphere-offcanvas-zindex */
  display: flex;
  flex-direction: column;
  max-width: 100%;
  width: 400px; /* --sportosphere-offcanvas-width */
  color: var(--sportosphere-body-color); /* --sportosphere-offcanvas-color */
  visibility: hidden;
  background-color: var(--sportosphere-quaternary-bg); /* --sportosphere-offcanvas-bg */
  background-clip: padding-box;
  outline: 0;
  box-shadow: var(--sportosphere-box-shadow-sm); /* --sportosphere-offcanvas-box-shadow */
  transition: transform 0.3s ease-in-out;
  transform: translateX(100%);
  max-width: 22rem; /* .settings-panel */
}

.smartNavBars-settings-panel.show {
  visibility: visible;
  transform: none;
}

.smartNavBars-settings-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1040;
  width: 100vw;
  height: 100vh;
  background-color: #000;
  display: none;
}

.smartNavBars-settings-backdrop.show {
  display: block;
  opacity: 0.5;
}

.smartNavBars-settings-panel-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 0.5rem 1.25rem;
  position: relative;
  overflow: hidden;
  background-color: var(--sportosphere-bg-shape-bg);
}

.smartNavBars-settings-panel-header:before,
.smartNavBars-settings-panel-header:after {
  position: absolute;
  content: "";
  border-radius: 50%;
  height: 12.5rem;
  width: 12.5rem;
}

.smartNavBars-settings-panel-header:before {
  left: 5.125rem;
  top: 1.188rem;
  background-image: var(--sportosphere-bg-shape-bg-dtl);
}

.smartNavBars-settings-panel-header:after {
  right: -6.25rem;
  top: -7.938rem;
  background-image: var(--sportosphere-bg-shape-bg-ltd);
}

.smartNavBars-settings-panel-header-inner {
  z-index: 1;
  padding: 0.25rem 0;
}

.smartNavBars-settings-reset-btn {
  border-radius: var(--sportosphere-border-radius-pill);
  margin-top: 0;
  margin-bottom: 0;
  font-size: 12px;
}

.smartNavBars-settings-close-btn {
  box-sizing: content-box;
  width: 1em;
  height: 1em;
  padding: 0.5rem 0.5rem;
  margin-top: -0.5rem;
  color: #000;
  background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center / 1em auto no-repeat;
  border: 0;
  border-radius: 0.25rem;
  opacity: 0.5;
  cursor: pointer;
  /* Кнопка всегда лежит на цветном/тёмном фоне .smartNavBars-settings-
     panel-header (bg-shape), независимо от текущей темы сайта — раньше
     это обеспечивал реальный Bootstrap-класс .btn-close вместе с
     оборачивающим data-bs-theme="dark" ([data-bs-theme=dark] .btn-close,
     theme.min.css — инвертирует чёрный крестик в белый). Крестик
     сознательно самодостаточен (design.md, Decision 5) и не зависит от
     оборачивающего атрибута — тот же эффект получен напрямую. */
  filter: invert(1) grayscale(100%) brightness(200%);
}

.smartNavBars-settings-close-btn:hover,
.smartNavBars-settings-close-btn:focus {
  opacity: 0.75;
}

.smartNavBars-settings-close-btn:focus {
  outline: 0;
  box-shadow: 0 0 0 0.25rem rgba(44, 123, 229, 0.25);
}

.smartNavBars-settings-panel-body {
  flex: 1 1 auto;
  padding: 1rem 1.25rem; /* --sportosphere-offcanvas-padding-y/x, px-x1 переопределяет padding-x на 1.25rem */
  overflow-y: auto;
  height: 100%;
}

/* Плавающая вкладка-триггер открытия панели (buildSettingsToggle()) — было
   card+setting-toggle: базовые визуальные свойства .card взяты из
   актуального, ничем не переопределяемого дальше по файлу блока (не из
   наивного первого объявления) — те же значения, что уже перепроверены и
   зафиксированы при проектировании .widget (sportosphereTheme.css). Второй
   box-shadow ниже (--sportosphere-setting-toggle-shadow) сознательно
   объявлен ПОСЛЕ первого и побеждает его — так же, как .setting-toggle
   (позже .card в theme.min.css) побеждал в исходном каскаде. */
.smartNavBars-settings-toggle {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background-color: var(--sportosphere-emphasis-bg);
  color: var(--sportosphere-body-color);
  border-radius: var(--sportosphere-border-radius-lg);
  box-shadow: var(--sportosphere-box-shadow);

  position: fixed;
  top: 50%;
  right: 0;
  text-decoration: none;
  z-index: 1016;
  transform: rotate(-90deg) translate3d(-25px, 39px, 0);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-top: -83px;
  box-shadow: var(--sportosphere-setting-toggle-shadow);
}

.smartNavBars-settings-toggle:hover {
  text-decoration: none !important;
}

@media (max-width: 575.98px) {
  .smartNavBars-settings-toggle {
    transform: rotate(-90deg);
    outline: none;
  }
  .smartNavBars-settings-toggle .smartNavBars-settings-toggle-badge {
    background-color: rgba(0, 0, 0, 0) !important;
  }
  .smartNavBars-settings-toggle small {
    display: none;
  }
}

.smartNavBars-settings-toggle-body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  padding: 0.25rem 0.5rem; /* py-1 px-2, база */
}

@media (min-width: 768px) {
  .smartNavBars-settings-toggle-body {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem; /* py-md-2 */
  }
}

.smartNavBars-settings-toggle-badge {
  position: relative;
  border-top-left-radius: var(--sportosphere-border-radius);
  border-bottom-left-radius: var(--sportosphere-border-radius);
  background-color: var(--sportosphere-primary-bg-subtle);
  height: 34px;
  width: 28px;
}

.smartNavBars-settings-toggle-popover {
  position: relative;
  padding: 0;
  height: 0.875rem;
  width: 0.875rem;
  outline: none;
  left: 29%;
  z-index: 2;
  top: 31%;
}

.smartNavBars-settings-toggle-ripple {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.smartNavBars-settings-toggle-ripple:after,
.smartNavBars-settings-toggle-ripple:before {
  position: absolute;
  content: "";
  height: 0.45rem;
  width: 0.45rem;
  border-radius: 50%;
  border: 3px solid rgba(var(--sportosphere-success-rgb), 0.4);
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: -1;
  animation: smartNavBars-settings-toggle-ripple 2s ease infinite;
}

.smartNavBars-settings-toggle-ripple:before {
  border: 5px solid rgba(var(--sportosphere-success-rgb), 0.3);
  animation-delay: 5s;
}

@keyframes smartNavBars-settings-toggle-ripple {
  0% {
    height: 0.75rem;
    width: 0.75rem;
  }
  100% {
    height: 2.125rem;
    width: 2.125rem;
    border-color: rgba(0, 0, 0, 0);
  }
}

.smartNavBars-settings-toggle-spin {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: smartNavBars-settings-toggle-fa-spin 3s linear infinite; /* было .fa-spin + .ripple .fa-spin{animation-duration:3s} */
}

.smartNavBars-settings-toggle-spin-inner {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: smartNavBars-settings-toggle-icon-spin 6s cubic-bezier(0.15, 1.15, 0.65, 1) infinite;
}

@keyframes smartNavBars-settings-toggle-fa-spin {
  0% {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(1turn);
  }
}

@keyframes smartNavBars-settings-toggle-icon-spin {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

.smartNavBars-settings-toggle-caption {
  text-transform: uppercase;
  color: var(--sportosphere-primary);
  font-weight: 700;
  background-color: var(--sportosphere-primary-bg-subtle);
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  padding-right: 0.5rem;
  padding-left: 0.25rem;
  border-top-right-radius: var(--sportosphere-border-radius);
  border-bottom-right-radius: var(--sportosphere-border-radius);
}

/* ---- Панель настроек: содержимое, генерируемое JS ----
   renderSettingsPanel()/buildThemeBlock()/buildStretchBlock()/buildLayoutBlock()/
   buildVerticalStyleBlock()/buildPickerBlock() в smartNavBars.js строят 4 блока
   настроек только классами smartNavBars-settings-* — без form-select/btn-check/
   row-col/fs-9 и прочих классов Bootstrap/theme.min.css. Вся визуальная часть
   того, что рисует сам скрипт, поэтому целиком лежит здесь, в одной
   библиотеке стилей, а не размазана между JS-классами и сторонней темой. */

.smartNavBars-settings-heading {
  margin: 0 0 .25rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--sportosphere-emphasis-color);
}

.smartNavBars-settings-hint {
  margin: 0 0 .75rem;
  font-size: .75rem;
  color: var(--sportosphere-gray-600);
}

.smartNavBars-settings-divider {
  margin: 1rem 0;
  border: 0;
  border-top: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
}

/* Строка с иконкой слева и заголовком/подсказкой/контролом справа —
   "Растянуть" и "Расположение меню навигации" */

.smartNavBars-settings-field {
  display: flex;
  align-items: flex-start;
  gap: .625rem;
}

.smartNavBars-settings-field-icon {
  flex: 0 0 auto;
  margin-top: .1875rem;
  color: var(--sportosphere-gray-600);
}

.smartNavBars-settings-field-body {
  flex: 1 1 auto;
  min-width: 0;
}

.smartNavBars-settings-field-body .smartNavBars-settings-hint:last-child,
.smartNavBars-settings-field-body .smartNavBars-settings-select {
  margin-bottom: 0;
}

/* Строка "заголовок+подсказка слева — переключатель справа" — "Растянуть" */

.smartNavBars-settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
}

/* Тумблер (аналог form-switch), без картинок/зависимостей от Bootstrap —
   квадратная область input делает всю плашку кликабельной */

.smartNavBars-settings-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  cursor: pointer;
}

.smartNavBars-settings-switch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.smartNavBars-settings-switch-track {
  position: relative;
  display: inline-block;
  width: 2rem;
  height: 1.15rem;
  border-radius: 10rem;
  background-color: var(--sportosphere-gray-300);
  transition: background-color .2s ease;
}

.smartNavBars-settings-switch-track::after {
  content: '';
  position: absolute;
  top: .1375rem;
  left: .1375rem;
  width: .875rem;
  height: .875rem;
  border-radius: 50%;
  background-color: #fff;
  transition: transform .2s ease;
}

.smartNavBars-settings-switch-input:checked + .smartNavBars-settings-switch-track {
  background-color: var(--sportosphere-primary);
}

.smartNavBars-settings-switch-input:checked + .smartNavBars-settings-switch-track::after {
  transform: translateX(.85rem);
}

.smartNavBars-settings-switch-input:focus-visible + .smartNavBars-settings-switch-track {
  box-shadow: 0 0 0 .2rem var(--sportosphere-primary-bg-subtle);
}

/* <select> "Расположение меню навигации" — свой внешний вид вместо form-select,
   стрелка — встроенный SVG (без зависимости от иконочного набора темы) */

.smartNavBars-settings-select {
  display: block;
  width: 100%;
  padding: .375rem 2rem .375rem .625rem;
  font-size: .8125rem;
  line-height: 1.5;
  color: var(--sportosphere-body-color);
  background-color: var(--sportosphere-emphasis-bg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23748194' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2 5l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .625rem center;
  background-size: .75rem;
  border: var(--sportosphere-border-width) solid var(--sportosphere-border-color);
  border-radius: .375rem;
  appearance: none;
  cursor: pointer;
}

.smartNavBars-settings-select:focus {
  outline: none;
  border-color: var(--sportosphere-primary);
  box-shadow: 0 0 0 .2rem var(--sportosphere-primary-bg-subtle);
}

/* Радио-группа с превью-картинками (buildPickerBlock()) — "Цветовая схема"
   и "Настройки вертикального меню". Подсветка выбранного варианта — тем же
   приёмом, что и в оригинале (border-color превью + псевдо-радио-точка в
   подписи), только на своих классах и через :checked + label, без .btn-check */

.smartNavBars-settings-picker {
  display: block;
  width: 100%;
}

.smartNavBars-settings-picker-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: .625rem;
}

.smartNavBars-settings-picker-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.smartNavBars-settings-picker-label {
  display: block;
  cursor: pointer;
  text-align: left;
}

.smartNavBars-settings-picker-preview {
  position: relative;
  display: block;
  overflow: hidden;
  margin-bottom: .5rem;
  border: 3px solid var(--sportosphere-border-color);
  border-radius: .375rem;
  background-color: var(--sportosphere-tertiary-bg);
  transition: border-color .2s ease;
}

.smartNavBars-settings-picker-preview::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: transparent;
  transition: background-color .2s ease-in-out;
}

.smartNavBars-settings-picker-label:hover .smartNavBars-settings-picker-preview::after {
  background-color: rgba(0, 0, 0, .2);
}

.smartNavBars-settings-picker-image {
  display: block;
  width: 100%;
  height: auto;
}

.smartNavBars-settings-picker-caption {
  position: relative;
  display: inline-block;
  padding-left: 1.5rem;
  font-size: .8125rem;
}

.smartNavBars-settings-picker-caption::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 1rem;
  height: 1rem;
  border: 1px solid var(--sportosphere-gray-500);
  border-radius: 50%;
  transform: translateY(-50%);
  transition: border-color .2s ease, border-width .2s ease;
}

.smartNavBars-settings-picker-input:checked + .smartNavBars-settings-picker-label .smartNavBars-settings-picker-preview {
  border-color: var(--sportosphere-success);
}

.smartNavBars-settings-picker-input:checked + .smartNavBars-settings-picker-label .smartNavBars-settings-picker-caption::after {
  border: 5px solid var(--sportosphere-success);
}

.smartNavBars-settings-picker-input:focus-visible + .smartNavBars-settings-picker-label .smartNavBars-settings-picker-preview {
  box-shadow: 0 0 0 .2rem var(--sportosphere-primary-bg-subtle);
}

/* Блокировка "Настроек вертикального меню" при раскладках без вертикальной
   панели (control.disabled переключает setLayout(), см. smartNavBars.js) —
   сама .smartNavBars-settings-picker-input визуально скрыта (см. выше),
   поэтому disabled-состояние показываем через соседний label */

.smartNavBars-settings-picker-input:disabled + .smartNavBars-settings-picker-label {
  cursor: not-allowed;
  opacity: .5;
}

.smartNavBars-settings-picker-input:disabled + .smartNavBars-settings-picker-label .smartNavBars-settings-picker-preview {
  pointer-events: none;
}

/* Варианты оформления вертикального меню (data-smart-navbar-control="style",
   см. setStyle() — добавляет класс navbar-<style> прямо на [data-smart-navbar=
   "vertical"]) отдельного CSS здесь больше не требуют: .navbar-card/
   .navbar-vibrant/.navbar-inverted — родные классы темы, и все их стили
   (фон/тень/скругление на .navbar-collapse, паддинг внешнего <nav>, высота
   max-height с учётом верхнего меню, цвета ссылок/подписей/разделителей)
   уже полностью описаны в theme.min.css для .navbar-vertical.navbar-<вариант>
   — работают "из коробки", как только элемент получает и класс .navbar-vertical,
   и класс варианта одновременно (оба класса у него есть, см. buildVerticalNav()
   в smartNavBars.js). */

.has-events {
  background-color: var(--sportosphere-warning-bg-subtle) !important;
}

/* ---- Мобильное поведение вертикального меню (< 1200px, раздел 3.5 docs/
   smart-navbars-spec.md) — ПЕРЕСОБРАНО ЕЩЁ РАЗ по прямому запросу
   пользователя. Предыдущая версия (список раскрывался в обычном потоке,
   .smartNavBars-vertical переставал быть position:fixed) сломала два
   смежных места, ей не принадлежащих: во-первых, .smartNavBars-top (верхняя
   строка с поиском), больше не имея соседа-подпорки над собой, "падала"
   под шапку вертикального меню — они СИБЛИНГИ на уровне rootId, и как
   только .smartNavBars-vertical стал участвовать в обычном потоке (а не
   плавать поверх), его высота начала сдвигать всё, что идёт следом;
   во-вторых, раскрытие списка раздвигало content вниз — пользователь явно ХОЧЕТ
   противоположное: чтобы список ложился ПОВЕРХ content, точно так же, как
   уже реализованный десктопный разворот по наведению
   ([data-smart-navbar-collapsed][data-smart-navbar-collapsed-hover], см.
   блок выше — там тоже "Content (margin-left) НЕ трогаем — панель просто
   ложится поверх него").

   Поэтому: .smartNavBars-vertical и .smartNavBars-vertical-toggle-row НЕ
   переопределяются здесь вообще — остаются position:fixed/absolute, как на
   десктопе (см. их блоки выше) — кнопка+логотип снова "встроены" в полосу
   .smartNavBars-top, поэтому её компенсация padding-left тоже возвращена.
   [data-smart-navbar-content] margin-left по-прежнему 0 — это единственное,
   что здесь остаётся общим с "модальным" вариантом: панель не раздвигает
   content ни в развёрнутом, ни в свёрнутом виде, только ложится поверх.

   Раскрывается ПО КЛИКУ ТОЙ ЖЕ кнопки сворачивания (buildVerticalToggle()
   в smartNavBars.js, data-smart-navbar-mobile-open вместо десктопного
   :hover/data-smart-navbar-collapsed-hover) — .smartNavBars-vertical-collapse
   при этом сам становится независимым position:fixed (выходит из узкого
   20.625rem-бокса родителя <nav>, который остаётся прежним ради шапки).

   ИСПРАВЛЕНО: left/width были вначале left:0;right:0 (во всю ширину
   экрана, вплотную к его краям) — по прямому запросу пользователя заменено
   на точную синхронизацию с шириной [data-smart-navbar-content]. Сделать
   это чистым CSS нельзя — content лежит в отцентрированном .container с
   переменными по брейкпоинту отступами (см. историю с left:0 в разделе 11
   docs/smart-navbars-spec.md), а его ширина ещё зависит от "Растянуть".
   Поэтому left/width — переменные --smartNavBars-mobile-collapse-left/
   -width, которые перед каждым открытием считает и пишет на this.root
   syncMobileCollapseWidth() в smartNavBars.js (getBoundingClientRect() у
   content), с тем же самым запасным значением (0/100%, edge-to-edge), что
   было тут раньше, — на случай если по какой-то причине переменные ещё не
   выставлены. width, а не right — при одновременном явном width движок
   CSS игнорирует right, если оба заданы (см. спецификацию position), поэтому
   right явно не задаётся вовсе. z-index — свой (панель больше не внутри
   стекового контекста <nav>, став position:fixed самостоятельно) — то же
   значение, что и у .smartNavBars-vertical, чтобы остаться выше
   .smartNavBars-top ---- */

@media (max-width: 1199.98px) {
  [data-smart-navbar-layout="base"] [data-smart-navbar-content],
  [data-smart-navbar-layout="combo"] [data-smart-navbar-content] {
    margin-left: 0;
  }

  [data-smart-navbar-layout="base"] .smartNavBars-top,
  [data-smart-navbar-layout="combo"] .smartNavBars-top {
    padding-left: 20.625rem;
  }

  .smartNavBars-vertical-collapse {
    display: none;
  }

  [data-smart-navbar-mobile-open] .smartNavBars-vertical-collapse {
    display: block;
    position: fixed;
    top: var(--sportosphere-top-nav-height);
    left: var(--smartNavBars-mobile-collapse-left, 0);
    width: var(--smartNavBars-mobile-collapse-width, 100%);
    height: auto;
    max-height: calc(100vh - var(--sportosphere-top-nav-height) - 1rem);
    overflow-y: auto;
    z-index: 1021;
  }

  [data-smart-navbar-mobile-open] .smartNavBars-vertical-content {
    width: auto;
    height: auto;
  }

  [data-smart-navbar-mobile-open] .smartNavBars-vertical:not(.smartNavBars-vertical-card) .smartNavBars-vertical-collapse {
    box-shadow: .625rem 0 .625rem -.5625rem rgba(0, 0, 0, .2);
  }
}

/* ---- Скрытие горизонтального меню на узких экранах (< 992px, раздел
   3.5.1 docs/smart-navbars-spec.md) — по прямому запросу пользователя.
   Брейкпоинт СОЗНАТЕЛЬНО отдельный от 1199.98px выше (см. её комментарий
   и раздел 3.5.1): тот — про десктопное/мобильное поведение вертикальной
   панели у раскладок, где она есть всегда; этот — про верхнее меню
   (topVariant 'menu'/'double') у ЛЮБОЙ раскладки, включая 'top'/
   'double-top', где вертикальной панели в остальных случаях нет вовсе.

   updateTopNavContent() (smartNavBars.js) на этой ширине убирает пункты
   меню из строки верхней панели у всех раскладок (схлопывает её к
   [поиск, виджеты], как у 'base') — самого CSS для этого не требуется,
   переставляются/убираются реальные DOM-узлы. updateVerticalNav() —
   показывает вертикальную панель (<nav class="smartNavBars-vertical">)
   независимо от раскладки (isVerticalVisible()) — весь её мобильный CSS
   выше (блок @media(max-width:1199.98px)) уже написан БЕЗ привязки к
   [data-smart-navbar-layout] (кроме padding-left строки ниже), поэтому
   срабатывает для неё "бесплатно", как только узел появляется в DOM.

   Единственное, что действительно нужно доопределить здесь — тот же
   padding-left, что уже есть у 'base'/'combo' в блоке выше: кнопка-
   тогглер+логотип вертикальной панели (.smartNavBars-vertical-toggle-row)
   встроены в полосу .smartNavBars-top тем же способом (position:absolute,
   bottom:100%, см. её блок выше) независимо от раскладки — без
   компенсации отступа собственное содержимое строки верхней панели
   (поиск/виджеты) визуально пересеклось бы с ними.

   ИСПРАВЛЕНО: в отличие от 'base'/'combo' выше (там вертикальная панель
   есть ВСЕГДА, layoutConfig.vertical), у 'top'/'double-top' она на этой
   ширине показывается, только если у страницы вообще заданы пункты меню
   (config.menu.items, см. hasMenuItems()/isVerticalVisible() в
   smartNavBars.js) — на страницах без них (пример — стартовая страница
   landing.html) панели в DOM нет вовсе, и компенсация паттерна ниже без
   доп. условия резервировала бы место под несуществующую панель, сдвигая
   бренд/виджеты вправо. Селектор поэтому дополнен
   [data-smart-navbar-vertical-shown] — атрибутом на rootId, который
   updateVerticalNav() ставит/убирает синхронно с фактическим наличием
   панели в DOM */
@media (max-width: 991.98px) {
  [data-smart-navbar-layout="top"][data-smart-navbar-vertical-shown] .smartNavBars-top,
  [data-smart-navbar-layout="double-top"][data-smart-navbar-vertical-shown] .smartNavBars-top {
    padding-left: 20.625rem;
  }
}

/*
* Стили для модуля smartWebSocket.js
* Значения — дословный перенос инлайн-style, ранее заданного прямо в JS
* (default/templates/websocket.html), не переизобретены (rules.md, п. 7)
*/

.smartWebSocket-user-status {
  position: fixed;
  top: 10px;
  right: 10px;
  color: white;
  padding: 8px 12px;
  border-radius: 5px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  z-index: 1000;
}

.smartWebSocket-user-status-online {
  background: #4CAF50;
  border: 2px solid #388e3c;
}

.smartWebSocket-user-status-offline {
  background: #ff9800;
  border: 2px solid #f57c00;
}

.smartWebSocket-user-status-faded {
  opacity: 0.5;
}

.smartWebSocket-status {
  position: fixed;
  bottom: 10px;
  right: 10px;
  color: white;
  padding: 5px 10px;
  border-radius: 3px;
  font-size: 12px;
  z-index: 1000;
  opacity: 0.9;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.smartWebSocket-status-online {
  background: #4CAF50;
}

.smartWebSocket-status-offline {
  background: #f44336;
}

/*
* Стили для модуля smartSectionCard.js
* Шапка карточки раздела конструктора Публичной страницы БА (тоглер
* видимости + вкладки «Настройка»/«Просмотр»), заглушка ненастроенного
* раздела и контейнер масштабирования превью (organization/templates/
* businessaccount_visitsite_constructor.html), см.
* docs/visitsite-constructor-spec.md.
*/

.smartSectionCard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  width: 100%;
}

.smartSectionCard-header-start {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.smartSectionCard-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--sportosphere-gray-900);
}

.smartSectionCard-header-end {
  display: flex;
  gap: 0.25rem;
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius-pill);
  padding: 0.1875rem;
}

.smartSectionCard-tab-btn {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0.25rem 0.75rem;
  border-radius: var(--sportosphere-border-radius-pill);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--sportosphere-gray-600);
  cursor: pointer;
  transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out;
}

.smartSectionCard-tab-btn:hover {
  color: var(--sportosphere-gray-900);
}

.smartSectionCard-tab-btn-active,
.smartSectionCard-tab-btn-active:hover {
  background-color: var(--sportosphere-primary);
  color: var(--sportosphere-white);
}

/* Тоглер «Показывать на сайте» — чекбокс визуально скрыт (не display:none,
   чтобы остаться доступным для скринридеров/табуляции), переключатель
   рисуется соседним span через :checked + .smartSectionCard-toggle-slider */
.smartSectionCard-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
}

.smartSectionCard-toggle-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.smartSectionCard-toggle-slider {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
  width: 2.25rem;
  height: 1.25rem;
  border-radius: var(--sportosphere-border-radius-pill);
  background-color: var(--sportosphere-gray-400);
  transition: background-color 0.15s ease-in-out;
}

.smartSectionCard-toggle-slider::before {
  content: "";
  position: absolute;
  top: 0.125rem;
  left: 0.125rem;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background-color: var(--sportosphere-white);
  box-shadow: var(--sportosphere-box-shadow);
  transition: transform 0.15s ease-in-out;
}

.smartSectionCard-toggle-input:checked + .smartSectionCard-toggle-slider {
  background-color: var(--sportosphere-primary);
}

.smartSectionCard-toggle-input:checked + .smartSectionCard-toggle-slider::before {
  transform: translateX(1rem);
}

.smartSectionCard-toggle-input:focus-visible + .smartSectionCard-toggle-slider {
  outline: 2px solid var(--sportosphere-primary);
  outline-offset: 2px;
}

.smartSectionCard-toggle-label {
  font-size: 0.8125rem;
  color: var(--sportosphere-gray-600);
}

/* Заглушка «Выполните настройки для этого раздела» — рендерится
   Django-шаблоном внутри previewContainerId, когда раздел не настроен
   (sections_is_configured.<раздел> ложно) */
.smartSectionCard-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 8rem;
  padding: 1.5rem;
  border: var(--sportosphere-border-width) dashed var(--sportosphere-gray-300);
  border-radius: var(--sportosphere-border-radius);
  color: var(--sportosphere-gray-600);
  font-size: 0.875rem;
  text-align: center;
}

/* Контейнер масштабирования превью раздела «Логотип» (верхняя навигация
   витрины, см. Decision 2 design.md change'а visitsite-constructor-card-ui).
   Клики не блокируются целиком: ссылка-логотип (.smartSectionCard-
   navPreview-brand) — единственная в этом превью, ведущая на реальный,
   осмысленный адрес (Публичная страница БА) — остаётся кликабельной;
   pointer-events:none стоит точечно на пунктах меню (ниже), которые в
   превью ведут на несуществующие на этой странице якоря (#intro и т.п.) */
.smartSectionCard-preview-scale {
  overflow: hidden;
  height: calc(var(--sportosphere-top-nav-height) * 0.6);
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-gray-200);
  border-radius: var(--sportosphere-border-radius);
}

.smartSectionCard-preview-scale-inner {
  width: calc(100% / 0.6);
  transform: scale(0.6);
  transform-origin: top left;
}

/* Статическая мини-копия верхней навигации витрины для «Просмотра»
   раздела «Логотип» — НЕ переиспользует классы .smartNavBars-top/
   .smartNavBars-bottom-list напрямую (те задают position:fixed/sticky и
   z-index, рассчитанные на полноразмерную панель, а не на превью внутри
   карточки, см. Decision 2 design.md change'а visitsite-constructor-card-ui).
   Цвета/шрифт — точные значения из [data-smart-navbar-type="visitsite"]/
   .smartNavBars-top (sportosphereTools.css выше), не изобретены заново
   (rules.md, п. 7) */
.smartSectionCard-navPreview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 100%;
  padding: 0 1rem;
  background-color: rgb(11, 23, 39);
  font-family: var(--sportosphere-font-sans-serif);
  font-size: .8333333333rem;
  font-weight: 600;
}

.smartSectionCard-navPreview-brand {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}

.smartSectionCard-navPreview-brand-icon {
  max-height: 1.75rem;
  width: auto;
}

.smartSectionCard-navPreview-menu {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  pointer-events: none;
}

.smartSectionCard-navPreview-menu-item > a {
  color: rgba(255, 255, 255, .55);
  text-decoration: none;
}

/* Превью настроенного раздела (Баннер/Интро/«О нас»/Тренеры/Тренировки/
   Галерея/Контакты) — переиспользует ДОСЛОВНО вёрстку/классы
   соответствующей секции `visitsite_page_landing.html` (rules.md, п. 7:
   переносить точные значения/структуру, не упрощать), в масштабе карточки:
   контент рендерится в естественную ширину/высоту внутри
   `-sitePreview-inner`, сам `-sitePreview-inner` уменьшается `transform:
   scale()`, внешний `-sitePreview` обрезает лишнее по высоте и не даёт
   немасштабированному контенту раздвинуть карточку (`overflow: hidden`).
   pointer-events:none — превью некликабельно (реальные ссылки/кнопки
   раздела ведут на публичную страницу/её якоря, которых здесь нет) */
.smartSectionCard-sitePreview {
  overflow: hidden;
  max-height: 26rem;
  border: var(--sportosphere-border-width) var(--sportosphere-border-style) var(--sportosphere-gray-200);
  border-radius: var(--sportosphere-border-radius);
  pointer-events: none;
}

.smartSectionCard-sitePreview-inner {
  width: 200%;
  transform: scale(.5);
  transform-origin: top left;
}

/*
* Стили для модуля smartTooltips.js
* Одна общая (singleton) всплывающая подсказка, позиционируемая JS
* (position: fixed, координаты — из getBoundingClientRect() цели, см.
* smartTooltips.js и docs/smartTooltips-spec.md). Показ/скрытие управляется
* инлайн-стилем display, выставляемым JS, — .is-visible используется
* инструментом только как маркер состояния, не как единственный триггер
* видимости.
* Фон/текст/цвет стрелки — локальные переменные --sportosphere-tooltip-bg/
* -color (см. комментарий у их объявления в .smartTooltips-bubble: ВАЖНО,
* найдено и исправлено 2026-08-28 — тёзка --sportosphere-tooltip-bg из
* theme.min.css объявлена ТОЛЬКО внутри `.tooltip { ... }`, вне этого
* селектора резолвится в unset, поэтому напрямую не переиспользуется). Тот
* же класс дефекта, что CSS-1/CSS-2 в known-issues.md, но не по опечатке в
* имени, а по области видимости переменной — существование var()
* недостаточно проверить по имени, нужно смотреть, на каком селекторе она
* объявлена. По той же причине цвет текста задан литералом, а не
* var(--sportosphere-warning-text-emphasis) — эта переменная нигде не
* объявлена в theme.min.css (только используется внутри .alert-warning/
* .list-group-item-warning), вне их резолвится в unset. Остальные параметры
* (отступы/радиус стрелки/z-index) — либо локальные литералы/переменные
* этого правила (по образцу --isModal-zindex/--sportosphere-toast-zindex),
* либо var(--sportosphere-border-radius)/var(--sportosphere-box-shadow),
* которые реально объявлены на :root.
*/

.smartTooltips-bubble {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  --smartTooltips-arrow-width: 0.8rem;
  --smartTooltips-arrow-height: 0.4rem;
  /*
  * Локальные переменные, а не var(--sportosphere-tooltip-bg)/
  * var(--sportosphere-warning-text-emphasis) напрямую — ни одна из них не
  * резолвится вне своего исходного Bootstrap-компонента в theme.min.css
  * (см. комментарий в шапке файла). Значения — цветовая схема
  * Bootstrap-алерта warning (#fff3cd/#664d03), по прямому запросу
  * пользователя 2026-09-06 (ранее — литерал #F0E68C/var(--sportosphere-
  * gray-700), скопированный из theme.min.css `.tooltip`).
  */
  --sportosphere-tooltip-bg: #fff3cd;
  --sportosphere-tooltip-color: #664d03;
  max-width: 200px;
  padding: 0.5rem;
  background-color: var(--sportosphere-tooltip-bg);
  color: var(--sportosphere-tooltip-color);
  font-size: 0.875rem;
  text-align: center;
  border-radius: var(--sportosphere-border-radius);
  box-shadow: var(--sportosphere-box-shadow);
  z-index: 1080;
}

.smartTooltips-bubble-text {
  word-wrap: break-word;
}

.smartTooltips-bubble-link {
  display: inline-block;
  margin-top: 0.25rem;
  color: var(--sportosphere-tooltip-color);
  text-decoration: underline;
}

.smartTooltips-bubble-link:hover,
.smartTooltips-bubble-link:focus {
  color: var(--sportosphere-tooltip-color);
  text-decoration: none;
}

.smartTooltips-bubble-arrow {
  position: absolute;
  width: 0;
  height: 0;
  border-style: solid;
}

.smartTooltips-bubble--top .smartTooltips-bubble-arrow {
  bottom: calc(-1 * var(--smartTooltips-arrow-height));
  left: 50%;
  transform: translateX(-50%);
  border-width: var(--smartTooltips-arrow-height) calc(var(--smartTooltips-arrow-width) / 2) 0 calc(var(--smartTooltips-arrow-width) / 2);
  border-color: var(--sportosphere-tooltip-bg) transparent transparent transparent;
}

.smartTooltips-bubble--bottom .smartTooltips-bubble-arrow {
  top: calc(-1 * var(--smartTooltips-arrow-height));
  left: 50%;
  transform: translateX(-50%);
  border-width: 0 calc(var(--smartTooltips-arrow-width) / 2) var(--smartTooltips-arrow-height) calc(var(--smartTooltips-arrow-width) / 2);
  border-color: transparent transparent var(--sportosphere-tooltip-bg) transparent;
}

.smartTooltips-bubble--right .smartTooltips-bubble-arrow {
  left: calc(-1 * var(--smartTooltips-arrow-height));
  top: 50%;
  transform: translateY(-50%);
  border-width: calc(var(--smartTooltips-arrow-width) / 2) var(--smartTooltips-arrow-height) calc(var(--smartTooltips-arrow-width) / 2) 0;
  border-color: transparent var(--sportosphere-tooltip-bg) transparent transparent;
}

.smartTooltips-bubble--left .smartTooltips-bubble-arrow {
  right: calc(-1 * var(--smartTooltips-arrow-height));
  top: 50%;
  transform: translateY(-50%);
  border-width: calc(var(--smartTooltips-arrow-width) / 2) 0 calc(var(--smartTooltips-arrow-width) / 2) var(--smartTooltips-arrow-height);
  border-color: transparent transparent transparent var(--sportosphere-tooltip-bg);
}