/* Компоненты дизайн-системы. Расширяется по фазам; здесь — базовый набор,
   которым пользуются макросы macros/{ui,forms,status,empty}.html. */

/* — кнопки — */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Иконка кнопки не сжимается: в узкой ячейке она схлопывалась в ноль, и от
   кнопки оставалась одна подпись */
.btn > svg {
  flex: none;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

.btn-primary:hover:not(:disabled) {
  background: var(--accent-700);
  border-color: var(--accent-700);
  color: #fff;
}

.btn-ghost {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink);
}

.btn-ghost:hover:not(:disabled) {
  background: var(--hover);
  color: var(--ink);
}

.btn-quiet {
  color: var(--muted);
}

.btn-quiet:hover:not(:disabled) {
  background: var(--hover);
  color: var(--ink);
}

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

.btn-danger-outline {
  border-color: color-mix(in srgb, var(--red) 45%, var(--border));
  color: var(--red);
}

.btn-danger-outline:hover:not(:disabled) {
  background: color-mix(in srgb, var(--red) 8%, transparent);
}

.btn-sm {
  padding: 5px 10px;
  font-size: 11.5px;
}

.btn-icon {
  width: 34px;
  height: 34px;
  padding: 0;
}

/* В строке таблицы кнопка-иконка идёт рядом с полем input-sm: полный размер
   распирал бы строку и ломал общий ритм */
.btn-icon.btn-sm {
  width: 30px;
  height: 30px;
}

/* Пустое место кнопки-иконки: строки без неё должны кончаться на той же
   ширине, что и соседние с кнопкой */
.icon-slot {
  flex: none;
  width: 30px;
}

.btn-block {
  width: 100%;
}

/* Маркер подгрузки следующей страницы списка */
.load-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 17px;
}

/* Чекбокс в виде кнопки-иконки: сам квадратик прячем, состояние показываем
   заливкой — серой, чтобы не спорить с акцентом соседней кнопки */
.icon-toggle {
  position: relative;
  display: inline-flex;
  width: 34px;
  height: 34px;
  flex: none;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--muted);
  cursor: pointer;
}

.icon-toggle input {
  position: absolute;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.icon-toggle:hover {
  background: var(--hover);
  color: var(--ink);
}

.icon-toggle:has(input:checked) {
  background: var(--track);
  color: var(--ink);
}

.icon-toggle:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* — карточки — */

.card {
  /* Как в макете: колонка — подвал карточки прижимается к низу, а таблица
     внутри растягивается */
  display: flex;
  flex-direction: column;
  background: var(--surface);
  /* В макете карточка обведена: тени мало, край читается именно рамкой */
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow);
  padding: 16px 17px;
}

/* Указатель «потянуть вниз, чтобы обновить» — только на телефоне: мышью
   тянуть нечего, там есть кнопка обновления и автоперезапрос */
.ptr {
  display: none;
}

/* Карточка открывается целиком, как строка списка: переход ведёт data-href
   (app.js), поэтому нажимается вся плитка, а не одно название */
.card-link {
  cursor: pointer;
}

.card-link:hover {
  border-color: color-mix(in srgb, var(--ink) 20%, var(--border));
}

/* Форма во всю высоту карточки: ряд кнопок прижимается к низу, поэтому
   в плитке одинаковых по высоте карточек кнопки стоят на одной линии */
.form-fill {
  flex: 1;
}

.form-fill > .form-actions {
  margin-top: auto;
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  margin-bottom: 12px;
}

.card-title {
  font-size: 14px;
  font-weight: 600;
}

/* Шапка карточки, в которой сразу за ней идёт таблица: свои отступы и линия */
.card-head-flush {
  margin: 0;
  padding: 14px 17px;
  border-bottom: 1px solid var(--border);
}

.card-sub {
  font-size: 12px;
  color: var(--muted);
}

.card-flush {
  padding: 0;
  overflow: hidden;
}

/* — статусы — */

.dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  flex: none;
  border-radius: 50%;
  background: var(--gray);
  vertical-align: middle;
}

.dot-green {
  background: var(--green);
}

.dot-amber {
  background: var(--amber);
  animation: pa-pulse 1.3s infinite;
}

.dot-red {
  background: var(--red);
}

.dot-sm {
  width: 7px;
  height: 7px;
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 600;
  background: var(--gray-bg);
  color: var(--muted);
}

/* QR для подключения 2FA: SVG приходит без размеров, задаём здесь. */
.qr {
  flex: none;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: #fff;
}

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

.soft {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  background: var(--gray-bg);
  color: var(--muted);
}

.soft-green {
  background: var(--green-bg);
  color: var(--green);
}

.soft-amber {
  background: var(--amber-bg);
  color: var(--amber-700);
}

.soft-red {
  background: var(--red-bg);
  color: var(--red);
}

/* Действия строки прокси: место под результат замера занято заранее, иначе
   появление спиннера и плашки дёргало ширину всей таблицы. */
/* Идёт замер: значения прячутся, а на их месте прочерки шаблона бегут волной —
   «змейкой». Класс htmx-request ячейке ставит кнопка замера (hx-indicator) */
/* Какое состояние видно: змейка — только на время запроса, значение — всё
   остальное время. Селектор с :not сильнее правила плашки в слоте ниже,
   иначе змейка оставалась бы на экране и после замера */
.probe-cell:not(.htmx-request) .probe-snake,
.probe-cell.htmx-request .probe-value {
  display: none;
}

/* Прочерк из трёх сегментов шириной ровно в знак моноширинного шрифта — как
   сам символ «—», поэтому замена прочерка змейкой ничего не сдвигает. Высоты
   у прочерка нет: он стоит по середине строки, как и глиф тире. Задержка у
   каждого сегмента своя — номер приходит из разметки переменной --i и идёт
   сквозь оба прочерка, поэтому волна проходит от «мс» к «Мбит/с» */
.snake {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  width: 1ch;
  vertical-align: middle;
}

.snake i {
  flex: 1;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  opacity: 0.3;
  animation: pa-snake 1.1s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 90ms);
}

@keyframes pa-snake {
  0%,
  55%,
  100% {
    opacity: 0.3;
    transform: none;
  }

  25% {
    opacity: 1;
    transform: translateY(-3px);
  }
}

/* Кому движение мешает — прочерки просто светятся ровно, без волны */
@media (prefers-reduced-motion: reduce) {
  .snake i {
    animation: none;
    opacity: 0.6;
  }
}

/* Ширину колонки держит раскладка таблицы, слоту остаётся не пускать плашку
   за её пределы: длинный результат обрезается многоточием */
.probe-slot {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  overflow: hidden;
}

/* Все состояния ячейки — одна и та же плашка: прочерки, змейка, итог и отказ.
   Раньше итог был плашкой (11 px, полужирный, отступы), а прочерки и змейка —
   текстом таблицы, и при каждой смене прыгали кегль, высота строки и левый
   край. Блок, а не flex: во flex пробелы между словами змейки схлопывались, и
   она расходилась по ширине со строкой прочерков */
