﻿/* Нагульщик кук — оформление личного кабинета.
   Язык интерфейса: Apple. Матовые слои поверх контента, системный шрифт,
   пружинные кривые вместо линейных, тёмная тема наравне со светлой.
   Всё, что задаёт вид и движение, живёт здесь — в разметке ни одного стиля. */

/* ------------------------------------------------------------ токены */
:root {
  color-scheme: light dark;

  --bg:            #f2f2f7;
  --bg-deep:       #e8e8ed;
  --surface:       rgba(255, 255, 255, 0.72);
  --surface-solid: #ffffff;
  --surface-2:     rgba(255, 255, 255, 0.55);
  --hairline:      rgba(0, 0, 0, 0.10);
  --hairline-soft: rgba(0, 0, 0, 0.06);

  --text:      #1d1d1f;
  /* 🔴 Вторичный и третичный серый ПОДНЯТЫ по замеру контраста:
     было #6e6e73 (4.54:1, впритык) и #8e8e93 (2.92:1, вдвое ниже нормы).
     Третичным набраны подписи под числами на плитках и в карточках
     профилей — кегль 11–12px, и именно они читались плохо. */
  --text-2:    #5f5f64;
  --text-3:    #67676c;

  /* На фоне страницы #f2f2f7 прежний #0071e3 давал 4.21:1 — ниже нормы
     для пунктов меню (14px). #0062cc даёт 5.20:1 и остаётся синим Apple. */
  --accent:      #0062cc;
  --accent-soft: rgba(0, 98, 204, 0.12);
  --green:       #1c8c3c;
  --green-soft:  rgba(28, 140, 60, 0.12);
  --amber:       #9a6400;
  --amber-soft:  rgba(180, 120, 0, 0.14);
  --red:         #c92a2a;
  --red-soft:    rgba(201, 42, 42, 0.12);

  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-xl: 24px;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.05), 0 4px 14px rgba(0, 0, 0, 0.06);
  --shadow-2: 0 8px 40px rgba(0, 0, 0, 0.14);
  --switch-off:  rgba(120, 120, 128, 0.24);
  /* Заливка плашек (чипы, бейджи, жёлоба) — системный fill Apple. Отдельно от
     --surface-2: тот белый и на белой карточке пропадает, он только для hover. */
  --fill: rgba(120, 120, 128, 0.12);

  /* Пружина без перелёта — критически задемпфированная, отклик ~0.4 с.
     Перелёт оставлен только там, где движению предшествует «бросок». */
  --spring:      cubic-bezier(0.32, 0.72, 0, 1);
  --spring-soft: cubic-bezier(0.4, 0, 0.2, 1);

  --sidebar-w: 232px;
  --log-w: 340px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:            #000000;
    --bg-deep:       #0a0a0c;
    --surface:       rgba(28, 28, 30, 0.72);
    --surface-solid: #1c1c1e;
    --surface-2:     rgba(44, 44, 46, 0.55);
    --hairline:      rgba(255, 255, 255, 0.12);
    --hairline-soft: rgba(255, 255, 255, 0.07);

    --text:   #f5f5f7;
    --text-2: #a1a1a6;
    --text-3: #8a8a8e;

    --accent:      #0a84ff;
    --accent-soft: rgba(10, 132, 255, 0.20);
    --green:       #30d158;
    --green-soft:  rgba(48, 209, 88, 0.18);
    --amber:       #ffd60a;
    --amber-soft:  rgba(255, 214, 10, 0.16);
    --red:         #ff453a;
    --red-soft:    rgba(255, 69, 58, 0.18);

    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 28px rgba(0, 0, 0, 0.45);
    --shadow-2: 0 12px 48px rgba(0, 0, 0, 0.6);
    --switch-off: rgba(120, 120, 128, 0.42);
    --fill: rgba(120, 120, 128, 0.26);
  }
}

