/* ═══════════════════════════════════════════════════════════════════════════
   chkip.info — страничные стили поверх industry.css.

   industry.css даёт токены (--color-*, --font-*, --space-*) и компоненты
   (.btn, .tag, .table, .blueprint, .card). Здесь — только то, чего в системе
   нет: раскладка главной, четыре состояния полосы и шесть состояний карточки
   проверки. Подключать строго ПОСЛЕ industry.css.

   Источники разметки: turn2-6b (десктоп), turn2-6a (390px), turn2-6c
   (состояния карточек, карта, таблица источников), turn1-7a (полоса).
   Палитра тёмной темы — turn3-5a / turn3-5b.

   Правила: цвета только через var(--…); сырой hex допускается единственно
   в определении тёмной палитры (§2). Ни один цвет не задан только внутри
   media-запроса — иначе ручное переключение темы осталось бы без значений.

   Оглавление
     1. Токены страницы
     2. Тёмная палитра и переключение темы
     3. Ключевые кадры и утилиты
     4. Шапка
     5. Герой
     6. Реквизиты
     7. Карта
     8. Полоса анонимности + [data-bar-state]
     9. Промо VPN
    10. Проверки + [data-state]
    11. Сравнение источников
    12. Адаптив: 620 / 900 / 1180
    13. prefers-reduced-motion
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 1. Токены страницы ──────────────────────────────────────────────────
   Раскладочные величины, которых нет в дизайн-системе, и чернила полосы.
   Полоса залита тёмным в обеих темах, поэтому её текст не может брать
   var(--color-text): держим для неё отдельную пару «фон / чернила». */
:root {
  color-scheme: light;

  --page-max: 1280px;     /* ширина контентной колонки, turn2-6b */
  --page-edge: 16px;      /* боковое поле на узком экране */
  --page-edge-lg: 40px;   /* боковое поле на десктопе */
  --aside: 440px;         /* колонка карты в герое */

  --bar-bg: var(--color-accent-900);
  --bar-ink: var(--color-accent-100);   /* почти белый в светлой теме */

  --hairline: color-mix(in srgb, var(--color-text) 9%, transparent);
  --muted: color-mix(in srgb, var(--color-text) 55%, transparent);
  --skeleton: color-mix(in srgb, var(--color-text) 14%, transparent);
}


/* ── 2. Тёмная палитра и переключение темы ───────────────────────────────
   Значения взяты из turn3-5a / turn3-5b (фон #161b21, текст #e9ecef,
   акцент #7ea7d1) и достроены до полных шкал: в тёмной теме нейтральная
   и акцентная шкалы разворачиваются — светлеют к 900 и к 800 соответственно,
   поэтому tag-accent (фон 100, текст 800) читается в обеих темах.

   Сырые значения живут здесь, на голом :root, а не в media-запросе: ниже
   их только раскладывают по ролям — один раз для [data-theme="dark"]
   и один раз для системной тёмной темы. */
:root {
  --dk-bg: #161b21;
  --dk-surface: #1d242c;
  --dk-text: #e9ecef;
  --dk-accent: #7ea7d1;
  --dk-shadow-ink: #05070a;

  --dk-accent-100: #22303d;
  --dk-accent-200: #2b3d4e;
  --dk-accent-300: #a8c6e4;
  --dk-accent-400: #7ea7d1;
  --dk-accent-500: #8bb1d7;
  --dk-accent-600: #9dbcdc;
  --dk-accent-700: #a8c6e4;
  --dk-accent-800: #cfe0f0;
  --dk-accent-900: #22303d;

  --dk-accent-2-100: #232f3a;
  --dk-accent-2-200: #2c3c49;
  --dk-accent-2-300: #a3bcd0;
  --dk-accent-2-400: #86a3bb;
  --dk-accent-2-500: #93aec4;
  --dk-accent-2-600: #a3bcd0;
  --dk-accent-2-700: #aec6d8;
  --dk-accent-2-800: #d0dfe9;
  --dk-accent-2-900: #232f3a;

  --dk-neutral-100: #232a32;
  --dk-neutral-200: #2b333b;
  --dk-neutral-300: #3a434c;
  --dk-neutral-400: #5a636c;
  --dk-neutral-500: #7a828b;
  --dk-neutral-600: #9aa1a9;
  --dk-neutral-700: #b6bcc2;
  --dk-neutral-800: #cdd3d9;
  --dk-neutral-900: #e4e8ec;

  /* Основная кнопка в тёмной теме. Наведение и нажатие ТЕМНЕЕ фона:
     при светлении контраст падал до 4.25 — ниже нормы AA. */
  --dk-btn-bg: #3d6a99;
  --dk-btn-bg-hover: #335a82;
  --dk-btn-bg-active: #2c4f73;
  --dk-btn-ink: #eef4fa;
}

/* Явный выбор пользователя — кнопка в шапке ставит data-theme на <html>. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --color-bg: var(--dk-bg);
  --color-surface: var(--dk-surface);
  --color-text: var(--dk-text);
  --color-accent: var(--dk-accent);
  --color-accent-2: var(--dk-accent-2-400);
  --color-divider: color-mix(in srgb, var(--dk-text) 20%, transparent);

  --color-accent-100: var(--dk-accent-100);
  --color-accent-200: var(--dk-accent-200);
  --color-accent-300: var(--dk-accent-300);
  --color-accent-400: var(--dk-accent-400);
  --color-accent-500: var(--dk-accent-500);
  --color-accent-600: var(--dk-accent-600);
  --color-accent-700: var(--dk-accent-700);
  --color-accent-800: var(--dk-accent-800);
  --color-accent-900: var(--dk-accent-900);

  --color-accent-2-100: var(--dk-accent-2-100);
  --color-accent-2-200: var(--dk-accent-2-200);
  --color-accent-2-300: var(--dk-accent-2-300);
  --color-accent-2-400: var(--dk-accent-2-400);
  --color-accent-2-500: var(--dk-accent-2-500);
  --color-accent-2-600: var(--dk-accent-2-600);
  --color-accent-2-700: var(--dk-accent-2-700);
  --color-accent-2-800: var(--dk-accent-2-800);
  --color-accent-2-900: var(--dk-accent-2-900);

  --color-neutral-100: var(--dk-neutral-100);
  --color-neutral-200: var(--dk-neutral-200);
  --color-neutral-300: var(--dk-neutral-300);
  --color-neutral-400: var(--dk-neutral-400);
  --color-neutral-500: var(--dk-neutral-500);
  --color-neutral-600: var(--dk-neutral-600);
  --color-neutral-700: var(--dk-neutral-700);
  --color-neutral-800: var(--dk-neutral-800);
  --color-neutral-900: var(--dk-neutral-900);

  --shadow-sm: 0 1px 2px color-mix(in srgb, var(--dk-shadow-ink) 55%, transparent);
  --shadow-md: 0 3px 10px color-mix(in srgb, var(--dk-shadow-ink) 60%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, var(--dk-shadow-ink) 70%, transparent);

  /* В тёмной теме accent-100 сам стал тёмным — чернила полосы берём из 800. */
  --bar-ink: var(--dk-accent-800);
}