.probe-slot .soft {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  line-height: 16px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Прочерки до замера и змейка во время него — плашка без фона */
.soft-blank {
  background: transparent;
  color: var(--muted);
}

/* Обёртка итога — flex, как и сам слот: строчный блок вокруг плашки добавлял
   по базовой линии пару пикселей, и строка таблицы проседала при появлении
   змейки (у неё обёртки нет) и снова росла, когда приходил итог */
.probe-value {
  display: inline-flex;
}

/* Давность — фиксированной высоты: «—» и «только что» набраны разными глифами,
   и по «естественной» высоте шрифта строка отличалась на полпикселя */
.probe-when {
  line-height: 14px;
}

/* Итог замера и его давность — столбиком: дата под значениями, а не рядом, иначе
   строка «42 мс · 8.1 Мбит/с · 3 ч назад» не помещалась в колонку */
.probe-body {
  display: inline-flex;
  flex-direction: column;
  gap: 1px;
  max-width: 100%;
  white-space: nowrap;
}

/* Колонки поровну: в списках с короткими значениями обычная раскладка жмёт всё
   влево, а отданный одной колонке остаток превращается в дыру посреди строки */
.tbl-even {
  table-layout: fixed;
}

.tbl-even td,
.tbl-even th {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Подтверждение копирования: цвет держится полторы секунды, без всплывашек. */
.is-copied {
  color: var(--green);
}

/* — формы — */

.field {
  display: block;
}

.field > .label,
.field > label {
  display: block;
  margin-bottom: 6px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
}

.input {
  display: block;
  width: 100%;
  min-height: 38px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 13.5px;
}

.input::placeholder {
  color: var(--muted);
}

.input:hover {
  border-color: color-mix(in srgb, var(--ink) 25%, var(--border));
}

.input:focus-visible {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}

.input-mono {
  font-family: var(--font-mono);
}

/* Нативный select рисует свою стрелку и свой ритм — приводим к нашим полям */
select.input {
  appearance: none;
  padding-right: 34px;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right 11px center;
  cursor: pointer;
}

select.input:disabled {
  color: var(--muted);
  background-color: var(--track);
  cursor: not-allowed;
}

textarea.input {
  min-height: 120px;
  resize: vertical;
}

.field-hint {
  margin-top: 5px;
  font-size: 11.5px;
  color: var(--muted);
}

.field-error {
  color: var(--red);
  font-size: 12.5px;
}

/* — состояния — */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 34px 20px;
  border: 1px dashed var(--border);
  border-radius: var(--r-xl);
  /* Подложка карточки: вне карточки («Задач пока нет») область просвечивала
     серым фоном страницы, и штриховка висела в пустоте. Внутри карточки фон
     совпадает с её собственным — там ничего не меняется */
  background: var(--surface);
  text-align: center;
}

/* Прямо в карточке без полей (список, у которого нет записей): штриховка
   упиралась в края карточки — даём ей те же поля, что у строк списка */
.card-flush > .empty {
  margin: 14px 17px 17px;
}

/* Внутри карточки: те же элементы, но плотнее — иначе карточка вырастает
   вдвое против соседних */
.empty-compact {
  gap: 6px;
  padding: 18px 14px 20px;
}

.empty-compact .empty-icon {
  width: 40px;
  height: 40px;
}

.empty-compact .empty-title {
  font-size: 14px;
}

.empty-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--r-xl);
  background: var(--track);
  color: var(--muted);
  font-size: 20px;
}

.empty-icon-green {
  background: var(--green-bg);
  color: var(--green);
}

.empty-title {
  font-size: 15px;
  font-weight: 600;
}

.empty-text {
  max-width: 44ch;
  font-size: 12.5px;
  color: var(--muted);
}

.no-data {
  color: var(--muted);
  font-size: 12.5px;
}

.spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid color-mix(in srgb, var(--muted) 35%, transparent);
  border-top-color: var(--muted);
  border-radius: 50%;
  animation: pa-spin 0.7s linear infinite;
}

/* — плашки, аватары, полосы — */

.kv {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  padding: 13px 16px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
  font-size: 12.5px;
}

/* Внутри карточки плашки превращаются в строки списка: иначе получается
   «карточка в карточке» с двойными тенями и рваными отступами. */
.card .kv {
  padding: 10px 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  border-bottom: 1px solid var(--border);
}

.card .grid-2 > .kv:nth-last-child(-n + 2),
.card .kv:last-child {
  border-bottom: 0;
}

.kv-key {
  color: var(--muted);
}

.kv-value {
  font-weight: 500;
  text-align: right;
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  /* Радиус и кегль задаёт макрос: в макете они пропорциональны размеру */
  border-radius: 6px;
  background: var(--track);
  color: var(--muted);
  font-weight: 700;
}

.avatar-accent {
  background: var(--accent-soft);
  color: var(--accent);
}

.bar {
  display: block;
  width: 100%;
  overflow: hidden;
  border-radius: var(--r-sm);
  background: var(--track);
}

.bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-sm);
  background: var(--accent);
}

.bar-green {
  background: var(--green);
}
.bar-amber {
  background: var(--amber);
}
.bar-red {
  background: var(--red);
}

/* — тумблер — */

/* Внутри поля тумблер остаётся строкой: правило `.field > label` специфичнее
   и превращало его в блок — трек схлопывался в ноль */
.field > label.switch-row {
  display: flex;
  margin-bottom: 0;
}

.switch-row {
  /* Прячем чекбокс абсолютом — без этого он считается от корня страницы,
     вылезает из .page-scroll и добавляет документу второй скролл */
  position: relative;
  display: flex;
  align-items: center;
  gap: 11px;
  cursor: pointer;
}

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

.switch-track {
  position: relative;
  width: 38px;
  height: 22px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--track);
  transition: background 0.15s;
}

.switch-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: left 0.15s;
}

.switch:checked + .switch-track {
  background: var(--accent);
}

.switch:checked + .switch-track::after {
  left: 19px;
}

.switch:focus-visible + .switch-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.switch-text {
  display: flex;
  flex-direction: column;
}

.switch-label {
  font-size: 13px;
  font-weight: 500;
}

.switch-hint {
  font-size: 11.5px;
  color: var(--muted);
}

/* — сегмент-переключатель — */

.seg {
  display: inline-flex;
  /* В колоночных раскладках flex растягивал бы переключатель на всю ширину */
  align-self: flex-start;
  width: max-content;
  max-width: 100%;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
}

.seg-opt {
  /* см. .switch-row: скрытая радиокнопка не должна убегать из скроллера */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* Те же метрики, что у обычной кнопки: группа стоит в одном ряду с ними */
  min-height: 34px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
}

/* Иконочный вариант: квадрат вместо подписи */
.seg-opt-icon {
  width: 34px;
  padding: 0;
}

.seg-opt + .seg-opt {
  border-left: 1px solid var(--border);
}

.seg-opt input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.seg-opt:has(input:checked) {
  /* Заливка вместо внутренней обводки: выбранный вариант читается сразу */
  background: var(--accent);
  color: #fff;
  font-weight: 500;
}

/* Разделитель рядом с выбранным вариантом сливается с заливкой */
.seg-opt:has(input:checked) + .seg-opt {
  border-left-color: transparent;
}

