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

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

/* Фон шапки и меню без фирменного цвета — фон листа (--bs-body-bg) любой
   темы: белым в светлой его делает сам Bootstrap.
   Своих цветовых переменных платформа больше не заводит. Их было четырнадцать,
   и держались они ради экранов, которые в платформу попали по недосмотру и
   отсюда убраны; каждая была вторым именем токена Bootstrap, то есть лишним
   способом сказать то же самое. */


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

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

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

/* Заголовок страницы с кнопками: h1 слева, действия справа, на узком экране
   переносятся. Страница пишет <div class="page-head"><h1>…</h1>
   <div class="actions">кнопки</div></div>. */
.page-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; margin-bottom: 1rem; }
.page-head h1 { margin: 0; min-width: 0; }
.page-head .actions { margin-left: auto; display: flex; flex-wrap: wrap; gap: .5rem; }

/* Блок «Аккаунт» карточки (тег account_section): отступ от формы выше —
   стилем, как у всех карточек (DEVELOPER_GUIDE, «Из чего собирается
   страница»: отступ карточки — стилем, не классом). */
#account-card { margin-top: 1rem; }


/* Заголовок зоны меню — классами Bootstrap в разметке (nav_menu.html):
   мелкий полужирный, черта сверху у всех, кроме первой. Свёрнутая зона —
   кнопка того же вида, список спрятан атрибутом hidden; шеврон
   поворачивается по aria-expanded — этого утилиты не умеют. */
button.zone-toggle .bi { transition: transform .15s; }
button.zone-toggle[aria-expanded="true"] .bi { transform: rotate(180deg); }

/* Страница стоит посередине места справа от меню: пустое место на широком
   экране делится поровну, а не копится справа. Меню прижато к левому краю
   окна; спрятал его человек — страница встаёт посередине всего окна. */
.page { padding: var(--gutter); margin-left: auto; margin-right: auto; width: 100%; max-width: var(--w-list, 1400px); }
body.w-form .page { max-width: var(--w-form, 780px); }
/* Столбец шириной формы внутри широкой страницы: вкладки и таблицы страницы
   идут во всю её ширину, а поля — той же длины строки, что на узкой странице
   (минус её поля по краям). Класс ставит шаблон, а не правило на все формы:
   у проектов есть широкие формы намеренно (две записи рядом), и общее правило
   сузило бы их молча. У Bootstrap предела ширины в пикселях классом нет. */
.form-column { max-width: calc(var(--w-form, 780px) - 2 * var(--gutter)); }

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

body.w-full .page { max-width: none; }

/* Меню: колонка с подписями — от 1200px; уже — за кнопкой «три полоски»,
   выдвижной панелью поверх страницы. Полосы значков без подписей нет нигде:
   значок без слова человек угадывает, а не читает (DEVELOPER_GUIDE, «Меню —
   колонкой или за кнопкой»). */
@media (max-width: 1199.98px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .sb { display: none; }
}
/* От 1200px колонку прячет и показывает человек кнопкой; браузер помнит выбор
   метками на <html> (core.js, до первой отрисовки). Решает человек, а не
   страница: на стартовой (`data-nav-home`) колонка по умолчанию видна, на
   остальных — за кнопкой; выбор для стартовой и для остальных помнится
   отдельно. */
@media (min-width: 1200px) {
  html:not([data-nav-home]):not(.nav-shown) .app,
  html[data-nav-home].nav-home-hidden .app { grid-template-columns: minmax(0, 1fr); }
  html:not([data-nav-home]):not(.nav-shown) .sb,
  html[data-nav-home].nav-home-hidden .sb { display: none; }
}
/* «Свернуть» — внизу колонки меню, значком у правого края: наверху колонку
   и так прячет кнопка в шапке, а низ колонки свободен. Полоса липнет к низу
   колонки, пока меню прокручивается. */
.sb-foot { background: var(--sidebar-bg, var(--bs-body-bg)); }
/* Вход в просмотр чужими глазами: список людей по ролям бывает длинным —
   прокрутка внутри меню, а не во всю страницу. */