/* Ручной переключатель темы должен побеждать системный в обе стороны. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f2f2f7; --bg-deep: #e8e8ed;
  --surface: rgba(255,255,255,.72); --surface-solid: #fff; --surface-2: rgba(255,255,255,.55);
  --hairline: rgba(0,0,0,.10); --hairline-soft: rgba(0,0,0,.06);
  --text: #1d1d1f; --text-2: #5f5f64; --text-3: #67676c;
  --accent: #0062cc; --accent-soft: rgba(0,98,204,.12);
  --green: #1c8c3c; --green-soft: rgba(28,140,60,.12);
  --amber: #9a6400; --amber-soft: rgba(180,120,0,.14);
  --red: #c92a2a; --red-soft: rgba(201,42,42,.12);
  --shadow-1: 0 1px 2px rgba(0,0,0,.05), 0 4px 14px rgba(0,0,0,.06);
  --shadow-2: 0 8px 40px rgba(0,0,0,.14); --switch-off: rgba(120,120,128,.24); --fill: rgba(120,120,128,.12);
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #000; --bg-deep: #0a0a0c;
  --surface: rgba(28,28,30,.72); --surface-solid: #1c1c1e; --surface-2: rgba(44,44,46,.55);
  --hairline: rgba(255,255,255,.12); --hairline-soft: rgba(255,255,255,.07);
  --text: #f5f5f7; --text-2: #a1a1a6; --text-3: #8a8a8e;
  --accent: #0a84ff; --accent-soft: rgba(10,132,255,.20);
  --green: #30d158; --green-soft: rgba(48,209,88,.18);
  --amber: #ffd60a; --amber-soft: rgba(255,214,10,.16);
  --red: #ff453a; --red-soft: rgba(255,69,58,.18);
  --shadow-1: 0 1px 2px rgba(0,0,0,.5), 0 8px 28px rgba(0,0,0,.45);
  --shadow-2: 0 12px 48px rgba(0,0,0,.6); --switch-off: rgba(120,120,128,.42); --fill: rgba(120,120,128,.26);
}

/* --------------------------------------------------------------- основа */
* { box-sizing: border-box; }

/* Любой класс с собственным `display` (а у .btn это inline-flex) перебивает
   браузерное правило для `hidden` — и «спрятанная» кнопка остаётся на виду. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
        system-ui, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-optical-sizing: auto;
}

h1, h2, h3 { margin: 0; font-weight: 600; }
/* Крупный кегль требует отрицательного трекинга — иначе буквы «разъезжаются». */
h1 { font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem); line-height: 1.12; letter-spacing: -0.021em; }
h2 { font-size: 1.0625rem; line-height: 1.25; letter-spacing: -0.012em; }
h3 { font-size: 0.9375rem; line-height: 1.3; letter-spacing: -0.008em; }
p  { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.muted { color: var(--text-2); font-size: 0.8125rem; }
.mono { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}

.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 100;
  padding: 10px 16px; border-radius: var(--r-md);
  background: var(--surface-solid); color: var(--text);
  box-shadow: var(--shadow-2);
  transform: translateY(-160%);
  transition: transform .28s var(--spring);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ------------------------------------------------------------- каркас */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--log-w);
  min-height: 100vh;
}

/* ------------------------------------------------------------ сайдбар */
.sidebar {
  position: sticky; top: 0; align-self: start;
  height: 100vh;
  display: flex; flex-direction: column; gap: 4px;
  padding: 18px 12px 14px;
  background: var(--surface);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border-right: 1px solid var(--hairline);
}

.brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px 18px; }
.brand-mark {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border-radius: 9px;
  background: linear-gradient(160deg, var(--accent), #6c4bff);
  color: #fff; font-size: 12px; font-weight: 700; letter-spacing: .02em;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.brand-text strong { font-size: 0.875rem; letter-spacing: -0.008em; }
.brand-text small { color: var(--text-3); font-size: 0.6875rem; }

.nav-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.nav-list a {
  display: flex; align-items: center; gap: 10px;
  min-height: 38px; padding: 0 10px;
  border-radius: 9px;
  color: var(--text); font-size: 0.875rem; text-decoration: none;
  transition: background-color .18s var(--spring-soft), transform .18s var(--spring);
}
.nav-list a:hover { background: var(--surface-2); text-decoration: none; }
.nav-list a:active { transform: scale(0.98); }
.nav-list a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
/* Иконки меню. Штрих и его атрибуты заданы один раз здесь, а не в каждом svg:
   один вес линии на все семь — иначе набор выглядит собранным с миру по нитке.
   Скруглённые концы и стыки — это язык SF Symbols, к нему всё и приводится. */
.nav-ico { width: 20px; height: 20px; flex: none; display: grid; place-items: center; }
.nav-ico svg {
  width: 20px; height: 20px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  /* На ступень светлее подписи, но не блёклые: знак и слово читаются парой. */
  color: var(--text-2);
  transition: color .18s var(--spring-soft);
}
.nav-list a:hover .nav-ico svg { color: var(--text); }
/* У активного пункта иконка берёт цвет ссылки — плашка, текст и знак заодно. */
.nav-list a[aria-current="page"] .nav-ico svg { color: currentColor; }
.nav-badge {
  margin-left: auto; padding: 1px 7px; border-radius: 20px;
  background: var(--fill); color: var(--text-2);
  font-size: 0.6875rem; font-variant-numeric: tabular-nums;
}

.sidebar-foot { margin-top: auto; display: grid; gap: 8px; padding: 12px 6px 0; border-top: 1px solid var(--hairline-soft); }
.threads { display: flex; align-items: baseline; justify-content: space-between; font-size: 0.75rem; color: var(--text-2); }
.threads strong { font-size: 0.8125rem; color: var(--text); font-variant-numeric: tabular-nums; }
.conn { display: flex; align-items: center; gap: 7px; font-size: 0.75rem; color: var(--text-2); }
.conn .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-3); flex: none; }
.conn[data-state="ok"] .dot { background: var(--green); }
.conn[data-state="warn"] .dot { background: var(--amber); }
.conn[data-state="bad"] .dot { background: var(--red); }