.seg-opt:not(:has(input:checked)):not(.seg-opt-off):hover {
  background: var(--hover);
}

/* Вариант, который ещё не настроен: виден, но выбрать нельзя */
.seg-opt-off {
  color: var(--muted);
  opacity: 0.55;
  cursor: not-allowed;
}

.seg-opt:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* — код 2FA — */

.code-row {
  display: flex;
  gap: 8px;
}

.code-box {
  flex: 1;
  min-width: 0;
  height: 56px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 23px;
  font-weight: 600;
  text-align: center;
}

.code-box:focus-visible {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}

/* — таблицы — */

.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 11.5px;
}

.tbl th {
  padding: 6px 10px;
  background: var(--llbg);
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}

/* Управление, которое нужно только на телефоне (там, где широкий экран решает
   ту же задачу иначе — например, заголовками таблицы) */
.only-mobile {
  display: none;
}

/* Разметка по заданным ширинам (colgroup). Значение, которое не помещается,
   обрезается многоточием: перенос строки делал бы записи разной высоты, а
   целиком его показывает подсказка и кнопка JSON */
.tbl-fixed {
  table-layout: fixed;
  width: 100%;
}

.tbl-fixed td,
.tbl-fixed th {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Заголовок-ссылка сортировки: подпись остаётся подписью, стрелка появляется
   у колонки, по которой список отсортирован */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: inherit;
}

.th-sort:hover {
  color: var(--ink);
}

.right > .th-sort {
  /* У числовых колонок подпись прижата вправо — стрелка идёт за ней */
  flex-direction: row;
}

.th-sort-mark {
  display: inline-flex;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.th-sort:hover .th-sort-mark {
  opacity: 0.45;
}

.th-sort.is-sorted {
  color: var(--accent);
}

.th-sort.is-sorted .th-sort-mark {
  opacity: 1;
}

/* Возрастание — та же галка, развёрнутая: отдельной иконки в наборе нет, а
   две разные стрелки в одном ряду читались бы как разные элементы */
.th-sort-mark.is-up {
  transform: rotate(180deg);
}

.tbl td {
  /* По горизонтали ячейки уже заголовков — так в прототипе */
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

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

/* Крайние колонки живут на одной вертикали с отступом карточки */
.tbl th:first-child,
.tbl td:first-child {
  padding-left: 17px;
}

.tbl th:last-child,
.tbl td:last-child {
  padding-right: 17px;
}

.tbl tbody tr:hover {
  background: var(--hover);
}

.tbl .right,
.tbl th.right {
  text-align: right;
}

.tbl-row-link {
  cursor: pointer;
}

/* Ссылка в таблице цвета текста: в макете названия задач не синие */
.link-ink,
/* И при наведении тоже: строка целиком кликается и подсвечивается фоном, а
   синева на имени обещала бы, что клик работает только по нему. Правило нужно
   отдельным селектором — у базового `a:hover` специфичность выше класса */
.link-ink:hover {
  color: inherit;
}

.link-ink:hover .tbl-strong {
  text-decoration: underline;
}

.tbl-strong {
  font-size: 12.5px;
  font-weight: 600;
}

/* Название записи в таблице: в прототипе это средний вес, а не полужирный */
.tbl-name {
  font-weight: 500;
}

/* Отпечаток ключа: строка заведомо длиннее колонки, поэтому её обрезает сам
   текст — ячейке ширину не задаём, иначе на телефоне, где она занимает всю
   строку карточки, значение всё равно не помещалось */
.fingerprint {
  display: inline-block;
  max-width: 220px;
  vertical-align: bottom;
}

.tbl-sticky thead th {
  position: sticky;
  top: 0;
  z-index: 1;
}

.tbl-wrap {
  overflow-x: auto;
}

.tbl-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  /* 17px по горизонтали — та же вертикаль, что у колонок таблицы и полей карточки */
  padding: 11px 17px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--muted);
}

/* — пагинатор списка — */

/* Живёт в подвале карточки, как `.tbl-foot`: та же граница сверху и те же
   поля, чтобы список и его страницы читались одним блоком */
.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  padding: 9px 13px 9px 17px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}

.pager-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
}

.pager-num,
.pager-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 7px;
  border-radius: var(--r-sm);
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.pager-num:hover,
.pager-btn:not(.is-off):hover {
  background: var(--hover);
  color: var(--ink);
}

.pager-num.is-current {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

/* Край списка: стрелка остаётся на месте, чтобы ряд не прыгал при переходе */
.pager-btn.is-off {
  opacity: 0.35;
  pointer-events: none;
}

.pager-gap {
  padding: 0 2px;
  color: var(--gray);
}

/* Сколько записей на странице. Группа-сегмент из ссылок: выбор — это адрес,
   а не состояние формы, иначе им нельзя было бы поделиться */
.pager-size {
  display: inline-flex;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}

.pager-size-opt {
  min-width: 30px;
  padding: 3px 8px;
  color: var(--muted);
  font-size: 11.5px;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.pager-size-opt + .pager-size-opt {
  border-left: 1px solid var(--border);
}

.pager-size-opt:hover {
  background: var(--hover);
  color: var(--ink);
}

.pager-size-opt.is-current {
  background: var(--accent);
  color: #fff;
  font-weight: 500;
}

/* Положение в списке — только для телефона, где номера скрыты */
.pager-state {
  display: none;
  font-variant-numeric: tabular-nums;
}

/* — табы — */

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--border);
}

.tab {
  padding: 0 0 9px;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
  margin-bottom: -1px;
}

.tab:hover {
  color: var(--ink);
}

/* Вкладки с иконкой: на широком экране иконка идёт перед подписью и приглушена,
   как в боковом списке настроек */
.tabs-icons .tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.tabs-icons .tab svg {
  flex: none;
  color: var(--muted);
}

.tabs-icons .tab[aria-current] svg {
  color: inherit;
}

.tab[aria-current] {
  border-bottom-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.tabs-side {
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  top: 20px;
}

.tabs-side .tab {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  border: 0;
  border-radius: var(--r-md);
  margin: 0;
}

/* Иконка приглушена: она подсказывает раздел, а не спорит с подписью */
.tabs-side .tab svg {
  flex: none;
  color: var(--muted);
}

.tabs-side .tab[aria-current] svg {
  color: inherit;
}

/* Узкий экран: колонки схлопнулись, и боковой список вкладок становится
   строкой — вертикальным он занял бы весь первый экран. Правило живёт здесь,
   а не в 02-layout: этот файл идёт по каскаду позже */
@media (max-width: 1300px) {
  .tabs-side {
    position: static;
    flex-direction: row;
    gap: 6px;
    overflow-x: auto;
    scrollbar-width: thin;
  }

  .tabs-side .tab {
    white-space: nowrap;
  }
}

.tabs-side .tab[aria-current] {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}

/* Карточка интеграции: короткая — статус сверху, действие снизу */
.integration {
  gap: 12px;
  justify-content: space-between;
}

.integration-foot {
  margin-top: auto;
}

/* — чипы-фильтры — */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 500;
}

.chip:hover {
  background: var(--hover);
  color: var(--ink);
}

