/* ==========================================================================
   Smart-Soft Auth - стили интерфейса

   Направление: рабочий инструмент, а не витрина. Сотрудник открывает эту
   страницу несколько раз в день на минуту, поэтому здесь нет ни приветствий,
   ни иллюстраций - только то, что нужно, чтобы войти или выдать доступ.

   Из этого следует всё остальное. Плотная сетка вместо воздуха: экономить
   прокрутку важнее, чем дышать. Моноширинный шрифт для всего, что читают
   посимвольно - коды приложений, ULID, роли, номера сборок: их сверяют
   глазами, и путать 0 с O здесь дорого. Один акцентный цвет, взятый из
   темы тенанта; цвет не украшает, а означает: «здесь действие» или
   «здесь роль выше обычной».

   Тема тенанта приходит отдельным файлом /theme.css и перекрывает
   --primary, --secondary и --font-family. Всё остальное - общее.
   ========================================================================== */

/* --- Токены ------------------------------------------------------------- */

:root {
  /* Перекрываются темой тенанта */
  --primary: #2563eb;
  --secondary: #64748b;
  --font-family: "Golos Text", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Нейтральная шкала: холодная, с лёгким уходом в синий, чтобы не
     спорить ни с одним из цветов тенантов */
  --ink: #14161f;
  --ink-soft: #4a5065;
  --ink-faint: #7d8397;
  --line: #e3e6ef;
  --line-strong: #cfd4e2;
  --paper: #fbfbfd;
  --surface: #ffffff;
  --sunken: #f4f5f9;

  /* Служебные */
  --danger: #c02637;
  --danger-bg: #fdf1f2;
  --success: #10714a;
  --success-bg: #eefaf4;
  --warning: #8a5a00;
  --warning-bg: #fff8e8;

  /* Моноширинный для кодов и чисел */
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Consolas, monospace;

  /* Плотные, но не тесные: 6px базовый шаг */
  --radius: 8px;
  --radius-sm: 5px;
  --shadow-sm: 0 1px 2px rgb(20 22 31 / 0.06);
  --shadow: 0 1px 3px rgb(20 22 31 / 0.07), 0 6px 20px rgb(20 22 31 / 0.05);

  --topbar-h: 56px;
  --page-max: 1120px;
}

/* --- Основа ------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Атрибут hidden должен побеждать собственные display-правила классов */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-family);
  font-size: 15px;
  line-height: 1.5;
  background: var(--paper);
  color: var(--ink);
  /* Цифры одинаковой ширины: колонки с датами и счётчиками не пляшут */
  font-variant-numeric: tabular-nums;
}

a {
  color: var(--primary);
  text-decoration: none;
  text-underline-offset: 2px;
}

a:hover {
  text-decoration: underline;
}

/* Видимый фокус - обязателен: без него форма непроходима с клавиатуры */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

h1,
h2,
h3 {
  margin: 0 0 0.6rem;
  line-height: 1.2;
  letter-spacing: -0.015em;
  font-weight: 650;
}

h1 {
  font-size: 1.45rem;
}

h2 {
  font-size: 1.05rem;
}

h3 {
  font-size: 0.95rem;
}

/* --- Раскладка ---------------------------------------------------------- */

