/* ============================================================================
   Единая страница входа id.1t.ru
   Заменяет: css/authv2/style.css и все css/themes/*.css

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

   Имена классов совпадают с текущим шаблоном, серверная часть не переписывается.

   Различия между сервисами — ШЕСТЬ переменных цвета ниже, и всё. Логотипов
   -картинок нет ни у кого: название системы выводится текстом в шапке
   карточки, красится вместе с интерфейсом и читается в любой теме. Прежние
   логотипы существовали в двух начертаниях и подменялись сценарием; когда
   подмена не срабатывала, тёмный знак пропадал на тёмном фоне.
   ========================================================================== */

:root {
  /* ---- цвета ---- */
  /* Фон страницы заметно глубже карточки, а шапка — светлый оттенок ВНУТРИ
     белой карточки. Если шапку сделать темнее фона, верх карточки читается
     не как заголовок, а как дырка, сквозь которую видно страницу. */
  --auth-page-bg:          #e8ebef;
  --auth-card-bg:          #ffffff;
  --auth-card-head-bg:     #f5f7f9;
  --auth-card-line:        #e5e9ee;

  /* Второстепенный текст, ссылки и подсказки в полях затемнены до нормы
     читаемости: белое на белом выглядит воздушно ровно до того момента,
     когда это надо прочитать. Замер прежних значений — 3,14 у пояснений
     и 2,30 у подсказок в полях при норме 4,5. Стало 5,14 и 4,76. */
  --auth-text:             #1b1f24;
  --auth-text-muted:       #676e79;
  --auth-link:             #636e7c;

  --auth-field-bg:         #eff1f3;
  --auth-field-bg-focus:   #ffffff;
  --auth-field-border:     #e2e5e9;
  --auth-field-border-hov: #d3d8de;
  --auth-placeholder:      #636b76;

  /* ---- фирменные цвета сервиса --------------------------------------
     Сервис переопределяет ШЕСТЬ переменных: три светлых ниже и три тёмных
     следом. --auth-on-accent в этот набор не входит — цвет надписи на кнопке
     остаётся белым, а подбирается под него сам фирменный цвет.
     Файл темы подключается отдельной строкой в <head> страницы, см. шаблоны.
     Если ничего не подставлено — работают значения по умолчанию отсюда.

     Выбирая свой цвет, проверьте контраст с --auth-on-accent: он должен
     быть не ниже 4,5:1, иначе надпись на кнопке будет плохо читаться.
     Значение по умолчанию — indigo, тот самый из списка имён цветов
     в браузере; белая надпись на нём даёт 12,95:1 с большим запасом.      */
  --auth-accent:           #4b0082;   /* indigo: кнопка, галка, рамка фокуса */
  --auth-accent-hover:     #3f006e;   /* та же кнопка под курсором */
  --auth-accent-ring:      rgba(75, 0, 130, .32);   /* подсветка фокуса */
  --auth-on-accent:        #ffffff;   /* текст на главной кнопке и галочка */

  /* Отдельный акцент для тёмной темы.
     ⚠ ЭТИ ТРИ СТРОКИ ТЕМА ОБЯЗАНА ЗАДАТЬ САМА. Вывести их формулой нельзя:
     кнопка в тёмной теме должна одновременно нести читаемую белую надпись
     и отличаться от тёмной карточки, а эти два требования тянут яркость
     в разные стороны — единой доли осветления, годной для всех фирменных
     цветов, не существует (проверено перебором на восьми). Поэтому значения
     считает генератор тем замером, и проверка тем требует их наличия.
     Для indigo без них кнопка отличалась бы от карточки в 1,26 раза, то
     есть была бы почти не видна; здесь тот же тон светлее — белая надпись
     держит 4,92, отделение от карточки 3,32.                              */
  --auth-accent-dark:      #9255be;
  --auth-accent-dark-hover: #9d63c6;
  --auth-accent-dark-ring: rgba(146, 85, 190, .40);

  /* ⚠ Ниже — ДЕЙСТВУЮЩИЙ цвет: именно его читают кнопки, галка и рамки.
     Разделение не формальность. Файл темы подключается ПОСЛЕ этого и задаёт
     свои --auth-accent и --auth-accent-dark. Если бы кнопки читали
     --auth-accent напрямую, строка темы оказалась бы ниже переключения
     на тёмный вариант и перебила бы его: в тёмной теме кнопка красилась бы
     светлым цветом. Тема эти три переменные не трогает, поэтому
     переключение остаётся за нами.                                        */
  --auth-accent-current:       var(--auth-accent);
  --auth-accent-current-hover: var(--auth-accent-hover);
  --auth-accent-current-ring:  var(--auth-accent-ring);

  --auth-danger:           #c3324b;

  /* ---- размеры ---- */
  --auth-card-width:       380px;
  --auth-radius-card:      16px;
  --auth-radius-field:     8px;

  /* ---- что задаёт файл бренда -----------------------------------------
     Здесь только значения по умолчанию, то есть «бренд ничего не сказал».
     Сами значения приезжают блоком стилей после файла темы; их формы и
     заслон — в shared/services/brand_style.ts.

     Заливка кнопки отделена от акцента намеренно: акцент — это ЦВЕТ, им же
     красятся галка, рамка фокуса и ссылки, а заливка бывает градиентом,
     у которого цвета нет вовсе.                                          */
  --auth-accent-fill:      var(--auth-accent-current);
  --auth-page-image:       none;   /* фон страницы */
  --auth-side-image:       none;   /* иллюстрация сбоку (только «полотно») */
  --auth-logo:             none;   /* логотип над формой (только «полотно») */
  --auth-logo-w:           158px;
  --auth-logo-h:           50px;
  --auth-canvas-width:     500px;  /* ширина колонки с формой в «полотне» */
  --auth-shadow:           0 1px 2px rgba(16, 24, 40, .05),
                           0 10px 34px rgba(16, 24, 40, .09);

  /* ---- шрифт ----------------------------------------------------------
     Системный: тот, что уже установлен у пользователя. Ничего не грузится,
     нечему не доехать, текст виден сразу. Первое подходящее имя из списка:
     макбук и айфон → San Francisco, Windows → Segoe UI, Android → Roboto,
     Linux → Noto или Liberation. Кириллица есть во всех.                  */
  --auth-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif;
}