.chip[aria-current] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent);
}

.chip-count {
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 1px 5px;
  border-radius: var(--r-sm);
  background: var(--track);
  color: var(--muted);
}

.chip[aria-current] .chip-count {
  background: var(--accent-soft-strong);
  color: var(--accent);
}

/* — сегмент-группа фильтров с счётчиками — */

.segbar {
  display: inline-flex;
  align-items: center;
  height: 36px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  overflow: hidden;
}

.segbar a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 100%;
  padding: 0 13px;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 500;
}

.segbar a + a {
  border-left: 1px solid var(--border);
}

.segbar a:hover {
  background: var(--hover);
  color: var(--ink);
}

.segbar a[aria-current] {
  color: var(--ink);
  font-weight: 700;
}

/* Взаимоисключающие способы доступа: показываем набор полей выбранного
   сегмента. Переключение — на :checked, без своего JS */
.authmode {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Подзаголовок группы внутри таблицы: длинный список параметров иначе
   читается сплошной простынёй */
.tbl-group td {
  padding: 7px 17px;
  background: var(--llbg);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Список внутри карточки с полями (ключи доступа, ssh-ключи): таблица идёт во
   всю ширину карточки, как в card-flush, иначе её заливка и линии обрываются
   раньше края, а содержимое ячеек уезжает вправо от заголовка блока — рядом
   стоящие списки выглядели по-разному.

   Только для карточки с полями: у card-flush их нет, и вычитание уводило
   таблицу за край — правая колонка обрезалась */
.card-inset-list:not(.card-flush) > .tbl-wrap {
  margin-inline: -17px;
  width: auto;
}

/* Содержимое с полями внутри карточки без полей (форма, текстовый редактор).
   Линия сверху: у последней строки таблицы её нет, и список сливался с формой */
.card-inset {
  padding: 14px 17px;
  border-top: 1px solid var(--border);
}

/* Сразу под шапкой карточки своя линия не нужна: её уже нарисовал `.card-head`,
   и две подряд читались как одна толстая */
.card-head + .card-inset {
  border-top: 0;
}

/* Перед таблицей линия нужна снизу: у полосы заголовков колонок своей верхней
   границы нет, и форма сливалась с ней */
.card-inset:has(+ .tbl-wrap) {
  border-bottom: 1px solid var(--border);
}

/* Отбивка внутри обычной карточки: линию тянем на её поля, иначе она обрывается
   на padding и выглядит короче края */
.card-rule {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  margin-inline: -17px;
  padding-inline: 17px;
}

/* Плашки итогов прогона ansible: отделяем от шапки и от лога */
.recap {
  flex-wrap: wrap;
  margin: 2px 0 4px;
}

/* Заказы машин: карточка на заявку — статус, полоса шагов и действия */
.orders {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 14px;
}

.order {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 12px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
}

.order-failed {
  border-color: color-mix(in srgb, var(--red) 35%, var(--border));
}

/* Полоса шагов нагляднее слов «шаг 3 из 5»: видно, сколько осталось */
.order-steps {
  display: flex;
  gap: 4px;
}

.order-step {
  height: 4px;
  flex: 1;
  border-radius: var(--r-pill);
  background: var(--track);
}

.order-step-done {
  background: var(--amber);
}

.order-error {
  margin: 0;
  /* Ошибка провайдера бывает длинной — не растягиваем ей модалку */
  max-height: 3.6em;
  overflow: hidden;
}

.order-actions {
  align-items: center;
}

/* Выпадающий выбор в форме: вид поля ввода, список — как в остальных меню */
.picker {
  width: 100%;
}

.picker-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 38px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--ink);
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}

.picker-btn:hover {
  border-color: color-mix(in srgb, var(--ink) 25%, var(--border));
}

.picker[open] > .picker-btn {
  border-color: var(--accent);
}

/* Выбор недоступен (нет ключей, не выбран регион) — то же поле, но неактивное */
.picker-btn-off {
  background: var(--track);
  color: var(--muted);
  cursor: not-allowed;
}

/* Пока меню открыто, блок его не режет. Карточка без полей обрезает
   содержимое по скруглённым углам (иначе таблица вылезает за край), и
   выпадающий список обрывался по её нижней границе */
.card:has(.picker[open]),
.card-inset:has(.picker[open]),
.tbl-wrap:has(.picker[open]) {
  overflow: visible;
}

/* Селектор с потомком: .dd-menu объявлен ниже и иначе перебивает ширину */
.picker > .picker-menu {
  left: 0;
  right: 0;
  width: auto;
  max-height: 260px;
  overflow-y: auto;
}

/* Показываем набор полей выбранного сегмента. Селекторы с потомком: иначе
   утилита .stack (объявлена позже) перебивает display у скрытого набора */
.authmode > [data-mode] {
  display: none;
}

.authmode:has(input[value="token"]:checked) > [data-mode="token"],
.authmode:has(input[value="app"]:checked) > [data-mode="app"],
.authmode:has(input[value="deploy"]:checked) > [data-mode="deploy"],
/* Способ доступа к хосту: за туннелем правила ufw не нужны вовсе */
.authmode:has(input[value="tunnel"]:checked) > [data-mode="tunnel"],
.authmode:has(input[value="ports"]:checked) > [data-mode="ports"],
/* Ssh-доступ: пароль вводится строкой, ключ — многострочным блоком */
.authmode:has(input[value="ssh_password"]:checked) > [data-mode="ssh_password"],
.authmode:has(input[value="ssh_key"]:checked) > [data-mode="ssh_key"] {
  display: flex;
}

/* Свёрнутые подробности (раскладка и лог деплоя): сам блок остаётся карточкой,
   под шапкой — только строка-переключатель. Стрелка поворачивается вместо
   стандартного маркера: он рисуется по-разному в каждом браузере */
.fold > summary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 0 0;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}

.fold > summary::-webkit-details-marker,
.fold > summary::marker {
  display: none;
}

.fold > summary:hover {
  color: var(--ink);
}

.fold > summary svg {
  flex: none;
  transition: transform 0.15s ease;
}

.fold[open] > summary svg {
  transform: rotate(90deg);
}

.fold[open] > summary {
  padding-bottom: 8px;
}

/* — выпадающие меню на <details> — */

.dd {
  position: relative;
}

.dd > summary {
  /* display:block здесь ломал flex у .topbar-btn и .user-chip: иконка не
     центрировалась, а шеврон уезжал на вторую строку */
  display: flex;
  align-items: center;
  list-style: none;
  cursor: pointer;
}

.dd > summary::-webkit-details-marker,
.dd > summary::marker {
  display: none;
  content: "";
}

.dd-menu {
  position: absolute;
  top: calc(100% + 7px);
  right: 0;
  z-index: 40;
  width: 240px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 11px;
  background: var(--surface);
  box-shadow: var(--shadow-menu);
  animation: pa-in 0.14s ease;
}

.dd-menu-wide {
  width: 340px;
  padding: 0;
  overflow: hidden;
}

.dd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 13.5px;
  font-weight: 600;
}

.dd-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  text-align: left;
}

