/* Общие для всех страниц: футер, баннер согласия и skip-link.
   Раньше эти стили жили только в main.css, который минимальные страницы
   (вход, регистрация, сброс пароля) не грузят, — там футер слипался
   в строку, а баннер согласия выглядел чужеродной коробкой. */

/* ---------------------------------------------------------------------------
   Базовый слой

   Браузерные умолчания раньше снимала Bulma: поля заголовков и списков,
   подчёркивание ссылок, box-sizing, наследование шрифта элементами формы.
   Вместе с ней это ушло, и здесь ровно тот минимум, который нужен, —
   не чужой reset целиком, а то, на что опирается вёрстка проекта.

   У @rightech/ui своего сброса нет намеренно: её main.css кладётся
   поверх живого проекта и чужую вёрстку не трогает.
   --------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Рабочая область страницы: одна мера, одни поля, один способ
   центрировать. Ставится на внешний блок страницы, а не на каждый
   кусок внутри неё. */
.page-area {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-gutter);
}

h1,
h2,
h3,
h4,
h5,
h6,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

/* Маркеры снимаются только у списков с классом: список в markdown —
   это текст, и маркеры ему нужны.
   
   Селектор обёрнут в :where, чтобы его вес был нулевым. Без этого
   «ul[class]» весит больше одиночного класса и перебивал собственные
   правила проекта: список состава импорта, фасеты каталога и шаги
   на главной объявляли свои поля и оставались без них. Сброс должен
   уступать любому правилу, которое пишется после него. */