/* ------------------------------------------------------------ контент */
.content { min-width: 0; padding: 0 26px 60px; }
.content:focus { outline: none; }

.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 20px;
  padding: 22px 0 14px;
  /* 🔴 Настоящее стекло: под эту полосу контент реально уезжает, и без
     размытия шапка таблицы накладывалась на подзаголовок страницы. */
  /* Плотность подобрана замером, а не на глаз: при 80% и маске с 76% из-под
     полосы читались призраки строки «НАЗВАНИЕ ДОМЕН КАТЕГОРИЯ» — маска
     обнуляла материал раньше, чем текст успевал уйти. Держим материал почти
     до самого низа и растворяем в последних 8%. */
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  mask-image: linear-gradient(to bottom, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 92%, transparent);
  /* Кромка вместо жёсткой линейки: контент растворяется под панелью. */
}
.topbar-title { min-width: 0; }
.topbar-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
/* Поля в формах тянутся на всю ширину — в панели действий это распирало её и
   сбрасывало кнопку на второй этаж при полностью свободном месте справа. */
.topbar-actions input { width: 236px; }

/* ------------------------------------------- меню «ещё» в панели действий
   Пять равнозначных кнопок в ряд — не панель, а список всего, что умеет
   раздел. Наружу выходит главный путь, остальное живёт на слой глубже. */
.menu-wrap { position: relative; display: inline-flex; }
.menu-wrap > .btn[aria-expanded="true"] { background: var(--surface-2); }
.menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  /* Ширины хватает самому длинному пункту: перенос в две строки превращает
     список действий в абзац. */
  min-width: 262px; padding: 5px;
  display: grid; gap: 2px;
  border: 1px solid var(--hairline); border-radius: 14px;
  /* Плотнее обычной карточки: сквозь меню не должно читаться содержимое под
     ним. Размытие остаётся — это материал, а не глухая плита. */
  background: color-mix(in srgb, var(--surface-solid) 90%, transparent);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  box-shadow: var(--shadow-2);
  transform-origin: top left;  /* растёт из своей кнопки, а не из ниоткуда */
  animation: menu-in .28s var(--spring) both;
}
.menu[hidden] { display: none; }
.menu .btn {
  width: 100%; justify-content: flex-start; text-align: left;
  min-height: 33px; padding: 0 10px;
  white-space: nowrap;
  border-color: transparent; background: transparent;
  font-weight: 400;
}
.menu .btn:hover { --btn-bg: var(--surface-2); }
.menu .btn:active { transform: scale(.985); }
@keyframes menu-in {
  from { opacity: 0; transform: scale(.94) translateY(-5px); }
  to   { opacity: 1; transform: none; }
}

/* 🔴 `minmax(0, 1fr)`, а не просто `grid`. У элемента сетки размер по
   умолчанию `min-width: auto`, то есть он НЕ сжимается ниже своего
   содержимого. Из-за этого широкая таблица распирала карточку, карточка —
   всю страницу, и на узком экране кабинет ехал вбок: 922px при окне 430.
   Причём у тела карточки своя прокрутка (`.scroll-x`) и она обязана была
   спасти — но не спасала, потому что распирало снаружи, а не внутри. */
.view { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }
.view > * { min-width: 0; }
.card, .card-head, .card-body { min-width: 0; }

/* ------------------------------------------------------------- кнопки */
.btn {
  --btn-bg: var(--surface-solid);
  --btn-fg: var(--text);
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 34px; padding: 0 14px;
  border: 1px solid var(--hairline);
  border-radius: 9px;
  background: var(--btn-bg); color: var(--btn-fg);
  font: inherit; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer;
  transition: transform .12s var(--spring), background-color .18s var(--spring-soft),
              opacity .18s var(--spring-soft);
}
/* 🔴 Меняем ПЕРЕМЕННУЮ, а не само свойство. Было `background: var(--surface-2)`
   — прямое объявление перебивало `--btn-bg`, который выставляют `.btn-primary`
   и `.btn-danger`, и при наведении СИНЯЯ кнопка становилась белой, сохраняя
   белый текст. То есть «Запустить нагул», «Сохранить», «Создать», «Войти»
   пропадали под курсором во всём кабинете.
   Поймано снимком страницы входа: кнопки на нём просто не было — курсор после
   клика остался на ней. Ни линтер, ни замер геометрии такого не видят:
   элемент на месте, размер верный, «просто» невидимый. */
.btn:hover { --btn-bg: var(--surface-2); }
.btn:active { transform: scale(0.97); }          /* отклик на нажатие, не на отпускание */
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; }
.btn-sm { min-height: 28px; padding: 0 10px; font-size: 0.75rem; }