/* Системная тема. Тот же набор ролей; не применяется, если пользователь
   явно выбрал светлую. Значения — те же --dk-*, определённые выше. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --color-bg: var(--dk-bg);
    --color-surface: var(--dk-surface);
    --color-text: var(--dk-text);
    --color-accent: var(--dk-accent);
    --color-accent-2: var(--dk-accent-2-400);
    --color-divider: color-mix(in srgb, var(--dk-text) 20%, transparent);

    --color-accent-100: var(--dk-accent-100);
    --color-accent-200: var(--dk-accent-200);
    --color-accent-300: var(--dk-accent-300);
    --color-accent-400: var(--dk-accent-400);
    --color-accent-500: var(--dk-accent-500);
    --color-accent-600: var(--dk-accent-600);
    --color-accent-700: var(--dk-accent-700);
    --color-accent-800: var(--dk-accent-800);
    --color-accent-900: var(--dk-accent-900);

    --color-accent-2-100: var(--dk-accent-2-100);
    --color-accent-2-200: var(--dk-accent-2-200);
    --color-accent-2-300: var(--dk-accent-2-300);
    --color-accent-2-400: var(--dk-accent-2-400);
    --color-accent-2-500: var(--dk-accent-2-500);
    --color-accent-2-600: var(--dk-accent-2-600);
    --color-accent-2-700: var(--dk-accent-2-700);
    --color-accent-2-800: var(--dk-accent-2-800);
    --color-accent-2-900: var(--dk-accent-2-900);

    --color-neutral-100: var(--dk-neutral-100);
    --color-neutral-200: var(--dk-neutral-200);
    --color-neutral-300: var(--dk-neutral-300);
    --color-neutral-400: var(--dk-neutral-400);
    --color-neutral-500: var(--dk-neutral-500);
    --color-neutral-600: var(--dk-neutral-600);
    --color-neutral-700: var(--dk-neutral-700);
    --color-neutral-800: var(--dk-neutral-800);
    --color-neutral-900: var(--dk-neutral-900);

    --shadow-sm: 0 1px 2px color-mix(in srgb, var(--dk-shadow-ink) 55%, transparent);
    --shadow-md: 0 3px 10px color-mix(in srgb, var(--dk-shadow-ink) 60%, transparent);
    --shadow-lg: 0 12px 32px color-mix(in srgb, var(--dk-shadow-ink) 70%, transparent);

    --bar-ink: var(--dk-accent-800);
  }
}


/* ── 3. Ключевые кадры и утилиты ─────────────────────────────────────────
   om-pulse и om-spin используются разметкой (скелеты, маркер loading,
   точка полосы, спиннер кнопки); .k и .mono в дизайн-системе отсутствуют,
   но макет на них опирается. */
@keyframes om-pulse { 0%, 100% { opacity: .35; } 50% { opacity: .85; } }
@keyframes om-spin  { to { transform: rotate(360deg); } }

/* ── Основная кнопка ──────────────────────────────────────────────────────
   В дизайн-системе .btn-primary — фон accent, текст цвета фона страницы.
   Это давало три беды:
     1. В светлой теме контраст 3.71 — ниже нормы AA для текста 14px.
     2. В шапке `.nav a { color: inherit }` сильнее .btn-primary по
        специфичности (0,1,1 против 0,1,0): надпись становилась тёмной.
     3. `.nav a:hover { color: accent }` красил надпись в цвет фона —
        при наведении текст пропадал целиком.
   Цвета держим токенами; наведение темнит фон, а не текст. */
:root {
  --btn-primary-bg: var(--color-accent-700);        /* в светлой теме #416180 */
  --btn-primary-bg-hover: var(--color-accent-800);
  --btn-primary-bg-active: var(--color-accent-900);
  --btn-primary-ink: var(--color-neutral-100);
}
:root[data-theme="dark"] {
  --btn-primary-bg: var(--dk-btn-bg);
  --btn-primary-bg-hover: var(--dk-btn-bg-hover);
  --btn-primary-bg-active: var(--dk-btn-bg-active);
  --btn-primary-ink: var(--dk-btn-ink);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --btn-primary-bg: var(--dk-btn-bg);
    --btn-primary-bg-hover: var(--dk-btn-bg-hover);
    --btn-primary-bg-active: var(--dk-btn-bg-active);
    --btn-primary-ink: var(--dk-btn-ink);
  }
}

.btn-primary {
  background: var(--btn-primary-bg);
  border-color: var(--btn-primary-bg);
  color: var(--btn-primary-ink);
}
.btn-primary:hover  { background: var(--btn-primary-bg-hover);  border-color: var(--btn-primary-bg-hover); }
.btn-primary:active { background: var(--btn-primary-bg-active); border-color: var(--btn-primary-bg-active); }

/* Шапка: правила `.nav a` сильнее — перебиваем их явно, во всех состояниях. */
.nav a.btn-primary,
.nav a.btn-primary:hover,
.nav a.btn-primary:focus-visible,
.nav a.btn-primary[aria-current='page'] { color: var(--btn-primary-ink); }

/* Кнопка VPN в шапке: значок телефона перед текстом — сразу видно, что речь
   о мобильном приложении, а не о ещё одной проверке. */
.nav-cta { gap: 8px; padding-inline: 14px; letter-spacing: .01em; }
.nav-cta svg { flex: none; opacity: .9; }

html {
  background: var(--color-bg);
  scroll-behavior: smooth;
}
body { padding-bottom: env(safe-area-inset-bottom, 0px); }