:where(ul[class], ol[class]) {
  margin: 0;
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

/* Полужирный в системе — 600. Браузерное умолчание для strong и b даёт
   700: в Open Sans это заметно тяжелее остального набора, и выделение
   в тексте начинает выглядеть чужим шрифтом. */
strong,
b {
  font-weight: 600;
}

/* Элементы формы шрифт не наследуют — приходится сказать это явно. */
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

img,
svg,
video {
  max-width: 100%;
}

/* Один SVG в обеих шапках: надпись наследует токен темы без подмены файла. */
.brand-logo {
  display: block;
  flex: none;
  width: auto;
  height: 40px;
  color: var(--ric-text-primary);
}

/* Пропорции корпоративного SVG сохраняются при высоте знака 36 px. */
@media (max-width: 640px) {
  .brand-logo {
    height: 36px;
  }
}

/* ---------------------------------------------------------------------------
   Основа страницы

   Фон, цвет и шрифт объявлены здесь, а не в main.css: минимальные
   страницы — вход, регистрация, сброс пароля, подтверждение почты —
   main.css не подключают. Из-за этого они с первой волны набирались
   не Open Sans, а запасным стеком Bulma: --bulma-body-family это
   Inter/SF Pro/Segoe UI, и половина сайта выглядела чужим шрифтом.

   Семейство задаёт проект: токены дизайн-системы его не объявляют.
   Файлы локальные (frontend/vendor/fonts, собирает ops/copy-vendor.js),
   системный стек остаётся запасным на время загрузки.
   --------------------------------------------------------------------------- */

/* Ссылка внутри текста отличается от текста не только цветом (WCAG 1.4.1).
   Сброс выше делал ссылки в подписи согласия, в баннере и под формами
   неотличимыми от соседних слов (контраст 1,00–1,22), а в тексте
   документов — отличимыми одним цветом при 2,69:1 к тексту (нужно 3:1).
   Ссылки-компоненты — кнопки, чипы, пункты меню — несут свой класс и
   свой вид; правило касается только ссылок без класса и всех ссылок
   внутри текста документов и баннера. */
:where(p, li, label, .alert, dd) a:not([class]),
.cookie-consent__text a,
.markdown-content a {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Широкая раскладка — выбор человека, а не устройства.
   --------------------------------------------------------------------
   Потолок полосы снимается: она занимает окно за вычетом полей. Мера
   набора при этом не трогается — текст остаётся в колонке чтения, а
   каталог получает больше колонок, а не более длинные строки. Ровно
   этим отличается «шире» от «длиннее»: у GitLab это Fixed/Fluid
   в настройках пользователя, у GitHub — «full width» у репозитория. */
html.layout-wide,
html.layout-wide body {
  --page-max: none;
}

@property --prose-column {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

html,
body {
  /* Ширина рабочей области — одна на каталог. Страницы разъехались:
     главная и страница автора держали 68rem, каталог и модели — 80 %
     окна (1152 пикселя на широком экране и 1024 на обычном), страница
     записи не держала ничего и упиралась в края окна. Одна мера и одни
     поля вместо трёх разных решений.

     1440 — потолок, а не доля экрана: доля без потолка на мониторе 2560
     дала бы строку в 160 знаков. Замер соседей по ремеслу на том же
     окне 1920: у GitHub полоса 1280, у GitLab 1296 — мы держим ту же
     идею и чуть шире. Раньше здесь стояло 1200, и на 1920 треть экрана
     пустовала.

     Поле растёт вместе с экраном: 16 на телефоне, 24 на ноутбуке, 48 на
     мониторе. Фиксированные 24 на 2560 выглядели случайными. Нижняя
     граница 16 — та же, что была в медиазапросе, поэтому его больше
     нет. */
  --page-max: 1440px;
  --page-gutter: clamp(16px, 2.5vw, 48px);

  /* Мера строки — одна на проект, объявленная в одном месте. Абзац,
     который читают строка за строкой, за 75 знаков теряет начало
     следующей строки; 45 — нижняя граница. Раньше мера стояла четырьмя
     числами в четырёх правилах (60, 62, 62, 68 знаков) — то есть решения
     о наборе принимались по месту. Размер элемента, а не роль системы,
     поэтому живёт здесь, а не в слое токенов. */
  --measure: 68ch;

  /* Та же мера, но посчитанная один раз и здесь. «ch» — единица шрифта
     того элемента, где её читают: у абзаца в 16 пикселей выходит 622, у
     заголовка в 18 — 700, и колонка получается рваной. Регистрация
     свойства как длины (ниже) заставляет вычислить его на теле и
     наследовать уже числом. */
  --prose-column: var(--measure);

  /* Кегль прозы — отступление от шкалы дизайн-системы, объявленное
     отступлением.
     --------------------------------------------------------------------
     В шкале Rightech самая крупная ступень основного текста — 16/24, и
     она рассчитана на интерфейс: короткая строка, которую сканируют.
     Описание решения и страницы-документы читают подряд, абзацами, по
     несколько экранов — для такого чтения 16 это нижняя граница, а не
     оптимум; типографская практика для длинного чтения 18–20 при
     интерлиньяже около 1,5.

     Ступень 18/28 запрошена у владельцев системы (docs/ds-request-body-xl.md).
     Пока её нет, она живёт здесь — и только для прозы: ни одна кнопка,
     подпись или карточка каталога её не видит. Граница сторожится
     проверкой «набран ступенями системы»: вне .markdown-content
     разрешены только ступени пакета.

     В rem, а не в пикселях: текст, который читают абзацами, обязан
     слушать настройку размера шрифта в браузере. */
  --prose-size: 1.125rem;
  --prose-leading: 1.75rem;

  /* Высота полосы шапки — одно число на две роли: саму шапку и отступ
     тела под неё. Раньше их было два (68 и 64), и они разошлись: первые
     четыре пикселя каждой страницы лежали под шапкой.

     Не доля экрана: та же доля даёт 68 пикселей на ноутбуке 768 и 128
     на мониторе 1440, хотя внутри всё те же значки 20 и текст 14. Высота
     полосы — сумма её содержимого, снизу её держит цель нажатия 44
     (WCAG 2.5.5), а при увеличении масштаба текст растёт, доля экрана —
     нет. */
  --bar-h: 68px;

  margin: 0;
  background-color: var(--ric-bg-primary);
  color: var(--ric-text-primary);
  font-family: 'Open Sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

/* На телефоне поле уже: 24 пикселя с каждой стороны на экране в 375
   съедали седьмую часть ширины.

   Правило стоит здесь, а не выше по файлу, и повторяет тот же селектор:
   объявление в «html, body» перекрывало медиазапрос, написанный для
   одного body, — не специфичностью (она равна), а порядком. Поле
   на телефоне оставалось прежним, и заметить это по разметке было
   нельзя: переменная-то менялась, только не там, где её читают. */
/* ---------------------------------------------------------------------------
   Роли текста, которые нужны в разметке

   Здесь их ровно четыре, и все названы по роли, а не по цвету. До этого
   на их месте стояли цветовые помощники Bulma — has-text-white, has-text-grey
   и родня, 81 штука по шаблонам. Имя у них врало: помощник про тему ничего
   не знает, и чтобы страница не оказалась белым по белому, каждый был
   перекрыт токеном в bulma-overrides.css. Перекрытое враньё остаётся
   враньём, а читать «has-text-white» на тёмной надписи невозможно.

   Основной текст своего класса не имеет: --ric-text-primary и так
   наследуется от <html>. Двадцать пять has-text-white просто снялись.
   --------------------------------------------------------------------------- */

/* Заголовки страниц и разделов написаны классами типографики пакета
   (.ric-text-header-*). Они задают только шрифт, а поля у h1–h6 обнуляет
   минисброс Bulma — до этого отбивку давал её же класс .title. Пакет
   про отбивку ничего не знает: у него заголовки живут в своих раскладках. */
h1[class*='ric-text-header'],
h2[class*='ric-text-header'],
h3[class*='ric-text-header'],
p[class*='ric-text-header'] {
  margin-bottom: 0.75rem;
}

/* ---------------------------------------------------------------------------
   Кнопки дизайн-системы: поправка на читаемость

   Пакет раскрашивает кнопки так же, как ссылки: на покой достаётся цвет
   послабее, на наведение — тот, что проходит AA. Замеры белого текста
   (норма 4.5 для текста обычного размера, кнопки набраны 14-м):

     --ric-bg-btn-primary  #2986ff — 3.53      -hover #086de0 — 4.91
     --ric-bg-btn-danger   #ec5750 — 3.47      -hover #c64741 — 4.80

   Поэтому в покое берётся цвет наведения, а само наведение показывается
   кольцом исходного цвета: оно заметно и ничего не стоит по контрасту.
   Понизить контраст на время, пока курсор стоит над кнопкой, — плохой
   размен: подпись читают и в этот момент.

   Вторичная кнопка в тёмной теме давала 4.31 своим --ric-text-btn-secondary;
   основная роль текста даёт 8.98 там и 10.48 в светлой.

   Всё это — правка чужой палитры, и держится она на измерениях выше.
   Если пакет исправит свои значения, блок снимается целиком.
   --------------------------------------------------------------------------- */
.ric-btn-primary {
  background-color: var(--ric-bg-btn-primary-hover);
}

.ric-btn-primary:hover {
  background-color: var(--ric-bg-btn-primary-hover);
  box-shadow: 0 0 0 2px var(--ric-bg-btn-primary);
}

.ric-btn-danger {
  background-color: var(--ric-bg-btn-danger-hover);
}

.ric-btn-danger:hover {
  background-color: var(--ric-bg-btn-danger-hover);
  box-shadow: 0 0 0 2px var(--ric-bg-btn-danger);
}

.ric-btn-secondary,
.ric-btn-secondary:hover {
  color: var(--ric-text-primary);
}

/* Пакет на наведении красит подпись вторичной кнопки в primary-600: 1,96:1
   на сером фоне тёмной темы — в том числе «Только необходимые» в баннере
   согласия. Подпись остаётся полноконтрастной, а наведение показывает
   кольцо того самого цвета — как у основной кнопки выше. */
.ric-btn-secondary:hover {
  box-shadow: 0 0 0 2px var(--ric-text-btn-secondary-hover);
}

/* Кнопка-ссылка живёт на фоне страницы, и роль ей нужна та же, что
   обычной ссылке: --ric-text-btn-link не добирает AA в светлой теме,
   а -hover — в тёмной. Роли ссылки этот перекос уже учитывают
   (frontend/css/main.css, оговорка 1). */
.ric-btn-link {
  color: var(--ric-text-link-hover);
}

.ric-btn-link:hover {
  color: var(--ric-text-link-active);
}

/* ---------------------------------------------------------------------------
   Поле формы

   В пакете его нет: там есть контрол, а подпись, подсказка и ошибка —
   забота приложения. Образец — FormV2/Field.css в ric-web:

   - подпись без своего шрифтового класса, отличается только ролью цвета;
   - обязательность помечается звёздочкой через ::after, а не словом;
   - подсказка и ошибка одного размера (body-s) и различаются ролью;
   - white-space: pre-wrap — сообщения сервера приходят с переносами.

   Раскладка взята вариантом «во всю ширину» (в образце — .field.fw):
   подпись сверху, контрол под ней. Трёхколоночная сетка оригинала
   рассчитана на плотные формы настроек, а здесь формы узкие и по центру.
   --------------------------------------------------------------------------- */
.form-field {
  margin: 0 0 12px;
}

/* Interactive boundaries exceed 3:1 against the field in both themes. */
.ric-ui-input-box:where(:not(:disabled, [aria-disabled='true'])) {
  --ric-input-border-color: var(--ric-border-interactive);
}

.ric-ui-input-box:where(:not(:disabled, [aria-disabled='true'])):is(:hover, :focus, :focus-within) {
  --ric-input-border-color: var(--ric-border-interactive);
}

.ric-ui-input-box.is-invalid {
  --ric-input-border-color: var(--ric-text-critical);
}

.editor-validation-summary {
  color: var(--ric-text-critical);
  margin-block: 8px;
}

.form-field__label {
  display: block;
  margin-bottom: 4px;
  color: var(--ric-text-secondary);
}

.form-field--required .form-field__label::after {
  content: '*';
  margin-inline-start: 2px;
  color: var(--ric-text-critical);
}

.form-field__error,
.form-field__help {
  margin-top: 4px;
  white-space: pre-wrap;
}

.form-field__error {
  color: var(--ric-text-critical);
}

.form-field__help {
  color: var(--ric-text-secondary);
}

/* ---------------------------------------------------------------------------
   Сообщение в рамке

   Не тост: это блок внутри страницы — «ссылка устарела», «запись чужая»,
   ответ сервера под формой. У Bulma это была .notification с оттенками
   is-danger/is-warning, которые про тему ничего не знают.

   Плашки категориального цвета в тёмной теме остаются светлыми, поэтому
   текст на них — основной ролью. Вид сообщения несут заливка и рамка.
   --------------------------------------------------------------------------- */
/* Плашки состояний и категорий: поверхность под них.
   ---------------------------------------------------------------------------
   Роли --ric-shape-bg-* в пакете отданы фигурам и данным: заливка сектора
   диаграммы обязана быть одинаковой в обеих темах, иначе один и тот же ряд
   на светлой и тёмной странице выглядит разными данными. Поэтому в тёмной
   теме они остаются светлыми — orange-55 это почти бежевый, — и плашка
   «Выбор редакции» светилась на тёмной странице: светлота 0.72 против
   0.006 у фона страницы.

   Поверхности состояния в пакете нет вовсе. Единственная цветная —
   --ric-bg-helper, синяя, и она тему как раз переворачивает: в светлой
   primary-50, в тёмной primary-900. Здесь то же самое делается для
   остальных слотов: в тёмной теме акцентная роль разводится фоном
   страницы до тона, который остаётся узнаваемо цветным и не слепит.

   Значение выводится из роли пакета, своего цвета проект не заводит —
   это же проверяет frontend/css/theme.test.js. Светлая тема не меняется:
   там поверхность равна фигурной роли, как и была.
   --------------------------------------------------------------------------- */
html.ric-variables {
  --shape-surface-01: var(--ric-shape-bg-01);
  --shape-surface-02: var(--ric-shape-bg-02);
  --shape-surface-03: var(--ric-shape-bg-03);
  --shape-surface-04: var(--ric-shape-bg-04);
  --shape-surface-05: var(--ric-shape-bg-05);

  /* Цвета подсветки кода — по схеме VS Code (Light+ / Dark+): синие
     ключевые слова, красные строки, зелёные числа и комментарии, охра
     у функций, тёмно-синие ключи объектов. Схема выбрана не из вкуса:
     её видят ежедневно, и знакомая раскладка цветов читается без
     привыкания.

     Ролей для синтаксиса в системе нет, поэтому берутся ступени палитры
     — свои для каждой темы: насыщенный цвет, читаемый на светлом фоне
     блока кода, на тёмном даёт 1,7–3,4 при норме 4,5, и наоборот.
     Зелёного и охры в светлой палитре нет вовсе, они получены
     затемнением существующих ступеней через color-mix — приём, уже
     принятый выше для подложек категориального ряда.

     Замеры на фоне блока кода (--ric-bg-card-primary):
     primary-700 5.34, red-800 9.13, зелёный 5.37, охра 4.55,
     primary-800 9.2, grey-700 5.57. */
  --code-keyword: var(--ric-color-primary-700);
  --code-string: var(--ric-color-red-800);
  --code-number: color-mix(in srgb, var(--ric-color-green-600) 70%, var(--ric-color-grey-900));
  --code-comment: color-mix(in srgb, var(--ric-color-green-600) 70%, var(--ric-color-grey-900));
  --code-function: color-mix(in srgb, var(--ric-color-yellow-500) 45%, var(--ric-color-grey-900));
  --code-property: var(--ric-color-primary-800);
  --code-punct: var(--ric-color-grey-700);
}

html.ric-variables.ric-theme-dark {
  --shape-surface-01: color-mix(in srgb, var(--ric-shape-accent-01) 14%, var(--ric-bg-primary));
  --shape-surface-02: color-mix(in srgb, var(--ric-shape-accent-02) 14%, var(--ric-bg-primary));
  --shape-surface-03: color-mix(in srgb, var(--ric-shape-accent-03) 14%, var(--ric-bg-primary));
  --shape-surface-04: color-mix(in srgb, var(--ric-shape-accent-04) 14%, var(--ric-bg-primary));
  --shape-surface-05: color-mix(in srgb, var(--ric-shape-accent-05) 14%, var(--ric-bg-primary));

  /* Те же роли по схеме Dark+: здесь светлые ступени палитры подходят
     напрямую, затемнять ничего не нужно. Контраст на тёмном фоне блока
     кода: primary-300 9.12, orange-300 10.82, green-300 9.57,
     green-500 6.76, yellow-500 11.34, ocean-300 10.12, grey-400 7.39. */
  --code-keyword: var(--ric-color-primary-300);
  --code-string: var(--ric-color-orange-300);
  --code-number: var(--ric-color-green-300);
  --code-comment: var(--ric-color-green-500);
  --code-function: var(--ric-color-yellow-500);
  --code-property: var(--ric-color-ocean-300);
  --code-punct: var(--ric-color-grey-400);
}

.alert {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-block: 12px;
  padding: 12px;
  border-radius: 8px;
  background: var(--ric-bg-secondary);
  box-shadow: inset 0 0 0 1px var(--ric-border-primary);
  color: var(--ric-text-primary);
}

.alert--critical {
  background: var(--shape-surface-03);
  box-shadow: inset 0 0 0 1px var(--ric-shape-accent-03);
  color: var(--ric-text-primary);
}

.alert--attention {
  background: var(--shape-surface-02);
  box-shadow: inset 0 0 0 1px var(--ric-shape-accent-02);
  color: var(--ric-text-primary);
}

.alert--success {
  background: var(--shape-surface-01);
  box-shadow: inset 0 0 0 1px var(--ric-shape-accent-01);
  color: var(--ric-text-primary);
}

/* Ссылки внутри сообщения наследуют его цвет: синий на цветной плашке
   в тёмной теме нечитаем, а подчёркивание отличает ссылку и без цвета. */
.alert--critical a,
.alert--attention a,
.alert--success a {
  color: inherit;
  text-decoration: underline;
}

/* Выпадающий список.

   У пакета список свой — react-select со своей разметкой, — и к родному
   <select> его классы не прикладываются. Но выглядеть они должны
   одинаково, поэтому индикатор воспроизводится по размерам пакета:
   черта, 7px, значок 20×20, 7px — итого 35 пикселей справа.

   Сам значок — глиф пакета (Select/index.js, viewBox 0 0 20 20,
   path «m5.5 8 4.5 4 4.5-4»), а не нарисованный заново: рисовать свою
   стрелку значило бы придумывать знак. Вставлен маской, а не фоном,
   чтобы цвет оставался ролью и следовал теме.

   Обёртка нужна потому, что у <select> нет собственных псевдоэлементов:
   ::before и ::after на нём браузеры не рисуют. */
.form-select {
  position: relative;
  display: flex;
}

.form-select > select {
  appearance: none;
  padding-inline-end: 35px;
  cursor: pointer;
}

.form-select::before,
.form-select::after {
  content: '';
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
}

/* Черта, отделяющая индикатор от значения. */
.form-select::before {
  inset-inline-end: 34px;
  width: 1px;
  height: 20px;
  background-color: var(--ric-border-primary);
}

.form-select::after {
  inset-inline-end: 7px;
  width: 20px;
  height: 20px;
  background-color: var(--ric-icon-tertiary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='m5.5 8 4.5 4 4.5-4' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E")
    center / 20px 20px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='m5.5 8 4.5 4 4.5-4' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E")
    center / 20px 20px no-repeat;
}

.form-select:hover::after {
  background-color: var(--ric-icon-primary-hover);
}

/* Список подменён компонентом дизайн-системы (frontend/ui/entries/select.jsx).
   Коробку, стрелку и меню рисует он сам, поэтому запасная отрисовка
   выключается, а родной <select> убирается с глаз — значение по-прежнему
   хранится в нём. */
.form-select--upgraded::before,
.form-select--upgraded::after {
  content: none;
}

.form-select__native {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
}

.form-select__control {
  flex: 1 1 auto;
  min-width: 0;
}

/* Флажок дизайн-системы — фигура рядом с подписью, а не подпись
   с флажком внутри: отсюда строка. Родной input остаётся в документе
   и хранит состояние (frontend/ui/entries/checkbox.jsx). */
.form-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

.form-checkbox label {
  cursor: pointer;
}

/* Фигура флажка 20 пикселей, строка текста рядом — 24 (16/24). Коробка
   берёт высоту строки и центрирует фигуру в ней: без этого флажок
   вставал на два пикселя выше текста, к которому относится.
   display: flex заодно убирает базовую щель под строчным блоком —
   из-за неё коробка была 26 пикселей вместо 20. */
.form-checkbox__control {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  min-height: 24px;
}

.form-checkbox__native {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
}

/* Поле и кнопка в строку: поле тянется, кнопка нет. */
.form-row {
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

.form-row__grow {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

/* Пути со страницы входа и регистрации: ссылками под основным
   действием, а не кнопками рядом с ним. Кнопка рядом с кнопкой
   заставляет выбирать, хотя выбор здесь неравнозначный. */
.auth-links {
  margin-top: 12px;
  text-align: center;
}

.auth-links__dot {
  margin-inline: 6px;
  color: var(--ric-text-secondary);
}

/* Ряд действий формы: у Bulma это была .field.is-grouped с обёрткой
   .control вокруг каждой кнопки. Кнопки и так флекс-элементы. */
.form-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

/* На телефоне поле набирается крупнее. Ступень --ric-input-m это 14px,
   а Safari на iOS увеличивает страницу, когда фокус попадает в поле
   мельче шестнадцати: человек теряет из виду форму, в которую печатает.
   Берём размер ступени xl — 16px и 44 пикселя высоты, то есть и размер
   тапа заодно, — но свойствами, а не внутренними переменными пакета.
   Проверяет e2e/mobile.spec.js. */
@media screen and (max-width: 768px) {
  .ric-ui-input-m {
    font-size: 16px;
    line-height: 24px;
    height: 44px;
  }
}

/* Плашка: отметка каталога, вид записи, тег. Раньше геометрию давал
   бульмовский .tag — чужая тема решала, какого размера надпись, какой
   у неё радиус и цвет, а cursor: pointer обещал нажатие там, где его
   нет. Теперь это свой класс на ролях системы; семейства (badge-official,
   kind-badge, tag-type) отличаются заливкой и геометрией поверх него.

   Радиус 4: в ric-web он не унифицирован — четыре у мелких пометок,
   восемь у счётчиков, и выбирается по размеру. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 4px;
  background-color: var(--ric-bg-tag);
  color: var(--ric-text-secondary);
  font: var(--ric-font-body-s);
  font-family: inherit;
  white-space: nowrap;
}

/* Плашка-ссылка ведёт себя ссылкой: подчёркивания нет, цвет свой. */
a.badge {
  text-decoration: none;
}

/* Скрытый элемент. Класс совпадает по имени с хелпером Bulma — она
   держала правило на всех страницах, common.css делает то же самое.
   Важность нужна: класс снимает и возвращает скрипт поверх любых
   display, заданных раскладкой. */
.is-hidden {
  display: none !important;
}

/* Поле выбора файла. Кнопку внутри рисует браузер — своим шрифтом
   и по-своему в каждом; единственное, до чего дотягивается CSS, —
   ::file-selector-button, и через него она берёт вид малой вторичной
   кнопки. Нативное поле оставлено намеренно: браузер сам пишет имя
   выбранного файла, а за размеченной кнопкой с <label> это имя
   потребовало бы скрипта. */
input[type='file'].ric-ui-input-box {
  padding-block: 0.45rem;
  cursor: pointer;
}

input[type='file']::file-selector-button {
  margin-inline-end: 0.6rem;
  padding: 4px 12px;
  border: 0;
  border-radius: 6px;
  background: var(--ric-bg-btn-secondary);
  color: var(--ric-text-btn-secondary);
  font: var(--ric-font-header-s);
  font-family: inherit;
  cursor: pointer;
}

input[type='file']:hover::file-selector-button {
  color: var(--ric-text-btn-secondary-hover);
}

/* Контурный значок в кнопке остаётся контурным. Пакет красит значок
   заливкой (.ric-btn > svg { fill: currentColor }) — это верно для
   сплошных значков, а очертаниям Rightech, которые проект рисует
   контуром, заливка превращает рисунок в пятно. Своего набора
   дизайн-система не публикует, поэтому уточнение живёт здесь. */
.ric-btn > svg[fill='none'] {
  fill: none;
}

/* Палец шире мыши: на телефоне действие должно быть не меньше сорока
   четырёх пикселей. Кнопка ric-btn-s даёт 24, обычная — 32. */
@media screen and (max-width: 768px) {
  .ric-btn,
  .profile-tab,
  .profile-public-link {
    /* Вкладка размечена в content-box: без этой строки сорок четыре
       пикселя считались бы от содержимого и с полями давали бы 57. */
    box-sizing: border-box;
    min-height: 44px;
  }

  /* Ссылка стоит отдельной строкой, то есть работает действием, а не
     словом в предложении: ей нужна та же область тапа. */
  .profile-public-link {
    display: inline-flex;
    align-items: center;
  }
}

/* Многострочному полю ступень даёт только шрифт: высоту держит rows.
   В ступени .ric-ui-input-m есть height — однострочному полю это и нужно,
   а textarea означало бы три строки, сжатые в одну строку. */
textarea.ric-ui-input-m {
  height: auto;
}

/* Автозаполнение браузера. Chrome красит заполненное поле своим
   бледно-голубым, и вместо поля дизайн-системы на форме входа
   оказывался чужой прямоугольник — с чужим фоном и незаметной рамкой.

   Фон у :-webkit-autofill свойством не перебить: он рисуется отдельным
   слоем поверх background-color. Зато внутренняя тень во всю высоту
   ложится поверх него, а рамка коробки остаётся снаружи и видна.
   Цвет текста — своим свойством, обычный color браузер тоже
   перекрывает.

   Бесконечный transition — против второй попытки: спустя мгновение
   после подстановки Chrome возвращает свой фон анимацией, и она
   не начинается, если ей отведено сто тысяч секунд. */
.ric-ui-input:-webkit-autofill,
.ric-ui-input:-webkit-autofill:hover,
.ric-ui-input:-webkit-autofill:focus,
.ric-ui-input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--ric-text-primary);
  box-shadow: inset 0 0 0 1000px var(--ric-bg-primary);
  caret-color: var(--ric-text-primary);
  transition: background-color 100000s;
}

/* То же стандартным селектором — для Firefox. Отдельным правилом,
   а не одним списком: неизвестный селектор в списке отменяет всё
   правило целиком, и один браузер отнял бы починку у другого. */
.ric-ui-input:autofill,
.ric-ui-input:autofill:hover,
.ric-ui-input:autofill:focus,
.ric-ui-input:autofill:active {
  -webkit-text-fill-color: var(--ric-text-primary);
  box-shadow: inset 0 0 0 1000px var(--ric-bg-primary);
  caret-color: var(--ric-text-primary);
  transition: background-color 100000s;
}

/* Поле с негодным значением. Красится свойство, а не внутренняя
   переменная пакета: --ric-input-* это его кухня, она может измениться
   в любой версии, а border-color — обычное свойство коробки. */
.ric-ui-input-box.is-invalid {
  border-color: var(--ric-text-critical);
}

/* Значок внутри поля. У пакета такого слота нет: есть префикс с заливкой
   (для единиц измерения) и аддон (для кнопок), а «бледный знак у левого
   края» — решение проекта, поэтому и класс его. Коробка поля — флекс,
   так что значок просто встаёт первым. */
.form-field__icon {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  padding-inline-start: 8px;
  color: var(--ric-icon-primary);
}

.form-field__icon + .ric-ui-input {
  padding-inline-start: 6px;
}

/* Кнопки дизайн-системы. Пакет даёт вид и размеры, проект — две вещи,
   которых у него нет: растяжка на всю ширину контейнера и состояние
   «выбрано» у кнопки-фильтра. Обе живут здесь, а не в разметке. */
.ric-btn--full {
  width: 100%;
}

.ric-btn--selected {
  background-color: var(--ric-bg-btn-primary);
  color: var(--ric-text-btn-primary);
}

.ric-btn--selected:hover {
  background-color: var(--ric-bg-btn-primary-hover);
  color: var(--ric-text-btn-primary);
}

/* Кнопка в работе. У пакета такого состояния нет, а у Bulma оно было
   (.button.is-loading) и ушло вместе с её классами: формы отправлялись
   молча. Подпись остаётся на месте и скрывается видимостью — иначе
   кнопка дёргается в ширине, — а поверх крутится кольцо из currentColor. */
.ric-btn.is-loading {
  position: relative;
  pointer-events: none;
}

.ric-btn.is-loading > * {
  visibility: hidden;
}

.ric-btn.is-loading::after {
  content: '';
  position: absolute;
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: 50%;
  animation: ric-btn-spin 0.6s linear infinite;
}

@keyframes ric-btn-spin {
  to {
    transform: rotate(1turn);
  }
}

/* Движение отключено настройкой системы — кольцо замирает, но остаётся
   видимым: оно и есть признак того, что кнопка занята. */
@media (prefers-reduced-motion: reduce) {
  .ric-btn.is-loading::after {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
   Фокус

   Раньше это было одно правило на всё: outline в два пикселя и ещё два
   отступа, с !important. У ссылок и кнопок оно и задумывалось, а поля
   получали синий квадрат вокруг себя при каждом клике — :focus-visible
   у текстового поля срабатывает и от мыши, не только от клавиатуры.

   Рамка фокуса остаётся: без неё клавиатурная навигация слепнет. Но
   у контролов дизайн-системы она своя — цветом собственной рамки,
   ролью --ric-border-interactive, без отдельного квадрата снаружи.
   --------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--ric-border-interactive);
  outline-offset: 2px;
}

/* Элементы перечислены поимённо не для красоты: у Bulma есть
   a:focus-visible, button:focus-visible, и по весу селектора одинокий
   псевдокласс им проигрывает — ссылки подсвечивались бы её синим,
   а не ролью системы. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--ric-border-interactive);
  outline-offset: 2px;
}

/* Поле, список, кнопка, вкладка и флажок рисуют фокус сами — своей
   рамкой. Внешний квадрат поверх неё только шумит. */
.ric-ui-input-box:focus-within {
  border-color: var(--ric-border-interactive);
}

.ric-ui-input:focus-visible,
.ric-ui-input-box:focus-visible,
.ric-ui-input-box :focus-visible {
  outline: none;
}

.ric-btn:focus-visible,
.ric-tabmenu-item:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--ric-border-interactive);
}

.form-checkbox__control:focus-within {
  border-radius: 4px;
  box-shadow: 0 0 0 2px var(--ric-border-interactive);
}

.form-checkbox__control :focus-visible {
  outline: none;
}

/* Цель skip-link получает программный фокус — рамка контейнеру не нужна. */
#main-content:focus-visible {
  outline: none;
}

/* Второстепенный текст: даты, счётчики, подсказки под полями. */
.is-muted {
  color: var(--ric-text-secondary);
}

/* Состояние на служебных страницах — подтверждение почты, отказ, лимит.
   Цветом здесь отмечен только значок и заголовок; текст под ними
   остаётся обычным. */
.status-icon--success,
.status-title--success {
  color: var(--ric-text-success);
}

.status-icon--attention {
  color: var(--ric-text-attention);
}

.status-icon--critical,
.status-title--critical {
  color: var(--ric-text-critical);
}

.site-footer {
  background: none;
  padding: 0;
}

/* Было color: gray (3.74:1 в светлой теме) и x-small — 10px с тапом
   в 11px высотой. Токен даёт 5.4:1 в обеих темах, 0.75rem читаем. */
.site-footer__content {
  color: var(--ric-text-secondary);
  font-size: 12px;
  padding: 0.75rem 0.5rem;
  /* Центрирование раньше приходило хелпером has-text-centered из чужой
     темы — вместе с .content, которая правила и весь остальной набор. */
  text-align: center;
}

.site-footer__content a {
  color: var(--ric-text-secondary);
}

.site-footer__content a:hover {
  color: var(--ric-text-primary);
}

/* Отзыв согласия на аналитику — кнопка (она меняет состояние страницы,
   а не ведёт по адресу), но в ряду ссылок обязана выглядеть так же. */
.site-footer__link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ric-text-secondary);
  cursor: pointer;
  text-decoration: underline;
}