.btn-primary { --btn-bg: var(--accent); --btn-fg: #fff; border-color: transparent; }
.btn-primary:hover { --btn-bg: color-mix(in oklab, var(--accent) 88%, #000); }
.btn-ghost { --btn-bg: transparent; border-color: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--surface-2); color: var(--text); }
.btn-danger { --btn-bg: var(--red); --btn-fg: #fff; border-color: transparent; }
.btn-danger-ghost { --btn-bg: transparent; color: var(--red); border-color: transparent; }
.btn-danger-ghost:hover { background: var(--red-soft); }

/* Иконочные кнопки: видимая часть маленькая, зона попадания — нет. */
.icon-btn {
  position: relative; width: 30px; height: 30px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--hairline); border-radius: 8px;
  background: var(--surface-solid); color: var(--text-2);
  cursor: pointer;
  transition: transform .12s var(--spring), background-color .18s var(--spring-soft);
}
.icon-btn::after { content: ""; position: absolute; inset: -7px; }
/* Кнопка во flex-строке сжимается по умолчанию: в строке прокси «✕» ужимался
   до 13 px и переставал попадаться под курсор. Ширина кнопки не обсуждается. */
.btn, .icon-btn { flex: none; }
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn:active { transform: scale(0.94); }

/* ------------------------------------------------------------- карточки */
.card {
  /* Стекла тут нет намеренно: карточка — базовый слой, под ней не проходит
     ничего, и размытие было бы платой процессору за невидимое. */
  background: var(--surface-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
}
.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 18px 12px;
}
.card-body { padding: 0 18px 18px; }
.card-body.tight { padding: 0; }

.grid-tiles {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
}
.tile {
  padding: 15px 16px;
  background: var(--surface-solid);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}
.tile-label { color: var(--text-2); font-size: 0.75rem; letter-spacing: .003em; }
.tile-value {
  margin-top: 5px;
  font-size: 1.625rem; font-weight: 600; line-height: 1.05;
  letter-spacing: -0.019em;
  font-variant-numeric: tabular-nums;
}
.tile-sub { margin-top: 3px; color: var(--text-3); font-size: 0.75rem; }

/* --------------------------------------------------------------- списки */
.rows { display: grid; }
.row {
  display: grid; align-items: center; gap: 12px;
  padding: 11px 18px;
  border-top: 1px solid var(--hairline-soft);
  font-size: 0.8125rem;
}
.rows .row:first-child { border-top: none; }
.row-head {
  /* Без капса и разрядки: в списках Apple (Finder, Mail, Numbers) шапка —
     обычный регистр вторичным цветом. Капс вразрядку — Bootstrap. */
  color: var(--text-3); font-size: 0.75rem; font-weight: 600;
  letter-spacing: .006em;
  padding-top: 8px; padding-bottom: 8px;
  border-top: none;
}
.row-link { cursor: pointer; transition: background-color .16s var(--spring-soft); }
.row-link:hover { background: var(--surface-2); }
.row .ell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .num { font-variant-numeric: tabular-nums; color: var(--text-2); }

/* 🔴 Сетка списка задаётся ОДИН раз на `.rows`, а строки её наследуют.
   Раньше её писали инлайном дважды — отдельно шапке, отдельно строке, — и
   копии разошлись: у шапки последний столбец стоял `auto` и схлопывался в
   ноль (ячейка-то пустая), у строки — жёсткие 96px. Итог на «Прокси»:
   подписи разъехались с колонками, а трём кнопкам не хватило 96px, и при
   `justify-content:flex-end` переполнение полезло ВЛЕВО — «Сессии» закрыла
   собой «83 мс» от значения задержки. На снимке это выглядело как обрезанное
   число, хотя обрезки не было вовсе.

   🔴 И второе, менее очевидное: КАЖДАЯ СТРОКА — ОТДЕЛЬНАЯ СЕТКА. Общий
   `--cols` этого не меняет: `fr` и `max-content` считаются внутри каждой
   строки по её собственному содержимому. У шапки ячейка действий пуста,
   `max-content` там ноль — и освободившееся место уходит соседним `fr`,
   отчего подписи снова разъезжаются со значениями. Одного общего объявления
   мало.
   Значит все негибкие столбцы задаём ТОЧНЫМИ пикселями: тогда `fr` делят
   одинаковый остаток и строки выстраиваются. Ширина колонки действий
   (`--act`) не угадана, а замерена по трём кнопкам — 188px, взято 196
   с запасом на «Проверить» в состоянии проверки. */
.rows[style*="--cols"] > .row { grid-template-columns: var(--cols); }
/* Ячейка действий: кнопки не жмутся и не наезжают на соседей. */
.row .row-actions {
  display: flex; gap: 6px; justify-content: flex-end; align-items: center;
}
.row .row-actions > * { flex: none; }

/* Диапазон «от — до»: два поля в одной строке под общей подписью.
   Раньше это были два самостоятельных поля во всю ширину друг под другом,
   и связь между ними держалась только на словах «, от» и «до» в подписях. */
.range-row { display: flex; align-items: center; gap: 8px; }
.range-row input { flex: 1 1 0; min-width: 0; }
.range-dash { color: var(--text-2); flex: none; }

/* «Тема» и «Выйти» в подвале — в одну строку и одной ширины: две кнопки
   разного размера друг под другом читались бы как разные по важности. */
/* Стадия профиля: слово + пояснение + полоса до порога.
   Цвет несёт смысл, но НЕ ЕДИНОЛИЧНО — рядом всегда слово. Иначе для
   человека с дальтонизмом «нагулян» и «копит» неразличимы. */
.pstage {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 10px 0 6px; font-size: 0.8125rem;
}
.pstage-word { font-weight: 600; letter-spacing: -0.005em; }
.pstage[data-tone="ready"]   .pstage-word { color: var(--green); }
.pstage[data-tone="rest"]    .pstage-word { color: var(--amber); }
.pstage[data-tone="growing"] .pstage-word { color: var(--text); }
.pstage[data-tone="phone"]   .pstage-word { color: var(--accent); }

/* Жёлоб — системный fill, а не полупрозрачный белый: на белой карточке
   тот пропадает совсем (это уже ловили на полосе прогресса и тумблерах). */
.pgrow { height: 4px; border-radius: 2px; background: var(--fill); overflow: hidden; }
.pgrow i { display: block; height: 100%; border-radius: 2px; background: var(--text-3);
           transition: width .5s var(--spring); }
.pgrow i[data-tone="ready"] { background: var(--green); }
.pgrow i[data-tone="rest"]  { background: var(--amber); }
.pgrow i[data-tone="phone"] { background: var(--accent); }

.foot-row { display: flex; gap: 8px; }
.foot-row > *, .foot-row form { flex: 1 1 0; }
.foot-row .btn { width: 100%; }

.scroll-x { overflow-x: auto; }

/* ---------------------------------------------------------- профили */
.profile-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); }
.pcard { display: grid; gap: 12px; padding: 16px; }
.pcard-top { display: flex; align-items: flex-start; gap: 11px; }
.avatar {
  width: 38px; height: 38px; flex: none; display: grid; place-items: center;
  border-radius: 11px; color: #fff; font-weight: 600; font-size: 0.875rem;
  letter-spacing: -0.01em;
}
.pcard-name { min-width: 0; flex: 1; }
.pcard-name strong { display: block; font-size: 0.9375rem; letter-spacing: -0.008em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pcard-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.pstat { text-align: left; }
.pstat b { display: block; font-size: 1rem; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.012em; }
.pstat span { color: var(--text-3); font-size: 0.6875rem; }
.pcard-foot { display: flex; align-items: center; gap: 8px; }
.pcard-foot .grow { flex: 1; }

.state {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px 2px 7px; border-radius: 20px;
  font-size: 0.6875rem; font-weight: 600;
  background: var(--fill); color: var(--text-2);
}
.state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
/* Цвет здесь несёт смысл, а не украшает: зелёный — идёт работа, синий — вот-вот
   пойдёт, красный — сломалось, серый — покой. Амбер бережём для настоящих
   предупреждений, иначе «в очереди» кричит наравне с отказом ZennoBrowser. */
.state[data-s="browsing"] { background: var(--green-soft); color: var(--green); }
.state[data-s="queued"], .state[data-s="connecting"] { background: var(--accent-soft); color: var(--accent); }
.state[data-s="error"] { background: var(--red-soft); color: var(--red); }

/* Полоса прогресса круга — растёт пружинно, а не рывком. */
/* Жёлоб — по контуру, а не полупрозрачным белым: на белой карточке `--surface-2`
   сливался с ней, и полоса при нуле процентов исчезала целиком. */
.bar { height: 5px; border-radius: 3px; background: var(--fill); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: 3px; background: var(--accent);
           transition: width .45s var(--spring); }

/* ------------------------------------------------------------- формы */
/* 🔴 `align-content: start` — иначе грид растягивает поле по высоте самого
   высокого соседа в ряду. Замер на «Прокси»: два поля 54px против 34px у
   третьего, ярлыки на разной высоте — ряд выглядел проваленным. */
.field { display: grid; gap: 6px; align-content: start; }
.field > label, .field-legend { font-size: 0.8125rem; font-weight: 600; color: var(--text); }
/* Подсказки и ошибки — не только внутри .field: в шторке они лежат сами по себе. */
.hint { color: var(--text-2); font-size: 0.75rem; line-height: 1.42; }
.err  { color: var(--red);    font-size: 0.75rem; line-height: 1.42; }

input[type="text"], input[type="number"], input[type="password"],
input[type="search"], select, textarea {
  width: 100%;
  min-height: 34px; padding: 6px 10px;
  border: 1px solid var(--hairline);
  border-radius: 9px;
  background: var(--surface-solid);
  color: var(--text);
  font: inherit; font-size: 0.8125rem;
  transition: border-color .18s var(--spring-soft), box-shadow .18s var(--spring-soft);
}
input:hover, select:hover, textarea:hover { border-color: var(--text-3); }
input[aria-invalid="true"] { border-color: var(--red); }
textarea { min-height: 70px; resize: vertical; }

.field-row { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.settings-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.settings-group { display: grid; gap: 13px; align-content: start; }

/* Переключатель: подпись и сам тумблер — одна зона нажатия, без мёртвых зон.
   Выключенная дорожка красится отдельным токеном: полупрозрачная светлая
   подложка на белой карточке не читалась бы вовсе. */
.switch {
  display: flex; align-items: center; gap: 10px;
  min-height: 34px; cursor: pointer;
  font-size: 0.8125rem;
}
.switch input, .cat input {
  appearance: none; -webkit-appearance: none;
  width: 42px; height: 25px; flex: none; margin: 0; padding: 0;
  border-radius: 20px; border: none;
  background: var(--switch-off);
  position: relative; cursor: pointer;
  transition: background-color .24s var(--spring);
}
.switch input::after, .cat input::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 21px; height: 21px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
  transition: transform .28s var(--spring);
}
.switch input:checked, .cat input:checked { background: var(--green); }
.switch input:checked::after, .cat input:checked::after { transform: translateX(17px); }
.switch input:hover, .cat input:hover { border: none; }
.switch .sw-text { display: grid; gap: 1px; }
.switch .sw-text small { color: var(--text-2); font-size: 0.75rem; }

/* ---------------------------------------------------------- категории */
.cat-grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); padding: 0 18px 18px; }
.cat {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--surface-solid);
  cursor: pointer; font-size: 0.8125rem;
  transition: border-color .18s var(--spring-soft), background-color .18s var(--spring-soft);
}
.cat:hover { border-color: var(--text-3); }
.cat .cat-count { margin-left: auto; color: var(--text-3); font-size: 0.6875rem; font-variant-numeric: tabular-nums; }
.cat input { width: 34px; height: 20px; }
.cat input::after { width: 16px; height: 16px; }
.cat input:checked::after { transform: translateX(14px); }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chips.nowrap { flex-wrap: nowrap; overflow: hidden; }
.chips.nowrap .chip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chip {
  padding: 3px 10px; border-radius: 20px;
  background: var(--fill); color: var(--text-2);
  font-size: 0.75rem; border: 1px solid transparent;
}
.chip[data-tone="accent"] { background: var(--accent-soft); color: var(--accent); }
.chip[data-tone="green"]  { background: var(--green-soft);  color: var(--green); }
.chip[data-tone="amber"]  { background: var(--amber-soft);  color: var(--amber); }
.chip[data-tone="red"]    { background: var(--red-soft);    color: var(--red); }