.view-as-menu { min-width: 240px; max-height: 420px; overflow-y: auto; }

/* Телефон — поля страницы уже. */
@media (max-width: 899.98px) {
  .page { padding: .75rem; }
}

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

/* ─── 3. ТАБЛИЦА СПИСКА → КАРТОЧКИ НА ТЕЛЕФОНЕ ──────────────
   Требует data-l="Заголовок" на каждой <td>
   и class="pri" на главной ячейке строки.                    */
.rt { font-size: .875rem; margin: 0; }
.rt th { font-weight: 600; font-size: .8125rem; color: var(--bs-secondary-color); white-space: nowrap; }
/* Ячейки — по верху строки: название бывает в несколько строк, и соседние
   ячейки по середине выглядели оторванными от его начала. */
.rt td.rt-status { white-space: nowrap; }
/* Строка-ссылка (`data-href`, переход по щелчку — main.js) показывает, что
   нажимается: утилиты курсора в Bootstrap нет, а поведение и его признак
   должны жить у одного владельца. */
[data-href] { cursor: pointer; }
/* Служебные ширины колонок: действия, узкая колонка «по содержимому». */
.col-act { width: 40px !important; }
.col-140 { width: 140px !important; }
.col-min { width: 1% !important; }
.num { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }

@media (max-width: 767.98px) {
  .rt thead { display: none; }
  .rt, .rt tbody, .rt tr, .rt td { display: block; width: 100%; }
  .rt tbody tr {
    border: 1px solid var(--bs-border-color); border-radius: .375rem;
    margin-bottom: .5rem; padding: .6rem .8rem; background: var(--bs-body-bg);
  }
  .rt td { border: 0; padding: .15rem 0; display: flex; justify-content: space-between; gap: 1rem; text-align: right; align-items: baseline; }
  /* Ячейка без подписи не прижимается вправо молча, пустая строка — посередине. */
  .rt td:not([data-l]):not(.pri) { justify-content: flex-start; text-align: left; }
  .rt td:not([data-l])::before { content: none; }
  .rt td[colspan] { justify-content: center; text-align: center; }
  .rt td::before { content: attr(data-l); color: var(--bs-secondary-color); font-size: .8125rem; text-align: left; flex: none; }
  .rt td.pri {
    font-size: 1rem; font-weight: 600; text-align: left;
    padding-bottom: .35rem; margin-bottom: .35rem;
    border-bottom: 1px solid var(--bs-border-color);
  }
  .rt td.pri::before { display: none; }
  /* Статус — в шапке карточки справа от заголовка, а не в конце его текста:
     там он то оставался в строке, то уезжал на следующую. Ячейка
     .rt-status стоит в разметке сразу после .pri; строка карточки — флекс с
     переносом, прочие ячейки по-прежнему во всю ширину.
     Основа заголовка — 60%, не 0: после ячейки во всю ширину (дата, флажок
     без .rt-pick) заголовок с основой 0 вставал в уже полную строку и
     получал нулевую ширину. С 60% он уходит на новую строку, а статус до 40%
     встаёт рядом. */
  .rt tbody tr { display: flex; flex-wrap: wrap; align-items: stretch; }
  .rt tbody tr > td { flex: 0 0 100%; }
  .rt tbody tr > td.pri { flex: 1 1 60%; min-width: 0; }
  /* Флажок выбора записи (слияние) — узкой ячейкой слева от заголовка, без
     подписи: во всю ширину он занимал строку карточки ради одного квадрата. */
  .rt td.rt-pick { flex: none; width: auto; margin-right: .5rem; }
  .rt td.rt-pick::before { display: none; }
  /* Шапка карточки — «название | статус»: ячейки одной высоты (строка
     тянет их, `stretch`), поэтому линия под шапкой одна сплошная, а бейдж
     стоит на уровне первой строки названия и вниз не уходит. */
  .rt td.rt-status {
    flex: none; width: auto; padding-left: .5rem; align-items: flex-start;
    padding-bottom: .35rem; margin-bottom: .35rem;
    border-bottom: 1px solid var(--bs-border-color);
  }
  .rt td.rt-status::before { display: none; }
  /* Главная ячейка не первая в строке таблицы (сначала, скажем, дата) —
     модификатор `rt-pri-first`: в карточке флажок, заголовок и статус встают
     первыми, остальное — под ними. Без модификатора ячейка перед заголовком
     остаётся строкой выше: так задумано для списков, где она — шапка. */
  .rt.rt-pri-first > tbody > tr > td.rt-pick { order: -3; }
  .rt.rt-pri-first > tbody > tr > td.pri { order: -2; }
  .rt.rt-pri-first > tbody > tr > td.rt-status { order: -1; }
  /* Предохранитель: таблица без карточного вида прокручивается вбок, а не
     вылезает за экран. Не overflow-wrap: anywhere — оно сжимало колонку до
     буквы (так ломалась «Обслуживание», колонка «Установлено»). */
  .page table:not(.rt) { display: block; overflow-x: auto; max-width: 100%; overflow-wrap: break-word; }
  /* Картинка, видео и рамка не шире экрана; fieldset не распирает форму. */
  .page img, .page video, .page iframe { max-width: 100%; height: auto; }
}

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

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

