/* ===========================================================================
   app.css — дизайн-система «простой белый минимализм».

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

   Структура файла:
     1. Токены (цвет, типографика, отступы)
     2. Сброс и базовые элементы
     3. Каркас страницы (хедер, контент, футер)
     4. Типографика и утилиты
     5. Компоненты (карточки, кнопки, поля, таблица, вкладки, бейджи…)
     6. Состояния HTMX и анимации
     7. Адаптивность и печать
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. Токены
   --------------------------------------------------------------------------- */
:root {
  /* Поверхности: белый — основной фон, серый — только для выделения зон. */
  --bg: #ffffff;
  --bg-soft: #fafafa;
  --bg-mute: #f4f4f5;

  /* Линии: тонкие, вместо теней и заливок. */
  --line: #e7e7e9;
  --line-strong: #d3d3d6;

  /* Текст. */
  --fg: #131316;
  --fg-soft: #55555c;
  --fg-mute: #8b8b93;
  --fg-faint: #adadb4;

  /* Смысловые цвета: используются точечно, чтобы белый оставался главным. */
  --ok: #0a7d33;
  --ok-soft: #f0f8f2;
  --warn: #9a6300;
  --warn-soft: #fdf7ec;
  --err: #b3261e;
  --err-soft: #fdf2f1;

  --focus: rgba(19, 19, 22, 0.14);

  --radius: 10px;
  --radius-sm: 6px;
  --radius-pill: 999px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --page: 1240px;
}

/* ---------------------------------------------------------------------------
   2. Сброс и базовые элементы
   --------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Атрибут `hidden` должен скрывать ЛЮБОЙ элемент.
   Правило браузера `[hidden] { display: none }` живёт в user-agent-стилях, а
   они слабее авторских: любой наш `display` его перебивает независимо от
   специфичности. Из-за этого поиск по зонам «не работал» — JS исправно ставил
   `hidden` на неподходящие строки, но `.zone { display: flex }` продолжал их
   показывать. Одно правило закрывает это для всех элементов, скрываемых из JS
   (строки зон, «ничего не найдено», состояние фильтров таблицы). */
[hidden] {
  display: none !important;
}

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

body {
  margin: 0;
  min-height: 100vh;
  /* dvh: на мобильных 100vh считает адресную строку браузера, и футер
     прыгает при её появлении. Где dvh нет, остаётся 100vh. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Страховка мобильной вёрстки: один элемент, вылезший за правый край,
     расширял документ, и Safari позволял «отщипнуть» страницу — справа
     открывалась пустота. clip не создаёт скролл-контейнер, поэтому sticky-
     шапка и всплывающие списки цен продолжают работать. */
  overflow-x: clip;
}