.layout {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Непрозрачная шапка. Полупрозрачная с blur выглядела эффектнее, но
     backdrop-filter дорого стоит при прокрутке таблиц и, что важнее,
     ломает снятие скриншотов в headless-браузере - а скриншоты нужны для
     проверки вёрстки. */
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.topbar__inner {
  max-width: var(--page-max);
  height: var(--topbar-h);
  margin: 0 auto;
  padding: 0 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  justify-content: space-between;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.brand:hover {
  text-decoration: none;
}

/* Марка - три слоя, как в иконке: тот же знак, что и в фавиконке, иначе
   вкладка и шапка выглядят как разные сервисы */
.brand__mark {
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  background: var(--primary);
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  flex: 0 0 auto;
}

.nav {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex-wrap: wrap;
}

.nav a {
  padding: 0.35rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: 0.88rem;
  font-weight: 500;
  transition: background-color 0.12s, color 0.12s;
}

.nav a:hover {
  background: var(--sunken);
  color: var(--ink);
  text-decoration: none;
}

.nav a[aria-current="page"] {
  background: var(--sunken);
  color: var(--ink);
  font-weight: 600;
}

.content {
  flex: 1;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 1.75rem 1.25rem 3rem;
}

/* Страницы входа и регистрации: одна колонка, узкая, по центру */
.content--narrow {
  max-width: 420px;
  padding-top: 2rem;
  padding-bottom: 2rem;
}

/* Ноутбук с невысоким экраном: форма должна помещаться целиком, иначе
   кнопка «Войти» уезжает под сгиб */
@media (max-height: 780px) {
  .content--narrow {
    padding-top: 1rem;
  }

  .content--narrow .auth-header {
    margin-bottom: 1rem;
  }

  .content--narrow .auth-header h1 {
    font-size: 1.25rem;
  }

  .content--narrow .card {
    padding: 1.1rem 1.2rem;
  }

  .content--narrow .form {
    gap: 0.6rem;
  }
}

.footer {
  border-top: 1px solid var(--line);
  padding: 1rem 1.25rem;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 0.55rem;
  flex-wrap: wrap;
  color: var(--ink-faint);
  font-size: 0.8rem;
}

/* Номер сборки: служебная подпись, поэтому моноширинный и тише названия */
.footer__build {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--ink-faint);
  letter-spacing: -0.02em;
}

/* --- Карточки ----------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  box-shadow: var(--shadow-sm);
}

.card + .card {
  margin-top: 0.85rem;
}

.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.9rem;
}

.card__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 650;
}

/* --- Формы -------------------------------------------------------------- */

.form {
  display: grid;
  gap: 0.85rem;
}

.content--narrow .form {
  gap: 0.7rem;
}

.field {
  display: grid;
  gap: 0.3rem;
}

.field label {
  font-size: 0.82rem;
  font-weight: 550;
  color: var(--ink-soft);
  letter-spacing: 0.005em;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-color 0.12s, box-shadow 0.12s;
}

.field input:hover,
.field select:hover,
.field textarea:hover {
  border-color: var(--ink-faint);
}

/* Фокус даёт и рамку, и кольцо: одной рамки мало, когда поле уже
   подсвечено ошибкой */
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}

.field input:disabled,
.field select:disabled {
  background: var(--sunken);
  color: var(--ink-faint);
  cursor: not-allowed;
}

.field__hint {
  font-size: 0.78rem;
  color: var(--ink-faint);
  line-height: 1.4;
}

.field__error {
  font-size: 0.8rem;
  color: var(--danger);
}

.field--checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

/* Специфичность выше, чем у .field input: чекбокс не растягивается */
.field.field--checkbox input {
  width: auto;
  height: auto;
  min-width: 0;
  margin: 0.2rem 0 0;
  padding: 0;
  flex: 0 0 auto;
  accent-color: var(--primary);
}

.field--checkbox label {
  font-weight: 400;
  font-size: 0.86rem;
  color: var(--ink-soft);
  cursor: pointer;
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.5rem 0.9rem;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 550;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s, border-color 0.12s, box-shadow 0.12s;
}

.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.btn--primary {
  background: var(--primary);
  color: #fff;
  box-shadow: var(--shadow-sm);
}

.btn--primary:hover:not(:disabled) {
  background: color-mix(in srgb, var(--primary) 88%, black);
  text-decoration: none;
}

.btn--secondary {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--ink);
}

.btn--secondary:hover:not(:disabled) {
  background: var(--sunken);
  border-color: var(--ink-faint);
  text-decoration: none;
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}

.btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 88%, black);
  text-decoration: none;
}

.btn--sm {
  padding: 0.3rem 0.6rem;
  font-size: 0.82rem;
}

.btn--block {
  width: 100%;
  padding: 0.6rem 1rem;
}

.btn-row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
}

/* --- Сообщения ---------------------------------------------------------- */

.alert {
  padding: 0.65rem 0.85rem;
  border-radius: var(--radius-sm);
  border: 1px solid;
  font-size: 0.86rem;
  margin-bottom: 0.85rem;
  /* Полоска слева кодирует важность быстрее, чем цвет фона */
  border-left-width: 3px;
}

.alert--error {
  background: var(--danger-bg);
  border-color: color-mix(in srgb, var(--danger) 22%, transparent);
  border-left-color: var(--danger);
  color: color-mix(in srgb, var(--danger) 80%, black);
}