.dd-item:hover {
  background: var(--hover);
  color: var(--ink);
}

/* Заголовок раздела в меню: тарифы делятся по типу плана */
.dd-group {
  padding: 10px 10px 4px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.dd-group:first-child {
  padding-top: 2px;
}

/* Вторая строка пункта: конфигурация и цена в одну строку не помещаются */
.dd-item-sub {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 12px;
}

.dd-item-on .dd-item-sub,
.dd-item:hover .dd-item-sub {
  color: inherit;
  opacity: 0.75;
}

.dd-item-danger {
  color: var(--red);
}

.dd-item-danger:hover {
  background: color-mix(in srgb, var(--red) 8%, transparent);
  color: var(--red);
}

.dd-sep {
  height: 1px;
  margin: 5px 4px;
  background: var(--border);
}

.dd-user-head {
  padding: 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 5px;
}

/* — модалки — */

dialog.modal {
  width: min(500px, calc(100vw - 32px));
  padding: 0;
  border: 0;
  border-radius: var(--r-2xl);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-modal);
}

dialog.modal-wide {
  width: min(620px, calc(100vw - 32px));
}

/* Окно, из которого выпадающие меню выходят наружу. У <dialog> браузерный
   стиль — overflow: auto, поэтому меню нижнего поля не «выпадало», а
   прокручивало окно и обрезалось его краем. Содержимое таких окон короткое,
   собственная прокрутка им не нужна. */
dialog.modal-overflow {
  overflow: visible;
}

/* Меню поля у нижнего края окна открывается вверх: вниз ему всё равно некуда */
.picker-up .picker-menu {
  top: auto;
  bottom: calc(100% + 7px);
}

/* Лог ansible — длинные строки: узкая модалка ломала их на каждом слове */
dialog.modal-log {
  width: min(980px, calc(100vw - 32px));
}

dialog.modal::backdrop {
  background: rgba(15, 18, 25, 0.5);
  backdrop-filter: blur(2px);
}

.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--r-md);
  padding: 18px 20px 12px;
}

.modal-title {
  font-size: 17px;
  font-weight: 600;
}

.modal-sub {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--muted);
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 0 20px 18px;
}

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--bg);
  border-radius: 0 0 var(--r-2xl) var(--r-2xl);
}

.modal-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--red) 14%, transparent);
  color: var(--red);
}

/* — карточки метрик — */

.stat {
  display: flex;
  flex-direction: column;
  padding: 16px 17px;
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow);
}

/* Плитка-ссылка: ведёт на свой экран, но выглядит как остальные */
.stat-link {
  color: inherit;
  transition: border-color 0.15s ease;
}

.stat-link:hover {
  border-color: var(--accent);
}

.stat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-md);
  /* Отступы внутри карточки заданы полями блоков, а не gap контейнера:
     так же, как в прототипе (12px под шапкой, 4px над подписью) */
  margin-bottom: 12px;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}

.stat-value {
  display: flex;
  flex-wrap: wrap;
  /* Таблетка встаёт по центру цифры. Базовая линия здесь не работает: у
     inline-flex её берут от нижнего края, и плашка проваливалась на треть
     высоты числа вместо того, чтобы стоять с ним на одной линии */
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.stat-head .dot {
  width: 8px;
  height: 8px;
}

.stat-sub {
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--muted);
}

.metric-value {
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.15;
}

/* — столбчатый график: CSS вместо JS-библиотеки — */

.chart {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin-top: 6px;
}

/* В рамке со шкалой отступ задаёт сама рамка: свой сдвигал столбцы на 6px
   вниз, и линия «$0» проходила выше их основания. Столбцы поверх сетки —
   линия, пересекающая столбец, читается как его граница */
.plot .chart {
  position: relative;
  z-index: 1;
  margin-top: 0;
}

.chart-col {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.chart-area {
  /* column-reverse: сегменты копятся снизу, как принято у столбчатых графиков,
     и порядок в разметке совпадает с порядком статей в легенде */
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-start;
  width: 100%;
  height: 128px;
}

/* Одиночный столбец — экран данных: там у дня одно значение и делить нечего */
.chart-bar {
  width: 100%;
  /* Нулевой день — всё равно столбец: тонкая линия читается как артефакт */
  min-height: 7px;
  border-radius: 8px 8px 3px 3px;
  background: color-mix(in srgb, var(--accent) 22%, var(--track));
}

.chart-seg {
  /* Уже колонки: во всю ширину тридцать дней читались как забор из плашек,
     а не как график. Промежуток между днями должен быть виден */
  width: min(100%, 18px);
  margin: 0 auto;
  background: color-mix(in srgb, var(--accent) 22%, var(--track));
}

/* Скругление — только у краёв столбца, иначе стык сегментов выглядит разрывом.
   Радиус мелкий: при узком столбце крупный превращал его в таблетку */
.chart-area > .chart-seg:first-child {
  border-radius: 0 0 2px 2px;
}

.chart-area > .chart-seg:last-child {
  border-radius: 3px 3px 0 0;
}

.chart-seg-vps {
  background: color-mix(in srgb, var(--accent) 62%, var(--track));
}

.chart-seg-ipv4 {
  background: color-mix(in srgb, var(--accent) 24%, var(--track));
}

/* Прокси — своим тоном: это не аренда железа, и в столбце их видно отдельно */
.chart-seg-proxy {
  background: color-mix(in srgb, var(--amber) 55%, var(--track));
}

/* Платная капча — третьим тоном: разовые решения, а не аренда */
.chart-seg-captcha {
  background: color-mix(in srgb, var(--green) 55%, var(--track));
}

/* Нулевой день — всё равно столбец: пустота читается как сбой сбора */
.chart-seg-zero {
  min-height: 7px;
  border-radius: 3px;
  background: var(--track);
}

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 12px;
  color: var(--muted);
  font-size: 11.5px;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.legend-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 3px;
  background: var(--track);
}

.legend-vps {
  background: color-mix(in srgb, var(--accent) 62%, var(--track));
}

.legend-ipv4 {
  background: color-mix(in srgb, var(--accent) 24%, var(--track));
}

.legend-proxy {
  background: color-mix(in srgb, var(--amber) 55%, var(--track));
}

.legend-captcha {
  background: color-mix(in srgb, var(--green) 55%, var(--track));
}

/* Линейные ряды помечены отрезком, а не квадратом: квадрат обещает заливку */
.legend-now,
.legend-prev,
.legend-forecast {
  width: 16px;
  height: 2px;
  border-radius: 1px;
}

.legend-now {
  background: var(--accent);
}

.legend-prev {
  background: var(--muted);
  opacity: 0.6;
}

.legend-forecast {
  background: repeating-linear-gradient(90deg, var(--accent) 0 4px, transparent 4px 8px);
}

/* Поле графика: слева подписи шкалы, по ним же протянуты линии сетки. Столбцы
   и кривая живут в одинаковой рамке — иначе два графика подряд читаются как
   нарисованные разными руками */
.plot {
  position: relative;
  padding-left: 46px;
  padding-top: 6px;
}

.plot-scale {
  position: absolute;
  inset: 6px 0 auto 0;
  height: 128px;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 10.5px;
}