h1,
h2,
h3,
p,
dl,
dd,
figure {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

a:hover {
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}

code,
kbd,
samp,
pre {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 20px 0;
}

/* Единая рамка фокуса: видна с клавиатуры, не мешает мыши. */
:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection {
  background: #e8e8ea;
}

/* Скроллбар в тон интерфейсу. */
* {
  scrollbar-color: var(--line-strong) transparent;
  scrollbar-width: thin;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: #dcdce0;
  border: 3px solid var(--bg);
  border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover {
  background: #c4c4ca;
}

/* ---------------------------------------------------------------------------
   3. Каркас страницы
   --------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 16px;
  background: var(--fg);
  color: #fff;
  border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus {
  left: 0;
}

.wrap {
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 24px;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: saturate(180%) blur(8px);
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: 20px;
  min-height: 60px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.brand__mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--fg);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.brand__name {
  font-size: 18px;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.brand__sub {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-mute);
  font-weight: 400;
  letter-spacing: 0;
}

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

.nav__link {
  padding: 7px 12px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  color: var(--fg-soft);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav__link:hover {
  background: var(--bg-mute);
  color: var(--fg);
}

.nav__link[aria-current="page"] {
  color: var(--fg);
  font-weight: 500;
  background: var(--bg-mute);
}

/* API — служебная ссылка, а не инструмент: стоит в конце и выглядит тише.
   Класс используется в base.html, без этого правила он ничем не отличался. */
.nav__link--muted {
  color: var(--fg-mute);
}

.main {
  flex: 1 0 auto;
  padding: 32px 0 56px;
}

.site-footer {
  border-top: 1px solid var(--line);
  padding: 20px 0 28px;
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  justify-content: space-between;
  font-size: 12px;
  color: var(--fg-mute);
}

/* Вертикальный ритм страницы. */
.stack {
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.stack--tight {
  gap: 16px;
}

.stack--list {
  gap: 6px;
}

/* ---------------------------------------------------------------------------
   4. Типографика и утилиты
   --------------------------------------------------------------------------- */
.page-title {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.page-lead {
  margin-top: 6px;
  font-size: 14px;
  color: var(--fg-soft);
  max-width: 68ch;
}

.section-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.section-title--plain {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--fg);
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px 24px;
}

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

.muted {
  color: var(--fg-mute);
}

.soft {
  color: var(--fg-soft);
}

.small {
  font-size: 12px;
}

.tiny {
  font-size: 11px;
}

.nowrap {
  white-space: nowrap;
}

.right {
  text-align: right;
}

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.row--between {
  justify-content: space-between;
}

.row--center {
  justify-content: center;
}

.row--tight {
  gap: 6px;
}

.push {
  margin-left: auto;
}

.hint {
  font-size: 12px;
  color: var(--fg-mute);
  line-height: 1.45;
}

/* Микро-утилиты: точечные отступы и акценты, чтобы не заводить inline-стили
   (единственное исключение — ширина заполнения прогресс-бара). */
.mt-1 {
  margin-top: 4px;
}

.mt-2 {
  margin-top: 6px;
}

.mt-3 {
  margin-top: 8px;
}

.mt-4 {
  margin-top: 14px;
}

.block {
  display: block;
}

.text-ok {
  color: var(--ok);
}

/* Подсказка-предупреждение: «источник не ответил», «цена не проверена». Не
   алерт — это не ошибка страницы, а обстоятельство ответа. */
.hint--warn {
  color: var(--warn);
}

.hint--warn .tag {
  background: var(--warn-soft);
  color: var(--warn);
}

.text-warn {
  color: var(--warn);
}

.text-err {
  color: var(--err);
}

code.tag,
.tag {
  display: inline-block;
  padding: 1px 6px;
  background: var(--bg-mute);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg-soft);
}

.grid {
  display: grid;
  gap: 16px;
}

.filters__foot {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* Индикатор «обновляюсь» рядом с кнопкой «Применить» переключается через
   display, а не только прозрачностью: opacity:0 оставлял невидимый, но
   занимающий место блок, который отталкивал кнопку от правого края. */
.filters__foot .htmx-indicator {
  display: none;
}

.filters.htmx-request .filters__foot .htmx-indicator,
.filters__foot .htmx-request .htmx-indicator {
  display: inline-flex;
}

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

.grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.grid--stats {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.layout-admin {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

/* --- Форма входа в админку ------------------------------------------------ */
/* Одна карточка по центру страницы: форма не теряется на широком экране,
   но живёт в том же каркасе (шапка, футер, типографика), что и весь сайт. */
.auth {
  display: flex;
  justify-content: center;
  padding-top: clamp(16px, 8vh, 96px);
}

.auth__card {
  width: min(400px, 100%);
}

.auth__head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.auth__head .brand__mark {
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 600;
}

/* ---------------------------------------------------------------------------
   5. Компоненты
   --------------------------------------------------------------------------- */

/* --- Карточка ------------------------------------------------------------ */
.card {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}

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

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

.card--inset {
  padding: 12px 14px;
}

.card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
}

.card__foot {
  padding: 12px 18px;
  border-top: 1px solid var(--line);
}

/* --- Полоса «последнее сканирование» ------------------------------------- */
.last-scan {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
  padding: 22px 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.last-scan__label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.last-scan__value {
  margin-top: 4px;
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.last-scan__meta {
  margin-top: 4px;
  font-size: 13px;
  color: var(--fg-soft);
}

.last-scan__side {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

/* --- Метрика ------------------------------------------------------------- */
.stat {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
}

.stat__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.stat__value {
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.stat__value--ok {
  color: var(--ok);
}

.stat__note {
  margin-top: 6px;
  font-size: 12px;
  color: var(--fg-mute);
}

.stat--accent {
  border-color: #cfe3d5;
  background: var(--ok-soft);
}

/* --- Кнопки -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--fg);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.3;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease,
    color 0.15s ease, opacity 0.15s ease;
  white-space: nowrap;
}

.btn:hover {
  background: var(--bg-soft);
  border-color: var(--fg-mute);
}

.btn:active {
  background: var(--bg-mute);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  background: var(--bg);
  border-color: var(--line);
}

.btn--primary {
  background: var(--fg);
  border-color: var(--fg);
  color: #fff;
}

.btn--primary:hover {
  background: #000;
  border-color: #000;
}

.btn--primary:active {
  background: #26262b;
}

.btn--danger {
  color: var(--err);
  border-color: #e6c9c6;
}

.btn--danger:hover {
  background: var(--err-soft);
  border-color: #d9a8a3;
}

.btn--ghost {
  border-color: transparent;
  background: transparent;
  color: var(--fg-soft);
}

.btn--ghost:hover {
  background: var(--bg-mute);
  border-color: transparent;
  color: var(--fg);
}

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

/* Кнопка с состоянием «занят».

   `hx-disabled-elt` на время запроса делает кнопку неактивной, и от неё
   оставался полупрозрачный прямоугольник без подписи: непонятно, нажалось ли
   вообще. Оба состояния лежат в разметке рядом, переключается видимость —
   никакого JS, и ширина кнопки не прыгает, потому что подписи наложены друг на
   друга в общей сетке. */
.btn--busy {
  display: inline-grid;
  place-items: center;
}

.btn--busy > .btn__label,
.btn--busy > .btn__busy {
  grid-area: 1 / 1;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}

.btn--busy > .btn__busy {
  visibility: hidden;
}

/* Признак занятости — атрибут `disabled`, который на время запроса ставит
   `hx-disabled-elt="find button"`. Именно он, а не класс `htmx-request`: класс
   HTMX вешает на элемент-инициатор (форму) и на указанные индикаторы, но не на
   вложенную кнопку, поэтому селектор по классу здесь просто не срабатывал бы.
   Форма-инициатор оставлена вторым признаком — на случай формы без
   `hx-disabled-elt`. */
.btn--busy[disabled] > .btn__label,
.htmx-request > .btn--busy > .btn__label,
.htmx-request .btn--busy[disabled] > .btn__label {
  visibility: hidden;
}

.btn--busy[disabled] > .btn__busy,
.htmx-request > .btn--busy > .btn__busy,
.htmx-request .btn--busy[disabled] > .btn__busy {
  visibility: visible;
}

/* Кнопка «занята» остаётся читаемой.

   Прежняя версия этого блока меняла только прозрачность — и кнопка всё равно
   выглядела исчезнувшей. Причина была в общем правиле `.btn[disabled]`: оно
   перекрашивает фон в белый, а у `.btn--primary` текст белый, то есть подпись
   становилась белым по белому. Проявлялось это ровно так, как и описано:
   «кнопка прозрачная, видно только при наведении» (hover возвращал фон).

   Поэтому занятая кнопка сохраняет СВОЙ вид: тёмный фон у главной, светлый у
   обычной. Гасить её до 45% тоже незачем — подпись внутри и так сообщает, что
   идёт работа. Меняется только курсор: работа идёт, нажимать больше нечего. */
.btn--busy[disabled],
.btn--busy[aria-disabled="true"] {
  opacity: 1;
  cursor: progress;
  background: var(--bg);
  border-color: var(--line-strong);
}

.btn--primary.btn--busy[disabled],
.btn--primary.btn--busy[aria-disabled="true"] {
  background: var(--fg);
  border-color: var(--fg);
  color: #fff;
}

.btn--ghost.btn--busy[disabled] {
  background: var(--bg-mute);
  border-color: transparent;
}

/* --- Поля ввода ---------------------------------------------------------- */
.field {
  display: block;
}

.field--full {
  grid-column: 1 / -1;
}

.field__note {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--fg-mute);
}

.field__label {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-soft);
}

.input,
.select {
  width: 100%;
  padding: 8px 11px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  appearance: none;
}

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

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

.input:focus,
.select:focus {
  outline: none;
  border-color: var(--fg);
  box-shadow: 0 0 0 3px var(--focus);
}

.select {
  padding-right: 30px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%2355555c' stroke-width='1.4' d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 12px;
}

.input--narrow {
  width: 96px;
}

.select--auto {
  width: auto;
}

.checkbox {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
  font-size: 13px;
}

.checkbox input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 1px 0 0;
  accent-color: var(--fg);
  cursor: pointer;
  flex-shrink: 0;
}

.checkbox__title {
  display: block;
  color: var(--fg);
}

.checkbox__note {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-mute);
}

.checkbox--card {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.checkbox--card:hover {
  border-color: var(--line-strong);
  background: var(--bg-soft);
}

fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

legend {
  padding: 0;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-soft);
}

.inline-field {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--fg-soft);
}

/* --- Выбор доменных зон --------------------------------------------------- */
.zones {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px 14px;
}

.zones__legend {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0;
  padding: 0 6px;
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-soft);
}

.zones__counter {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ok);
}

.zones__counter--empty {
  color: var(--fg-mute);
}

.zones__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.zones__search {
  flex: 1 1 160px;
  min-width: 130px;
}

.zones__presets,
.zones__sort {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.zones__sort .btn[aria-pressed="true"] {
  border-color: var(--fg);
  color: var(--fg);
  background: var(--bg-mute);
}

/* Сетка зон: прокручиваемая, чтобы 255 зон не растягивали страницу. */
.zones__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 4px;
  max-height: 320px;
  overflow-y: auto;
  padding: 2px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.zone {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.zone:hover {
  background: var(--bg-soft);
  border-color: var(--line);
}

.zone input[type="checkbox"] {
  width: 14px;
  height: 14px;
  accent-color: var(--fg);
  cursor: pointer;
  flex-shrink: 0;
}

.zone:has(input:checked) {
  background: var(--ok-soft);
  border-color: #cfe3d5;
}

.zone__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0 6px;
  min-width: 0;
  flex: 1;
}

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

.zone__price {
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-soft);
  text-align: right;
}

.zone__note {
  grid-column: 1 / -1;
  font-size: 10px;
  color: var(--fg-mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.zone__flag {
  color: var(--warn);
}

.zone--reserved .zone__name {
  color: var(--fg-mute);
}

.zones__empty {
  margin-top: 10px;
}

.zones__manual {
  margin-top: 10px;
}

.zones__manual-summary {
  font-size: 12px;
  color: var(--fg-soft);
  cursor: pointer;
}

.zones__manual-summary:hover {
  color: var(--fg);
}

/* --- Чипы зон ------------------------------------------------------------ */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.chip {
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-soft);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease,
    color 0.15s ease;
}

.chip:hover {
  border-color: var(--fg-mute);
  color: var(--fg);
}

.chip[aria-pressed="true"] {
  background: var(--fg);
  border-color: var(--fg);
  color: #fff;
}

/* --- Вкладки категорий --------------------------------------------------- */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 9px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.tab:hover {
  border-color: var(--line-strong);
  background: var(--bg-soft);
}

.tab[aria-selected="true"] {
  border-color: var(--fg);
}

.tab__title {
  display: block;
  font-size: 13px;
  font-weight: 500;
}

.tab__example {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-mute);
}

.tab__count {
  font-family: var(--font-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--fg-mute);
}

.tab__count b {
  display: block;
  font-weight: 600;
  color: var(--ok);
}

/* --- Таблица ------------------------------------------------------------- */
.table-scroll {
  overflow-x: auto;
}

.table {
  width: 100%;
  font-size: 13px;
}

.table thead th {
  padding: 9px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-soft);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
  text-align: left;
  white-space: nowrap;
}