/* Плитка-ярлык: значок и подпись в квадрате — для страниц-перечней разделов.
   Раскладку даёт сетка, здесь только вид самой плитки. */

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

/* ─── 6. ПЕЧАТЬ ────────────────────────────────────────────────
   Каркас при печати прячется сам. Своё, что при печати не нужно, проект
   прячет штатным классом Bootstrap `d-print-none`. */
@media print {
  .page { max-width: none; padding: 0; }
}

/* ─── 7. ФОРМЫ ──────────────────────────────────────────────
   Облик поля — классами Bootstrap (`form-control`, `form-select`,
   `form-check-input`): поля форм `StyledForm` получают их сами, поле,
   написанное в шаблоне руками, — в разметке (проверка запуска
   platforma.W015). Отступ поля — `mb-3`, подсказка — `form-text`. Своим
   осталась только компактная подпись поля в `.platform-form`.       */
.platform-form label { font-size: .8125rem; font-weight: 500; margin-bottom: .15rem; color: var(--bs-body-color); }
/* Сенсорный экран: поле не мельче 16px. Safari на iPhone при фокусе на поле
   с шрифтом мельче сам приближает страницу, и поля уезжают за края экрана.
   Запрет приближения (`maximum-scale=1`) убрал бы и это, и увеличение
   пальцами — тем, кому оно нужно, чтобы читать. На компьютере поля остаются
   компактными. Классы Bootstrap — здесь же: ими набраны поиск и отборы
   страниц проектов (DEVELOPER_GUIDE, «Поле ввода на телефоне — не мельче 16 px»). */
@media (pointer: coarse) {
  .form-control, .form-control-sm, .form-select, .form-select-sm,
  .input-group-sm > .form-control, .input-group-sm > .form-select { font-size: 16px; }
}
/* Ошибки поля — у любой формы: страницы вне .platform-form (формы базы
   знаний, формы проектов) выводили список браузерными маркерами и чёрным. */
.errorlist { list-style: none; padding: 0; margin: .2rem 0 0; color: var(--bs-danger); font-size: .8125rem; }

/* Полоса `progress-bar` шириной из данных: шаблон передаёт долю переменной
   (`style="--fill: {{ доля }}"` — данные, а не оформление: DEVELOPER_GUIDE,
   «Оформление мимо темы»), а
   ширину ставит это правило. У Bootstrap ширину полосы задают только
   встроенным стилем; без переменной правило не действует, и полоса со своим
   `style="width: …"` работает как прежде. */
.progress-bar { width: var(--fill); }

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

/* Состояния, которые ставят сценарии. Сценарий меняет класс, а не число в
   `style`: размер и длительность живут здесь, рядом с остальным оформлением. */
.ac-menu { max-height: 260px; }
.pagination-jump { width: 13rem; }           /* переход к номеру страницы    */