/* Метки расставлены по долям высоты, а не «поровну между собой»: у второго
   способа края строк ложатся на границы поля, и нижняя линия оказывалась на
   высоту подписи выше основания столбцов */
.plot-scale span {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  transform: translateY(-50%);
}

.plot-scale span:nth-child(1) {
  top: 0;
}

.plot-scale span:nth-child(2) {
  top: 50%;
}

.plot-scale span:nth-child(3) {
  top: 100%;
}

.plot-scale i {
  width: 40px;
  flex: none;
  text-align: right;
  font-style: normal;
}

.plot-scale span::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--border);
  opacity: 0.55;
}

/* Нижняя линия — ось: она гуще остальных */
.plot-scale span:last-child::after {
  opacity: 1;
}

.plot-axis {
  padding-left: 46px;
}

/* Накопительная кривая. SVG растянут по ширине карточки (preserveAspectRatio
   none), поэтому толщина линий держится на vector-effect, а не на координатах */
.curve {
  position: relative;
  height: 128px;
}

.curve svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.curve polyline {
  stroke-width: 2px;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.curve-now {
  stroke: var(--accent);
}

.curve-prev {
  stroke: var(--muted);
  stroke-width: 1.5px;
  opacity: 0.55;
}

.curve-forecast {
  stroke: var(--accent);
  stroke-dasharray: 4 4;
  opacity: 0.7;
}

.curve-fill {
  fill: var(--accent-soft);
  stroke: none;
}

/* Где мы сейчас: конец линии месяца. Точка вынесена из SVG — в растянутой
   системе координат круг стал бы эллипсом */
.curve-dot {
  position: absolute;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.chart-bar-today {
  background: var(--accent);
}

.chart-value {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--muted);
}

.chart-day {
  font-size: 11.5px;
  color: var(--muted);
}

/* Ось под графиком: подписи не привязаны к столбцам, поэтому не наезжают друг
   на друга ни на какой ширине */
.chart-axis {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
  color: var(--muted);
  font-size: 11.5px;
  white-space: nowrap;
}

.chart-day-today {
  color: var(--ink);
  font-weight: 600;
}

/* — микро-бары нагрузки — */

.microbars {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 130px;
}

.microbar {
  display: flex;
  align-items: center;
  gap: 7px;
}

.microbar-label {
  width: 26px;
  flex: none;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--muted);
}

/* — прочее — */

.tip-wrap {
  position: relative;
  display: inline-flex;
}

.tip {
  position: absolute;
  right: 0;
  bottom: calc(100% + 9px);
  z-index: 30;
  width: 250px;
  padding: 10px 12px;
  border-radius: 9px;
  background: var(--logbg);
  color: var(--loginkstrong);
  font-size: 11.5px;
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  box-shadow: var(--shadow-tip);
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
}

.tip::after {
  content: "";
  position: absolute;
  top: 100%;
  right: 16px;
  border: 6px solid transparent;
  border-top-color: var(--logbg);
}

.tip-wrap:hover .tip,
.tip-wrap:focus-within .tip {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.danger-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-top: 14px;
  padding: 14px 16px;
  border: 1px solid color-mix(in srgb, var(--red) 30%, var(--border));
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--red) 4%, var(--surface));
}

.danger-title {
  font-size: 13px;
  font-weight: 600;
}

.danger-text {
  font-size: 11.5px;
  color: var(--muted);
}

.inline-form {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.inline-form .input {
  min-height: 34px;
  padding: 6px 10px;
  font-size: 12.5px;
}

/* Кнопка в одну линию с полями и сегментом: btn-sm ниже них */
.inline-form .btn {
  min-height: 34px;
}

/* Подсказка под полем растягивала форму и уводила кнопки вниз */
.inline-form .field-hint {
  display: none;
}

/* Тумблер в строке формы: он выше полей, поэтому прижимаем к их линии */
.inline-form .switch-row {
  min-height: 34px;
  flex: none;
}

.inline-form .field {
  flex: 0 1 260px;
}

/* Форма с одним выбором: поле занимает всю свободную ширину строки. Списки
   прокси и хостов длинные — адрес с портом в 260 px не помещался, и выбранное
   значение обрезалось на середине */
.inline-form-wide .field {
  flex: 1 1 320px;
}

/* Меню пикера в такой форме шире кнопки: строки длиннее самого поля */
.inline-form-wide .picker-menu {
  min-width: 340px;
}

/* Утилита .grow объявлена в слое утилит, но правило выше специфичнее —
   поле, которому нужна вся свободная ширина, помечаем явно */
.inline-form .field.grow {
  flex: 1 1 auto;
}

.inline-form-wrap {
  flex-wrap: wrap;
}

/* Подвал карточки ресурса: разделитель и ряд действий одной высоты */
.card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}

.btn-accent-text {
  color: var(--accent);
}

/* Опасное действие в ряду тихих кнопок: цветом, а не заливкой */
.btn-danger-text {
  color: var(--red);
}

.btn-danger-text:hover:not(:disabled) {
  background: color-mix(in srgb, var(--red) 8%, transparent);
  color: var(--red);
}

/* Строка с проблемой подсвечена, как в макете «Свежесть данных» */
tr.row-error > td {
  background: color-mix(in srgb, var(--red) 6%, transparent);
}

.filters-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

/* Фильтры уходят вправо: слева сегменты, справа фильтры — как в макете */
.filters-bar > form:last-child,
.filters-bar > .row:last-child {
  margin-left: auto;
}

/* Кнопка фильтра: метрики поля ввода, но ведёт себя как меню */
.filter-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 37px;
  padding: 0 11px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
}

.filter-btn:hover {
  border-color: var(--accent);
}

.filter-dd[open] > .filter-btn {
  border-color: var(--accent);
}

.filter-btn-label {
  color: var(--muted);
}

.filter-btn-value {
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

.filter-btn-arrow {
  display: flex;
  margin-left: 2px;
  color: var(--muted);
}

.filter-dd[open] > .filter-btn > .filter-btn-arrow {
  transform: rotate(180deg);
}

.filter-menu {
  width: 220px;
  max-height: 320px;
  overflow-y: auto;
}

/* Выбранный пункт: галочка справа плюс акцентная подпись */
.dd-item-on {
  color: var(--accent);
  font-weight: 500;
}

/* — визард — */

.stepper {
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  top: 20px;
}

.step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 9px 10px;
  border-radius: var(--r-md);
  color: var(--ink);
  text-decoration: none;
}

.step:hover {
  background: var(--hover);
  color: var(--ink);
}

.step[aria-disabled="true"] {
  pointer-events: none;
  opacity: 0.6;
}

.step-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border: 1.5px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}

.step-current .step-mark {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 4px var(--accent-soft);
}

.step-done .step-mark {
  border-color: var(--green);
  background: var(--green);
  color: #fff;
}

.step-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.step-label {
  font-size: 12.5px;
}

.step-current .step-label {
  color: var(--accent);
  font-weight: 600;
}

.step-value {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wizard-panel {
  padding: 24px;
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow);
  min-height: 360px;
}