.k {
  /* Класс вешается и на div, и на h1 (заголовок героя): у заголовка браузер
     ставит свои отступы, поэтому обнуляем — иначе вёрстка разъедется. */
  margin: 0;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--color-accent-700);
}
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

/* Контентная колонка. Боковое поле не опускается ниже 16px ни на одной ширине. */
.wrap {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--page-edge);
}
.hr-line { border-bottom: 1px solid var(--hairline); }

/* Подвал с указанием источников. Юридическая строка намеренно мельче и тише
   основной, но остаётся читаемой: прятать её нельзя — этого требуют лицензии
   MaxMind, DB-IP и IP2Location. */
.site-footer { margin-top: 40px; border-top: 1px solid var(--hairline); }
.site-footer-in { padding-block: 18px 30px; }
.site-footer-credits { margin: 0 0 8px; font-size: 13px; line-height: 1.5; }
.site-footer-legal {
  margin: 0; font-size: 11px; line-height: 1.6; color: var(--muted);
}
.site-footer a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Якорные ссылки ведут на карточки и на таблицу источников. */
[data-check], .src { scroll-margin-top: 16px; }

/* После перехода по якорю цель на пару секунд подсвечивается: страница длинная,
   и без отметки непонятно, на какой именно блок смотреть. Гаснет сама. */
@keyframes om-flash {
  from {
    background: color-mix(in srgb, var(--color-accent) 18%, transparent);
    box-shadow: inset 3px 0 0 var(--color-accent);
  }
  to { background: transparent; box-shadow: inset 3px 0 0 transparent; }
}
.is-flash { animation: om-flash 2.2s ease-out 1; border-radius: 2px; }


/* ── 4. Шапка ────────────────────────────────────────────────────────────
   Узкий экран: логотип, тема, язык, бургер. Десктоп: добавляются ссылки
   и кнопка VPN, разделительная линия убирается (turn2-6b). */
.nav {
  gap: 10px;
  padding: 12px var(--page-edge);
  border-bottom: 1px solid var(--color-divider);
}
.nav-brand { margin-right: 0; font-size: 17px; letter-spacing: .04em; }
.nav-links { display: none; align-items: center; gap: 28px; }
.nav-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.nav-btn { width: 44px; height: 44px; padding: 0; flex: none; }   /* палец — 44px */
.nav-lang { height: 44px; padding: 0 12px; flex: none; gap: 4px; }
.nav-lang[aria-expanded="true"] { border-color: var(--color-text); }
.nav-lang[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Выпадающий список языков: под кнопкой, к правому краю, поверх страницы.
   Пункт — самоназвание языка плюс код; текущий помечен квадратом-маркером,
   как текущий раздел в мобильном меню: одного цвета в тёмной теме мало. */
.nav-lang-wrap { position: relative; flex: none; }
.nav-lang-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 41;
  min-width: 196px; margin: 0; padding: 6px; list-style: none;
  background: var(--color-bg);
  border: 1px solid var(--color-divider);
  box-shadow: var(--shadow-lg);
  animation: om-menu-in .16s ease-out;
}
.nav-lang-menu[hidden] { display: none; }
.nav-lang-menu a {
  display: flex; align-items: center; gap: 12px;
  min-height: 40px; padding: 0 12px;
  color: inherit; text-decoration: none; font-size: 15px; white-space: nowrap;
}
.nav-lang-menu a:hover, .nav-lang-menu a:focus-visible { background: var(--hairline); outline: none; }
.nav-lang-menu a[aria-current]::after {
  content: ""; width: 8px; height: 8px; margin-left: 4px; flex: none;
  background: var(--color-accent);
}
.nav-lang-code { margin-left: auto; font-size: 12px; letter-spacing: .04em; opacity: .6; }
.nav-burger { display: inline-flex; }
.nav-cta { display: none; }
.nav-menu-cta { display: none; }   /* пункт VPN существует только в раскрытой панели */

/* Значок бургера: три черты у закрытого меню, крестик у открытого.
   Селектор длиннее `.btn svg { display: block }` — иначе видны были бы оба. */
.nav-burger .nav-burger-close { display: none; }
.nav-burger[aria-expanded="true"] .nav-burger-open { display: none; }
.nav-burger[aria-expanded="true"] .nav-burger-close { display: block; }
.nav-burger[aria-expanded="true"] { border-color: var(--color-text); }

@keyframes om-menu-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@keyframes om-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* Раскрытое меню. Всё — под max-width, а не мобильной базой с откатом
   на 900px: десктопная шапка этих правил не видит вовсе и измениться
   не может. Панель ложится поверх страницы, а не сдвигает её: иначе
   при закрытии по якорю цель уехала бы на высоту панели. */
@media (max-width: 899.98px) {
  .nav { position: relative; }

  .nav.is-menu-open .nav-links {
    display: flex; flex-direction: column; align-items: stretch; gap: 0;
    position: absolute; top: calc(100% + 1px); left: 0; right: 0; z-index: 40;
    padding: 4px var(--page-edge) 18px;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-divider);
    box-shadow: var(--shadow-lg);
    /* Тень нужна только вниз: вверх она ложилась серой полосой на шапку. */
    clip-path: inset(0 0 -48px 0);
    animation: om-menu-in .18s ease-out;
  }
  .nav.is-menu-open .nav-links a:not(.nav-menu-cta) {
    display: flex; align-items: center; gap: 10px;
    min-height: 52px;
    font-family: var(--font-heading); font-weight: var(--font-heading-weight);
    font-size: 21px; line-height: 1.1;
    border-bottom: 1px solid var(--hairline);
  }
  /* Текущий раздел — квадратом-маркером, как у проверок: одного цвета мало,
     в тёмной теме акцент близок к основному тексту. */
  .nav.is-menu-open .nav-links a[aria-current="page"]::after {
    content: ""; width: 8px; height: 8px; margin-left: auto; flex: none;
    background: var(--color-accent);
  }
  .nav.is-menu-open .nav-menu-cta {
    display: flex; min-height: 48px; margin-top: 16px; gap: 8px;
    font-size: 15px;
  }

  /* Вуаль под панелью: гасит страницу и забирает нажатие «мимо», чтобы оно
     закрыло меню, а не сработало на ссылке под ним. Цвет — фон страницы,
     поэтому вуаль верна в обеих темах. Начинается под шапкой: кнопки шапки
     остаются живыми. Шапка не липкая — открыть меню можно только наверху
     страницы, так что высоты экрана хватает. */
  .nav.is-menu-open::after {
    content: "";
    position: absolute; top: calc(100% + 1px); left: 0; right: 0; z-index: 39;
    height: 100vh;
    background: color-mix(in srgb, var(--color-bg) 72%, transparent);
    animation: om-fade-in .18s ease-out;
  }
}