/* --------------------------------------------------------- живой лог */
.logpanel {
  position: sticky; top: 0; align-self: start;
  height: 100vh; display: flex; flex-direction: column;
  background: var(--surface);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border-left: 1px solid var(--hairline);
}
.logpanel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 16px 10px;
}
.log { list-style: none; margin: 0; padding: 0 12px 20px; overflow-y: auto; flex: 1; display: grid; gap: 3px; align-content: start; }
.log li {
  padding: 7px 10px; border-radius: 9px;
  font-size: 0.75rem; line-height: 1.4; color: var(--text-2);
  background: transparent;
  animation: logIn .32s var(--spring);
}
.log li b { color: var(--text); font-weight: 600; }
.log li time { display: block; color: var(--text-3); font-size: 0.6875rem; font-variant-numeric: tabular-nums; }
/* Уровень отмечаем полоской у края, а не заливкой всей строки: залитые плашки
   тяжелее собственного текста и семь предупреждений подряд забивали панель. */
.log li[data-l="ok"], .log li[data-l="warn"], .log li[data-l="error"] {
  padding-left: 10px;
  /* Именно граница, а не inset-тень: тень шла по скруглению и полоска
     получалась скобкой с пустой серединой. Левый угол выпрямлен под неё. */
  border-left: 2px solid currentColor;
  border-radius: 2px 9px 9px 2px;
}
.log li[data-l="ok"]    { color: var(--green); }
.log li[data-l="warn"]  { color: var(--amber); }
.log li[data-l="error"] { color: var(--red); background: var(--red-soft); }
.log li[data-l="visit"] { background: var(--fill); color: var(--text); }