/* ---- что выводится из фирменного цвета САМО --------------------------------
   Сервису, который задал только --auth-accent, наведение и подсветка фокуса
   считаются отсюда — иначе они остались бы цветом по умолчанию, и оранжевая
   кнопка синела бы под курсором.

   Наведение ЗАТЕМНЯЕТ, а не осветляет. Это не вкус: осветление уводит белую
   надпись за порог читаемости на цветах, которые и так у края — замер даёт
   3,7-4,1 при норме 4,5 у трёх фирменных цветов из семи. Затемнение контраст
   только повышает, поэтому безопасно для любого цвета.

   Блок отдельный: браузер, который не умеет смешивать цвета, его пропустит
   и оставит значения выше. Внутри переменной запасного варианта не сделать —
   такой браузер принял бы формулу за верное значение и оставил кнопку
   вообще без заливки.                                                        */
@supports (color: color-mix(in srgb, red, blue)) {
  :root {
    --auth-accent-hover: color-mix(in srgb, var(--auth-accent) 88%, #000000);
    --auth-accent-ring:  color-mix(in srgb, var(--auth-accent) 32%, transparent);
  }
}

/* ---- тёмная тема ----------------------------------------------------------
   Включается системной настройкой пользователя, без скрипта. Браузер, который
   про такую настройку не знает, просто покажет светлую — это безопасно.
   Если единый вход решено делать только светлым, весь блок удаляется.       */
@media (prefers-color-scheme: dark) {
  :root {
    --auth-page-bg:          #14171c;
    --auth-card-bg:          #1c2027;
    --auth-card-head-bg:     #22262e;
    --auth-card-line:        #2c313a;

    --auth-text:             #e8eaee;
    --auth-text-muted:       #8d95a1;
    --auth-link:             #949cab;

    --auth-field-bg:         #23272f;
    --auth-field-bg-focus:   #272c35;
    --auth-field-border:     #333944;
    --auth-field-border-hov: #434a57;
    --auth-placeholder:      #8b93a0;   /* было #767e8b — 3,66 при норме 4,5 */

    /* Переключаем ДЕЙСТВУЮЩИЙ цвет на тёмный вариант. Сам --auth-accent
       не трогаем: его задаёт тема, и переписывать чужое значение — верный
       способ получить спор о том, кто был последним. */
    --auth-accent-current:       var(--auth-accent-dark);
    --auth-accent-current-hover: var(--auth-accent-dark-hover);
    --auth-accent-current-ring:  var(--auth-accent-dark-ring);

    --auth-shadow:           0 1px 2px rgba(0, 0, 0, .4),
                             0 10px 34px rgba(0, 0, 0, .45);
  }
}

/* ==========================================================================
   Страница
   ========================================================================== */

/* Отступы и рамка считаются ВНУТРИ заданной ширины. Без этой строки поле
   шириной 100% вылезает за карточку ровно на свои внутренние отступы:
   Chrome так считает сам, Safari — нет. Правило ограничено страницей входа. */
.auth-page,
.auth-page *,
.auth-page *:before,
.auth-page *:after {
  box-sizing: border-box;
}

.auth-page {
  display: flex;
  min-height: 100vh;
  min-height: 100dvh;      /* на телефоне — без прыжка от адресной строки */
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  margin: 0;
  /* Раздельно, а не сокращённой записью: сокращённая сбрасывает картинку,
     и фон бренда исчезал бы молча. Картинку рисуем в ОБЕИХ композициях —
     ключ в файле бренда общий, и «задал фон, а он ничего не делает»
     объяснить нечем. */
  background-color: var(--auth-page-bg);
  background-image: var(--auth-page-image);
  background-size: cover;
  background-position: center;
  color: var(--auth-text);
  font-family: var(--auth-font);
  font-size: 15px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;   /* iOS не увеличивает шрифт при повороте */
  text-size-adjust: 100%;
}

/* ==========================================================================
   Типографские классы шаблонов
   --------------------------------------------------------------------------
   В шаблонах эти классы стоят рядом с обычными: например
   class="form__row__title font-header-3". Они были в прежней базе, и без них
   весь текст на всех страницах потерял бы заданные размеры. Здесь они
   приведены к новой шкале.

   ⚠ Блок должен идти ВЫШЕ описания блоков карточки: у одиночных классов
   одинаковый вес, и при споре побеждает тот, что описан ниже. То есть
   font-header-3 задаёт размер там, где своего правила нет, и уступает
   там, где есть.
   ========================================================================== */

.font-header-3 { font-size: 22px; font-weight: 700; line-height: 28px; }
.font-body-1   { font-size: 16px; font-weight: 600; line-height: 22px; }
.font-body-2   { font-size: 15px; font-weight: 400; line-height: 20px; }
.font-caption-1 { font-size: 13px; font-weight: 600; line-height: 17px; }
.font-caption-2 { font-size: 13px; font-weight: 400; line-height: 17px; }

/* ==========================================================================
   Карточка
   ========================================================================== */

.form {
  width: var(--auth-card-width);
  max-width: 100%;
  overflow: hidden;                 /* чтобы шапка обрезалась по скруглению */
  border-radius: var(--auth-radius-card);
  background: var(--auth-card-bg);
  box-shadow: var(--auth-shadow);
}

/* ---- шапка: название сервиса и заголовок страницы ---------------------
   Название стоит ОТДЕЛЬНОЙ строкой, а не внутри фразы — тогда его не надо
   склонять. Шапка есть всегда; если сервер не передал названия, в ней
   остаётся один заголовок страницы. Текст красится вместе с интерфейсом
   и потому читается и в светлой теме, и в тёмной. */

.form__brand {
  padding: 24px 32px 20px;
  border-bottom: 1px solid var(--auth-card-line);
  background: var(--auth-card-head-bg);
  text-align: center;
}

/* ---- подтонировка под фирменный цвет ---------------------------------
   Заголовки и надписи на кнопках подмешивают к тексту фирменный цвет —
   на треть. Тогда страница читается одной гаммой, а не «серый лист
   с одной цветной кнопкой». Смешивание идёт от той же переменной, что
   красит кнопку, поэтому сервису по-прежнему достаточно задать ОДИН цвет:
   тон заголовков подстроится сам.

   Строка с обычным цветом стоит ПЕРВОЙ намеренно. Браузер, который не умеет
   смешивать цвета (до 2023 года), просто не поймёт вторую строку и оставит
   первую — текст будет чёрный, но читаемый. Именно поэтому смешивание
   написано прямо в свойстве, а не спрятано в переменную: значение
   переменной такой браузер принял бы за верное и оставил бы текст
   вообще без цвета.                                                       */

.form__brand__name {
  margin-bottom: 6px;
  color: var(--auth-text);
  color: color-mix(in srgb, var(--auth-accent-current) 35%, var(--auth-text));
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .08em;
  line-height: 20px;
  /* Прописными — чтобы название читалось как метка сервиса, а не как первая
     строка обращения. Разрядка рядом нужна именно для прописных: без неё
     они слипаются. Пишется правилом, а не в базе: там название хранится
     по-человечески и в таком же виде уходит в другие места. */
  text-transform: uppercase;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.form__body { padding: 22px 32px 30px; }

/* ---- объявление сервиса ---------------------------------------------------
   Штатное место для «срочно напишите тут что-нибудь»: раньше такой текст
   вставляли оформлением поверх заголовка, и он оказывался невидимым для
   всего, кроме глаза.

   Тон спокойный, без тревожных красок: это объявление, а не ошибка. Длинный
   текст переносится и растит карточку вниз, а не распирает её вширь —
   у РосНОУ там было три абзаца.                                            */
.form__notice {
  padding: 12px 14px;
  margin-bottom: 20px;
  border: 1px solid var(--auth-field-border);
  border-radius: 10px;
  background: var(--auth-field-bg);
  color: var(--auth-text-muted);
  font-size: 13px;
  line-height: 18px;
  overflow-wrap: break-word;
}

.form__notice p { margin: 0; }
.form__notice p + p { margin-top: 10px; }

/* Кнопки исключены намеренно. Часть из них — ссылки («Войти», «Сменить»,
   «Восстановить пароль»), и без этой оговорки правило для ссылок красило бы
   надпись на кнопке в цвет ссылки: оно весит больше, чем правило кнопки.
   Белая надпись на цветной кнопке становилась серой. */
.form a:not(.form__row__button) {
  color: var(--auth-link);
  text-decoration: none;
}

.form a:not(.form__row__button):hover { color: var(--auth-text); }

.form a:not(.form__row__button):focus {
  outline: 2px solid var(--auth-accent-current);
  outline-offset: 2px;
}

/* ---- строки формы --------------------------------------------------------
   Шаблоны разбиты на блоки .form__row, и их бывает много в разных сочетаниях.
   Поэтому расстояние между блоками задаётся ЗДЕСЬ, один раз, а не отступами
   у каждого содержимого: иначе любая новая строка в шаблоне встала бы
   вплотную или, наоборот, с двойным зазором.
   Пустая строка (например невыведенная ошибка) места не занимает.          */

.form__row + .form__row { margin-top: 20px; }

/* ⚠ Ни :empty, ни :has(> .auth-error:empty) здесь не работают, и по разным
   причинам — обе проверены на собранных страницах.

   :empty не срабатывает, потому что движок шаблонов оставляет внутри блока
   перевод строки: для браузера текстовый узел — содержимое, блок не пуст.

   :has(> .auth-error:empty) не срабатывает, потому что ПУСТОГО блока ошибки
   не существует вовсе: errors.ejs при отсутствии ошибки не выводит ни одного
   тега. Правило искало то, чего никогда нет, — и молчало ровно так же, как
   если бы работало. Отсюда лишние 20 пикселей на странице завершения
   регистрации, которые правило обещало убрать.

   Работает вопрос «есть ли внутри хоть один ТЕГ»: пробелы и переводы строк
   его не обманывают, а отсутствующий блок ошибки под него подходит. Второй
   строкой — тот же случай на уровень глубже: строка кнопок остаётся на
   странице, когда форма убрана флагом disable_form, а чужих входов клиент
   не запросил.

   В браузерах без :has (до Chrome 105 / Safari 15.4 / Firefox 121) пустой
   блок сохранит свой отступ. Это расхождение в 20 пикселей, а не поломка:
   страница остаётся рабочей и читаемой.

   ⚠ И ещё одна мина, на которую я наступил уже здесь: эти два случая нельзя
   записывать ОДНИМ правилом через запятую. Второй отбор в первой редакции
   выглядел как has(> .form__row__buttons:not(:has(*))) — has внутри has, а
   такая запись недопустима. Браузер выбрасывает не её одну, а ВСЁ правило
   целиком: исправная первая половина умирала вместе с ошибочной второй, и
   ни одна из 22 пустых строк не схлопывалась. Отдельными правилами ошибка
   в одном не трогает другое.                                              */
.form__row:not(:has(*)) { display: none; }

/* Строка кнопок, в которой не осталось ни одной кнопки: форму убрал
   disable_form, а чужих входов клиент не запросил. */
.form__row:has(> .form__row__buttons):not(:has(a, button, input)) { display: none; }

/* «Добро пожаловать!» — живёт в шапке, под названием сервиса */
.form__row__title {
  margin: 0;
  color: var(--auth-text);
  color: color-mix(in srgb, var(--auth-accent-current) 35%, var(--auth-text));
  font-size: 22px;
  font-weight: 700;
  line-height: 28px;
  text-align: center;
}

/* «Войдите в свой аккаунт» — первая строка в теле карточки */
.form__row__description {
  margin: 0;
  color: var(--auth-text-muted);
  font-size: 15px;
  line-height: 20px;
  text-align: center;
}

/* ---- поля ---- */

/* Отступ сверху даёт .form__row, здесь он не нужен — иначе сложится вдвое */
.form__row__inputs { margin-top: 0; }



.form__row__input-wrapper { position: relative; }

.form__row__input-wrapper + .form__row__input-wrapper { margin-top: 12px; }

.form__row__input-wrapper input {
  display: block;
  width: 100%;
  height: 50px;
  padding: 0 16px;
  border: 1px solid var(--auth-field-border);
  border-radius: var(--auth-radius-field);
  background: var(--auth-field-bg);
  color: var(--auth-text);
  font-family: var(--auth-font);
  /* 16px — не меньше: при меньшем размере iOS сам увеличивает страницу,
     как только пользователь ставит курсор в поле */
  font-size: 16px;
  line-height: 20px;
  -webkit-appearance: none;   /* иначе iOS рисует полю свою рамку и скругление */
  appearance: none;
}

.form__row__input-wrapper input::-webkit-input-placeholder { color: var(--auth-placeholder); }
.form__row__input-wrapper input::placeholder { color: var(--auth-placeholder); opacity: 1; }

.form__row__input-wrapper input:hover { border-color: var(--auth-field-border-hov); }

/* Прозрачная рамка вместо outline:none — в режиме высокой контрастности
   Windows система заменяет её на видимую, а тени там отключены вовсе.
   Без этого при высокой контрастности фокус пропадал совсем. */
.form__row__input-wrapper input:focus {
  border-color: var(--auth-accent-current);
  outline: 2px solid transparent;
  outline-offset: 2px;
  background: var(--auth-field-bg-focus);
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

/* Edge рисует в поле пароля свой глаз — рядом с нашим их было бы два */
.form__row__input-wrapper input::-ms-reveal,
.form__row__input-wrapper input::-ms-clear { display: none; }

/* Safari ставит в поле пароля кнопку связки ключей — сдвигаем, чтобы
   не легла поверх нашего глаза */
.form__row__input-wrapper input::-webkit-credentials-auto-fill-button { margin-right: 26px; }

/* Chrome закрашивает подставленное поле жёлтым — гасим внутренней тенью */
.form__row__input-wrapper input:-webkit-autofill,
.form__row__input-wrapper input:-webkit-autofill:hover {
  -webkit-box-shadow: 0 0 0 1000px var(--auth-field-bg) inset;
  -webkit-text-fill-color: var(--auth-text);
  caret-color: var(--auth-text);
}

/* У поля, заполненного менеджером паролей, тень выше перебивала подсветку
   фокуса, и она пропадала совсем. Поэтому в фокусе рисуем обе сразу:
   сначала подсветку снаружи, потом заливку внутри. */
.form__row__input-wrapper input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 3px var(--auth-accent-current-ring),
                      0 0 0 1000px var(--auth-field-bg-focus) inset;
  -webkit-text-fill-color: var(--auth-text);
  caret-color: var(--auth-text);
}

/* ---- глаз «показать пароль» ---- */

.form__row__input-icon {
  position: absolute;
  top: 50%;
  right: 12px;
  width: 30px;
  height: 30px;
  padding: 5px;
  margin-top: -15px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--auth-text-muted);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.form__row__input-icon svg {
  display: block;
  width: 20px;
  height: 20px;
}

/* спрятанный вариант иконки: правило выше иначе перебило бы атрибут hidden */
.form__row__input-icon svg[hidden] { display: none; }

.form__row__input-icon:hover { color: var(--auth-text); }

.form__row__input-icon:focus {
  outline: 2px solid transparent;
  outline-offset: 1px;
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

/* Место под кнопку показа пароля. Признак берём у самого поля: data-target
   стоит РОВНО у тех полей, которые эта кнопка переключает. Прежнее правило
   опиралось на соседство «кнопка, затем поле» и держало кнопку в разметке
   ПЕРЕД полем — из-за чего между почтой и паролем появлялся лишний шаг
   табуляции, которого в исходной версии не было (там значок был картинкой
   и фокус не принимал). Теперь порядок естественный: сначала поле, потом
   его кнопка. */
.form__row__input-wrapper input[data-target] { padding-right: 48px; }

/* ---- «Запомнить» и «Забыли пароль?» ---- */

/* Переносится, если не влезает: на увеличенном шрифте или узком экране
   «Запомнить» и «Забыли пароль?» иначе смыкались впритык */
.form__row__inputs-sub-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  margin-top: 12px;
  font-size: 13px;
  line-height: 16px;
}

.form__row__inputs-sub-row > * { margin-top: 4px; }

/* Настоящая галка спрятана от глаз, но НЕ выключена: до неё по-прежнему
   можно дойти клавишей Tab и нажать пробелом */
.form__row__inputs-sub-row__remember-me input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  border: 0;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.form__row__inputs-sub-row__remember-me label {
  display: inline-block;
  cursor: pointer;
}

.form__row__inputs-sub-row__remember-me .checkbox {
  position: relative;
  display: inline-block;
  width: 15px;
  height: 15px;
  margin-right: 8px;
  border: 1px solid var(--auth-field-border-hov);
  border-radius: 3px;
  background: var(--auth-card-bg);
  vertical-align: -3px;
}

.form__row__inputs-sub-row__remember-me input:checked + label .checkbox {
  border-color: var(--auth-accent-current);
  background: var(--auth-accent-current);
}

.form__row__inputs-sub-row__remember-me input:checked + label .checkbox:after {
  position: absolute;
  top: 1px;
  left: 4px;
  width: 4px;
  height: 8px;
  border: solid var(--auth-on-accent);
  border-width: 0 2px 2px 0;
  content: '';
  transform: rotate(45deg);
}

.form__row__inputs-sub-row__remember-me input:focus + label .checkbox {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

/* В режиме высокой контрастности система рисует свои цвета, но галочку
   внутри квадрата — нет: она собрана из рамок и была бы не видна. */
@media (forced-colors: active) {
  .form__row__inputs-sub-row__remember-me input:checked + label .checkbox {
    forced-color-adjust: none;
    border-color: Highlight;
    background: Highlight;
  }
  .form__row__inputs-sub-row__remember-me input:checked + label .checkbox:after {
    border-color: HighlightText;
  }
}

.form__row__inputs-sub-row__remember-me span { color: var(--auth-text-muted); }

/* ---- согласия при регистрации ---- */

/* Устроены как «Запомнить»: настоящая галка спрятана от глаз, но не выключена.
   Здесь это ещё и обязательное поле, а спрятанное через display:none браузер
   не может ни сфокусировать, ни подсветить — форма молча не отправлялась бы. */
.form__consent {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}

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

/* Флекс, а не строка: текст длиннее одной строки, и перенос должен идти под
   текстом, а не под галкой. У «Запомнить» на входе строка одна, там хватает
   строчной раскладки, здесь — нет. */
.form__consent label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  color: var(--auth-text-muted);
  cursor: pointer;
}

.form__consent .checkbox {
  position: relative;
  /* Галка не сжимается и не тянется: колонку задаёт текст. */
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  /* Выравнивание по ПЕРВОЙ строке текста, а не по середине абзаца. */
  margin-top: 1px;
  border: 1px solid var(--auth-field-border-hov);
  border-radius: 3px;
  background: var(--auth-card-bg);
}

.form__consent input:checked + label .checkbox {
  border-color: var(--auth-accent-current);
  background: var(--auth-accent-current);
}

.form__consent input:checked + label .checkbox:after {
  position: absolute;
  top: 1px;
  left: 4px;
  width: 4px;
  height: 8px;
  border: solid var(--auth-on-accent);
  border-width: 0 2px 2px 0;
  content: '';
  transform: rotate(45deg);
}

.form__consent input:focus + label .checkbox {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

@media (forced-colors: active) {
  .form__consent input:checked + label .checkbox {
    forced-color-adjust: none;
    border-color: Highlight;
    background: Highlight;
  }
  .form__consent input:checked + label .checkbox:after {
    border-color: HighlightText;
  }
}

/* Удвоение класса — тем же приёмом, что у приглашения зарегистрироваться:
   общее правило .form a:not(.form__row__button) снимает подчёркивание, а здесь
   ссылка ведёт на юридический документ и обязана читаться как ссылка. */
.form__consent.form__consent a {
  color: var(--auth-link);
  text-decoration: underline;
}

/* ---- кнопки ---- */

.form__row__buttons { margin-top: 0; }

/* Высота задана минимумом, а не жёстко: длинная надпись («Подтвердить
   регистрацию» на узком экране, увеличенный шрифт) переносится на вторую
   строку и кнопка растёт, а не обрезает текст. */
.form__row__button {
  display: flex;
  width: 100%;
  min-height: 48px;
  align-items: center;
  justify-content: center;
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: var(--auth-radius-field);
  background: none;
  color: var(--auth-text);
  color: color-mix(in srgb, var(--auth-accent-current) 35%, var(--auth-text));
  cursor: pointer;
  font-family: var(--auth-font);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  -webkit-appearance: none;
  appearance: none;
}

.form__row__button + .form__row__button { margin-top: 10px; }

.form__row__button-primary {
  background: var(--auth-accent-fill);
  color: var(--auth-on-accent);
}

/* ⚠ Под курсором меняем не заливку целиком, а затемняем поверх неё. Прежняя
   запись подставляла плоский цвет и тем стирала градиент: кнопка бренда с
   переливом под курсором становилась одноцветной — заметно и необъяснимо.
   Тень внутрь работает поверх любой заливки, хоть цвета, хоть градиента. */
.form__row__button-primary:hover {
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, .12);
}

/* Вторичная кнопка — «Войти через ЕСИА» и подобные */
.form__row__button-transparent {
  border-color: var(--auth-field-border);
  background: var(--auth-card-bg);
  color: var(--auth-text);
  color: color-mix(in srgb, var(--auth-accent-current) 35%, var(--auth-text));
}

/* Вход через чужой сервис — надпись нейтральная, без фирменного оттенка.
   Подтонировка означает «это наше действие»; Leader ID и «2035» не наши,
   и красить их в цвет системы было бы неверно по смыслу. Правило стоит
   ПОСЛЕ подтонированных: при равном весе побеждает то, что ниже. */
.form__row__button-external { color: var(--auth-text); }

.form__row__button-transparent:hover { background: var(--auth-field-bg); }

.form__row__button:focus {
  outline: 2px solid transparent;   /* видна при высокой контрастности */
  outline-offset: 2px;
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

/* ---- «Нет аккаунта?» ---- */

.form__row__register {
  margin-top: 24px;
  color: var(--auth-text-muted);
  font-size: 13px;
  line-height: 16px;
  text-align: center;
}

/* Класс продублирован намеренно. Общее правило ссылок весит больше обычного
   (в нём есть оговорка про кнопки), и без второго класса эта ссылка красилась
   бы как рядовая — а она единственное целевое действие на странице. */
.form__row__register.form__row__register a {
  margin-left: 6px;
  color: var(--auth-text);
  font-weight: 600;
}

/* ---- сообщения и подсказки (общие с восстановлением и регистрацией) ---- */

.hidden { display: none; }

/* Блок ошибки живёт в разметке всегда; пока сервер не положил в него текст,
   он не занимает места. Так его не потеряют при правке шаблона. */
.auth-error {
  margin-top: 16px;
  color: var(--auth-danger);
  font-size: 14px;
  line-height: 19px;
  text-align: center;
}

.auth-error:empty { display: none; }

.form__row__helper-text,
.form__row__helper-link {
  display: block;
  margin-top: 12px;
  color: var(--auth-text-muted);
  font-size: 13px;
  text-align: center;
}

.password-requirements {
  margin-top: 8px;
  color: var(--auth-text-muted);
  font-size: 13px;
  line-height: 17px;
}

.grecaptcha-badge { display: none !important; }

/* ==========================================================================
   Блоки, которые есть не на каждой странице
   ========================================================================== */

/* ---- «Аккаунт»: вы уже вошли (страница auth2.html) ---- */

.account-summary {
  display: flex;
  align-items: center;
  padding: 18px;
  border: 1px solid var(--auth-field-border);
  border-radius: 12px;
  background: var(--auth-field-bg);
}

.account-summary__user {
  display: flex;
  min-width: 0;
  width: 100%;
  align-items: center;
}

.account-summary__avatar { flex: none; margin-right: 14px; }

.account-summary__avatar-wrapper {
  display: flex;
  overflow: hidden;
  width: 56px;
  height: 56px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--auth-field-border);
  border-radius: 50%;
  background: var(--auth-card-bg);
}

.account-summary__avatar-wrapper img { width: 100%; height: 100%; object-fit: cover; }

.account-summary__details { min-width: 0; }

/* Длинная почта не должна распирать карточку — обрезаем многоточием */
.account-summary__name,
.account-summary__login {
  display: block;
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-summary__name { color: var(--auth-text); font-weight: 600; }
.account-summary__login { margin-top: 2px; color: var(--auth-text-muted); }

/* Кнопка выхода стоит справа в самой сводке: она относится к показанной
   учётной записи, а не к странице целиком. */
.account-actions__logout {
  flex: none;
  display: flex;
  align-items: center;
  margin-left: 12px;
  padding: 6px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--auth-text-muted);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.account-actions__logout:hover { color: var(--auth-text); }

.account-actions__logout:focus {
  outline: 2px solid transparent;
  outline-offset: 1px;
  box-shadow: 0 0 0 3px var(--auth-accent-current-ring);
}

.account-actions__logout svg { display: block; width: 22px; height: 22px; }

/* ---- две кнопки в строку («Сменить» / «Войти») ---- */

.form__row__buttons--inline { display: flex; }

.form__row__buttons--inline .form__row__button { width: auto; }

.form__row__buttons--inline .form__row__button + .form__row__button {
  margin-top: 0;
  margin-left: 10px;
}

.form__row__button--half { flex: 1 1 0; min-width: 0; }

.mt-0 { margin-top: 0; }

/* ---- теги под заголовком (у сервисов, где они включены) ---- */

.form__row__tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 14px;
}

.form__row__tag {
  display: flex;
  align-items: center;
  margin: 4px 3px 0;
  padding: 5px 10px;
  border: 1px solid var(--auth-field-border);
  border-radius: 999px;
  background: var(--auth-field-bg);
  color: var(--auth-text-muted);
  font-size: 12px;
  line-height: 16px;
}

.form__row__tag img { height: 14px; margin-right: 6px; }

/* ---- вход через ЕСИА ---- */

/* Leader ID — обычная надпись, своего правила не требует: логотипом он
   больше не выводится (чёрный знак на тёмном фоне пропадал). */
.text-2035 { margin-right: 14px; font-weight: 400; }
.form__row__button img { height: 14px; }

/* ---- сообщение об ошибке, когда класс не задан явно ----
   partials/errors.ejs подставляет form-error, если страница не передала свой */
.form-error {
  margin-top: 16px;
  color: var(--auth-danger);
  font-size: 14px;
  line-height: 19px;
  text-align: center;
}

.form-error:empty { display: none; }

/* ---- проверка «я не робот» ---- */

.recaptcha-fix,
.recaptcha-fix + div:has(.grecaptcha-badge) { display: none; }

.g-recaptcha { display: flex; justify-content: center; }

/* ---- окно выбора провайдера (нужно не всем сервисам) ---- */

.modal-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 999;
}

.modal-overlay__backdrop {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(16, 24, 40, .45);
}

.modal {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1000;
  width: 380px;
  max-width: 92%;
  max-height: 86%;
  padding: 24px;
  overflow: auto;
  border-radius: var(--auth-radius-card);
  background: var(--auth-card-bg);
  box-shadow: var(--auth-shadow);
  transform: translate(-50%, -50%);
}

.modal__header {
  margin-bottom: 16px;
  text-align: center;
}

.modal__title {
  color: var(--auth-text);
  font-size: 18px;
  font-weight: 700;
}

.modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  border: 0;
  background: none;
  color: var(--auth-text-muted);
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
}