/* ── 5. Герой ────────────────────────────────────────────────────────────
   Узкий экран — одна колонка; десктоп — 1fr + 440px под карту. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  padding-top: 20px;
}

/* Узкий экран: адрес занимает строку целиком, под ним — копирование
   и «Сменить IP». В одной строке с адресом кнопки не помещаются: при 46px
   даже IPv4 рвался на две строки, а IPv6 — на три. */
.hero-ip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 10px; margin-top: 8px; }
.hero-ip {
  flex: 1 0 100%;
  min-width: 0;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 46px; line-height: .9; letter-spacing: -.02em;
  overflow-wrap: anywhere;   /* IPv6 переносится, только если иначе не влезает */
}
.hero-ip.is-long { font-size: 30px; line-height: 1.05; }
.hero-copy { width: 44px; height: 44px; padding: 0; flex: none; }
/* Итог копирования показываем самой кнопкой — без всплывающих окон. */
.hero-copy.is-copied { color: var(--color-accent); border-color: var(--color-accent); }
.hero-copy.is-failed { color: var(--color-neutral-600); border-color: var(--color-neutral-500); }
.hero-change { min-height: 44px; gap: 8px; padding-inline: 14px; font-size: 15px; }
.hero-change svg { flex: none; }

.hero-geo { display: flex; align-items: center; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.hero-flag { display: block; flex: none; width: 30px; height: 30px; border-radius: 50%; }
.hero-place {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 23px; line-height: 1;
}

/* Строка согласия источников: квадрат-маркер, счётчик, ссылка «сравнить». */
.hero-agree { display: flex; align-items: center; gap: 8px; flex: 1 0 100%; margin-top: 4px; }
.hero-agree-dot { width: 8px; height: 8px; flex: none; border: 1px solid var(--color-neutral-500); }
.hero-agree-dot.is-conflict { border: 0; background: var(--color-accent); }
.hero-agree-text { font-size: 13px; color: var(--muted); }
.hero-agree .link-dotted { margin-left: auto; }

.link-dotted {
  font-size: 12px; color: var(--color-accent-700);
  text-decoration: none; border-bottom: 1px dotted currentColor;
}


/* ── 6. Реквизиты ────────────────────────────────────────────────────────
   Восемь строк «ключ — иконка — значение». На десктопе раскладываются
   в две колонки по четыре строки, заполняясь по столбцам (turn2-6b). */
.facts { display: grid; grid-template-columns: minmax(0, 1fr); margin-top: 14px; }
.fact {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--hairline);
}
.fact-key { width: 100px; flex: none; white-space: nowrap; font-size: 13px; color: var(--muted); }
.fact-ico { flex: none; color: var(--color-accent-700); }
.fact-ico-mask { width: 14px; height: 14px; flex: none; background: var(--color-accent-700); }
.fact-val { min-width: 0; font-size: 14px; overflow-wrap: anywhere; }
.fact-val.mono { font-size: 13px; }
.fact-val.is-risk { color: var(--color-accent-800); }
.fact-val.is-empty { color: var(--muted); }
.fact-link { margin-left: auto; flex: none; }

/* Скелет — пока проверка, питающая строку, не ответила. */
.fact-skel { flex: 1; max-width: 150px; }
.fact-skel span {
  display: block; height: 9px; width: 100%;
  background: var(--skeleton);
  animation: om-pulse 1.3s ease-in-out infinite;
}


/* ── 7. Карта ────────────────────────────────────────────────────────────
   Рамка .blueprint с четырьмя уголками; сам круг и точку рисует iframe. */
.map-box { position: relative; height: 210px; }
.map-box iframe { display: block; width: 100%; height: 100%; border: 0; }
.map-empty {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: 16px; text-align: center; font-size: 13px; color: var(--muted);
}
.map-caption {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin: 10px 0 20px;
}
.map-place { font-family: var(--font-heading); font-size: 19px; }
.map-radius { font-size: 11px; color: var(--muted); text-align: right; }


/* ── 8. Полоса анонимности ───────────────────────────────────────────────
   Тёмная плашка во всю ширину. Узкий экран — четыре строки друг под другом,
   десктоп — цифра слева, шкала по центру, кнопка справа (turn1-7a). */
.bar { background: var(--bar-bg); color: var(--bar-ink); }
.bar-in {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "score" "meter" "action" "hint";
  padding: 20px var(--page-edge);
}