.site-footer__link:hover {
  color: var(--ric-text-primary);
}

.site-footer__sep {
  margin: 0 0.5rem;
  opacity: 0.7;
}

/* Баннер висит поверх страницы и закрывал собой нижнюю часть контента —
   в том числе кнопку «Зарегистрироваться» на форме регистрации. Пока он
   показан, страница получает запас снизу, чтобы до контента можно было
   доскроллить, а фокус с клавиатуры не уходил под баннер (WCAG 2.4.11).
   Запас — настоящая высота баннера, её пишет cookie-consent.js; до него —
   14rem: на телефонах 360–390 px баннер с отступом занимает до 216 px.
   :has() поддерживают все актуальные браузеры; в старых баннер просто
   останется поверх, как и был. */
body:has(#cookie-consent:not([hidden])) .page-wrapper,
.consent-pending .page-wrapper {
  padding-bottom: var(--consent-reserve, 14rem);
}

html:has(#cookie-consent:not([hidden])),
html.consent-pending {
  scroll-padding-bottom: var(--consent-reserve, 14rem);
}

/* Выбора ещё нет — баннер виден с первой отрисовки (класс ставит
   синхронный theme.js), не дожидаясь отложенного cookie-consent.js.
   Идентификатор и !important перебивают [hidden] { display: none
   !important } из main.css. */
.consent-pending #cookie-consent[hidden] {
  display: flex !important;
}

.cookie-consent {
  position: fixed;
  left: 1rem;
  right: 1rem;
  bottom: 1rem;
  z-index: 60;
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.9rem 1rem;
  border-radius: 8px;
  background: var(--ric-bg-secondary);
  border: 1px solid var(--ric-border-primary);
  box-shadow: none;
  outline: 1px solid var(--ric-border-primary);
}

.cookie-consent[hidden] {
  display: none;
}

.cookie-consent__text {
  flex: 1 1 260px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ric-text-secondary);
}