.table tbody td {
  padding: 10px 16px;
  border-bottom: 1px solid #f1f1f3;
  vertical-align: middle;
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table tbody tr:hover {
  background: var(--bg-soft);
}

.table th.right,
.table td.right {
  text-align: right;
}

.domain {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  /* Длинное имя не должно раздвигать ячейку и всю таблицу на телефоне. */
  overflow-wrap: anywhere;
}

/* Имя домена и его пометки (premium, ★-оценка) — один flex-ряд: бейджи не
   липнут к имени встык, а перенесённые на новую строку не слипаются без
   вертикального промежутка. */
.cell-domain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}

.domain--free {
  color: var(--ok);
}

.price {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.price--empty {
  color: var(--fg-faint);
}

/* Пол зоны: цифра есть, но она про зону, а не про это имя. Приглушена
   намеренно — спутать её с проверенной ценой домена нельзя. */
.price--floor {
  color: var(--fg-mute);
  cursor: help;
}

.offers__floor {
  margin: 0 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  line-height: 1.45;
  color: var(--fg-mute);
}

/* --- Раскрывающийся список цен регистраторов ----------------------------- */
.offers {
  display: inline-block;
  position: relative;
  text-align: right;
}

.offers__summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  list-style: none;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.offers__summary::-webkit-details-marker {
  display: none;
}

.offers__summary::after {
  content: "";
  width: 0;
  height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid var(--fg-mute);
  transition: transform 0.15s ease;
}

.offers__summary:hover {
  border-color: var(--line-strong);
  background: var(--bg-soft);
}

.offers[open] > .offers__summary {
  border-color: var(--fg);
  background: var(--bg);
}

.offers[open] > .offers__summary::after {
  transform: rotate(180deg);
}

/* Список цен: всплывает над таблицей и не растягивает строку. */
.offers__body {
  position: absolute;
  right: 0;
  z-index: 20;
  min-width: 320px;
  max-width: min(420px, 90vw);
  max-height: 340px;
  overflow-y: auto;
  margin-top: 6px;
  padding: 10px 12px;
  text-align: left;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(19, 19, 22, 0.1);
}

.offers__checked {
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
}

.offers__list {
  display: flex;
  flex-direction: column;
}

.offers__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid #f4f4f5;
}

.offers__item:last-child {
  border-bottom: 0;
}

.offers__link {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr);
  align-items: baseline;
  gap: 2px 10px;
  flex: 1;
  min-width: 0;
  padding: 2px 4px;
  margin: -2px -4px;
  border-radius: var(--radius-sm);
}

.offers__link:hover {
  background: var(--bg-soft);
}