.alert--success {
  background: var(--success-bg);
  border-color: color-mix(in srgb, var(--success) 22%, transparent);
  border-left-color: var(--success);
  color: color-mix(in srgb, var(--success) 85%, black);
}

.alert--warning {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 24%, transparent);
  border-left-color: var(--warning);
  color: var(--warning);
}

.alert--info {
  background: color-mix(in srgb, var(--primary) 5%, white);
  border-color: color-mix(in srgb, var(--primary) 20%, transparent);
  border-left-color: var(--primary);
  color: color-mix(in srgb, var(--primary) 75%, black);
}

/* --- Таблицы ------------------------------------------------------------
   Таблица - основной носитель данных в админке, поэтому она не в карточке
   с отступами, а во всю ширину, с липкой шапкой при прокрутке.
   ------------------------------------------------------------------------ */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.86rem;
}

th,
td {
  text-align: left;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

/* Числа и коды выравниваются по правому краю и не переносятся */
td:has(.code),
td.code {
  white-space: nowrap;
}

th {
  background: var(--sunken);
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  white-space: nowrap;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr {
  transition: background-color 0.1s;
}

tbody tr:hover {
  background: color-mix(in srgb, var(--primary) 3%, var(--surface));
}

/* Ячейка с действиями: прижата к правому краю, чтобы кнопки стояли
   в одну линию во всех строках */
td:last-child .btn,
td:last-child form {
  margin-left: auto;
}

/* --- Бейджи -------------------------------------------------------------
   Плоские, без рамки: рамка конкурирует с рамкой карточки и делает
   таблицу рябой. Цвет фона несёт смысл, текст - значение.
   ------------------------------------------------------------------------ */

.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.6;
  background: var(--sunken);
  color: var(--ink-soft);
}

.badge--ok {
  background: var(--success-bg);
  color: var(--success);
}

.badge--off {
  background: var(--danger-bg);
  color: var(--danger);
}

.badge--neutral {
  background: var(--sunken);
  color: var(--ink-faint);
}

/* --- Маркер роли приложения ---------------------------------------------
   Сигнатурный элемент: в матрице доступов роль читается полоской слева,
   а не только словом. Четыре роли образуют лестницу от полного доступа к
   закрытому, и полоска заполняется по этой лестнице: admin - сплошная,
   user - три четверти, observer - четверть, closed - перечёркнутая.
   Так столбец ролей просматривается как диаграмма, и сразу видно, у кого
   доступ шире, чем предполагалось.

   Цвет не новый: это тот же --primary, что и у действий, но насыщенность
   падает вместе с уровнем доступа. Отдельная палитра для ролей спорила бы
   с темой тенанта.
   ------------------------------------------------------------------------ */

.role {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* Полоска слева: ширина заливки и есть уровень доступа */
.role::before {
  content: "";
  width: 3px;
  height: 1.1em;
  border-radius: 2px;
  flex: 0 0 auto;
  background: var(--line-strong);
}

.role--admin::before {
  background: var(--primary);
}

.role--user::before {
  /* Три четверти высоты заливки: видно, что доступ есть, но не полный */
  background: linear-gradient(
    to bottom,
    var(--primary) 0 72%,
    var(--line-strong) 72% 100%
  );
}

.role--observer::before {
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--primary) 55%, white) 0 38%,
    var(--line-strong) 38% 100%
  );
}

/* closed - доступ закрыт: полоска перечёркнута, цвет нейтральный, потому
   что это не «плохая» роль, а осознанно отозванный доступ */
.role--closed::before {
  background: repeating-linear-gradient(
    -45deg,
    var(--ink-faint) 0 2px,
    transparent 2px 4px
  );
}

.role--closed {
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--line-strong);
}

/* --- Статистика ---------------------------------------------------------
   Плитки плотные, число крупное и моноширинное: значения сверяют между
   собой, поэтому цифры должны стоять в колонку.

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

/* Сетка по числу плиток, а не по минимальной ширине: auto-fill оставлял
   пустую ячейку справа, которая растягивалась на всю оставшуюся ширину и
   выглядела как незагруженный блок. Здесь колонки делят ширину поровну,
   а на узком экране переходят в две. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.stat {
  padding: 0.85rem 1rem;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* Плитка не должна рисовать внешнюю рамку поверх рамки контейнера */
.stat-grid > .stat:last-child {
  border-right: none;
}

.stat__label {
  font-size: 0.72rem;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}