/* Счётчик схлопнутых повторов. */
.log-x {
  display: inline-block; margin-left: 7px; padding: 0 6px;
  border: 1px solid currentColor; border-radius: 20px;
  font-size: 0.625rem; font-weight: 600; font-variant-numeric: tabular-nums;
  white-space: nowrap; opacity: .72; vertical-align: 1px;
}

@keyframes logIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* ------------------------------------------------------------ график */
.chart { display: flex; align-items: flex-end; gap: 5px; height: 92px; padding: 0 18px 6px; }
.chart div {
  flex: 1; min-width: 4px; border-radius: 4px 4px 2px 2px;
  background: var(--accent); opacity: .82;
  transition: height .5s var(--spring);
}
/* День без заходов — тоже деление шкалы, его должно быть видно. Полупрозрачный
   белый `--surface-2` на белой карточке не виден вовсе. */
.chart div[data-zero="1"] { background: var(--fill); opacity: 1; }
.chart-legend { display: flex; justify-content: space-between; padding: 0 18px 16px; color: var(--text-3); font-size: 0.6875rem; }

/* -------------------------------------------------------------- шторка */
.sheet {
  width: min(460px, calc(100vw - 32px));
  padding: 0; border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  background: var(--surface-solid); color: var(--text);
  box-shadow: var(--shadow-2);
}
.sheet::backdrop { background: rgba(0, 0, 0, 0.32); backdrop-filter: blur(3px); }
.sheet[open] { animation: sheetIn .34s var(--spring); }
/* Материал прибывает: масштаб и размытие вместе, а не голая прозрачность. */
@keyframes sheetIn {
  from { opacity: 0; transform: scale(0.94) translateY(12px); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}
.sheet-body { display: grid; gap: 14px; padding: 22px; max-height: min(86vh, 900px); overflow-y: auto; }
.sheet-actions {
  display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap;
  position: sticky; bottom: -22px; margin: 4px -22px -22px; padding: 14px 22px;
  background: var(--surface-solid);
  border-top: 1px solid var(--hairline-soft);
}
.sep { border: none; border-top: 1px solid var(--hairline-soft); margin: 2px 0; }

.qstats { display: grid; gap: 8px; padding: 12px; border-radius: var(--r-md); background: var(--fill); }
.qstats-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.qlist { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.qlist li { display: flex; align-items: baseline; gap: 10px; font-size: 0.75rem; }
.qlist .q { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qlist .n { font-variant-numeric: tabular-nums; color: var(--text-2); }
.qlist .when { color: var(--text-3); font-size: 0.6875rem; }

/* -------------------------------------------------------------- тосты */
.toast-wrap {
  position: fixed; right: 18px; bottom: 18px; z-index: 90;
  display: grid; gap: 8px; justify-items: end;
  pointer-events: none;
}
.toast {
  max-width: 380px; padding: 11px 15px;
  border-radius: var(--r-md);
  background: var(--surface-solid); color: var(--text);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-2);
  font-size: 0.8125rem;
  pointer-events: auto;
  animation: toastIn .38s var(--spring);
}
.toast[data-tone="error"] { border-color: var(--red); }
.toast[data-tone="ok"] { border-color: var(--green); }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px) scale(.96); } to { opacity: 1; transform: none; } }