.bar-score { grid-area: score; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.bar-num {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 56px; line-height: 1; opacity: .72;
  padding-bottom: 2px;
  border-bottom: 3px dashed color-mix(in srgb, var(--bar-ink) 40%, transparent);
}
.bar-pct { font-family: var(--font-heading); font-size: 22px; opacity: .7; }
.bar-tag {
  margin-left: 6px; background: transparent; color: var(--bar-ink);
  border: 1px solid color-mix(in srgb, var(--bar-ink) 45%, transparent);
}
.bar-label { color: var(--color-accent-300); margin-left: auto; }

.bar-meter { grid-area: meter; }
.bar-track { height: 10px; margin-top: 12px; background: color-mix(in srgb, var(--bar-ink) 22%, transparent); }
.bar-fill { height: 100%; background: var(--color-accent-300); transition: width .35s ease; }
.bar-counters-row { display: flex; align-items: baseline; gap: 8px; margin-top: 10px; }
.bar-dot {
  display: none; width: 8px; height: 8px; flex: none;
  background: var(--color-accent-300);
  animation: om-pulse 1.3s ease-in-out infinite;
}
.bar-counters { font-size: 13px; opacity: .85; text-wrap: pretty; }
.bar-hint { grid-area: hint; margin-top: 8px; font-size: 12px; opacity: .65; text-wrap: pretty; }
.bar-hint:empty { display: none; }

.bar-action {
  grid-area: action;
  width: 100%; min-height: 48px; margin-top: 14px; font-size: 15px;
  background: transparent; color: var(--bar-ink);
  border-color: color-mix(in srgb, var(--bar-ink) 45%, transparent);
}
.bar-action svg { display: none; }              /* нужную иконку включает состояние */
.bar-action .ico-spin { animation: om-spin 1s linear infinite; }

/* Четыре состояния полосы — CONTRACT §6. JS меняет только data-bar-state. */
[data-bar-state="running"] .bar-action,
[data-bar-state="rerunning"] .bar-action { opacity: .6; }
[data-bar-state="running"] .bar-action .ico-spin,
[data-bar-state="rerunning"] .bar-action .ico-spin { display: block; }
[data-bar-state="running"] .bar-dot,
[data-bar-state="rerunning"] .bar-dot { display: block; }

/* Осталось ручное и всё готово: кнопка светлая, активная. */
[data-bar-state="manual"] .bar-action,
[data-bar-state="done"] .bar-action {
  opacity: 1;
  background: var(--bar-ink); color: var(--bar-bg); border-color: var(--bar-ink);
}
[data-bar-state="manual"] .bar-action .ico-play { display: block; }
[data-bar-state="done"] .bar-action .ico-redo { display: block; }

/* Итоговая цифра: без пунктира и без тега «предварительно».
   Гасим цвет рамки, а не саму рамку, — иначе строка подпрыгнет на 3px. */
[data-bar-state="done"] .bar-num { opacity: 1; border-bottom-color: transparent; }
[data-bar-state="done"] [data-slot="provisional"] { display: none; }


/* ── 9. Промо VPN ────────────────────────────────────────────────────────
   Только узкий экран: на десктопе та же ссылка живёт кнопкой в шапке. */
.promo { padding: 18px var(--page-edge); border-bottom: 1px solid var(--color-divider); }
.promo-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 20px; line-height: 1.15;
}
.promo-sub { margin-top: 4px; font-size: 13px; color: var(--muted); }
.promo-btn { width: 100%; min-height: 48px; margin-top: 12px; font-size: 15px; }
.promo-note { margin-top: 8px; text-align: center; font-size: 12px; color: var(--muted); }


/* ── 10. Проверки ────────────────────────────────────────────────────────
   Одна и та же разметка в двух обличьях: на узком экране проверка —
   строка списка (turn2-6a), на широком — карточка в сетке (turn2-6b).
   Переключение делает сама сетка, разметку JS не трогает. */
.checks { padding-top: 18px; padding-bottom: 24px; }
.checks-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.checks-updated { font-size: 13px; color: var(--muted); }
.checks-report { display: none; margin-left: auto; }

.group-title { margin: 0 0 2px; }
.group { margin-bottom: 18px; }

/* Строка: маркер · заголовок · тело, прижатое вправо. */
.chk {
  display: grid;
  grid-template-columns: 9px 118px minmax(0, 1fr);
  column-gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hairline);
}
.chk-head { display: contents; }   /* маркер, заголовок и тег — прямые ячейки сетки */
.chk-marker { width: 9px; height: 9px; flex: none; margin-top: 6px; }
.chk-title {
  min-width: 0; overflow-wrap: anywhere;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 16px; line-height: 1.2;
}
.chk-tag { grid-column: 3; justify-self: end; }
.chk-tag:empty { display: none; }  /* нет тега — тело поднимается в первую строку */
.chk-body { grid-column: 3; min-width: 0; text-align: right; }

.chk-line { display: block; margin-top: 4px; font-size: 12.5px; opacity: .75; word-break: break-word; }
.chk-note { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); text-wrap: pretty; }
.chk-more {
  display: inline-block; margin-top: 5px; font-size: 12px;
  color: var(--color-accent-700); text-decoration: none;
  border-bottom: 1px dotted currentColor;
}
.chk-skel {
  display: block; height: 9px; margin-top: 7px; margin-left: auto;
  background: var(--skeleton);
  animation: om-pulse 1.3s ease-in-out infinite;
}
/* Кнопка карточки — всегда на своей строке. Рядом с ней может стоять ссылка
   «все порты»: как строчные элементы они вставали в одну строку и слипались
   (JS после ответа дописывает узлы без пробелов между ними). Блочный flex
   шириной по содержимому от этого не зависит; на узком экране тело карточки
   выровнено вправо — прижимаем кнопку правым краем. */
.chk-btn {
  display: flex; width: fit-content; margin-left: auto;
  margin-top: 10px; font-size: 12px; min-height: 44px; padding: 0 10px;
}

/* Шесть состояний — CONTRACT §2. Вид маркера 9×9 строго по таблице;
   JS меняет только data-state, ничего не пересчитывая руками. */
[data-state="loading"] .chk-marker {
  background: var(--color-accent-400);
  animation: om-pulse 1.3s ease-in-out infinite;
}
[data-state="ok"] .chk-marker { border: 1px solid var(--color-neutral-500); }
[data-state="risk"] .chk-marker { background: var(--color-accent); }
[data-state="risk"] .chk-title { color: var(--color-accent-800); }

/* Риск должен читаться с первого взгляда, а не угадываться по оттенку.
   Метка — сплошной заливкой на палитре основной кнопки: тот же язык, что у
   «открыт» на странице портов, контраст в обеих темах проверен (≥5:1).
   Саму карточку не подсвечиваем (решение пользователя): залитого маркера,
   цветного заголовка и метки достаточно, а тон на всю карточку шумел. */
[data-state="risk"] .chk-tag .tag {
  background: var(--btn-primary-bg); color: var(--btn-primary-ink);
  font-weight: 600;
}
[data-state="nodata"] .chk-marker,
[data-state="error"]  .chk-marker,
[data-state="notrun"] .chk-marker { border: 1px dashed var(--color-neutral-500); }


/* ── 11. Сравнение источников ────────────────────────────────────────────
   Шесть строк в порядке CONTRACT §4. Расхождение — акцентная черта слева,
   отсутствующее значение — прочерк приглушённым цветом. */
.src { padding-top: 6px; padding-bottom: 28px; }
.src-box { padding: 16px 18px 12px; margin-top: 10px; }
.src-table { font-size: 13px; }
.src-table td.is-empty { color: var(--muted); }
.src-table tr.is-conflict td:first-child {
  padding-left: 10px;
  border-left: 3px solid var(--color-accent);
  color: var(--color-accent-800);
}
.src-table tr.is-conflict td.src-country { color: var(--color-accent-800); }