.cookie-consent__text a {
  color: var(--ric-text-link-hover);
}

.cookie-consent__actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}

/* Skip-link: первый фокусируемый элемент любой страницы. Был продублирован
   в main.css и minimal.css — теперь объявлен один раз. */
.skip-link {
  position: fixed;
  top: -100%;
  left: 1rem;
  z-index: 2000;
  padding: 0.6rem 1rem;
  border-radius: 8px;
  background: var(--ric-bg-secondary);
  border: 1px solid var(--ric-text-link);
  color: var(--ric-text-link-hover);
  font-size: 14px;
}

.skip-link:focus {
  top: 1rem;
}

/* Полоса работы наверху страницы.
 *
 * Отвечает на вопрос «занят ли узел». Переходы ею не отмечаются: страницы
 * портала отвечают за 60–190 мс, и полоса была бы миганием — зажигают её
 * только долгие действия и передача файлов (frontend/js/loadbar.js).
 *
 * Слой выбран по замеру соседей, а не взят из образца: шапка прибита к
 * верхнему краю с z-index 1000 и непрозрачна, и полоса под ней была бы
 * не видна вовсе. Выше шапки, но ниже ссылки «к содержимому» (2000).
 *
 * Цвет один на обе темы — это вывод из замера. --ric-bg-btn-primary это
 * #2986ff в обеих; контраст к фону шапки, на которой полоса и лежит, —
 * 3.53 на светлой и 5.31 на тёмной при пороге 3:1 для нетекстовых
 * элементов (WCAG 1.4.11). Второе правило для тёмной темы не нужно. */