/* -------------------------------------------------------------- пустое */
.empty { display: grid; gap: 8px; justify-items: center; text-align: center; padding: 44px 24px; color: var(--text-2); }
.empty strong { color: var(--text); font-size: 0.9375rem; }
.empty .btn { margin-top: 6px; }

.spinner {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid var(--hairline); border-top-color: var(--accent);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ----------------------------------------------------------- адаптив */
@media (max-width: 1240px) {
  .shell { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
  .logpanel {
    grid-column: 1 / -1; position: static; height: auto; max-height: 320px;
    border-left: none; border-top: 1px solid var(--hairline);
  }
}

@media (max-width: 860px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  /* 🔴 ПЕРЕНОС, а не горизонтальная прокрутка. Замер на 430px: полоса меню
     имела scrollWidth 1184 при ширине окна 430 — 754px спрятанного вбок, и
     ни одного признака, что там что-то есть. Кнопки «Тема» и «Выйти» стояли
     на x=976, то есть за экраном совсем: выйти из кабинета с телефона было
     нельзя. Скрытая прокрутка без кромки и стрелки — это не компактность,
     это потерянные пункты. */
  .sidebar {
    position: static; height: auto; flex-direction: row; align-items: center;
    flex-wrap: wrap; row-gap: 8px;
    gap: 12px; padding: 10px 14px; overflow-x: visible;
    border-right: none; border-bottom: 1px solid var(--hairline);
  }
  .brand { padding: 0; }
  .brand-text { display: none; }
  .nav-list { display: flex; gap: 4px; flex-wrap: wrap; }
  .nav-list a { white-space: nowrap; }
  /* Подвал — своей строкой во всю ширину, а не «прижат вправо» за краем. */
  .sidebar-foot {
    margin: 0; padding: 0; border: none; order: 99; width: 100%;
    display: flex; align-items: center; gap: 10px;
  }
  .threads, .conn { white-space: nowrap; }
  /* В строке подвала подпись и значение слипались: «Потоки0 / 2». */
  .threads { display: flex; align-items: baseline; gap: 6px; }
  .content { padding: 0 14px 40px; }
  .pcard-stats { grid-template-columns: repeat(2, 1fr); }

  /* В ряд заголовку и кнопкам тесно: заголовок сжимался в колонку по слову,
     а кнопки вставали лесенкой. На узком они живут этажами. */
  .topbar { flex-direction: column; align-items: stretch; gap: 12px; padding-bottom: 12px; }
  .topbar-actions { justify-content: flex-start; }
  .topbar-actions .btn { flex: 1 1 auto; }
  .topbar-actions input { width: 100%; }
  .menu-wrap { flex: 0 0 auto; }
  /* На узком экране меню шире панели быть не может. */
  .menu { min-width: min(262px, calc(100vw - 40px)); }
}

/* ------------------------------------------------- предпочтения системы */
/* 🔴 Спиннеру вращение ВОЗВРАЩАЕМ. Общий сброс ниже гасит любую анимацию,
   и единственный индикатор неопределённой загрузки замирал — «мягче»
   превращалось в «без обратной связи». Равномерное вращение мелкого
   элемента вестибулярно безопасно, оно и остаётся. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .12s !important;
  }
  .btn:active, .icon-btn:active, .nav-list a:active { transform: none; }
  .sheet[open] { animation: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .sidebar, .logpanel, .card, .tile {
    background: var(--surface-solid);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .topbar { background: var(--bg); }
}

/* 🔴 РАЗБОР ПО ТЕМАМ ОБЯЗАТЕЛЕН. Прежний блок объявлял тёмно-серый
   `--text-2: #4a4a4f` без разбора и стоял в файле ПОСЛЕ блока тёмной темы —
   то есть перебивал её. Замер: при системной тёмной теме и включённом
   «повысить контраст» подпись плитки давала 1.93:1 вместо 7:1, а рамки
   становились чёрными на чёрном и исчезали совсем.
   Человек, включивший режим ради читаемости, получал кабинет ХУЖЕ обычного —
   то есть настройка доступности работала против себя. */
@media (prefers-contrast: more) {
  :root {
    --hairline: rgba(0,0,0,.4); --hairline-soft: rgba(0,0,0,.25);
    --text-2: #3a3a3f; --text-3: #45454a;
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --hairline: rgba(255,255,255,.5); --hairline-soft: rgba(255,255,255,.3);
      --text-2: #d8d8dc; --text-3: #c4c4c8;
    }
  }
  /* Ручной выбор темы бьёт системный и здесь — иначе режим контраста
     «чинил» бы не ту тему, которую человек видит. */
  :root[data-theme="light"] {
    --hairline: rgba(0,0,0,.4); --text-2: #3a3a3f; --text-3: #45454a;
  }
  :root[data-theme="dark"] {
    --hairline: rgba(255,255,255,.5); --hairline-soft: rgba(255,255,255,.3);
    --text-2: #d8d8dc; --text-3: #c4c4c8;
  }
  .card, .tile, .btn { border-width: 1px; }
}

@media (forced-colors: active) {
  .card, .tile, .btn, .icon-btn { border: 1px solid CanvasText; }
  .switch input { border: 1px solid CanvasText; }
  .switch input:checked::after { background: Highlight; }
}

/* 🔴 Отклик на НАЖАТИЕ, а не только на наведение. Он был всего у четырёх
   селекторов из десяти нажимаемых: кликабельные строки таблиц (их сотни),
   карточки категорий, тумблеры и карточки профилей отвечали только на hover
   — то есть на сенсорном экране не отвечали вовсе. Палец жмёт, ничего не
   происходит, пока не отработает переход. */
.row-link:active, .cat:active, .switch:active { background: var(--fill); }
.pcard { transition: transform .12s var(--spring); }
.pcard:active { transform: scale(.995); }

/* Спиннер под уменьшенным движением — см. комментарий выше. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 1.1s !important;
             animation-iteration-count: infinite !important; }
}

/* 🔴 Поля на узком экране — 16px. Safari на iPhone увеличивает страницу при
   фокусе в поле мельче 16px, и кабинет прыгает на каждом касании. На
   странице входа это уже учтено, в самом кабинете было 13px. */
@media (max-width: 860px) {
  input, select, textarea { font-size: 1rem; min-height: 40px; }
}

/* Безопасные зоны: `viewport-fit=cover` объявлен в разметке, а отступов под
   вырез и индикатор не было ни одного — на iPhone первый пункт меню и тосты
   уезжали под скругление. */
@media (max-width: 860px) {
  .sidebar { padding-left: max(12px, env(safe-area-inset-left));
             padding-right: max(12px, env(safe-area-inset-right)); }
  .content { padding-left: max(14px, env(safe-area-inset-left));
             padding-right: max(14px, env(safe-area-inset-right)); }
  .toast-wrap { bottom: max(18px, env(safe-area-inset-bottom)); }
}