.src-foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.src-foot-dot { width: 8px; height: 8px; flex: none; border: 1px solid var(--color-neutral-500); }
.src-foot.is-conflict .src-foot-dot { border: 0; background: var(--color-accent); }
.src-foot-text { font-size: 13px; color: var(--muted); }
.src-foot.is-conflict .src-foot-text { color: inherit; }   /* расхождение важно — не приглушаем */


/* ── 12. Адаптив ─────────────────────────────────────────────────────────
   База — 390px. Дальше три ступени: 620 (карточки в две колонки),
   900 (десктопная шапка, двухколоночный герой, полоса в строку),
   1180 (сетка проверок repeat(4,1fr), как в turn2-6b). */

/* 12.1 · Планшет: проверки становятся карточками. */
@media (min-width: 620px) {
  .checks-report { display: inline-flex; }

  .group-title { margin: 0 0 10px; }
  .group {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
    margin-bottom: 26px;
  }
  .chk {
    display: flex; flex-direction: column;
    padding: 14px 16px;
    border: 1px solid var(--color-divider);
  }
  .chk-head { display: flex; align-items: flex-start; gap: 8px; }
  .chk-title { flex: 1; font-size: 17px; line-height: 1.15; }
  .chk-tag { justify-self: auto; }
  .chk-body { margin-left: 19px; text-align: left; }
  .chk-skel { margin-left: 0; }
  .chk-btn { min-height: 34px; margin-left: 0; }   /* тело карточки здесь выровнено влево */

  /* Реквизиты: две колонки по четыре строки, заполнение по столбцам. */
  .facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(4, auto);
    grid-auto-flow: column;
    gap: 0 44px;
  }

  .map-box { height: 280px; }
}