.modal__list .form__row__button + .form__row__button { margin-top: 8px; }

/* Ссылка провайдера в окне: высота по содержимому, длинное название вуза
   переносится, а не обрезается */
/* Цвет здесь не задаётся намеренно: он приходит от правила чужих входов
   выше — эти ссылки помечены тем же признаком и красятся нейтрально,
   без фирменного оттенка. Своя строка с цветом только запутала бы: она
   выиграла бы у общего правила, и одна кнопка из ряда выпала бы. */
.provider-link {
  min-height: 44px;
  text-decoration: none;
}

/* ---- телефон ---- */

@media (max-width: 420px) {
  .auth-page { padding: 0; }

  .form {
    width: 100%;
    min-height: 100vh;
    min-height: 100dvh;
    border-radius: 0;
    box-shadow: none;
  }

  .form__brand { padding: 28px 22px 20px; }
  .form__body { padding: 22px 22px 32px; }
}

/* ============================================================================
   Композиция «полотно»
   ----------------------------------------------------------------------------
   Вторая из двух композиций: форма слева, иллюстрация справа, логотип вместо
   строки с названием системы. Включается классом на теле страницы, который
   ставит layout-start.ejs по значению из файла бренда.

   Главное свойство: РАЗМЕТКА ТА ЖЕ. Ни одного нового узла, ни второго шаблона —
   иллюстрация рисуется без собственного элемента. Поэтому композицию получают
   все страницы сразу, а правки формы приезжают всем брендам одинаково.

   Замер 03.09 по старым темам: своя композиция была нужна пяти брендам из
   семи — это не спец-путь под одного клиента.
   ========================================================================== */