.offers__price {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.offers__name {
  font-size: 13px;
}

.offers__link:hover .offers__name {
  text-decoration: underline;
}

/* Строка без ссылки: у реселлерского источника (Openprovider) перехода нет —
   цена есть, а вести некуда. Курсор и подчёркивание не должны обещать клик,
   которого не будет. */
.offers__link--nolink,
.offers__link--nolink:hover {
  background: transparent;
  cursor: default;
}

.offers__link--nolink:hover .offers__name {
  text-decoration: none;
}

/* --- Имя без зоны: список зон от дешёвых к дорогим ------------------------- */
.zonehits {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.zonehit {
  border-bottom: 1px solid #f4f4f5;
}

.zonehit:last-child {
  border-bottom: 0;
}

.zonehit--more {
  padding-top: 10px;
  border-bottom: 0;
}

.zonehit__head {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  align-items: baseline;
  gap: 2px 10px;
  padding: 6px 4px;
  margin: 0 -4px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  list-style: none;
}

.zonehit__head::-webkit-details-marker {
  display: none;
}

.zonehit__head:hover {
  background: var(--bg-soft);
}

.zonehit__price {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  /* Цена, слот под «?» и цена за два года — одна сетка во всех строках.
     Знак вопроса лежит в СВОЁМ столбце, который есть всегда, даже пустой:
     пока он стоял сразу за числом, он сдвигал цену влево и разряды соседних
     строк перестали совпадать. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) 9px;
  align-items: baseline;
  column-gap: 3px;
}

.zonehit__amount {
  text-align: right;
  white-space: nowrap;
}

.zonehit__flag {
  font-size: 11px;
  line-height: 1;
  text-align: left;
}

/* Цена за два года: вторая строка под ценой первого года, по той же правой
   кромке. Мелкая и приглушённая — это справка, а не то, что платят сейчас.
   Что именно за цифра, объясняет подсказка при наведении: в строке слов нет. */
/* Отступ не зависит от того, стоит ли в строке знак «?»:
   кружок «?» сам поднимает высоту первой строки, а без него цена за два
   года прилипала к цене регистрации — две цифры сливались в одну. */
.zonehit__2y {
  grid-column: 1 / -1;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  color: var(--fg-mute);
  white-space: nowrap;
  text-align: right;
  cursor: help;
}

/* Скелетон поиска живёт в контейнере результата и места не занимает, пока
   запроса нет. Именно display, а не opacity: невидимый блок с прозрачностью
   продолжал раздвигать hero между полем и подсказкой. */
.lookup-busy {
  display: none;
}

#lookup-progress.htmx-request,
#lookup-skeleton.htmx-request {
  display: block;
}

#lookup-skeleton.htmx-request {
  display: grid;
}

/* Фантом выглядит как будущее окно выдачи: та же карточка (рамка, радиус,
   паддинг 20/22), что у `.lookup__result`, — раньше скелетон был голой сеткой
   без контейнера и его строки «съезжали» относительно настоящих результатов. */
.lookup-skeleton {
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
}

/* Строки-фантомы повторяют сетку живых строк выдачи (.zonehit__head): цена у
   правой кромки первой колонки, имя во второй, мета в третьей. Те же серые
   тона и тот же сдвиг фона, что у живой полоски прогресса — ничего нового
   вводить не надо. */
.skeleton-row {
  display: grid;
  grid-template-columns: 110px minmax(160px, 1fr) minmax(0, 2fr);
  gap: 8px 16px;
  align-items: center;
  padding: 11px 0;
}

.skeleton-row + .skeleton-row {
  border-top: 1px solid #f4f4f5;
}

.skeleton {
  display: block;
  height: 11px;
  border-radius: var(--radius-sm);
  background-color: var(--bg-soft);
  background-image: linear-gradient(
    90deg,
    transparent 0,
    rgba(0, 0, 0, 0.05) 40%,
    rgba(0, 0, 0, 0.05) 60%,
    transparent 100%
  );
  /* Ширина плитки равна шагу keyframes `shift` (24px), иначе на стыке цикла
     фон прыгал назад — анимация выглядела обрывающейся раз в секунду. */
  background-size: 24px 100%;
  animation: shift 1.2s linear infinite;
}

.skeleton--price {
  justify-self: end;
  width: 56px;
}

.skeleton--name {
  width: min(180px, 50%);
}

.skeleton--meta {
  justify-self: end;
  width: min(160px, 30%);
}

/* Имя домена — ССЫЛКА НА ОТДЕЛЬНУЮ СТРАНИЦУ, и это единственное место строки,
   которое уводит с выдачи. Раньше `<a>` был прямым элементом грида и
   растягивался на всю колонку, поэтому клик по пустому месту справа от имени
   тоже уходил на страницу домена вместо раскрытия цен. Обёртка держит колонку,
   ссылка сжата по тексту. */
.zonehit__name {
  min-width: 0;
  display: flex;
  align-items: baseline;
}

.zonehit__domain {
  font-size: 13px;
  font-weight: 600;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-decoration-line: underline;
  text-decoration-color: transparent;
  text-underline-offset: 2px;
  transition: text-decoration-color 0.15s ease;
}

/* Подсказка «это ссылка, а не раскрытие»: подчёркивание появляется на самом
   имени, фон — на всей строке. Так видно, что клик по имени и клик по строке
   делают разное. */
.zonehit__domain:hover,
.zonehit__domain:focus-visible {
  text-decoration-color: currentColor;
}

/* «Цены» + стрелка: показывает, что строка раскрывается, а не только ведёт
   ссылкой. Стрелка поворачивается, когда подокно открыто. */
.zonehit__toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--fg-mute);
  white-space: nowrap;
}

.zonehit__toggle-text {
  font-size: 11px;
}

.zonehit__caret {
  width: 0;
  height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  transition: transform 0.15s ease;
}

.zonehit > .offers[open] .zonehit__caret {
  transform: rotate(180deg);
}

.zonehit__head:hover .zonehit__toggle {
  color: var(--fg);
}

.zonehit__meta {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--fg-mute);
}

/* --- Когда домен освободится ---------------------------------------------- */
.release {
  cursor: help;
}

.release__estimate,
.release--unknown {
  color: var(--fg-mute);
}

.offers__meta {
  grid-column: 2;
  font-size: 11px;
  color: var(--fg-mute);
}

.offers__promo {
  flex-shrink: 0;
  cursor: help;
}

.offers__foot {
  margin-top: 8px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--fg-mute);
}

.offers__foot a {
  text-decoration: underline;
}

/* --- Страница домена ------------------------------------------------------ */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--fg-mute);
}

.crumbs a {
  color: var(--fg-soft);
}

.crumbs a:hover {
  color: var(--fg);
}

.crumbs__sep {
  color: var(--line-strong);
}

.domain-hero__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.domain-hero__name {
  margin: 0;
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  /* Имя — моноширинное и без пробелов: без переноса длинный домен уедет
     за экран на телефоне. */
  overflow-wrap: anywhere;
}