/* 12.2 · Десктоп. */
@media (min-width: 900px) {
  .wrap { padding: 0 var(--page-edge-lg); }

  .nav {
    max-width: var(--page-max); margin: 0 auto;
    gap: 28px; padding: 18px var(--page-edge-lg);
    border-bottom: 0;
  }
  .nav-brand { font-size: 18px; }
  .nav-links { display: flex; }
  .nav-burger { display: none; }
  .nav-cta { display: inline-flex; }
  .nav-btn { width: 36px; height: 36px; }
  .nav-lang { height: 36px; }
  .nav-lang-menu a { min-height: 36px; font-size: 14px; }

  .promo { display: none; }

  /* Герой: контент слева, карта справа фиксированной колонкой 440px.
     minmax(0,…) у первой колонки — чтобы длинный IP не распирал сетку. */
  .hero {
    grid-template-columns: minmax(0, 1fr) var(--aside);
    gap: 48px;
    padding-top: 22px; padding-bottom: 32px;
  }
  /* Возвращаем строку «адрес + копирование» как в turn2-6b; VPN только
     мобильный, поэтому «Сменить IP» на десктопе не показываем. */
  .hero-ip-row { flex-wrap: nowrap; align-items: flex-end; gap: 16px; margin-top: 10px; }
  .hero-ip { flex: 0 1 auto; font-size: clamp(44px, 5vw, 72px); line-height: .86; }
  .hero-change { display: none; }
  .hero-ip.is-long { font-size: clamp(20px, 2.4vw, 40px); line-height: 1.05; }
  .hero-copy { width: 36px; height: 36px; margin-bottom: 12px; }
  .hero-flag { width: 42px; height: 42px; }
  .hero-geo { gap: 14px; margin-top: 18px; }
  .hero-place { font-size: 32px; }
  .hero-agree { flex: 0 0 auto; margin-top: 0; margin-left: auto; }
  .hero-agree .link-dotted { margin-left: 0; }

  .facts { margin-top: 22px; }

  .map-box { height: 340px; }
  .map-caption { margin: 10px 0 0; }
  .map-place { font-size: 22px; }
  .map-radius { font-size: 12px; }

  /* Полоса в строку: цифра — шкала — кнопка; подсказка под шкалой. */
  .bar-in {
    max-width: var(--page-max); margin: 0 auto;
    padding: 22px var(--page-edge-lg);
    grid-template-columns: minmax(230px, 250px) minmax(0, 1fr) auto;
    grid-template-areas:
      "score meter  action"
      "score hint   action";
    column-gap: 40px;
    align-items: center;
  }
  .bar-score { align-content: center; }
  .bar-num { font-size: 62px; }
  .bar-pct { font-size: 25px; }
  .bar-label { flex: 0 0 100%; margin-top: 6px; margin-left: 0; }  /* уходит под цифру */
  .bar-track { margin-top: 0; }
  .bar-counters { font-size: 14px; }
  .bar-hint { margin-top: 4px; font-size: 12.5px; opacity: .6; }
  .bar-action {
    width: auto; min-height: 46px; margin-top: 0;
    padding: 0 22px; align-self: center;
  }

  .checks { padding-top: 30px; padding-bottom: 44px; }
  .checks-head { margin-bottom: 16px; }
  .group { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  .src { padding-bottom: 44px; }
}

/* 12.3 · Широкий десктоп: четыре колонки проверок и крупный IP, как в turn2-6b. */
@media (min-width: 1180px) {
  /* Четыре, не пять. Пробовали пять (2026-09-17): при контентной колонке
     1280px карточка получается 229px, а заголовок с меткой требует ~255 —
     слова рвались посередине («Язык браузер а»). Одна карточка во втором
     ряду у групп из пяти — меньшее зло, чем сломанные заголовки. */
  .group { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .hero-ip { font-size: clamp(72px, 6.7vw, 96px); }
  .hero-ip.is-long { font-size: clamp(34px, 3.1vw, 44px); }
}


/* ── Страницы проверок: /ports и /dns-leak ──────────────────────────────
   Концепция «единая колонка». Классы pv-* общие для обеих страниц: у DNS те
   же колонка, сводка, таблица с маркерами и чипами состояний, легенда.
   Начиналась как страница портов, поэтому у чипов есть и ports-имена (их
   ставит JS портов), и нейтральные pv-chip: is-alert / is-calm / is-dim. Всё содержимое — один столбец шириной 720px,
   и у каждого блока (надзаголовок, заголовок, вводный текст, строка адреса,
   кнопка со статусом, сводка, таблица, пояснения, мелкий шрифт) общий левый
   и общий правый край. Отступов внутри блоков по горизонтали нет: край
   задают линейки во всю ширину колонки, а не рамки с полями, — иначе текст
   в рамке снова оказался бы уже или шире соседнего абзаца.

   Почему 720: вводный текст на десктопе набран 18px — это около 80 знаков
   в строке, верхняя граница удобного чтения; таблица из трёх столбцов
   при этой ширине не разъезжается. Шире — абзац читается хуже, уже —
   метки состояний прижимаются к названиям служб.

   Почему по центру: у страницы одна задача и нет второй колонки. Колонка,
   прижатая к левому краю сетки, оставляла на 1440px пустыми 460px справа
   и выглядела недоделанной. По центру она симметрична шапке — логотип
   слева и кнопка VPN справа отстоят от неё одинаково, и отступ 240px
   читается как намеренный, а не как промах. Центрируем уже от 620px:
   до 752px колонка и так заполняет .wrap целиком, а на планшете в портрете
   (752–899px) прижатая влево колонка оставляла справа до 150px пустоты —
   та же ошибка в миниатюре. Сдвиг относительно логотипа шапки здесь
   не важнее симметрии, как и на десктопе.

   Внутренняя сетка: дорожка ключей --pv-key. По ней выровнены значение
   адреса, колонка «Служба», текст пояснений и мелкого шрифта — вторая
   вертикаль колонки после левого края.

   Состояние порта JS пишет в ячейку как <span class="ports-state is-…">
   и ставит data-state на строку; вид меткой задаём здесь, классы JS
   не меняются. Сводку над таблицей JS заполняет по [data-ports-count]
   и ставит data-state на [data-ports-summary]. */

.pv-col {
  --pv-col: 720px;
  --pv-key: 112px;      /* узкий экран: первая колонка таблицы */
  --pv-state: 108px;    /* колонка состояния — под метку «фильтруется» */

  max-width: var(--pv-col);
  padding-top: 8px;
  padding-bottom: 40px;
}

/* Ссылка «назад». На узком экране — палец: высота 44px, а пунктир
   рисуем у текста, а не у нижнего края увеличенной области. */
.pv-back {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 13px; color: var(--color-accent-700); text-decoration: none;
}
.pv-back span { border-bottom: 1px dotted currentColor; }

.pv-title { margin: 6px 0 12px; font-size: 34px; }
.pv-lead {
  margin: 0 0 24px;
  font-size: 15px; line-height: 1.55; text-wrap: pretty;
}

/* ── Адрес и запуск ── */
.pv-target {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px;
  padding: 12px 0;
  border-top: 1px solid var(--color-divider);
  border-bottom: 1px solid var(--hairline);
}
.pv-ip { font-size: 15px; overflow-wrap: anywhere; }   /* IPv6 переносится только если не влезает */

.pv-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  padding-top: 16px;
}
.pv-run { width: 100%; min-height: 48px; padding: 0 20px; font-size: 15px; }
.pv-status { flex: 1 1 100%; min-width: 0; font-size: 14px; color: var(--muted); text-wrap: pretty; }
/* accent-700, а не 800: в тёмной теме 800 почти белый и не отличается от текста. */
.pv-status.is-risk { color: var(--color-accent-700); }

/* Скан выключен или адрес не публичный: сообщение вместо кнопки.
   Рамка стоит по краям колонки, поле внутри — как у любой плашки. */
.pv-notice {
  margin: 16px 0 0; padding: 12px 14px;
  font-size: 14px; border: 1px solid var(--color-divider);
}

/* ── Сводка ──
   Одна строка над таблицей. До скана — прочерки; открытые порты
   подсвечиваются акцентом, только когда они есть (data-state="risk"). */
.pv-results { margin-top: 32px; }
.pv-summary {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 4px 16px; padding-bottom: 10px;
}
.pv-counts { display: flex; flex-wrap: wrap; gap: 0 8px; margin: 0; font-size: 14px; }
.pv-count { white-space: nowrap; color: var(--muted); }
.pv-count b {
  margin-left: 3px; font-weight: 600; color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
.pv-sep { color: var(--muted); }
.pv-summary[data-state="idle"] .pv-count b { font-weight: 400; color: var(--muted); }   /* прочерк не должен кричать */
.pv-summary[data-state="risk"] .pv-count-open,
.pv-summary[data-state="risk"] .pv-count-open b { color: var(--color-accent-700); }
.pv-summary[data-state="pending"] .pv-count b { animation: om-pulse 1.2s ease-in-out infinite; }
.pv-total { display: none; font-size: 13px; color: var(--muted); }

/* ── Таблица ──
   table-layout: fixed — ширины колонок не зависят от текста, поэтому смена
   состояния (другая метка, другой цвет) ничего в строке не двигает.
   Горизонтальных полей у крайних ячеек нет: текст стоит ровно по краям
   колонки, как абзацы над и под таблицей. */
.pv-table {
  table-layout: fixed;
  font-size: 14px;
  border-top: 1px solid color-mix(in srgb, var(--color-text) 70%, transparent);
}
.pv-table th { padding: 8px 0; }
.pv-table th:first-child { width: var(--pv-key); }
.pv-table th:last-child { width: var(--pv-state); text-align: right; }
.pv-table td {
  padding: 9px 0; vertical-align: middle;
  border-bottom: 1px solid var(--hairline);
}
.pv-table tbody tr:last-child td { border-bottom-color: var(--color-divider); }
.pv-table tbody tr:hover { background: transparent; }   /* строки не кликаются */

.pv-port { white-space: nowrap; }
.pv-port .mono { font-size: 13px; }
.pv-svc { padding-right: 8px; overflow-wrap: anywhere; }
.pv-state { text-align: right; }

/* Маркер 9×9 — тот же язык, что у карточек проверок на главной:
   заливка — внимание, контур — спокойно, пунктир — данных нет.
   Он есть в каждой строке, поэтому выделение открытого порта
   не сдвигает текст, в отличие от прежней черты с padding-left. */
.pv-mark {
  display: inline-block; width: 9px; height: 9px; margin-right: 9px;
  vertical-align: 0;
  border: 1px dashed var(--color-neutral-500);
}
.pv-table tr[data-state="pending"] .pv-mark {
  border: 1px solid var(--color-accent-400); background: var(--color-accent-400);
  animation: om-pulse 1.2s ease-in-out infinite;
}
.pv-table tr[data-state="open"] .pv-mark,
.pv-table tr[data-state="foreign"] .pv-mark { border: 1px solid var(--color-accent); background: var(--color-accent); }
.pv-table tr[data-state="closed"] .pv-mark,
.pv-table tr[data-state="same"] .pv-mark { border: 1px solid var(--color-neutral-500); }
.pv-table tr[data-state="filtered"] .pv-mark,
.pv-table tr[data-state="unknown"] .pv-mark { border: 1px dashed var(--color-neutral-500); }
.pv-table tr[data-state="open"] .pv-port,
.pv-table tr[data-state="open"] .pv-svc,
.pv-table tr[data-state="foreign"] .pv-port,
.pv-table tr[data-state="foreign"] .pv-svc { color: var(--color-accent-700); }
/* Пустая таблица (до теста или когда он недоступен): одна ячейка на всю ширину. */
.pv-table td.pv-empty { padding: 12px 0; }
/* Две строки реквизитов подряд — без двойной линейки между ними. */
.pv-target + .pv-target { border-top: 0; }

/* Метка состояния — аналог .tag: квадратная, 12px, с полями 2×8.
   Правила перекрывают всё, что могло остаться от прежних .ports-state.
   «Не проверялся» и «проверяем…» — без рамки: двенадцать пунктирных
   рамок подряд рядом с пунктирными маркерами только шумят, а «данных нет»
   маркер уже сказал. Боковых полей у безрамочной метки тоже нет: иначе
   её текст отстоял бы от края колонки на 9px, а в пояснениях — от общего
   левого края. Высота у всех состояний одна (рамка сверху и снизу
   прозрачная), так что строка таблицы от смены состояния не прыгает. */
.pv-col .ports-state,
.pv-col .pv-chip {
  display: inline-flex; align-items: center;
  padding: 2px 0;
  font-size: 12px; line-height: 1.35; letter-spacing: .02em; font-weight: 400;
  white-space: nowrap;
  color: var(--muted); background: transparent;
  border: 1px solid transparent; border-inline-width: 0;
  animation: none;
}
.pv-col .ports-state.is-open,
.pv-col .ports-state.is-closed,
.pv-col .ports-state.is-filtered,
.pv-col .pv-chip.is-alert,
.pv-col .pv-chip.is-calm,
.pv-col .pv-chip.is-dim { padding-inline: 8px; }
.pv-col .ports-state.is-pending,
.pv-col .pv-chip.is-pending { animation: om-pulse 1.2s ease-in-out infinite; }
.pv-col .ports-state.is-open,
.pv-col .pv-chip.is-alert {
  color: var(--btn-primary-ink); background: var(--btn-primary-bg);
  border: 1px solid var(--btn-primary-bg); font-weight: 600;
}
.pv-col .ports-state.is-closed,
.pv-col .pv-chip.is-calm {
  color: var(--color-text); background: var(--color-neutral-200);
  border: 1px solid var(--color-neutral-200);
}
.pv-col .ports-state.is-filtered,
.pv-col .pv-chip.is-dim { color: var(--muted); border: 1px dashed var(--color-neutral-500); }   /* пунктир на neutral-500: контур на --color-divider был почти невидим (1,4–1,8:1) */

/* ── Пояснения ── */
.pv-h2 { margin: 44px 0 10px; font-size: 20px; }
.pv-legend { margin: 0; border-top: 1px solid var(--color-divider); }
.pv-legend > div {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px;
  padding: 12px 0; border-bottom: 1px solid var(--hairline);
}
.pv-legend dt { margin: 0; }
.pv-legend dd {
  margin: 0; font-size: 14px; line-height: 1.55; text-wrap: pretty;
  color: color-mix(in srgb, var(--color-text) 82%, transparent);
}

/* ── Мелкий шрифт ── ключ — та же дорожка, что у пояснений. */
.pv-fine {
  display: grid; gap: 8px; margin: 28px 0 0;
  font-size: 12.5px; line-height: 1.55; color: var(--muted);
}
.pv-fine > div { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; }
.pv-fine dt {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
}
.pv-fine dd { margin: 0; text-wrap: pretty; }

/* Планшет: ключи уходят в дорожку слева, кнопка — по ширине текста,
   колонка — по центру, как только .wrap становится шире неё. */
@media (min-width: 620px) {
  .pv-col { --pv-key: 184px; --pv-state: 132px; margin-inline: auto; }

  .pv-back { min-height: 0; margin: 12px 0 14px; }

  .pv-target,
  .pv-legend > div,
  .pv-fine > div {
    grid-template-columns: var(--pv-key) minmax(0, 1fr);
    gap: 0; align-items: baseline;
  }
  .pv-run { width: auto; min-height: 42px; }
  .pv-status { flex: 1 1 0; }
  .pv-total { display: inline; }
}

/* Десктоп: больше воздуха сверху и снизу, крупнее заголовок и ввод. */
@media (min-width: 900px) {
  .pv-col { padding-top: 22px; padding-bottom: 56px; }
  .pv-title { font-size: 42px; margin-bottom: 14px; }
  .pv-lead { margin-bottom: 30px; font-size: 18px; line-height: 1.55; }
  .pv-results { margin-top: 40px; }
  .pv-h2 { margin-top: 52px; font-size: 22px; }
}

@media (prefers-reduced-motion: reduce) {
  /* Без пульсации «проверяем…» не должно выглядеть погасшим. */
  .pv-col .ports-state.is-pending,
  .pv-col .pv-chip.is-pending,
  .pv-table tr[data-state="pending"] .pv-mark,
  .pv-summary[data-state="pending"] .pv-count b { animation: none; opacity: .6; }
}


/* ── 13. prefers-reduced-motion ──────────────────────────────────────────
   Пульсацию и вращение выключаем, но скелеты обязаны остаться видимыми:
   без анимации om-pulse замер бы на opacity .35. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .chk-marker, .chk-skel, .fact-skel span, .bar-dot, .bar-action .ico-spin { animation: none; }
  /* Меню появляется сразу, без выезда и проявления. */
  .nav.is-menu-open .nav-links, .nav.is-menu-open::after { animation: none; }
  /* Подсветку якоря не анимируем, но и не убираем: заливка держится, пока
     её не снимет JS, — иначе переход остался бы вовсе без подтверждения. */
  .is-flash {
    animation: none;
    background: color-mix(in srgb, var(--color-accent) 14%, transparent);
  }
  .chk-skel, .fact-skel span { opacity: .6; }
  .bar-fill { transition: none; }
}
