/* extra.css — экраны входа и личного кабинета для стиля «Тёмный дата-неон».
 *
 * СЛОЙ. Подключается ПЯТЫМ, после style.css:
 *   tokens.css → glass.css → product.css → style.css → extra.css
 * Класс на <html> тот же: class="dark product neon-data".
 *
 * ПОЧЕМУ ОТДЕЛЬНЫЙ ФАЙЛ, А НЕ ПРАВКИ В style.css. style.css описывает ЖАНР
 * (лента показаний, приборная панель, лист данных, таблица). Вход и кабинет —
 * это ЭКРАНЫ, а не жанр: они переиспользуют .nd-sheet, .nd-table, .nd-board,
 * .field, .btn и добавляют ровно то, чего в жанре нет. Держать их врозь нужно
 * затем, чтобы style.css оставался переносимым: его берут под лендинг, где ни
 * пин-кода, ни списка сессий не бывает.
 *
 * ПРАВИЛА ЖАНРА, КОТОРЫЕ ЗДЕСЬ НЕ НАРУШАЮТСЯ:
 *   • два цвета и ни одного третьего: циан — интерфейс, лайм — только «в норме»
 *     и положительная дельта, малиновый — только авария и опасная зона;
 *   • кнопка лаймовой не бывает никогда (лайм = показание, а не действие);
 *   • data-lens только на навбаре и кнопках, ни на одной панели с данными;
 *   • радиусы почти прямые, числа табличные, подписи моно-капсом.
 *
 * ЧИТАЕМОСТЬ БЕЗ JS. Переключение способов входа и состояний сделано классом
 * .js на <html>, который ставит инлайновый скрипт в <head>. Пока класса нет,
 * ВСЕ панели видимы и подписаны — страница превращается в длинную форму, где
 * доступны все четыре пути. Скрипт только сворачивает лишнее. Обратная схема
 * (hidden в разметке) без скриптов оставила бы пустой экран — так делать нельзя.
 *
 * ПРОЗРАЧНОСТЬ. Ни один элемент, объявленный НИЖЕ, не использует backdrop-filter:
 * форма входа, список сессий и опасная зона — сплошные заливки. Стекло на этих
 * экранах есть ровно в двух местах, и оба пришли из общего слоя: навбар
 * (.glass-nav) и кнопки .btn-glass. При prefers-reduced-transparency
 * glass-tier.js опускает документ на ярус «solid», и меняются только они —
 * заливка вместо размытия. Расположение, размеры и читаемость форм при этом не
 * трогаются вовсе.
 */

/* ─────────────────────────────────────────────────────────────────────────
   1. ПЕРЕКЛЮЧЕНИЕ ПАНЕЛЕЙ БЕЗ ПЕРЕЗАГРУЗКИ
   ───────────────────────────────────────────────────────────────────────── */

/* Панель способа входа / раздела кабинета. Скрываем ТОЛЬКО при живом JS. */
.neon-data .nd-pane { display: block; }
.js.neon-data .nd-pane { display: none; }
.js.neon-data .nd-pane.is-active { display: block; }

/* Без JS каждая панель обязана назвать себя сама — иначе четыре формы подряд
 * читаются как одна большая и непонятная. Заголовок печатается из атрибута,
 * чтобы не дублировать текст в разметке. При включённом JS он не нужен:
 * выбранный путь и так подписан во вкладках. */