/* Меню кнопкой на любой ширине (вкладка «Оформление», поле «Меню»): колонки
   нет нигде, кроме стартовой; кнопка открывает выдвижную панель. Класс ставит
   каркас на все страницы, кроме стартовой. */
body.nav-burger .app { grid-template-columns: minmax(0, 1fr); }
body.nav-burger .sb { display: none; }


/* Сортировка таблиц (main.js, initSortableTables): вид заголовка, выбранной
   колонки и стрелки ↑/↓ — классами Bootstrap, которые ставит сценарий.
   Своим осталась подсветка при наведении: утилит наведения у Bootstrap нет. */
.sortable-header:hover { color: var(--bs-primary); }

/* ─── СТАРЫЕ БРАУЗЕРЫ: ОТСТУПЫ БЕЗ ФЛЕКСОВОГО GAP ─────────────
   Safari ниже 14.1 (iPad с iOS 12) не понимает `gap` у флекс-блока: отступы
   пропадают, и значки, слова и кнопки стоят вплотную. Сценарий каркаса
   (core.js) меряет это при загрузке и ставит на <html> метку `no-flex-gap`;
   под ней те же отступы — полями. `@supports (gap: …)` для этого не годится:
   старый Safari понимает `gap` у сеток и отвечает «да». Сетки (`grid`) здесь
   не повторяются — у них `gap` работает. Порог браузеров и сторож — DEVELOPER_GUIDE,
   «Старые браузеры». */
.no-flex-gap .sb .nav-link i, .no-flex-gap #nav .nav-link i { margin-right: .6rem; }
.no-flex-gap .page-head > * { margin-right: .75rem; margin-bottom: .5rem; }
.no-flex-gap .page-head .actions > * { margin-right: .5rem; margin-bottom: .5rem; }
.no-flex-gap .rt td::before { margin-right: 1rem; }
.no-flex-gap .indicator > * + * { margin-left: .5rem; }
.no-flex-gap .pf-view-as > * + * { margin-left: .5rem; }

/* Классы Bootstrap `gap-1…gap-5` на флекс-блоках — те же величины полями.
   Ряд — отступ слева у каждого следующего, столбец — сверху; у переносимого
   ряда ещё и снизу, чтобы строки не слипались. */
.no-flex-gap .d-flex.gap-1 > * + *, .no-flex-gap .d-inline-flex.gap-1 > * + * { margin-left: .25rem; }
.no-flex-gap .d-flex.gap-2 > * + *, .no-flex-gap .d-inline-flex.gap-2 > * + * { margin-left: .5rem; }
.no-flex-gap .d-flex.gap-3 > * + *, .no-flex-gap .d-inline-flex.gap-3 > * + * { margin-left: 1rem; }
.no-flex-gap .d-flex.gap-4 > * + *, .no-flex-gap .d-inline-flex.gap-4 > * + * { margin-left: 1.5rem; }
.no-flex-gap .d-flex.gap-5 > * + *, .no-flex-gap .d-inline-flex.gap-5 > * + * { margin-left: 3rem; }
.no-flex-gap .flex-column.gap-1 > * + * { margin-left: 0; margin-top: .25rem; }
.no-flex-gap .flex-column.gap-2 > * + * { margin-left: 0; margin-top: .5rem; }
.no-flex-gap .flex-column.gap-3 > * + * { margin-left: 0; margin-top: 1rem; }
.no-flex-gap .flex-column.gap-4 > * + * { margin-left: 0; margin-top: 1.5rem; }
.no-flex-gap .flex-column.gap-5 > * + * { margin-left: 0; margin-top: 3rem; }
.no-flex-gap .flex-wrap.gap-1 > * { margin-bottom: .25rem; }
.no-flex-gap .flex-wrap.gap-2 > * { margin-bottom: .5rem; }
.no-flex-gap .flex-wrap.gap-3 > * { margin-bottom: 1rem; }
.no-flex-gap .flex-wrap.gap-4 > * { margin-bottom: 1.5rem; }
.no-flex-gap .flex-wrap.gap-5 > * { margin-bottom: 3rem; }