.stat__value {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  font-weight: 500;
  margin-top: 0.15rem;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

/* --- Прочее ------------------------------------------------------------- */

.muted {
  color: var(--ink-faint);
  font-size: 0.82rem;
}

.stack {
  display: grid;
  gap: 0.85rem;
}

.row {
  display: flex;
  gap: 0.65rem;
  align-items: center;
  flex-wrap: wrap;
}

.spacer {
  flex: 1;
}

/* Код: ULID, код приложения, адрес кошелька. Читается посимвольно,
   поэтому моноширинный и с фоном - чтобы не сливаться с текстом. */
.code {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: -0.02em;
  background: var(--sunken);
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  color: var(--ink-soft);
  word-break: break-all;
}

/* Одноразовый пароль: крупнее обычного кода и с выделением, потому что
   его переписывают с экрана вручную. Клик по нему копирует значение -
   обработчик в app.js навешен на класс .code. */
.code--secret {
  display: inline-block;
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  padding: 0.5rem 0.8rem;
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  color: var(--ink);
  cursor: pointer;
  user-select: all;
}

.code--secret:hover {
  border-style: solid;
  border-color: var(--primary);
}

.password-reveal {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.qr {
  background: #fff;
  padding: 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: inline-block;
}

.qr svg {
  display: block;
  width: 168px;
  height: 168px;
}

.pagination {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: space-between;
  margin-top: 0.85rem;
  flex-wrap: wrap;
}

.centered {
  text-align: center;
}

/* Заголовок формы входа: по центру над карточкой. В узкой колонке
   выключка влево читается как обрезанный текст, потому что строка
   начинается прямо от края экрана */
.auth-header {
  text-align: center;
  margin-bottom: 1.25rem;
}

.auth-header h1 {
  margin-bottom: 0.15rem;
}

.auth-header .muted {
  margin: 0;
}

.htmx-indicator {
  opacity: 0;
  transition: opacity 0.15s;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* --- Утилиты вместо инлайновых стилей ------------------------------------
   CSP страниц задаёт `style-src 'self'`, поэтому атрибут style="..."
   браузер игнорирует. Всё, что раньше писалось инлайново, живёт здесь.
   ------------------------------------------------------------------------ */

.title-flush {
  margin: 0;
}

.title-top-flush {
  margin-top: 0;
}

.last-child {
  margin-bottom: 0;
}

.mt-sm {
  margin-top: 0.4rem;
}

.mt {
  margin-top: 0.85rem;
}

.mt-lg {
  margin-top: 1.25rem;
}

/* Строка формы, выровненная по нижней границе: кнопка на одной линии
   с полями ввода, а не с подписями */
.row--form {
  align-items: flex-end;
}

/* Строка с двумя элементами по краям */
.row--split {
  justify-content: space-between;
}

.field--grow {
  flex: 1;
  min-width: 140px;
}

.field--grow-lg {
  flex: 1;
  min-width: 200px;
}

.field--grow-xl {
  flex: 1;
  min-width: 220px;
}

.field--w140 {
  min-width: 140px;
}

.field--w160 {
  min-width: 160px;
}

.field--w180 {
  min-width: 180px;
}

.field--w220 {
  min-width: 220px;
}

.stack--flush {
  margin: 0;
}

/* Подпись в списке «ключ - значение» одинаковой ширины во всех строках */
.stack__key {
  min-width: 160px;
}

.stack__key--wide {
  min-width: 180px;
}

/* Форма внутри текста: не разрывает абзац на отдельные строки */
.inline-form {
  display: inline;
}

/* Подпись для скринридеров: нужна полям в таблицах, где заголовок
   колонки уже объясняет смысл, а <label> визуально лишний */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- Адаптив ------------------------------------------------------------ */

@media (max-width: 700px) {
  .content {
    padding: 1.1rem 0.9rem 2rem;
  }

  .topbar__inner {
    padding: 0 0.9rem;
    gap: 0.6rem;
  }

  .card {
    padding: 1rem;
  }

  th,
  td {
    padding: 0.5rem 0.6rem;
  }

  /* В узком экране таблица прокручивается, а не ломает вёрстку */
  .table-wrap {
    border-radius: var(--radius-sm);
  }

  .stat-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 420px) {
  .stat-grid {
    grid-template-columns: 1fr;
  }
}

/* Уважение к системной настройке: анимации и переходы отключаются */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}