.neon-data .nd-pane::before {
  content: attr(data-pane-title);
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
  padding-bottom: 12px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.js.neon-data .nd-pane::before { display: none; }

/* Форма в роли панели: display у активной панели — block, а форме нужна
 * сетка с отступами. Правило идёт ПОСЛЕ .is-active и с той же длиной
 * селектора, иначе block выигрывает и все поля формы слипаются. */
.js.neon-data form.nd-pane.is-active { display: grid; gap: 14px; }

/* Служебный класс для «этого нет без скриптов»: обратный отсчёт, бегунок
 * ожидания, блок подтверждения. Показываем только когда скрипты работают. */
.neon-data .js-only { display: none; }
.js.neon-data .js-only { display: initial; }
.js.neon-data .js-only.row { display: flex; }
.js.neon-data .js-only.block { display: block; }

/* Атрибут hidden обязан побеждать любой display выше. Браузерное правило
 * [hidden]{display:none} задано на теге и слабее наших классов — без !important
 * скрытый элемент остаётся на экране. Это не украшательство: на нём держатся
 * все состояния «ожидание/подтверждение». */
.neon-data [hidden] { display: none !important; }

/* Вкладки способов входа. Не кнопки в ряд, а строка приборного селектора:
 * общая нижняя линия связывает их в один переключатель, а не в набор кнопок. */
.nd-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  padding: 0 6px;
  background: #06090d;
}
.nd-tabs > button {
  appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-4);
  padding: 12px 12px 11px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;      /* вкладка садится НА линию контейнера */
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.nd-tabs > button:hover { color: var(--ink-2); }
.nd-tabs > button[aria-selected="true"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.nd-tabs > button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ─────────────────────────────────────────────────────────────────────────
   2. КАРКАС ЭКРАНА ВХОДА
   ───────────────────────────────────────────────────────────────────────── */

/* Слева — что это за система и по каким правилам пускает; справа — сама форма.
 * Та же пропорция и та же логика, что у .nd-hero: левая колонка объясняет,
 * правая работает. Отличие одно — align-items:start вместо center: форма
 * меняет высоту при смене способа входа, и центрирование дёргало бы текст. */
.nd-auth {
  display: grid;
  grid-template-columns: 1fr minmax(0, 460px);
  gap: 56px;
  align-items: start;
  padding: 56px 0 64px;
}
@media (max-width: 1000px) {
  .nd-auth { grid-template-columns: minmax(0, 1fr); gap: 32px; padding-top: 32px; }
}

/* Форма шире 460 px не нужна: строка ввода длиннее 60 символов читается хуже,
 * а пустое место справа от поля в приборном жанре выглядит как недогруз. */
.nd-auth form { display: grid; gap: 14px; }

/* Метка поля — моно-капс над полем, а не плейсхолдер внутри. Плейсхолдер
 * исчезает при вводе, и пользователь перестаёт видеть, что он заполняет. */
.neon-data .nd-field-label {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin-bottom: 7px;
}
.neon-data .field { width: 100%; }

/* Кнопка на всю ширину формы — единственное место, где это уместно: в форме
 * входа действие ровно одно, и растянутая кнопка сама показывает, где конец. */
.neon-data .btn-block { width: 100%; justify-content: center; height: 40px; }

/* Разделитель «или». Линия с подписью посередине: без неё Google-кнопка
 * читается как ещё один равный способ, а она запасной. */
.nd-or {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin: 4px 0;
}
.nd-or::before, .nd-or::after { content: ""; height: 1px; background: var(--line); }

/* ─────────────────────────────────────────────────────────────────────────
   3. СООБЩЕНИЯ СОСТОЯНИЯ
   ───────────────────────────────────────────────────────────────────────── */

/* Полоса слева, а не заливка целиком: заливка красным на тёмном фоне
 * превращает текст ошибки в нечитаемый. Цвет несёт смысл, но дублируется
 * словом в начале строки — для тех, кто цвет не различает. */
.nd-alert {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: start;
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-left: 2px solid var(--ink-4);
  border-radius: var(--radius-sm);
  background: #06090d;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.45;
}
.nd-alert .tag {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-4);
  padding-top: 2px;
}
.nd-alert.bad  { border-left-color: var(--down); background: color-mix(in srgb, var(--down) 7%, #06090d); }
.nd-alert.bad  .tag { color: var(--down); }
.nd-alert.warn { border-left-color: var(--warn); background: color-mix(in srgb, var(--warn) 6%, #06090d); }
.nd-alert.warn .tag { color: var(--warn); }
.nd-alert.ok   { border-left-color: var(--up); background: color-mix(in srgb, var(--up) 6%, #06090d); }
.nd-alert.ok   .tag { color: var(--up); }
.nd-alert.info { border-left-color: var(--accent); }
.nd-alert.info .tag { color: var(--accent); }

/* Ожидание. Полоса-бегунок вместо крутящегося кружка: в приборном жанре
 * прогресс — это шкала, а не спиннер. Ширина «бегунка» фиксированная, ездит
 * влево-вправо; при reduced-motion остаётся неподвижная заполненная треть,
 * то есть индикатор не пропадает, а перестаёт двигаться. */
.nd-wait {
  position: relative;
  height: 2px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  overflow: hidden;
  border-radius: 1px;
}
.nd-wait::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 34%;
  background: var(--accent);
  box-shadow: var(--neon);
  /* Имя ДОЛЖНО отличаться от nd-sweep из style.css. @keyframes живут в одном
   * плоском пространстве имён на документ, и побеждает последнее объявление.
   * extra.css грузится ПОСЛЕ style.css — одноимённый набор кадров молча
   * переопределял развёртку радара (rotate) на этот проезд (translateX).
   * На landing.html этого не видно только потому, что там extra.css не
   * подключён; на любом экране, где радар и бегунок встретятся, радар бы
   * поехал вбок. Переименовывать при копировании файла — обязательно. */
  animation: nd-wait-run 1.15s var(--ease) infinite;
}
@keyframes nd-wait-run {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}

/* Кнопка в состоянии ожидания: гасим текст до вторичного и запрещаем повтор.
 * Именно disabled, а не только вид: двойная отправка кода — самая частая
 * причина «код не тот», потому что второй запрос обнуляет первый. */
.neon-data .btn[aria-busy="true"],
.neon-data .btn:disabled {
  opacity: .55;
  pointer-events: none;
  box-shadow: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   4. ВВОД 6-ЗНАЧНОГО КОДА
   ───────────────────────────────────────────────────────────────────────── */

/* Шесть отдельных клеток вместо одного поля. Причина не в моде: код диктуют
 * по телефону и переписывают с экрана — разбитый на клетки, он сверяется
 * взглядом без пересчёта символов. Клетки — настоящие <input>, поэтому
 * автоподстановка одноразового кода (autocomplete="one-time-code") работает,
 * а без JS форма всё равно отправляется. */
.nd-pin {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
}
.neon-data .nd-pin input {
  width: 100%;
  height: 52px;
  text-align: center;
  font-family: var(--mono);
  font-size: 22px;
  letter-spacing: 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  padding: 0;
}
@media (max-width: 420px) {
  .nd-pin { gap: 5px; }
  .neon-data .nd-pin input { height: 44px; font-size: 18px; }
}

/* Строка под кодом: слева адрес, справа повторная отправка со счётчиком.
 * Счётчик — моно и табличные цифры, иначе строка дёргается на каждой секунде. */
.nd-resend {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-4);
}
.nd-resend b { color: var(--ink-2); font-weight: 500; font-variant-numeric: tabular-nums; }
.neon-data .btn-link {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
}
.neon-data .btn-link:disabled { color: var(--ink-4); cursor: default; }
.neon-data .btn-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ─────────────────────────────────────────────────────────────────────────
   5. КНОПКИ ВНЕШНИХ ПРОВАЙДЕРОВ
   ───────────────────────────────────────────────────────────────────────── */

/* Telegram и Google — чужие бренды, и красить их в циан нельзя: пользователь
 * ищет знакомый значок. Но и фирменные заливки тащить в тёмный прибор незачем.
 * Компромисс жанра: нейтральная кнопка + фирменный значок цветом. */
.neon-data .btn-provider {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: #0a0f15;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.neon-data .btn-provider:hover { border-color: var(--line-2); background: #0d141b; }
.neon-data .btn-provider svg { width: 17px; height: 17px; flex: none; }
.neon-data .btn-provider:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Гнездо для настоящего <script src="telegram-widget.js">. Виджет рисует свой
 * iframe фиксированного размера — под него нужен ЗАРАНЕЕ зарезервированный
 * блок, иначе форма прыгает в момент загрузки чужого скрипта. */
.nd-tg-slot {
  min-height: 40px;
  display: grid;
  place-items: center;
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-sm);
  padding: 10px;
}

/* ─────────────────────────────────────────────────────────────────────────
   6. КАБИНЕТ: ШАПКА ПРОФИЛЯ
   ───────────────────────────────────────────────────────────────────────── */

.nd-profile {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 20px;
  align-items: center;
  padding: 20px;
}
@media (max-width: 720px) {
  .nd-profile { grid-template-columns: auto minmax(0, 1fr); }
  .nd-profile > .acts { grid-column: 1 / -1; }
}

/* Аватар квадратный, со срезанным радиусом. Круглая фотография — язык
 * соцсети; в приборе это ячейка записи, такая же, как остальные. Если фото
 * нет — инициалы моношрифтом: заглушка не должна выглядеть как поломка. */
.nd-avatar {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line-2);
  background: #0a0f15;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 20px;
  color: var(--accent);
  overflow: hidden;
  flex: none;
}
.nd-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Пары «поле → значение». Volume таблицы здесь не нужен: полей мало, а
 * таблица из трёх строк выглядит как обрубок. Сетка на волосяных линиях. */
.nd-kv { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.nd-kv > div {
  padding: 14px 20px;
  border-top: 1px solid var(--line);
  border-right: 1px solid var(--line);
  min-width: 0;
}
.nd-kv > div:nth-child(2n) { border-right: 0; }
.nd-kv dt {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin: 0 0 6px;
}
.nd-kv dd {
  margin: 0;
  color: var(--ink);
  font-size: 14px;
  overflow-wrap: anywhere;
}
.nd-kv dd .nd-label { display: block; margin-top: 4px; }
@media (max-width: 640px) {
  .nd-kv { grid-template-columns: minmax(0, 1fr); }
  .nd-kv > div { border-right: 0; }
}

/* Метка поля из БД. Ставим прямо у значения: в кабинете это экономит спор
 * «а какое поле правится» при заведении задачи. Перед продом убрать классом
 * на <html> — см. .hide-src в конце файла. */
.neon-data .src {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .06em;
  color: var(--ink-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  padding: 1px 5px;
  white-space: nowrap;
}
.hide-src .src { display: none; }

/* ─────────────────────────────────────────────────────────────────────────
   7. СТАТУСЫ И ТЕГИ
   ───────────────────────────────────────────────────────────────────────── */

/* Точка + слово. Точка одна на все статусы, различается только цветом, но
 * слово рядом обязательно: цвет — второй канал, не первый. */
.nd-state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.nd-state::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: var(--radius-pill);
  background: var(--ink-4);
  flex: none;
}
/* approved / pending / rejected — ровно значения users.status из schema.sql. */
.nd-state.approved::before { background: var(--up);   box-shadow: 0 0 10px color-mix(in srgb, var(--up) 55%, transparent); }
.nd-state.pending::before  { background: var(--warn); box-shadow: 0 0 10px color-mix(in srgb, var(--warn) 55%, transparent); }
.nd-state.rejected::before { background: var(--down); box-shadow: 0 0 10px color-mix(in srgb, var(--down) 55%, transparent); }
.nd-state.current::before  { background: var(--accent); box-shadow: var(--neon); }

/* Роль. Не цветная плашка: роль — не тревога, красить её незачем. */
.nd-role {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-radius: var(--radius-xs);
  padding: 3px 8px;
}

/* ─────────────────────────────────────────────────────────────────────────
   8. МАРШРУТЫ РОЛИ (RBAC)
   ───────────────────────────────────────────────────────────────────────── */

/* Матрица «роль → маршруты» из rbac.py показывается списком чипов, а не
 * галочками в таблице: маршрутов дюжина, таблица на 12 строк ради двух
 * состояний — перерасход. Три вида чипа: доступен по роли, доступен по
 * персональному allow, закрыт персональным deny (user_route_overrides). */
.nd-routes { display: flex; flex-wrap: wrap; gap: 7px; }
.nd-routes > span {
  font-family: var(--mono);
  font-size: 11.5px;
  padding: 5px 9px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--line);
  color: var(--ink-3);
  background: #06090d;
  white-space: nowrap;
}
.nd-routes > span.on {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--accent) 34%, transparent);
}
/* Персональное исключение помечается знаком в тексте (+/−), а не только
 * цветом. Знак ставится в разметке — он часть данных, а не оформления. */
.nd-routes > span.allow { color: var(--up);   border-color: color-mix(in srgb, var(--up) 40%, transparent); }
.nd-routes > span.deny  { color: var(--down); border-color: color-mix(in srgb, var(--down) 40%, transparent); text-decoration: line-through; }

/* ─────────────────────────────────────────────────────────────────────────
   9. СЕССИИ
   ───────────────────────────────────────────────────────────────────────── */

/* Список сессий — обычная .nd-table из style.css. Здесь только то, чего в
 * таблице жанра нет: колонка действий и подсветка текущей строки.
 * Текущая сессия подсвечивается кромкой слева, а не заливкой: заливка
 * конфликтует с hover, и строка «мигает» при наведении. */
.nd-table tr.is-current td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

/* Кнопка отзыва — тихая: в списке из десяти строк десять ярких кнопок
 * превращают таблицу в панель управления, а действие тут редкое. */
.neon-data .btn-quiet {
  appearance: none;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  padding: 5px 9px;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.neon-data .btn-quiet:hover { color: var(--down); border-color: color-mix(in srgb, var(--down) 50%, transparent); }
.neon-data .btn-quiet:disabled { color: var(--ink-4); border-color: var(--line); cursor: default; }
.neon-data .btn-quiet:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Отозванная строка не исчезает сразу, а гаснет: пользователь должен увидеть,
 * ЧТО именно он только что закрыл. Убирать её из списка — на следующем
 * обновлении данных. */
.nd-table tr.is-revoked td { opacity: .42; text-decoration: line-through; }

/* Устройство и адрес: моно, мелко, в две строки. Одной строкой не влезает на
 * телефоне, а сокращение user_agent до «Chrome» врёт — версия и платформа
 * как раз и нужны, чтобы узнать свой ноутбук среди трёх. */
.nd-ua { display: block; color: var(--ink); font-size: 13px; }
.nd-ua + .nd-label { display: block; margin-top: 3px; }

/* ─────────────────────────────────────────────────────────────────────────
   10. ТАРИФ И РАСХОД
   ───────────────────────────────────────────────────────────────────────── */

/* Расход показывается индикатором .meter из style.css плюс подпись
 * «сколько из сколько». Процент без абсолютных чисел бесполезен: 80 % от
 * тысячи и от миллиона — разные новости. */
.nd-usage { display: grid; gap: 14px; }
.nd-usage > div { display: grid; gap: 7px; }
.nd-usage .top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-4);
}
.nd-usage .top b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
/* Порог тревоги: выше 90 % полоса становится малиновой. Это статус, а не
 * украшение, поэтому цвет допустим и здесь. */
.nd-usage .meter.hot > i { background: var(--down); box-shadow: 0 0 12px color-mix(in srgb, var(--down) 50%, transparent); }

/* ─────────────────────────────────────────────────────────────────────────
   11. ОПАСНАЯ ЗОНА
   ───────────────────────────────────────────────────────────────────────── */

/* Малиновая кромка по всей рамке — единственное место на странице, где
 * тревожный цвет применяется к КОНТЕЙНЕРУ, а не к числу. Оправдано тем, что
 * это и есть предупреждение: дальше идут необратимые действия. */
.nd-danger { border-color: color-mix(in srgb, var(--down) 34%, var(--line)); }
.nd-danger .nd-sheet-head { color: var(--down); background: color-mix(in srgb, var(--down) 7%, #06090d); }

.nd-danger-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 16px;
  align-items: center;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
}
.nd-danger-row:last-child { border-bottom: 0; }
.nd-danger-row h3 { margin: 0 0 5px; font-size: 14px; color: var(--ink); }
.nd-danger-row p  { margin: 0; font-size: 13px; color: var(--ink-3); max-width: 62ch; }
@media (max-width: 640px) {
  .nd-danger-row { grid-template-columns: minmax(0, 1fr); }
}

.neon-data .btn-danger {
  background: transparent;
  color: var(--down);
  border: 1px solid color-mix(in srgb, var(--down) 45%, transparent);
  box-shadow: none;
}
.neon-data .btn-danger:hover { background: color-mix(in srgb, var(--down) 14%, transparent); }

/* Подтверждение удаления. Не alert() и не модальное окно: окно закрывают
 * не глядя. Здесь — раскрывающийся блок, где надо ВПЕЧАТАТЬ слово. Печать
 * слова — единственный способ подтверждения, который нельзя пройти
 * рефлекторным щелчком. */
.nd-confirm {
  border-top: 1px solid var(--line);
  padding: 16px 18px;
  display: grid;
  gap: 12px;
  background: color-mix(in srgb, var(--down) 5%, transparent);
}
.neon-data .nd-confirm .field { max-width: 320px; }

/* ─────────────────────────────────────────────────────────────────────────
   12. МЕЛОЧИ
   ───────────────────────────────────────────────────────────────────────── */

/* Строка ручки API под формой. Экран входа читают и разработчики — подпись
 * «какая ручка дёргается» экономит им заход в исходники. Перед продом
 * убирается тем же классом .hide-src. */
.neon-data .nd-api {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  color: var(--ink-4);
  word-break: break-all;
}
.neon-data .nd-api b { color: var(--accent); font-weight: 500; }
.hide-src .nd-api { display: none; }

/* Разделительная линия внутри листа данных. */
.neon-data .nd-hr { height: 1px; background: var(--line); border: 0; margin: 0; }

/* Ссылка в тексте формы: подчёркивание обязательно — в форме ссылка живёт
 * рядом с моно-подписями, и одним цветом она от них не отличается. */
.neon-data .nd-a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* ─────────────────────────────────────────────────────────────────────────
   13. ДВИЖЕНИЕ И ДОСТУПНОСТЬ
   ───────────────────────────────────────────────────────────────────────── */

/* Единственная анимация этих экранов — бегунок ожидания. Гасим его в
 * неподвижную заполненную треть: индикатор остаётся на месте и продолжает
 * означать «идёт запрос», просто перестаёт ездить. */
@media (prefers-reduced-motion: reduce) {
  .nd-wait::after { animation: none; width: 34%; }
}

/* Принудительные цвета: свечения убираем, границы оставляем — в этом режиме
 * система рисует свои, и наши тени поверх них дают грязь. */
@media (forced-colors: active) {
  .nd-state::before, .nd-wait::after, .nd-usage .meter > i { box-shadow: none; }
  .nd-tabs > button[aria-selected="true"] { border-bottom-color: Highlight; }
}