.wizard-title {
  font-size: 17px;
  font-weight: 600;
}

.wizard-hint {
  margin: 6px 0 18px;
  font-size: 12.5px;
  color: var(--muted);
}

.wizard-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 11px;
}

.pick {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  cursor: pointer;
}

.pick:hover {
  background: var(--hover);
}

.pick input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.pick-on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent);
}

.pick-label {
  font-size: 13.5px;
  font-weight: 500;
}

.pick-hint {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* — дерево географии — */

.tree {
  max-height: 400px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}

.tree-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
}

.tree-row:last-child {
  border-bottom: none;
}

.tree-region {
  background: var(--bg);
  font-weight: 500;
}

.tree-locality {
  padding-left: 34px;
}

.tree-pharmacy {
  padding-left: 62px;
  font-size: 12px;
}

.tree-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: none;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
}

.tree-toggle:hover {
  background: var(--track);
  color: var(--ink);
}

.tree-label {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  cursor: pointer;
}

.tree-meta {
  flex: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* — история событий — */

/* Разделитель дня в списке событий: те же боковые поля, что у записей */
.history-day {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 6px 17px;
  background: var(--llbg);
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Панель фильтров над списком: живёт внутри карточки без полей */
.list-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 11px 17px;
  border-bottom: 1px solid var(--border);
}

.attention-list {
  display: flex;
  flex-direction: column;
}

.attention-item {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  /* Записи идут во всю ширину карточки, поэтому отступы у них свои */
  padding: 12px 17px;
  border-bottom: 1px solid var(--border);
}

.attention-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 7px;
  background: var(--gray-bg);
  color: var(--muted);
}

/* Модификаторы .soft-* объявлены выше, поэтому цвет по severity дублируется
   здесь — иначе базовое правило гасит его и все иконки серые. */
.attention-icon.soft-red {
  background: var(--red-bg);
  color: var(--red);
}

.attention-icon.soft-amber {
  background: var(--amber-bg);
  color: var(--amber);
}

.attention-icon.soft-green {
  background: var(--green-bg);
  color: var(--green);
}

.attention-item-link {
  /* Без радиуса: он загибал концы разделительной линии записи — в прототипе
     записи прямоугольные во всю ширину карточки */
  color: inherit;
  text-decoration: none;
}

.attention-item-link:hover {
  background: var(--hover);
}

/* Запись-кнопка (открыть лог прогона): без сброса стилей кнопка центрирует
   текст и рисует свою рамку */
button.attention-item {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.attention-title,
.attention-text {
  display: block;
}

.attention-title {
  font-size: 12.5px;
  font-weight: 500;
}

.attention-text {
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--muted);
}

/* Сумма расхода: крупная цифра, как в плитках наверху */
.spend-total {
  font-size: 26px;
  font-weight: 650;
  letter-spacing: -0.02em;
}

.spend-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--muted);
}

/* Строка нагрузки в обзоре хоста: метрика, история за сутки, полоса и
   абсолютные величины — всё в одну линию */
.load-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  font-size: 12px;
  color: var(--muted);
}

/* Линия — только между строками: снизу она превращалась в лишний разделитель,
   когда строка в карточке одна (трафик) или за ней идёт форма */
.load-row + .load-row {
  border-top: 1px solid var(--border);
}

.load-label {
  width: 64px;
  flex: none;
  color: var(--ink);
  font-weight: 500;
}

.load-value {
  width: 46px;
  flex: none;
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--ink);
  text-align: right;
}

/* Трафик без лимита: вместо микро-бара знак бесконечности */
.microbar-none {
  display: block;
  color: var(--muted);
  font-size: 11px;
  line-height: 5px;
}

/* У трафика без лимита шкалы нет — вместо неё подпись на её месте */
.load-nobar {
  display: block;
  color: var(--muted);
  font-size: 11px;
}

.load-detail {
  width: 150px;
  flex: none;
  font-family: var(--font-mono);
  text-align: right;
}

.health-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 5px 12px;
  margin-bottom: 12px;
  font-size: 12px;
  /* Подпись приглушена, значение — обычного цвета (как в прототипе) */
  color: var(--muted);
}

.health-row .health-value {
  color: var(--ink);
}

.health-row .bar {
  grid-column: 1 / -1;
}

.health-value {
  font-family: var(--font-mono);
  font-weight: 500;
  text-align: right;
}

/* — вход — */

.login-page {
  display: grid;
  place-items: center;
  /* 100vh на телефоне больше видимой области — её занимает адресная строка:
     карточка центрировалась по невидимой высоте, уезжала вниз и тянула за
     собой прокрутку. dvh отмеряет ровно то, что видно */
  min-height: 100vh;
  min-height: -webkit-fill-available;
  min-height: 100dvh;
  /* Поля с оглядкой на вырез и домашнюю полосу iPhone */
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right))
    max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
  background-color: var(--bg);
  background-image: radial-gradient(
    circle at 1px 1px,
    color-mix(in srgb, var(--ink) 5%, transparent) 1px,
    transparent 0
  );
  background-size: 24px 24px;
}

.login-card {
  width: min(410px, 100%);
  padding: 42px 44px;
  border-radius: 18px;
  background: var(--surface);
  box-shadow: var(--shadow-card-lg);
  animation: pa-in 0.3s ease;
}

.login-brand {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-bottom: 26px;
}

.login-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 11px;
  background: var(--accent);
  color: #fff;
}

.login-brand-name {
  font-size: 16px;
  font-weight: 600;
  line-height: 1.15;
}

.login-brand-sub {
  font-size: 12px;
  color: var(--muted);
}

.login-title {
  font-size: 23px;
  letter-spacing: -0.015em;
}

.login-sub {
  margin: 6px 0 22px;
  font-size: 12.5px;
  color: var(--muted);
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.login-submit {
  padding: 11px;
  font-size: 14px;
}

.login-foot {
  margin: 18px 0 0;
  font-size: 11.5px;
  color: var(--muted);
  text-align: center;
}

/* ─── колокольчик и уведомления ─── */

.dd-notif {
  position: relative;
}

.notif-badge {
  position: absolute;
  top: 6px;
  right: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border: 1.5px solid var(--surface);
  border-radius: 8px;
  background: var(--red);
  color: #fff;
  font-size: 9.5px;
  font-weight: 700;
}

.notif-readall {
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
}

.notif-list {
  max-height: 340px;
  overflow-y: auto;
}

.notif-form {
  display: block;
}

.notif-item {
  display: flex;
  gap: 10px;
  width: 100%;
  padding: 11px 14px;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.notif-item:hover {
  background: var(--hover);
}

.notif-item-unread {
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}

.notif-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: none;
  border-radius: 7px;
  background: var(--track);
  color: var(--muted);
}

.notif-error {
  background: var(--red-bg);
  color: var(--red);
}

.notif-warn {
  background: var(--amber-bg);
  color: var(--amber);
}

.notif-info {
  background: var(--accent-soft);
  color: var(--accent);
}

.notif-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.notif-title {
  font-size: 12.5px;
  font-weight: 600;
}

.notif-detail,
.notif-time {
  font-size: 11.5px;
  color: var(--muted);
}

.notif-all {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  text-align: center;
}

.notif-card-unread {
  border-left: 3px solid var(--accent);
}

/* ─── палитра ⌘K ─── */

.search-placeholder {
  flex: 1;
  min-width: 0;
  /* Справа — место под подсказку ⌘K, она лежит абсолютом поверх кнопки */
  padding: 0 40px 0 34px;
  font-size: 12.5px;
  text-align: left;
  color: var(--muted);
  /* Поиск сжимается вместе с правой группой шапки — подсказка обрезается
     одной строкой, а не переносится и не растит кнопку по высоте */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search {
  height: 39px;
  padding: 0 9px 0 0;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--bg);
  cursor: pointer;
}

.search:hover {
  border-color: var(--accent);
}

.palette {
  width: min(620px, 94vw);
  max-width: 620px;
  padding: 0;
  overflow: hidden;
}

.palette-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 14px;
  border-bottom: 1px solid var(--border);
}