.domain-hero__verdict {
  margin: 12px 0 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--fg);
}

/* Раньше здесь лежало второе определение `.section-title` (14px, обычный
   регистр). Оно переопределяло первое во ВСЁМ интерфейсе — от админки до
   таблицы, — хотя предназначалось только странице домена. Общий стиль теперь
   один (см. выше), а локальное отличие живёт в своём классе. */
.domain-hero + .card .section-title,
.card .section-title {
  margin: 0;
}

.card__head--plain {
  display: block;
  padding: 0 0 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 12px;
  background: none;
}

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

   `.offers` наследуется от ячейки таблицы, где он inline-block и выровнен
   вправо: там это правильно (цена прижата к правому краю столбца). На странице
   домена то же правило превращало блок в кашу — цена слева в узкой колонке,
   а название регистратора и подпись улетали к правому краю и слипались там.
   Поэтому статический вариант сбрасывает и выравнивание, и раскладку строки. */
.offers--static {
  position: static;
  display: block;
  text-align: left;
}

.offers--static .offers__list {
  position: static;
  width: auto;
  min-width: 0;
  max-width: none;
  border: 0;
  box-shadow: none;
  padding: 0;
  margin: 0;
}

.offers--static .offers__item {
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.offers--static .offers__item:last-child {
  border-bottom: 0;
}

/* Три смысловых столбца: сколько, у кого, на каких условиях. */
.offers--static .offers__link {
  grid-template-columns: minmax(96px, 130px) minmax(140px, 1fr) minmax(0, 1.4fr);
  align-items: center;
  gap: 4px 20px;
  padding: 4px 6px;
  margin: -4px -6px;
}

.offers--static .offers__price {
  text-align: left;
  font-size: 15px;
}

.offers--static .offers__name {
  grid-column: 2;
  font-size: 14px;
}

.offers--static .offers__meta {
  grid-column: 3;
  grid-row: 1;
  text-align: right;
}

.offers--static .offers__kind {
  margin-top: 16px;
}

.facts {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: 8px 16px;
  margin: 12px 0 0;
  font-size: 13px;
}

.facts dt {
  color: var(--fg-mute);
}

.facts dd {
  margin: 0;
  color: var(--fg);
}

.domain-links {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 4px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.domain-links a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 9px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: 13px;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.domain-links a:hover {
  background: var(--bg-soft);
  border-color: var(--line);
}

.domain-links__meta {
  font-size: 11px;
  color: var(--fg-mute);
  white-space: nowrap;
}

/* --- Проверка любого домена ---------------------------------------------- */
.lookup {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.lookup__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}

.lookup__title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.lookup__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.lookup__input {
  flex: 1 1 260px;
  min-width: 180px;
  font-size: 15px;
  padding: 10px 12px;
}

.lookup__result:empty {
  display: none;
}

.lookup__summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}

.lookup__domain {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}

/* --- Плашка premium ------------------------------------------------------ */
.badge--premium {
  border-color: #e4d5f2;
  background: #f9f4fd;
  color: #6b3fa0;
  cursor: help;
  text-transform: lowercase;
  letter-spacing: 0.02em;
}

/* --- Переключатель языка ------------------------------------------------- */
.lang-switch {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.lang-switch__item {
  padding: 5px 9px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--fg-mute);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.lang-switch__item + .lang-switch__item {
  border-left: 1px solid var(--line);
}

.lang-switch__item:hover {
  background: var(--bg-soft);
  color: var(--fg);
}

.lang-switch__item[aria-current="true"] {
  background: var(--fg);
  color: #fff;
}

/* --- Знак «цена не проверена» -------------------------------------------- */
.qmark {
  display: inline-grid;
  place-items: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--warn);
  border-radius: 50%;
  background: var(--warn-soft);
  color: var(--warn);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

/* --- Бейджи и статусы ---------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--bg);
  font-size: 11px;
  font-weight: 500;
  color: var(--fg-soft);
  white-space: nowrap;
}

.badge--ok {
  border-color: #cfe3d5;
  background: var(--ok-soft);
  color: var(--ok);
}

.badge--warn {
  border-color: #ecdcbd;
  background: var(--warn-soft);
  color: var(--warn);
}

.badge--err {
  border-color: #e6c9c6;
  background: var(--err-soft);
  color: var(--err);
}

.badge--mute {
  background: var(--bg-mute);
  border-color: var(--bg-mute);
  color: var(--fg-mute);
}

.dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg-faint);
  flex-shrink: 0;
}

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

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

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

.dot--live {
  background: var(--ok);
  animation: pulse 1.4s ease-in-out infinite;
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  white-space: nowrap;
}

.status--free {
  color: var(--ok);
  font-weight: 500;
}

.status--taken {
  color: var(--fg-mute);
}

.status--unknown {
  color: var(--warn);
}

.status__note {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--fg-mute);
  max-width: 34ch;
  white-space: normal;
  line-height: 1.35;
}

/* --- Сообщения ----------------------------------------------------------- */
.alert {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-soft);
  font-size: 13px;
  color: var(--fg-soft);
}

.alert__body {
  flex: 1 1 320px;
  min-width: 0;
}

.alert__title {
  font-weight: 600;
  color: var(--fg);
}

.alert--ok {
  border-color: #cfe3d5;
  background: var(--ok-soft);
  color: #16603a;
}

.alert--warn {
  border-color: #ecdcbd;
  background: var(--warn-soft);
  color: #7a4f00;
}

.alert--err {
  border-color: #e6c9c6;
  background: var(--err-soft);
  color: #8d1f19;
}

/* --- Прогресс ------------------------------------------------------------ */
.bar {
  height: 6px;
  width: 100%;
  border-radius: var(--radius-pill);
  background: var(--bg-mute);
  overflow: hidden;
}

.bar--inline {
  width: 180px;
}

/* Полоска под формой проверки: по ширине поля. */
.bar--hero {
  max-width: 720px;
  margin: 2px auto 0;
}

/* Пока запроса нет, полоски нет вовсе: htmx-indicator показывает её только на
   время запроса, но `display` у `.bar` собственный, поэтому правило нужно
   явное — иначе пустая серая черта висела бы на странице всегда. */
.bar.htmx-indicator {
  display: none;
}

.bar.htmx-indicator.htmx-request {
  display: block;
}

.bar__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--fg);
  transition: width 0.5s ease;
}