.auth-page--canvas {
  justify-content: flex-start;
  gap: clamp(24px, 5vw, 80px);
  padding: 40px clamp(20px, 5vw, 72px);
}

/* Иллюстрация — вторая колонка. Своего узла в разметке у неё нет намеренно:
   как только для композиции понадобится отдельный тег, шаблон перестанет быть
   общим. */
/* ⚠ Колонка появляется ТОЛЬКО когда иллюстрация действительно есть — метку
   ставит шаблон, как и для логотипа. Без неё бренд, выбравший «полотно» без
   картинки, получал форму, прижатую к левому краю, и семь сотен точек пустоты
   справа: вид сломанной страницы, а не композиции. Условием на саму переменную
   этого не выразить — пустое значение здесь `none`, а оно вполне допустимо,
   и никакое @supports такую разницу не видит. */
.auth-page--canvas.auth-page--has-side::after {
  content: '';
  flex: 1 1 0;
  align-self: stretch;
  min-height: 320px;
  background-image: var(--auth-side-image);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}

/* Иллюстрации нет — колонки нет, и форма встаёт по центру, как в карточке:
   «полотно» без картинки остаётся осмысленной страницей. */
.auth-page--canvas:not(.auth-page--has-side) { justify-content: center; }

.auth-page--canvas .form {
  flex: 0 1 var(--auth-canvas-width);
  width: auto;
  background: none;
  box-shadow: none;
  overflow: visible;          /* логотип выходит за край формы */
}