.loadbar {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 1500;
  height: 2px;
  background: var(--ric-bg-btn-primary);
  transform: scaleX(0);
  transform-origin: left center;
  opacity: 0;
  pointer-events: none;
}

/* Длительность неизвестна: кривая сама доходит только до 96 %, до конца
 * полосу доводит ответ. Замер по ключевым точкам: 1 с — 43 %, 2 с — 66 %,
 * 6 с — 86 %, 12 с — 93 %. */
body.busy .loadbar {
  opacity: 1;
  animation: loadbar 24s cubic-bezier(0.15, 0.75, 0.3, 1) forwards;
}

@keyframes loadbar {
  0% {
    transform: scaleX(0);
  }
  4% {
    transform: scaleX(0.4);
  }
  12% {
    transform: scaleX(0.68);
  }
  40% {
    transform: scaleX(0.88);
  }
  100% {
    transform: scaleX(0.96);
  }
}

/* Длительность известна — угадывать нечего: доля приходит из события
 * передачи, и кривая выключается целиком. */
body.busy.loadbar-exact .loadbar {
  animation: none;
  transform: scaleX(var(--loadbar-progress, 0));
  transition: transform 0.12s linear;
}

body.done .loadbar {
  opacity: 0;
  transform: scaleX(1);
  transition:
    transform 0.18s ease-out,
    opacity 0.25s ease 0.18s;
}

@media (prefers-reduced-motion: reduce) {
  body.busy .loadbar {
    animation: none;
    transform: scaleX(1);
    opacity: 0.55;
  }

  body.busy.loadbar-exact .loadbar {
    transform: scaleX(var(--loadbar-progress, 0));
    transition: none;
  }

  body.done .loadbar {
    transition: opacity 0.2s ease;
  }
}

.info-message__heading {
  display: flex;
  align-items: center;
  gap: 8px;
}
.info-message__heading > svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}
.info-message__description {
  margin: 0;
  color: var(--ric-text-secondary);
  font: var(--ric-font-body-m);
}