.bar__fill--live {
  background-image: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.35) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.35) 50%,
    rgba(255, 255, 255, 0.35) 75%,
    transparent 75%
  );
  background-size: 24px 24px;
  animation: shift 1s linear infinite;
}

.counters {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
}

.counter {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  text-align: center;
  background: var(--bg);
}

.counter dt {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.counter dd {
  margin: 3px 0 0;
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.counter--ok dd {
  color: var(--ok);
}

.counter--warn dd {
  color: var(--warn);
}

/* --- Список журнала ------------------------------------------------------ */
.log {
  display: flex;
  flex-direction: column;
}

.log__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid #f1f1f3;
}

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

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

.log__dot {
  margin-top: 6px;
}

.log__main {
  min-width: 0;
  flex: 1;
}

.log__title {
  font-family: var(--font-mono);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.log__meta {
  margin-top: 2px;
  font-size: 11px;
  color: var(--fg-mute);
}

.log__side {
  text-align: right;
  flex-shrink: 0;
}

.log__found {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--ok);
}

.log__ratio {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-mute);
}

.reasons {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.reasons__item {
  font-size: 12px;
  line-height: 1.45;
}

.reasons__count {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--warn);
}

/* --- Пагинация ----------------------------------------------------------- */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.pager__pages {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

.pager__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg-soft);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease,
    background-color 0.15s ease;
}

.pager__link:hover {
  border-color: var(--fg-mute);
  color: var(--fg);
}

.pager__link[aria-current="page"] {
  border-color: var(--fg);
  color: var(--fg);
  font-weight: 600;
}

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

.pager__gap {
  color: var(--fg-faint);
  padding: 0 2px;
}

/* --- Пустое состояние ---------------------------------------------------- */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 56px 24px;
  text-align: center;
}

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

.empty__text {
  max-width: 56ch;
  font-size: 13px;
  color: var(--fg-mute);
  line-height: 1.55;
}

/* --- Страница ошибки ----------------------------------------------------- */
.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 96px 0;
  text-align: center;
}

.error-page__code {
  font-family: var(--font-mono);
  font-size: 56px;
  font-weight: 600;
  letter-spacing: -0.04em;
  color: var(--line-strong);
  line-height: 1;
}

/* --- Описания ключ/значение --------------------------------------------- */
.meta-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  font-size: 12px;
  color: var(--fg-mute);
}

.meta-list__item b {
  font-weight: 500;
  color: var(--fg-soft);
}

/* ---------------------------------------------------------------------------
   6. Состояния HTMX и анимации
   --------------------------------------------------------------------------- */
.htmx-indicator {
  opacity: 0;
  transition: opacity 0.2s ease;
}

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

.spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 1.5px solid var(--line-strong);
  border-top-color: var(--fg);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