.palette-search-icon {
  display: flex;
  color: var(--muted);
}

.palette-input {
  flex: 1;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
}

.palette-input:focus {
  outline: none;
}

.palette-kbd {
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  /* Системный шрифт: в IBM Plex Mono нет глифа ⌘ и он подменялся кириллицей */
  font-family: var(--font-sans);
  font-size: 10.5px;
  color: var(--muted);
}

.palette-results {
  max-height: min(60vh, 430px);
  overflow-y: auto;
}

.palette-group-title {
  padding: 9px 14px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  color: var(--ink);
  text-decoration: none;
}

.palette-item:hover,
.palette-item[data-active] {
  background: var(--hover);
}

.palette-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 7px;
  background: var(--track);
  color: var(--muted);
}

.palette-text {
  display: flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
}

.palette-title {
  font-size: 13px;
  font-weight: 600;
}

.palette-sub {
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.palette-foot {
  display: flex;
  gap: 14px;
  padding: 9px 14px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--muted);
}

/* Состояние push-подписки в меню колокольчика: строка живёт под списком и
   появляется, только когда есть что сказать */
.notif-push {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--r-sm);
  padding: 8px 14px 0;
}

.notif-push:not(:has([data-push-status]:not([hidden]))) {
  display: none;
}

.push-status-bad {
  color: var(--red);
}

/* ─── консоль хоста ─── */

/* Ровно две колонки: восемь команд встают 2×4 и не растягивают карточку */
.console-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.console-btn {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}

.console-btn:hover {
  border-color: var(--accent);
  background: var(--hover);
}

.console-btn > svg {
  flex: none;
  margin-top: 1px;
  color: var(--muted);
}

.console-btn-label,
.console-btn-hint {
  display: block;
}

.console-btn-label {
  font-size: 12.5px;
  font-weight: 600;
}

.console-btn-hint {
  font-size: 11px;
  color: var(--muted);
}

/* Пока вывод не приехал, слот пуст — подпись рисуем на нём самом */
.console-slot:empty::before {
  display: block;
  padding: 18px 20px;
  color: var(--muted);
  font-size: 13px;
  content: "…выполняется на хосте…";
}

/* ─── живой шелл ─── */

.pty-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 9px;
}

.pty-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11.5px;
  background: var(--track);
  color: var(--muted);
}

.pty-live {
  background: var(--green-bg);
  color: var(--green);
}

.pty-error {
  background: var(--red-bg);
  color: var(--red);
}

.pty-wait {
  background: var(--amber-bg);
  color: var(--amber);
}

/* Полноэкранный терминал: сессия та же, меняется только раскладка */
.pty-full {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  flex-direction: column;
  padding: 14px 16px;
  background: var(--logbg);
}

.pty-full .pty-bar {
  color: var(--logmuted);
}

.pty-full .pty-screen {
  flex: 1;
  height: auto;
  border-radius: 0;
  border: 0;
}

/* Пока терминал развёрнут, страница под ним не прокручивается */
html.pty-locked,
html.pty-locked body {
  overflow: hidden;
}

/* Класс сюда не подставить: макрос карточки уже задал свой class, поэтому
   зацепка — data-атрибут */
[data-console-shell] {
  height: 100%;
}

/* Терминал забирает всю свободную высоту карточки: она равна карточке команд */
[data-console-shell] #pty {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

[data-console-shell] .pty-screen {
  height: auto;
  flex: 1;
  min-height: 180px;
}

.pty-screen {
  height: 420px;
  margin: 0;
  padding: 12px 14px;
  overflow-y: auto;
  border-radius: 10px;
  border: 1px solid var(--logborder);
  background: var(--logbg);
  color: var(--logink);
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  cursor: text;
}

/* Незакрытая строка (то, что набирается сейчас) живёт в data-атрибуте */
.pty-screen::after {
  content: attr(data-tail) "▍";
}

.pty-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
}

/* Разделитель «или» на экране входа */
.login-or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0 12px;
  font-size: 11.5px;
  color: var(--muted);
}

.login-or::before,
.login-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* Компактное поле для строк таблиц: высота как у btn-sm */
.input-sm {
  min-height: 30px;
  padding: 4px 10px;
  font-size: 12.5px;
}

select.input-sm {
  padding-right: 30px;
  background-position: right 8px center;
}

/* Пояснение под таблицей внутри карточки без полей */
.tbl-note {
  margin: 0;
  padding: 11px 17px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--muted);
}

/* Подзаголовок группы внутри карточки: слабее заголовка, но заметнее подписи */
.group-title {
  margin-bottom: 10px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Канал доставки: тумблер, поле и пояснение одним блоком с разделителем */
.channel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.channel:first-child {
  padding-top: 0;
}

.channel:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

/* Поле с кнопкой внутри: иконка не должна наезжать на текст */
.input-wrap {
  position: relative;
  display: block;
}

.input-wrap .input {
  padding-right: 38px;
}

.input-eye {
  position: absolute;
  top: 50%;
  right: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  transform: translateY(-50%);
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.input-eye:hover {
  background: var(--hover);
  color: var(--ink);
}

.input-eye[aria-pressed="true"] {
  color: var(--accent);
}

/* Ряд полей одинаковой ширины: на всю карточку они смотрятся пусто */
.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.field-row > .field {
  flex: 0 1 240px;
}

/* Всплывающее уведомление о результате действия. Баннер в потоке страницы
   сдвигал содержимое вниз, поэтому сообщение живёт поверх и уходит само:
   успех — быстро, ошибка — вдвое дольше, её надо успеть прочитать. */
.toast {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: min(420px, calc(100vw - 36px));
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow-menu);
  color: var(--ink);
  font-size: 13px;
  cursor: pointer;
  animation: toast-in 0.16s ease-out, toast-out 0.3s ease-in 4s forwards;
}

.toast svg {
  flex: none;
  color: var(--green);
}

.toast-error {
  animation-delay: 0s, 8s;
}

.toast-error svg {
  color: var(--red);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateY(-8px);
    visibility: hidden;
  }
}

/* Уважаем системную настройку: без анимации сообщение просто появляется */
@media (prefers-reduced-motion: reduce) {
  .toast {
    animation: none;
  }
}