/* Карточки нет — значит нет и её шапки как плашки. Заголовок при этом
   ОСТАЁТСЯ текстом: он называет страницу («Восстановление пароля»), и убирать
   его насовсем — ровно то, из чего мы уходим. */
.auth-page--canvas .form__brand {
  padding: 0 0 18px;
  background: none;
  border-bottom: 0;
  text-align: start;
}

/* Логотип рисуется ТОЛЬКО когда он есть на диске, и ровно тогда же прячется
   название системы. Иначе опечатка в имени файла дала бы страницу входа без
   единого опознавательного знака — ни названия, ни логотипа. */
.auth-page--canvas.auth-page--has-logo .form__brand::before {
  content: '';
  display: block;
  width: var(--auth-logo-w);
  height: var(--auth-logo-h);
  margin-bottom: 22px;
  background-image: var(--auth-logo);
  background-repeat: no-repeat;
  background-position: left center;
  background-size: contain;
}
/* ⚠ Название СКРЫВАЕТСЯ ГЛАЗАМИ, но остаётся для чтения вслух. Логотип здесь
   рисуется фоном, а у фона нет подписи: убрав название совсем, мы оставили бы
   незрячего человека на странице ввода пароля без единого признака того, куда
   он этот пароль вводит. Приём обычный: элемент сжат в точку и убран из
   потока, но не выключен. */
.auth-page--canvas.auth-page--has-logo .form__brand__name {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.auth-page--canvas .form__row__title { text-align: start; }
.auth-page--canvas .form__body { padding: 0 0 24px; }

/* Без карточки центрировать нечего: текст держится левого края колонки. */
.auth-page--canvas .form__notice,
.auth-page--canvas .form__row__description,
.auth-page--canvas .form__row__register { text-align: start; }

/* Телефон: иллюстрация уходит, форма занимает ширину — та же страница,
   без второй вёрстки. */
@media (max-width: 780px) {
  .auth-page--canvas { justify-content: center; }
  .auth-page--canvas.auth-page--has-side::after { display: none; }
  .auth-page--canvas .form { flex-basis: 420px; }
  /* ⚠ На узком экране карточка тянется во весь экран правилом выше и берёт
     min-height в высоту окна. В «полотне» поля страницы остаются, поэтому та
     же высота вылезала за экран на их величину и давала прокрутку на пустом
     месте. Здесь высота не нужна: формы и так во весь экран. */
  .auth-page--canvas .form { min-height: 0; }
}