.fade-in {
  animation: fade 0.2s ease-out;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes fade {
  from {
    opacity: 0;
    transform: translateY(2px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

@keyframes shift {
  from {
    background-position: 0 0;
  }
  to {
    background-position: 24px 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}


/* ---------------------------------------------------------------------------
   5b. Инструменты: главная, трекер, хаки

   Здесь лежит всё, что появилось вместе с разделением страниц. Правило то же,
   что и во всём файле: никаких новых цветов и теней — только те же токены,
   отступы и линии.
   --------------------------------------------------------------------------- */

/* --- Главная: одно поле во всю ширину ------------------------------------ */
/* Раньше проверка домена стояла узкой строчкой в карточке, а справа зияла
   пустота: поле занимало треть экрана, кнопка была меньше поля по высоте, и
   главный инструмент выглядел второстепенным. Теперь это первый экран. */
.hero {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 40px 0 8px;
  text-align: center;
}

.hero__title {
  margin: 0;
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.hero__lead {
  margin: 0 auto;
  max-width: 62ch;
  font-size: 15px;
  line-height: 1.55;
  color: var(--fg-soft);
}

.hero__form {
  display: flex;
  gap: 10px;
  width: 100%;
  max-width: 720px;
  margin: 8px auto 0;
}

.input--hero {
  flex: 1 1 auto;
  min-width: 0;
  height: 52px;
  padding: 0 16px;
  font-size: 17px;
  border-radius: var(--radius);
}

/* Кнопка ровно той же высоты, что поле: разъезжающиеся по высоте контролы —
   первое, что бросается в глаза в форме из двух элементов. */
.btn--hero {
  height: 52px;
  padding: 0 26px;
  font-size: 15px;
  border-radius: var(--radius);
  flex: 0 0 auto;
}

.hero__hint {
  margin: 0;
  min-height: 18px;
  font-size: 12px;
  color: var(--fg-mute);
}

.hero__examples {
  display: block;
  max-width: 70ch;
  margin: 0 auto;
}


/* --- Карточки инструментов ------------------------------------------------ */
.tools {
  gap: 12px;
}

.tool {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  transition: border-color 0.15s ease, transform 0.15s ease,
    background-color 0.15s ease;
}

.tool:hover {
  border-color: var(--fg-mute);
  background: var(--bg-soft);
  transform: translateY(-1px);
}

.tool__name {
  font-size: 14px;
  font-weight: 600;
  color: var(--fg);
}

.tool__note {
  font-size: 12px;
  line-height: 1.5;
  color: var(--fg-mute);
}

/* --- Результат проверки во всю ширину ------------------------------------- */
/* Выдача больше не жмётся к левому краю: строка зоны — это сетка во всю
   ширину, где цена, имя и признаки разведены по колонкам, а не свалены в
   первые 200 пикселей. */
.lookup__result-wrap:empty {
  display: none;
}

.lookup__result-wrap .lookup__result,
.lookup__result-wrap .alert {
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
}

.zonefilter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px 16px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--line);
}

/* Прогресс выдачи по зонам: полоска + число проверенных имён. */
.zoneprogress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0 0;
}

.zoneprogress .bar {
  flex: 1 1 auto;
}

.zoneprogress__label {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

.zonehits {
  gap: 0;
}

/* Строку зоны оборачивает тот же `<details class="offers">`, что и ячейку цены
   в таблице, — а там он inline-block с выравниванием вправо. Из-за этого выдача
   по имени занимала левую половину экрана, а справа зияла пустота: блок был
   шириной по содержимому. Здесь он обязан быть блочным во всю ширину. */
.zonehit > .offers {
  display: block;
  width: 100%;
  text-align: left;
}

.zonehit > .offers > .offers__body {
  position: static;
  width: auto;
  min-width: 0;
  max-width: none;
  margin: 0 0 10px;
  box-shadow: none;
  background: var(--bg-soft);
}

.zonehit__head {
  grid-template-columns: 110px minmax(160px, 1fr) minmax(0, 2fr);
  align-items: center;
  gap: 6px 16px;
  padding: 12px 8px;
  margin: 0 -8px;
}

.zonehit__meta {
  grid-column: 3;
  grid-row: 1;
  justify-content: flex-end;
  gap: 8px;
}

.hacklist {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.hacklist__item {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 16px;
  padding: 10px 8px;
  margin: 0 -8px;
  border-bottom: 1px solid #f4f4f5;
  border-radius: var(--radius-sm);
  transition: background-color 0.15s ease;
}

.hacklist__item:last-child {
  border-bottom: 0;
}

.hacklist__item:hover {
  background: var(--bg-soft);
}

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

/* Граница разреза — весь смысл хака: левая часть читается как слово, зона
   продолжает его. Подсветка показывает, где именно проходит шов. */
.hacklist__tld {
  color: var(--ok);
}

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

/* --- Срок регистрации и освобождение -------------------------------------- */
/* Плашка дропа: единственное «событие» в интерфейсе, поэтому она заметная,
   но не кричащая — тот же зелёный, что у свободного статуса. */
.badge--drop {
  border-color: #cfe3d5;
  background: var(--ok-soft);
  color: var(--ok);
  cursor: help;
}

/* Спросить срок можно прямо из строки: это ленивый запрос к реестру, и
   элемент обязан выглядеть нажимаемым. */
.release--unknown[role="button"] {
  cursor: pointer;
  border-bottom: 1px dashed var(--line-strong);
}

.release--unknown[role="button"]:hover .release__ask {
  color: var(--fg);
}

.release__ask {
  color: var(--fg-soft);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Статус в таблице: раньше плашки шли встык, и «занят», whois и срок
   слипались в одну неразборчивую строку. */
.cell-status {
  display: table-cell;
}

.cell-status > * {
  margin-right: 8px;
}

.cell-status .status__note {
  display: inline-block;
  margin-top: 4px;
}

/* --- История статусов ----------------------------------------------------- */
.timeline {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}

.timeline__item {
  display: grid;
  grid-template-columns: minmax(120px, 180px) minmax(0, 1fr);
  align-items: baseline;
  gap: 6px 16px;
  padding: 8px 0;
  border-bottom: 1px solid #f4f4f5;
}

.timeline__item:last-child {
  border-bottom: 0;
}

.timeline__what {
  font-size: 13px;
}

/* --- Разделение цен в списке предложений ---------------------------------- */
/* Заголовок над каждой группой: «цена этого имени» и «ориентир по зоне» —
   разные утверждения, и одинаково выглядеть они не должны. */
.offers__kind {
  margin: 10px 0 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-mute);
}

.offers__kind:first-child {
  margin-top: 0;
}

.offers__kind--zone {
  padding-top: 8px;
  border-top: 1px solid var(--line);
  color: var(--fg-faint);
}

.offers__kind--zone .tiny {
  display: block;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

/* ---------------------------------------------------------------------------
   7. Адаптивность и печать
   --------------------------------------------------------------------------- */
@media (max-width: 1080px) {
  .layout-admin {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 760px) {
  /* --- Шапка: два ряда ------------------------------------------------------ */
  /* Пять пунктов навигации с переключателем языка в один ряд на телефоне
     не помещаются. Бренд остаётся сверху, навигация уходит в ленту с
     горизонтальной прокруткой: лента тянется от края до края (отрицательные
     поля компенсируют паддинг .wrap), скроллбар спрятан — прокручивается
     жестом, как любая мобильная лента. */
  /* Два класса на одном элементе с `.wrap`: вертикальный отступ объявляем
     ДОЛГОЙ записью — шортхенд `padding: 16px 0 0` здесь обнулял горизонтальные
     поля .wrap, из-за чего логотип прилипал к левому краю, а лента навигации
     со своими отрицательными полями вылезала за правый край экрана (и страница
     позволяла «отщипнуть» лишнее справа). Горизонталь по-прежнему приходит из
     `.wrap`: 24px на десктопе, 16px на телефоне. */
  .site-header .site-header__inner {
    flex-wrap: wrap;
    gap: 0;
    min-height: 0;
    /* Воздух над логотипом: без него бренд прижат к верхней кромке экрана.
       16px — те же поля, что по бокам у .wrap. */
    padding-top: 16px;
  }

  .nav {
    order: 1;
    flex: 1 0 100%;
    margin: 8px -16px 0;
    padding: 0 16px 8px;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .nav::-webkit-scrollbar {
    display: none;
  }

  .nav__link,
  .lang-switch {
    flex-shrink: 0;
  }

  /* --- Поля ввода: не меньше 16px ------------------------------------------- */
  /* Меньше — iOS Safari при фокусе сам приближает страницу, и после каждого
     касания поля её приходится отдалять руками. */
  .input,
  .select {
    font-size: 16px;
  }

  /* --- Таблица: строки-карточки вместо горизонтальной прокрутки -------------
     Листать выдачу вбок, чтобы увидеть цену и дату проверки, — занятие для
     того, кто уже знает, что там справа. Новичку это неочевидно. Поэтому на
     телефоне каждая строка становится карточкой: поля идут в порядке важности
     (`order`), а второстепенные подписывает `data-label` из разметки. */
  #table-wrap .table-scroll,
  #expiring-wrap .table-scroll {
    overflow-x: visible;
  }

  #table-wrap .table thead,
  #expiring-wrap .table thead {
    display: none;
  }

  #table-wrap .table tbody,
  #expiring-wrap .table tbody {
    display: block;
  }

  #table-wrap .table tbody tr,
  #expiring-wrap .table tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    padding: 12px 14px;
  }

  #table-wrap .table tbody tr:last-child,
  #expiring-wrap .table tbody tr:last-child {
    border-bottom: 0;
  }

  #table-wrap .table tbody td,
  #expiring-wrap .table tbody td {
    display: block;
    min-width: 0;
    padding: 0;
    border-bottom: 0;
  }

  /* Подпись второстепенного поля («проверено:», «истекает:») — содержимое
     data-label ячейки. Псевдоэлемент возможен, потому что td теперь блочный. */
  #table-wrap .table tbody td[data-label]::before,
  #expiring-wrap .table tbody td[data-label]::before {
    content: attr(data-label) ": ";
    font-size: 11px;
    color: var(--fg-mute);
  }

  /* Каталог: имя · цена · статус · категория · проверено. */
  #table-wrap .table td:nth-child(1) {
    order: 1;
    flex: 1 1 auto;
  }

  #table-wrap .table td:nth-child(4) {
    order: 2;
    flex: 0 0 auto;
  }

  #table-wrap .table td.cell-status {
    order: 3;
    flex: 1 1 100%;
  }

  #table-wrap .table td:nth-child(2) {
    order: 4;
  }

  #table-wrap .table td:nth-child(5) {
    order: 5;
    margin-left: auto;
  }

  /* Трекер: имя · цена · дата · статус. Дата разворачивается на всю строку:
     «истекает 19 мая · своб. после …» не должно зажимать соседние колонки. */
  #expiring-wrap .table td:nth-child(1) {
    order: 1;
    flex: 1 1 auto;
  }

  #expiring-wrap .table td.right {
    order: 2;
    flex: 0 0 auto;
  }

  #expiring-wrap .table td[data-label] {
    order: 3;
    flex: 1 1 100%;
    white-space: normal;
  }

  #expiring-wrap .table td.cell-status {
    order: 4;
    flex: 1 1 100%;
  }

  /* Список цен — шторка снизу во всю ширину экрана. Прежнее раскрытие внутри
     ячейки было зажато шириной колонки цены (~80px): список регистраторов
     слипался в вертикальный столбик. Fixed-позиция выносит шторку из-под
     overflow таблицы и даёт ей весь экран. */
  .table .offers {
    display: block;
    text-align: right;
  }

  .table .offers[open] > .offers__body {
    position: fixed;
    left: 10px;
    right: 10px;
    bottom: 10px;
    z-index: 50;
    /* min/max-width у всплывающей версии здесь вредны: max-width не давал
       шторке дотянуться до правого края при заданных left/right. */
    min-width: 0;
    max-width: none;
    max-height: 65vh;
    margin-top: 0;
    box-shadow: 0 12px 40px rgba(19, 19, 22, 0.18);
  }

  /* --- Вкладки категорий ------------------------------------------------------ */
  /* Примеры (vc.vc, aa.cc…) стоят по строке на вкладку — на телефоне это
     простыня. Названия и счётчики остаются. */
  .tab__example {
    display: none;
  }

  /* --- Крупнее цель для пальца ------------------------------------------------ */
  /* Пагинация и чипы зон были высотой около 26px — вдвое меньше минимальной
     кнопки по HIG. */
  .pager__link {
    min-width: 38px;
    padding: 7px 10px;
  }

  .chip {
    padding: 5px 10px;
  }

  .btn--sm {
    padding: 8px 12px;
  }

  /* --- Фильтры: «Применить» как главное действие ----------------------------- */
  /* Маленькая кнопка в дальнем углу под всеми полями выглядела случайной.
     На телефоне она становится полноразмерной строкой футера — так понятен
     и её размер, и что она относится ко всем фильтрам сразу. */
  .filters__foot .push {
    margin-left: 0;
    flex: 1 0 100%;
  }

  .filters button[type="submit"] {
    flex: 1 1 auto;
    padding: 11px 16px;
    font-size: 14px;
  }

  /* --- Сводные метрики: компактная сетка 2×2 ---------------------------------
     Четыре крупные карточки съедали весь первый экран, и таблицу приходилось
     искать прокруткой. Две колонки без заметок вдвое ниже; подробности
     остаются на широком экране. */
  .grid--stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .stat {
    padding: 10px 12px;
  }

  .stat__value {
    margin-top: 4px;
    font-size: 18px;
  }

  .stat__note {
    display: none;
  }

  /* --- Скелетон поиска складывается, как живые строки ------------------------ */
  /* Тот же коллапс сетки, что у .zonehit__head: цена справа, под ней имя,
     затем мета — смена фантома на выдачу не прыгает разметкой. */
  .skeleton-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 7px;
  }

  .skeleton--meta {
    justify-self: start;
    width: 40%;
  }

  .domain-hero__name {
    font-size: 24px;
  }

  /* Форма проверки на узком экране: кнопка во всю ширину под полем — иначе
     она уезжает на вторую строку и повисает без выравнивания. */
  .hero {
    padding-top: 24px;
    text-align: left;
  }

  .hero__title {
    font-size: 26px;
  }

  .hero__form {
    flex-direction: column;
    gap: 8px;
  }

  .btn--hero {
    width: 100%;
  }

  .grid--3 {
    grid-template-columns: minmax(0, 1fr);
  }

  .zonehit__head,
  .hacklist__item,
  .timeline__item {
    grid-template-columns: minmax(0, 1fr);
  }

  .zonehit__meta {
    grid-column: 1;
    grid-row: auto;
    justify-content: flex-start;
  }

  .zonefilter {
    gap: 8px 12px;
  }

  .wrap {
    padding: 0 16px;
  }

  .main {
    padding: 24px 0 40px;
  }

  /* Вертикальный ритм плотнее, но иерархия сохраняется: обычная секция —
     20px, «тесная» — по-прежнему ближе обычной. */
  .stack {
    gap: 20px;
  }

  .stack--tight {
    gap: 12px;
  }

  .grid--2,
  .grid--4 {
    grid-template-columns: minmax(0, 1fr);
  }

  .page-title {
    font-size: 20px;
  }

  .last-scan {
    padding: 18px;
  }

  .last-scan__value {
    font-size: 22px;
  }

  .last-scan__side {
    margin-left: 0;
    width: 100%;
  }

  .brand__sub {
    display: none;
  }

  .nav__link {
    padding: 7px 9px;
  }

  .card {
    padding: 16px;
  }
}

/* Совсем узкий экран: на странице домена три колонки «цена · регистратор ·
   условия» сжимаются в кашу — колонка условий переезжает под регистратора
   и выравнивается влево. */
@media (max-width: 560px) {
  .offers--static .offers__link {
    grid-template-columns: minmax(72px, 96px) minmax(0, 1fr);
    gap: 4px 12px;
  }

  .offers--static .offers__meta {
    grid-column: 2;
    grid-row: 2;
    text-align: left;
  }
}

@media print {
  .site-header,
  .site-footer,
  .filters,
  .tabs,
  .pager,
  .btn {
    display: none !important;
  }

  body {
    font-size: 11px;
  }

  .card {
    border: 0;
  }
}
