/* ============ Hodlometer design system ============
   Палитра построена на Radix Colors 3 (github.com/radix-ui/colors), значения вкопированы
   хексами — рантайм-зависимости нет, шкала и шаг указаны в комментарии у каждой группы.
   Шкалы: slate (нейтрали), teal (бренд), jade/red/amber (статусы),
   indigo→cyan→slate→amber→tomato (термометр гейджа).
   Ступени Radix: 3 — мягкий фон, 5/6 — границы, 9 — насыщенная заливка,
   10 — hover-заливка, 11 — текст на мягком фоне, 12 — основной текст.

   Здесь живут ТОКЕНЫ (:root + тёмная тема) и компоненты, которые есть только в KURS
   (графики, прогнозы, портфель, админка). Всё, что видно на обоих движках — шапка,
   подвал, карточка, кнопка, чип, таблица, лонгрид, фокус — в assets/css/components.css.
   :root целиком уезжает в тему /learn/ через wp-theme/sync.mjs: новые значения добавлять
   токенами сюда, а не хардкодом в theme.css. */
:root {
  /* нейтрали — slate 1…12 */
  --bg: #f0f0f3;          /* slate 3 */
  --card: #ffffff;
  --card-2: #f9f9fb;      /* slate 2 */
  --ink: #1c2024;         /* slate 12 */
  --ink2: #60646c;        /* slate 11 */
  --muted: #686c75;       /* между slate 10 и 11: slate 10 даёт на белом 3.8:1 — ниже AA */
  --line: #e0e1e6;        /* slate 5 */
  --line-soft: #e8e8ec;   /* slate 4 */
  /* тень у края горизонтально прокручиваемой таблицы (.tbl-scroll-wrap): признак того,
     что справа/слева есть спрятанные колонки. Не линия — линию читают как границу
     таблицы; градиент читается как «продолжается». В тёмной теме плотнее: на #111113
     чёрный с .13 неразличим. */
  --edge-fade: rgb(0 0 0 / .13);

  /* бренд — teal */
  --brand: #007c6b;       /* teal 11, на полтона темнее: teal 11 как есть даёт на --bg 4.0:1 */
  --brand-2: #12a594;     /* teal 9 — акцент в графиках и прогресс-барах */
  --brand-ink: #0d3d38;   /* teal 12 */
  --brand-bg: #e0f8f3;    /* teal 3 */
  --focus: #007c6b;       /* = --brand: teal 7 давал 1.83:1 на фоне, кольцо фокуса требует 3:1 */
  --brand-hover: #0d3d38; /* teal 12 — заливка кнопки на hover (в тёмной, наоборот, светлеет) */
  --brand-solid: var(--brand);  /* заливка primary-кнопки: в светлой теме = --brand */
  --on-solid: #ffffff;    /* текст поверх насыщенных заливок (.btn, .btn.danger, аватар) */

  /* монеты — внешние бренд-цвета, вне шкал Radix */
  --btc: #DD7A00;
  --btc-soft: #F7931A;
  --eth: #5A68D6;
  --eth-soft: #6472E0;

  /* Wise — тоже внешние бренд-цвета (перетяжка-объявление .wise-bar). В тёмной теме
     НЕ переопределяются: это чужой фиксированный бренд, а не наша поверхность, и в обеих
     темах полоса выглядит одинаково. Контраст на forest #163300: белый 13.9:1,
     bright green 9.4:1 — оба с запасом к AA, поэтому и зелёный годится как цвет фокуса. */
  --wise-green: #9FE870;  /* Wise bright green — CTA и кольцо фокуса внутри полосы */
  --wise-ink: #163300;    /* Wise forest green — фон полосы и текст на зелёной пилюле */
  --wise-on: #ffffff;     /* текст на forest; --on-solid не годится — он переворачивается в тёмной */

  /* Bybit — вторая перетяжка той же конструкции (.bybit-bar), правила те же, что у Wise:
     чужой фиксированный бренд, в тёмной теме НЕ переопределяется. Контраст на #17181E:
     белый 17.7:1, gold 8.8:1; тёмный текст на золотой пилюле — те же 8.8:1, вдвое выше
     порога AA. Кольцо фокуса внутри полосы — золото (8.8:1 при пороге 3:1). */
  --bybit-gold: #F7A600;  /* Bybit gold — CTA и кольцо фокуса внутри полосы */
  --bybit-ink: #17181E;   /* фон полосы и текст на золотой пилюле */
  --bybit-on: #ffffff;    /* текст на почти-чёрном; --on-solid переворачивается в тёмной */

  /* статусы — jade / red / amber, ступени 11 (текст) и 3 (фон).
     У jade и amber ступень 11 не дотягивает до 4.5:1 на своей же ступени 3 — притемнены. */
  --up: #1d7a61;
  --up-bg: #e6f7ed;
  --down: #ce2c31;
  --down-bg: #feebec;
  --down-ink: #b5252a;   /* red 11 притемнён: --down на --down-bg даёт 4.54:1, запас к AA нулевой */
  --warn: #9c5c00;
  --warn-bg: #fff7c2;
  --neutral-bg: #f0f0f3;  /* slate 3 */

  /* Термометр Hodlometer Index: 1 Deep Freeze → 5 Overheated. Расходящаяся порядковая
     шкала: холод → нейтраль → тепло. Ступени пере-шагнуты в D3a по измерениям, а не на глаз
     (OKLCH-светлота, контраст к обеим поверхностям, симуляция протан/дейтан):
       z3 8b8d98 → 80838d  ΔL к z2 0.015→0.049, к белой карточке 3.30→3.78:1, CVD 7.3→8.5
       z4 ffc53d → ef5f00  L .854→.662: янтарь давал 1.58:1 к белой карточке («Warm» пропадал
                           в светлой теме) и 11.15:1 к тёмной — кричал громче белого числа
       z5 e54d2e → d13415  L .627→.566, держит разрыв светлоты от нового z4
     `-ink` — текст чипа на своём `-bg`, все пять пар ≥4.8:1 в обеих темах; z4 переехал
     с янтарной пары Radix на оранжевую. */
  --therm-1: #3e63dd; --therm-1-bg: #edf2fe; --therm-1-ink: #3a5bc7;  /* indigo */
  --therm-2: #00a2c7; --therm-2-bg: #def7f9; --therm-2-ink: #0f748d;  /* cyan   */
  --therm-3: #80838d; --therm-3-bg: #f0f0f3; --therm-3-ink: #60646c;  /* slate  */
  --therm-4: #ef5f00; --therm-4-bg: #ffefd6; --therm-4-ink: #b04300;  /* orange */
  --therm-5: #d13415; --therm-5-bg: #feebe7; --therm-5-ink: #c53013;  /* tomato */

  /* Палитра серий графиков. Серия — это ЛИБО актив (носит токен актива), ЛИБО метрика
     (носит --data-line): смысл в этом случае несут зоны. Третьего не дано.
     --brand/--brand-2 в графиках запрещены: бренд-цвет означает «интерактивное». */
  --data-btc: var(--btc);
  --data-eth: var(--eth);
  /* Бенчмарки /tools/compare — тоже активы, но ЧУЖИЕ: на графике они опора, а не предмет,
     и поэтому носят низкую насыщенность (холодный сланец / тёплая бронза) против ярких
     токенов BTC и ETH. Ярче их красить нечем: teal занят брендом, зелёный и красный —
     статусом значения, серый ступени 9 — нейтралью зонной шкалы.
     Четыре линии на одном поле цветом при дальтонизме не разделяются (минимум попарного
     расстояния 0.12 в светлой теме, 0.11 в тёмной — измерено по Vienot на OKLab). Это
     тот же случай, что пара --therm-4/5: различение вынесено во вторичные каналы —
     штрих (сплошная / длинный пунктир / точки), толщина (2px крипта, 1.5px бенчмарк)
     и подпись у конца линии. */
  --data-spx: #6b7280;           /* S&P 500 total return */
  --data-gold: #8a5a28;          /* золото, LBMA PM */
  --data-line: var(--ink2);      /* сама метрика: Mayer, F&G */
  --data-line-2: var(--ink);     /* вторая серия того же типа */
  --data-ref: var(--muted);      /* опорные линии, всегда пунктир 4 4 */
  --data-grid: var(--line-soft);
  --data-axis: var(--line);

  /* Тон чипа AI-источника. Имя токена — ключ источника в БД (`gemini`), а не подпись:
     публично он называется «AI» (карта — SRC_NAMES в app.js). */
  --gemini-bg: #edf2fe; --gemini-ink: #3a5bc7;   /* indigo 3 / 11 */

  --tip-bg: #1c2024; --tip-ink: #ffffff; --tip-muted: #b0b4ba;

  /* Радиусы — четыре, больше быть не должно: карточка / контрол / инлайн / пилюля.
     Мобильного оверрайда 14px нет: карточка одного радиуса на всех ширинах. */
  --radius: 16px;      /* карточка */
  --radius-s: 10px;    /* кнопка, поле, поповер, вложенная поверхность */
  --radius-xs: 6px;    /* инлайн-код, свотч, конец полосы */
  --radius-pill: 999px;

  /* Высоты кликабельных элементов — ровно три */
  --control-h: 40px;
  --control-h-sm: 32px;
  --control-h-xs: 24px;

  /* Тени: покой, наведение на карточку, поповер, активный сегмент. Пятой нет. */
  --shadow: 0 1px 2px rgba(28, 32, 36, .05), 0 10px 28px -16px rgba(28, 32, 36, .14);
  --shadow-hover: var(--shadow), 0 0 0 1px var(--line);
  --shadow-pop: 0 2px 6px -2px rgba(28, 32, 36, .10), 0 14px 34px -10px rgba(28, 32, 36, .26);
  --shadow-inset-sm: 0 1px 3px rgba(28, 32, 36, .14);

  /* Движение: две длительности на всё интерактивное. Было четыре почти неразличимых
     (.15/.16/.18/.2). Третья, --dur-slow, нужна ровно одному сценарию — затуханию
     подсветки :target в глоссарии: её задача не «отреагировать», а дать глазу время
     найти термин среди 47 и уйти, не оставив цветного пятна на странице. */
  --dur-fast: 120ms;
  --dur: 200ms;
  --dur-slow: 1200ms;
  --ease: cubic-bezier(.2, .8, .3, 1);
  --font: "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* Space Grotesk без кириллицы: русские заголовки автоматически падают на Inter */
  --font-display: "Space Grotesk", "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Типографическая шкала — одна на оба движка, девять ступеней и ни одного значения вне
     таблицы. До D3a рядом с шестью токенами жили 25 захардкоженных кеглей, из них 13 ступеней
     с шагом 0.5px между 11 и 17 — иерархии это не давало, разнобой давало.
     Полоса 13.5–16.5 схлопнута в --fs-sm 13 / --fs-base 15 / --fs-h3 17.
     Значения на узких экранах переопределяются в components.css (переопределяем токены,
     а не правила); ступени применяются там же, здесь — только источник правды. */
  --fs-4xl: 60px;       --lh-4xl: 1;       /* только .err-code */
  --fs-3xl: 44px;       --lh-3xl: 1.05;    /* hero-число, ровно одно на экран */
  --fs-2xl: 34px;       --lh-2xl: 1.1;     /* значение показания (.read-val) */
  --fs-xl: 26px;        --lh-xl: 1.15;     /* значение KPI (.stat-value), .hv-clock .num */
  --fs-h1: 40px;        --lh-h1: 1.1;
  --fs-h1-hero: 46px;   /* единственное исключение: герой главной */
  --fs-h2: 24px;        --lh-h2: 1.25;
  --fs-lg: 20px;        --lh-lg: 1.35;     /* h3 лонгрида, .gloss-term, название зоны гейджа */
  --fs-h3: 17px;        --lh-h3: 1.35;     /* заголовок карточки */
  --fs-prose: 17px;     --lh-prose: 1.7;   /* лонгрид */
  --fs-base: 15px;      --lh-base: 1.5;    /* интерфейс: сюда 14 / 14.5 / 15.5 / 16 */
  --fs-sm: 13px;        --lh-sm: 1.5;      /* мета, подписи: сюда 13.5 */
  --fs-xs: 12px;        --lh-xs: 1.3;      /* чипы, надзаголовки, th: сюда 11.5 / 12.5 */
  --fs-2xs: 11px;       --lh-2xs: 1.2;     /* только засечки осей SVG */

  /* Отступы: шкала 4px. Значений вне неё быть не должно — до D3a в трёх файлах жили
     57 различных padding и ~24 значения gap. Вёрстка использует семантические алиасы
     ниже, а не числа; на узких экранах переопределяются сами алиасы (components.css). */
  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
  --space-5: 20px;  --space-6: 24px;  --space-7: 32px;  --space-8: 40px;
  --space-9: 48px;  --space-10: 56px; --space-11: 64px; --space-12: 80px;

  --gap-grid: var(--space-4);        /* шаг между карточками, оба движка */
  --pad-card: var(--space-5);        /* внутренние поля карточки */
  --space-section: var(--space-10);  /* между секциями: было 26px при gap 16 — макро-ритма
                                        не возникало, страница читалась одной текстурой.
                                        Отношение к внутрисеточному шагу должно быть ≥3× */
  --space-block: var(--space-3);     /* надзаголовок → содержимое */
  --space-cta: var(--space-7);       /* содержимое секции → хвостовое действие. Строго между
                                        --gap-grid (16) и --space-section (56): при 16 CTA
                                        читается третьей карточкой ряда, при 56 — новой
                                        секцией. 16 : 32 : 56 = 1 : 2 : 3.5 */
  --space-page-top: var(--space-8);  /* шапка → h1, одинаково в обоих движках */

  /* Две ширины на весь сайт, третьей быть не должно */
  --measure-prose: 720px;   /* колонка чтения */
  --measure-full: 1140px;   /* содержимое .wrap за вычетом паддингов */
  /* Боковик с оглавлением: 720 + 48 (gap --space-9) + 260 = 1028 ≤ 1140. Третьей
     колонкой чтения не является — это служебная полоса, а не текст. */
  --side: 260px;

  color-scheme: light;
}

/* ---------- Тёмная тема (slate dark / teal dark и т.д.) ---------- */
:root[data-theme="dark"] {
  --bg: #111113;          /* slate dark 1 */
  --card: #18191b;        /* slate dark 2 */
  --card-2: #212225;      /* slate dark 3 */
  --ink: #edeef0;         /* slate dark 12 */
  --ink2: #b0b4ba;        /* slate dark 11 */
  --muted: #8b8f98;       /* между slate dark 10 и 11: 10 даёт на --card-2 3.8:1 */
  --line: #2e3135;        /* slate dark 5 */
  --line-soft: #272a2d;   /* slate dark 4 */
  --edge-fade: rgb(0 0 0 / .38);

  --brand: #0eb39e;       /* teal dark 10 */
  --brand-2: #0bd8b6;     /* teal dark 11 */
  --brand-ink: #adf0dd;   /* teal dark 12 */
  --brand-bg: #0d2d2a;    /* teal dark 3 */
  --focus: #0eb39e;       /* = --brand: teal dark 7 давал 2.91:1 */
  --brand-hover: #0bd8b6; /* teal dark 11 */
  /* Заливка primary-кнопки на ступень темнее ссылки: teal dark 10 делал кнопку
     вторым по яркости пятном тёмного экрана. Ступень 9 держит 5.55:1 к --on-solid
     (порог 4.5) и перестаёт спорить с содержимым. Ссылки и кольцо фокуса при этом
     остаются на ступени 10 — им яркость нужна. */
  --brand-solid: #12a594; /* teal dark 9 */
  --on-solid: #06201d;    /* тёмный текст поверх ярких заливок */

  --btc: #F7931A;
  --btc-soft: #FFB04D;
  --eth: #8E9BF5;
  --eth-soft: #A3ADF8;

  /* Серии бенчмарков (/tools/compare): та же роль, что в светлой теме, своя светлота */
  --data-spx: #9aa3af;
  --data-gold: #a8794a;

  --up: #1fd8a4;
  --up-bg: #0f2e22;
  --down: #ff9592;
  --down-bg: #3b1219;
  --down-ink: #ff9592;
  --warn: #ffca16;
  --warn-bg: #302008;
  --neutral-bg: #212225;

  /* Термометр: ступень 9 у Radix одинакова в обеих темах, поэтому четыре из пяти зон
     совпадают со светлыми. Своя ступень нужна только нейтрали (#7c8087 — 4.44:1
     к тёмной карточке против 3.43:1 у прежнего #696e77). */
  --therm-1: #3e63dd; --therm-1-bg: #182449; --therm-1-ink: #9eb1ff;
  --therm-2: #00a2c7; --therm-2-bg: #082c36; --therm-2-ink: #4ccce6;
  --therm-3: #7c8087; --therm-3-bg: #212225; --therm-3-ink: #b0b4ba;
  --therm-4: #ef5f00; --therm-4-bg: #331e0b; --therm-4-ink: #ffa057;
  --therm-5: #d13415; --therm-5-bg: #391714; --therm-5-ink: #ff977d;

  --gemini-bg: #182449; --gemini-ink: #9eb1ff;

  --tip-bg: #2e3135; --tip-ink: #edeef0; --tip-muted: #b0b4ba;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 10px 28px -16px rgba(0, 0, 0, .7);
  --shadow-pop: 0 2px 6px -2px rgba(0, 0, 0, .5), 0 14px 34px -10px rgba(0, 0, 0, .75);
  --shadow-inset-sm: 0 1px 3px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

/* ---------- Тема «как в системе» до первого кадра ----------
   Куки kurs_theme нет (первый заход — а это все переходы из поиска и соцсетей) —
   сервер отдаёт data-theme="auto" и решение принимает CSS. Инлайн-скрипт, который
   обычно ставит тему до отрисовки, запрещён CSP (script-src 'self'), поэтому раньше
   фолбэком был light: при тёмной ОС человек видел белую вспышку и перекраску.

   Явный выбор всегда сильнее системы: data-theme="light" и "dark" сюда не попадают,
   селектор ловит только "auto". Как только отработает ui.js, атрибут становится
   конкретным (light|dark) и этот блок перестаёт участвовать вовсе.

   Значения — копия блока :root[data-theme="dark"] выше. Списком селекторов их не
   объединить: медиа-условие в селектор не выносится. Правишь тёмный токен — правь оба
   места; wp-theme/sync.mjs переносит в тему WP оба блока. */
:root[data-theme="auto"] { color-scheme: light dark; }

@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg: #111113;
    --card: #18191b;
    --card-2: #212225;
    --ink: #edeef0;
    --ink2: #b0b4ba;
    --muted: #8b8f98;
    --line: #2e3135;
    --line-soft: #272a2d;
    --edge-fade: rgb(0 0 0 / .38);

    --brand: #0eb39e;
    --brand-2: #0bd8b6;
    --brand-ink: #adf0dd;
    --brand-bg: #0d2d2a;
    --focus: #0eb39e;
    --brand-hover: #0bd8b6;
    --brand-solid: #12a594;
    --on-solid: #06201d;

    --btc: #F7931A;
    --btc-soft: #FFB04D;
    --eth: #8E9BF5;
    --eth-soft: #A3ADF8;

    --data-spx: #9aa3af;
    --data-gold: #a8794a;

    --up: #1fd8a4;
    --up-bg: #0f2e22;
    --down: #ff9592;
    --down-bg: #3b1219;
    --down-ink: #ff9592;
    --warn: #ffca16;
    --warn-bg: #302008;
    --neutral-bg: #212225;

    --therm-1: #3e63dd; --therm-1-bg: #182449; --therm-1-ink: #9eb1ff;
    --therm-2: #00a2c7; --therm-2-bg: #082c36; --therm-2-ink: #4ccce6;
    --therm-3: #7c8087; --therm-3-bg: #212225; --therm-3-ink: #b0b4ba;
    --therm-4: #ef5f00; --therm-4-bg: #331e0b; --therm-4-ink: #ffa057;
    --therm-5: #d13415; --therm-5-bg: #391714; --therm-5-ink: #ff977d;

    --gemini-bg: #182449; --gemini-ink: #9eb1ff;

    --tip-bg: #2e3135; --tip-ink: #edeef0; --tip-muted: #b0b4ba;

    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 10px 28px -16px rgba(0, 0, 0, .7);
    --shadow-pop: 0 2px 6px -2px rgba(0, 0, 0, .5), 0 14px 34px -10px rgba(0, 0, 0, .75);
    --shadow-inset-sm: 0 1px 3px rgba(0, 0, 0, .5);
    color-scheme: dark;
  }
}

/* Моноширинные цифры — ровно четыре места: таблицы, определения, засечки осей и мета.
   На --fs-xl и выше — пропорциональные: на display-числах tabular разрежает «25.4»
   и число рассыпается. Класс .num остаётся, но теперь он значит только «выровнять вправо». */
table.tbl, .kv dd, .chart-tick, .meta-row, .comp-meta, .spark-axis, .provenance {
  font-variant-numeric: tabular-nums;
}

/* ---------- Сетки ---------- */
.grid { display: grid; gap: var(--gap-grid); }
/* У grid-элемента `min-width` по умолчанию `auto` = его min-content: карточка с широкой
   таблицей растягивает колонку шире экрана, и страница едет вбок (нашлось смоуком P6 на
   /tools/halving при 375px). Ноль возвращает право сжиматься, а прокрутку берёт на себя
   .tbl-wrap внутри карточки. */
.grid > * { min-width: 0; }
.grid-cards { grid-template-columns: repeat(4, 1fr); }
.grid-2 { grid-template-columns: 1fr 1fr; }
/* Карточка-соседка сетки — часть той же сетки, а не начало новой мысли: без этого
   таблица «Episodes deeper than 30%» стояла вплотную к карточкам графиков (замер: 0px
   при 16px между самими карточками), и три блока читались как один слипшийся. Шаг
   вертикальный обязан равняться горизонтальному, иначе сетка перестаёт быть сеткой.
   `:not(.section)` обязателен: на /dca следом за сеткой идёт блок, который сам является
   секцией, и правило перебило бы межсекционный ритм 56px своими 16px. */
/* Четвёртая комбинация — `.card + .card` — здесь отсутствовала, и это была настоящая
   дыра, а не мелочь: две карточки подряд БЕЗ обёртки-сетки стояли вплотную, разделённые
   только собственными границами (замер на /tools/attention: 0px между карточками BTC и
   ETH при 16px внутри любой сетки). Пока карточки всегда лежали в `.grid`, случай не
   всплывал; первая же страница, где они выведены циклом прямо в секцию, его нашла. */
.grid + .card:not(.section),
.card + .grid:not(.section),
.grid + .grid:not(.section) { margin-top: var(--gap-grid); }
/* Там, где соседи заведомо разной длины, карточка не тянется по высоте более длинного */
.grid-start { align-items: start; }

/* Межсекционный ритм — главный рычаг макро-иерархии: 56px против 16px внутри сетки
   даёт отношение 3.5× и превращает «одну текстуру» в главы. Было 26px (1.6×). */
.section { margin-top: var(--space-section); scroll-margin-top: 76px; }

/* Интро секции — на шкале, а не на UA-дефолте: своего margin у .sub не было, браузерный
   1em (15px) схлопывался с --space-block заголовка и ломал ритм «заголовок → интро → сетка».
   Селектор прямого потомка: .sub внутри карточек (их 40 из 44) не задет. */
.section > .sub { margin: 0 0 var(--space-4); }

#charts { display: flex; flex-direction: column; gap: var(--gap-grid); }

/* Стат-карточки */
.stat-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.asset-dot { width: 10px; height: 10px; border-radius: var(--radius-xs); flex: none; }
/* Марка актива (coin_icon). Заменила цветной квадрат .asset-dot: различала она BTC и ETH
   ОДНИМ цветом, а цвет — единственный канал, который отваливается и в ч/б печати, и при
   дальтонизме. Форма несёт то же различие сама, цвет остаётся вторым каналом.
   Кегль чуть крупнее иконки заголовка (1.15em против 1em): знак сложнее галочки, на 15px
   его внутренние просветы схлопываются. `.card h3 .ic{color:--muted}` перебивается —
   у токена актива специфичность ниже, поэтому селекторы ниже двухклассовые. */
.coin-ic { width: 1.15em; height: 1.15em; flex: none; }
.card h2 .coin-ic.coin-btc, .card h3 .coin-ic.coin-btc, .coin-ic.coin-btc { color: var(--btc); }
.card h2 .coin-ic.coin-eth, .card h3 .coin-ic.coin-eth, .coin-ic.coin-eth { color: var(--eth); }
/* Тикер в ячейке таблицы: марка и буквы не должны разъезжаться при переносе строки */
.cell-sym { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
/* Пара активов (карточка ETH/BTC). Две марки НЕ перекрывают друг друга внахлёст, как это
   принято у круглых залитых бейджей монет: у нас штриховые знаки, и на пересечении их
   линии сливаются в кляксу. Кегль на ступень меньше одиночной марки (1em против 1.15em) —
   иначе карточка пары перевешивала бы соседние в ряду KPI, где марка всего одна. */
.coin-pair { display: inline-flex; align-items: center; gap: 2px; flex: none; }
.coin-pair .coin-ic { width: 1em; height: 1em; }
.stat-name { font-weight: 700; font-size: var(--fs-base); }
.stat-sym { color: var(--muted); font-size: var(--fs-xs); }
.stat-value { font-size: var(--fs-xl); line-height: var(--lh-xl); font-weight: 700; letter-spacing: -.02em; }
/* min-height под две строки: иначе спарклайны соседних KPI-карточек стоят на разной высоте */
.stat-sub { min-height: 38px; }
.stat-row { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.stat-foot { margin-top: var(--space-2); }

/* ---------- Графики ---------- */
.chart-card { padding: var(--pad-card); }
.chart-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-2); }
.chart-head h2 { display: flex; align-items: center; gap: var(--space-2); }
.chart-head .spacer { flex: 1; }
.kchart { position: relative; width: 100%; }
.kchart svg { display: block; width: 100%; }
.kchart .ktip {
  position: absolute; pointer-events: none; z-index: 5; display: none;
  background: var(--tip-bg); color: var(--tip-ink); border-radius: var(--radius-s); padding: var(--space-2) var(--space-3);
  font-size: var(--fs-xs); line-height: 1.45; white-space: nowrap;
  box-shadow: var(--shadow-pop);
}
.ktip .tl { color: var(--tip-muted); font-size: var(--fs-xs); }
.ktip .tv { font-weight: 700; }

/* Сегменты — главные органы управления графика: диапазон и источник прогноза.
   Высота была --control-h-xs (24) и на ≤640 уменьшалась ещё; это ниже любого разумного
   минимума для пальца. Теперь --control-h-sm (32) везде и --control-h (40) на телефоне,
   кегль при этом не мельчает. Группе нужны имя и role=group — их ставит app.js. */
.seg { display: inline-flex; background: var(--neutral-bg); border-radius: var(--radius-pill); padding: var(--space-1); gap: var(--space-1); }
.seg button {
  border: 0; background: none; height: var(--control-h-sm); padding: 0 var(--space-3); border-radius: var(--radius-pill); cursor: pointer;
  font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--ink2);
}
.seg button.active, .seg button[aria-pressed="true"] { background: var(--card); color: var(--ink); box-shadow: var(--shadow-inset-sm); }

/* Имя группы сегментов. «Statistical model · AI» без подписи не говорит, чем именно
   переключаешь. */
.seg-lbl { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.fc-src-note { margin: var(--space-1) 0 0; }

.toggle { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--control-h-sm); font-size: var(--fs-sm); color: var(--ink2); cursor: pointer; user-select: none; }
.toggle input { accent-color: var(--brand); width: 15px; height: 15px; }

/* Границы нарисованного окна и всего ряда — стоит вплотную под осью X, до легенды */
.chart-note { font-size: var(--fs-xs); color: var(--muted); margin: var(--space-1) 0 0; }
.chart-note:empty { display: none; }

.legend-note { font-size: var(--fs-xs); color: var(--muted); display: flex; gap: var(--space-3); align-items: center; padding: var(--space-1) 0 var(--space-2); flex-wrap: wrap; }
.legend-note .li { display: inline-flex; align-items: center; gap: var(--space-2); }
.legend-note .sw { width: 14px; height: 0; border-top: 2px solid; border-radius: 2px; }
.legend-note .sw.dash { border-top-style: dashed; }
.legend-note .sw.band { width: 14px; height: 10px; border: 0; border-radius: 2px; opacity: .35; }

/* ---------- Прогнозы ---------- */
.fc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap-grid); }
/* min-width:0 обязателен: у grid-элемента min-width по умолчанию auto, и карточка с
   неразрывными подписями («measured coverage →», «self-reported») распирала дорожку
   шире 1fr — страница получала горизонтальный скролл. */
.fc-card { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.fc-src { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
/* тона источников — модификаторы общего .chip (components.css), своих размеров не заводят */
.chip.gemini { background: var(--gemini-bg); color: var(--gemini-ink); }
.chip.statistical { background: var(--brand-bg); color: var(--brand-ink); }
.chip.backtest { background: var(--neutral-bg); color: var(--ink2); }
.fc-date { color: var(--muted); font-size: var(--fs-xs); }
.fc-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); background: var(--card-2); border: 1px solid var(--line-soft); border-radius: var(--radius-s); }
.fc-h { font-size: var(--fs-xs); font-weight: 700; color: var(--ink2); min-width: 68px; flex: none; white-space: nowrap; }
.fc-main { min-width: 0; }
.fc-mid { font-size: var(--fs-h3); font-weight: 700; }
.fc-range { font-size: var(--fs-xs); color: var(--muted); }
.fc-right { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-1); flex: none; }
.fc-dir { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-base); font-weight: 700; white-space: nowrap; }
.fc-dir.up { color: var(--up); } .fc-dir.down { color: var(--down); } .fc-dir.flat { color: var(--muted); }
.conf { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
/* Самоотчёт LLM: без полосы (её место занимает подпись) и с переносом — «self-reported»
   не должно распирать карточку, но и молчать о происхождении числа нельзя (аудит E1-F4). */
.conf-self { white-space: normal; text-align: right; justify-content: flex-end; }
.conf-self .sub { font-size: var(--fs-2xs, var(--fs-xs)); }
/* «range not evaluated» — отдельной строкой под диапазоном, не в подбор (аудит E1-F2) */
.fc-note { display: block; }
.conf-bar { width: 54px; height: 5px; border-radius: var(--radius-xs); background: var(--line-soft); overflow: hidden; }
.conf-bar.sm { width: 40px; }
.conf-bar i { display: block; height: 100%; background: var(--brand-2); border-radius: var(--radius-xs); }

details.fc-why { border-top: 1px dashed var(--line); padding-top: var(--space-2); }
details.fc-why summary, details.fc-long summary { cursor: pointer; font-size: var(--fs-sm); font-weight: 600; color: var(--brand); list-style: none; }
details.fc-why summary::-webkit-details-marker, details.fc-long summary::-webkit-details-marker { display: none; }
details.fc-why summary::before, details.fc-long summary::before { content: "▸ "; }
details.fc-why[open] summary::before, details.fc-long[open] summary::before { content: "▾ "; }
/* Длинные горизонты прогноза (аудит E3-4): свёрнуты, но остаются в разметке. Сводка
   в summary — приглушённая, потому что это не действие, а список того, что внутри. */
details.fc-long summary { color: var(--muted); font-weight: 400; }
details.fc-long summary:hover { color: var(--brand); }
.fc-why-body { font-size: var(--fs-sm); color: var(--ink2); margin-top: var(--space-2); display: flex; flex-direction: column; gap: var(--space-2); }
.provenance {
  background: var(--card-2); border: 1px solid var(--line-soft); border-radius: var(--radius-s);
  padding: var(--space-3); font-size: var(--fs-xs); color: var(--ink2);
}
.provenance b { color: var(--ink); }
.provenance .pv-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* ---------- Таблица точности ---------- */
.acc-note { margin-top: var(--space-2); }
.acc-h { margin: var(--space-5) 0 0; }
.acc-h:first-child { margin-top: 0; }
/* Статус живого трек-рекорда — строка, а не заголовок. Пока ни один прогноз не созрел,
   сказать об этом надо один раз и мелко: это оговорка к измеренным числам ниже, а не
   содержание блока. Крупный заголовок на его месте делал главным отсутствие данных. */
.acc-lede {
  display: flex; align-items: flex-start; gap: var(--space-2);
  margin: 0 0 var(--space-4); font-size: var(--fs-sm); color: var(--ink2);
}
.acc-lede .ic { color: var(--muted); flex: none; margin-top: 2px; }
.acc-fold { margin-top: var(--space-5); border-top: 1px solid var(--line-soft); }
/* Строка без оценок приглушена целиком: класс проставлялся в разметке с самого начала,
   но стиля для него не было — двенадцать неоценённых строк выглядели равноправно
   с оценёнными и весили в макете столько же. */
.tbl .row-muted > td { color: var(--muted); }

/* Свёрнутая таблица (контракт B5, empty). Не .empty-state: содержимое не отсутствует,
   а сложено, и заголовок здесь --fs-h3, а не --fs-h2 — блок живёт внутри карточки,
   под уже существующим заголовком секции, и второй крупный кегль спорил бы с ним. */
.tbl-fold > summary {
  cursor: pointer; list-style: none;
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-3) 0;
}
.tbl-fold > summary::-webkit-details-marker { display: none; }
.tbl-fold-h { font-family: var(--font); font-size: var(--fs-h3); font-weight: 700; color: var(--ink); }
.tbl-fold-s { font-size: var(--fs-sm); color: var(--ink2); max-width: 460px; }
.tbl-fold-a {
  display: inline-flex; align-items: center; gap: var(--space-1);
  margin-top: var(--space-1); font-size: var(--fs-sm); font-weight: 600; color: var(--brand);
}
.tbl-fold > summary:hover .tbl-fold-a { color: var(--brand-hover); }
.tbl-fold[open] .tbl-fold-a .ic { transform: rotate(180deg); }
.tbl-fold-a .ic { transition: transform var(--dur-fast) var(--ease); }

/* ---------- Gauge FNG ----------
   Тот же прибор, что у индекса: одна геометрия на все реализации, но своя шкала зон
   (25/45/55/75 alternative.me против ровных пятин индекса — см. charts.js).
   Гейдж стоит в том же слоте, где у соседей по ряду KPI спарклайн, то есть вторичным
   элементом: значение и зона уже прочитаны выше, в общем для всего ряда слоте.
   Числа внутри дуги здесь поэтому нет (showValue:false) — оно бы дублировало верхнюю
   строку и было бы крупнее её. */
.fng-wrap { display: flex; justify-content: center; }
.fng-wrap > div { width: 100%; max-width: 160px; }

/* ---------- Циклы и деривативы ---------- */
.cy-row {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-2) 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-sm);
}
.cy-row:last-child { border-bottom: 0; }
.cy-row > span:first-child { color: var(--ink2); flex: 1; min-width: 150px; }
.cy-row b { font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.cy-row i { font-style: normal; color: var(--muted); cursor: help; font-size: var(--fs-xs); }
.cy-spark { width: 90px; flex: none; min-width: 0; overflow: hidden; }

/* ---------- Алерты ----------
   Раскладка формы — классами, а не инлайн-стилями с магическими px (контракт B8). */
.al-form { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
.al-fields { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.al-row { display: flex; gap: var(--space-3); align-items: end; flex-wrap: wrap; }
.al-f-asset { flex: 1; min-width: 90px; }
.al-f-kind { flex: 2; min-width: 190px; }
.al-f-thr { flex: 1; min-width: 130px; }
.al-f-chan { flex: 1; min-width: 150px; }
.al-list { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
.al-cond { font-weight: 700; font-size: var(--fs-sm); }
.al-note { margin-top: var(--space-2); }
/* Питч для анонима — одинокая карточка во всю ширину секции (1140), и мера чтения у неё
   потерялась: строка шла в 116 знаков, тогда как .lede и .sub на том же экране держат 76
   (720 = --measure-prose), а .teaser p — 62. Ограничиваем СОДЕРЖИМОЕ, а не карточку:
   поверхность обязана остаться во всю ширину, как у .chart-card выше, иначе секция «Alerts»
   станет вдвое уже остальной страницы. `> *` берёт и .al-sample с .hero-cta — образец письма
   на 1098px был почти пустой коробкой, а в залогиненном виде он живёт в колонке ~558. */
.al-pitch > * { max-width: var(--measure-prose); }

/* ---------- Портфель ---------- */
.pf-sub { margin-bottom: var(--space-4); }
.pf-save-row { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-1); }
.pf-saved { color: var(--up); }
.pf-alloc-wrap { margin-top: var(--space-5); }
.pf-alloc-wrap .legend-note { margin-top: var(--space-2); }
.pf-value-row { margin-top: var(--space-1); }
.pf-value-row .read-val { margin: 0; }
.pf-chips { margin-top: var(--space-2); gap: var(--space-2); }
.pf-fc-wrap { margin-top: var(--space-4); }
.pf-fc-wrap .section-title { margin-bottom: var(--space-2); }
.pf-fc { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
.pf-inputs { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--space-3); align-items: end; }
.pf-asset-row { display: grid; grid-template-columns: 1.1fr 1fr 1fr; gap: var(--space-3); margin-bottom: var(--space-3); }
.field label { display: block; font-size: var(--fs-xs); font-weight: 600; color: var(--ink2); margin-bottom: var(--space-1); }
.field input, .field select {
  width: 100%; height: var(--control-h); padding: 0 var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-s);
  font: inherit; font-size: var(--fs-base); background: var(--card); color: var(--ink);
}
.field input:focus, .field select:focus { outline: 2px solid var(--focus); border-color: var(--brand-2); }
/* Питч алертов для анонима: пример письма — вложенная поверхность, --radius-s,
   моноширинный текст письма, чтобы он читался как письмо, а не как ещё один абзац. */
.al-sample {
  margin: var(--space-4) 0; padding: var(--space-3);
  background: var(--neutral-bg); border: 1px solid var(--line-soft); border-radius: var(--radius-s);
}
.al-sample .eyebrow { margin-bottom: var(--space-2); }
.al-sample-subj { margin: 0 0 var(--space-1); font-weight: 700; font-size: var(--fs-sm); }
.al-sample-body { margin: 0; font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--ink2); }

.alloc-bar { display: flex; height: 12px; border-radius: var(--radius-pill); overflow: hidden; gap: 2px; background: var(--line-soft); }
.alloc-bar i { display: block; height: 100%; }

/* ---------- Формы ---------- */
.flash { border-radius: var(--radius-s); padding: var(--space-3) var(--space-4); font-size: var(--fs-base); margin-bottom: var(--space-4); display: flex; align-items: center; gap: var(--space-2); }
.flash .ic { flex: none; }
.flash.ok { background: var(--up-bg); color: var(--up); }
.flash.err { background: var(--down-bg); color: var(--down); }
.flash.warn { background: var(--warn-bg); color: var(--warn); }

/* ---------- Auth ---------- */
.auth-wrap { min-height: calc(100vh - 120px); display: flex; align-items: center; justify-content: center; padding: var(--space-8) var(--space-5); }
.auth-card { width: 400px; max-width: 100%; }
.auth-card .card { padding: var(--space-7); }
.auth-logo { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); margin-bottom: var(--space-6); text-align: center; }
.auth-h { margin-bottom: var(--space-1); }
.auth-lede { margin-bottom: var(--space-4); }
.auth-note { color: var(--ink2); }
.auth-form { display: flex; flex-direction: column; gap: var(--space-3); }
.auth-foot { text-align: center; margin-top: var(--space-4); font-size: var(--fs-sm); color: var(--ink2); }
.auth-note + .auth-note { margin-top: var(--space-3); }
.auth-note + .auth-form, .auth-lede + .auth-resend { margin-top: var(--space-4); }
/* Повторная отправка ссылки — отдельный блок над формой входа, с разделителем */
.auth-resend { padding-bottom: var(--space-4); border-bottom: 1px solid var(--line-soft); margin-bottom: var(--space-4); }
.auth-fine { margin-top: var(--space-4); }
/* «or» между формой и входом через Google: линия с подписью по центру */
.auth-or { display: flex; align-items: center; gap: var(--space-3); margin: var(--space-4) 0; color: var(--muted); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
/* Вторая кнопка блока идёт во всю ширину карточки — иначе она читается как что-то мельче формы */
.auth-wide { width: 100%; justify-content: center; }
/* Переключатель темы на страницах входа (топбара там нет) */
.theme-float { position: fixed; top: var(--space-4); right: var(--space-4); z-index: 50; }

/* ---------- Админка ---------- */
/* wrap в базе, а не в медиа: единственная переносимая подпись («System and data») забирала
   всё сжатие ряда, складывалась в две строки и резалась пилюлей 32px (861–1140px). Перенос
   вместо сжатия работает на любой ширине и не зависит от числа табов. */
.admin-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-5); }
.admin-tabs a { display: inline-flex; align-items: center; gap: var(--space-2); height: var(--control-h-sm); padding: 0 var(--space-4); border-radius: var(--radius-pill); font-weight: 600; font-size: var(--fs-base); color: var(--ink2); }
.admin-tabs a.active { background: var(--brand-bg); color: var(--brand-ink); }
.admin-tabs a:hover { text-decoration: none; background: var(--neutral-bg); }
.role-pill { font-size: var(--fs-xs); color: var(--ink2); }
.inline-form { display: inline; }
.actions-cell { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.kv { display: grid; grid-template-columns: 220px 1fr; gap: var(--space-2) var(--space-4); font-size: var(--fs-base); }
.kv dt { color: var(--muted); } .kv dd { margin: 0; font-weight: 600; }
pre.log { background: var(--tip-bg); color: var(--tip-muted); border-radius: var(--radius-s); padding: var(--space-4); font-size: var(--fs-xs); overflow: auto; max-height: 320px; white-space: pre; }
.mono { font-family: var(--font-mono); font-size: .93em; }
code.mono { background: var(--neutral-bg); border-radius: var(--radius-xs); padding: 2px var(--space-2); font-size: var(--fs-xs); overflow-wrap: anywhere; max-width: 100%; }

/* Список вместо графика: при n<7 столбчатая диаграмма рисовала пустую область
   с двумя палочками у края и выглядела сломанной (аудит A39). */
.stat-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; }
.stat-list li {
  display: flex; align-items: baseline; gap: var(--space-3);
  padding: var(--space-2) 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-sm);
}
.stat-list li:last-child { border-bottom: 0; }
.stat-list .d { color: var(--ink2); font-variant-numeric: tabular-nums; }
.stat-list .v { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-list .bar { flex: 1; height: 6px; border-radius: var(--radius-xs); background: var(--line-soft); overflow: hidden; min-width: 40px; }
/* Длина полосы — единственная величина, которую вьюха обязана посчитать сама (доля от
   максимума). Приходит она пользовательским свойством, а не готовой декларацией width:
   так в разметке остаётся ДАННОЕ, а оформление целиком живёт здесь. */
.stat-list .bar i { display: block; height: 100%; width: var(--bar-w, 0); background: var(--data-line); }

.empty { color: var(--muted); font-size: var(--fs-sm); padding: var(--space-4) 0; }
/* Текст диалога подтверждения (собирается в ui.js): раньше стоял инлайн-стилем с 14.5px */
.dlg-text { font-size: var(--fs-base); color: var(--ink2); }

/* ---------- Меню пользователя ---------- */
.user-trigger {
  display: inline-flex; align-items: center; gap: var(--space-2);
  height: var(--control-h-sm);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: none; color: var(--ink); cursor: pointer;
  padding: 0 var(--space-3) 0 var(--space-1); font: inherit; font-size: var(--fs-sm); font-weight: 600;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.user-trigger:hover { border-color: var(--muted); background: var(--neutral-bg); }
.user-trigger:active { transform: translateY(1px); }
.user-trigger .avatar {
  width: var(--control-h-xs); height: var(--control-h-xs); border-radius: var(--radius-pill); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--brand), var(--brand-2)); color: var(--on-solid);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .02em;
}
.user-trigger .caret { color: var(--muted); width: 14px; height: 14px; }

/* ---------- Скелетоны загрузки ----------
   Скелетон обязан занимать столько же места, сколько займёт содержимое: иначе он не
   уменьшает сдвиг вёрстки, а откладывает его. На 400 kbps /markets набирала CLS 0.156
   при пороге 0.1 — карточка вырастала со 110 до 262px, график с 300 до 405, таблица
   точности с 94 до 209. Цифры ниже — измеренные высоты готового содержимого, а не
   подобранные на глаз, и потому это единственные «магические» px в файле. */
.sk-card { display: flex; flex-direction: column; gap: var(--space-2); }
.sk-line { height: 12px; border-radius: var(--radius-xs); background: var(--line-soft); animation: sk-pulse 1.4s ease-in-out infinite; }
.sk-line.w40 { width: 40%; } .sk-line.w60 { width: 60%; } .sk-line.w80 { width: 80%; }
.sk-line.tall { height: 28px; }
.sk-block { height: 240px; border-radius: var(--radius-s); background: var(--line-soft); animation: sk-pulse 1.4s ease-in-out infinite; }
#stat-cards .sk-card { min-height: 262px; }
.chart-card.sk-card .sk-block { height: 345px; }
#accuracy-box.sk-card { min-height: 209px; }
@keyframes sk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .sk-line, .sk-block { animation: none; } }

/* ---------- Движение ----------
   Общие правила и обоснование — в конце assets/css/components.css. Здесь только то,
   что живёт на страницах приложения. */
.seg button, .admin-tabs a {
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.flash { animation: var(--animation-slide-in-down) forwards; animation-duration: var(--dur); animation-timing-function: var(--ease); }
/* transition тени объявляет .card на все карточки, но hover есть не у всех — переход
   в никуда. Оставлен там, где :hover действительно определён. */
.chart-card:hover, .grid-cards .card:hover { box-shadow: var(--shadow-hover); }
details.fc-why[open] .fc-why-body { animation: var(--animation-fade-in); animation-duration: var(--dur); }

/* Данные встают на место скелетона: 200 мс fade без сдвига. Сдвиг (translateY) намеренно
   не используется — он вернул бы CLS, ради которого скелетоны держат высоту. Числа внутри
   при этом не анимируются: count-up на цене врёт про то, что значение «росло». */
#stat-cards > .card, #charts > .chart-card, #accuracy-box:not(.sk-card),
#cycles-cards > .card, #dca-stats > *, #wi-result > * {
  animation: data-in var(--dur) var(--ease);
}

/* ---------- Responsive ---------- */
@media (max-width: 1000px) {
  .grid-cards { grid-template-columns: repeat(2, 1fr); }
  .fc-grid { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
}
@media (max-width: 860px) {
  .user-trigger .uname { display: none; }
  .user-trigger { padding: 0 var(--space-2) 0 var(--space-1); }
}
@media (max-width: 640px) {
  .grid-cards { grid-template-columns: 1fr; }
  .pf-inputs { grid-template-columns: 1fr; }
  .pf-asset-row { grid-template-columns: 1fr; gap: var(--space-2); padding-bottom: var(--space-2); border-bottom: 1px dashed var(--line); }
  #charts { gap: var(--gap-grid); }
  /* Высоты содержимого на узком экране другие — резерв скелетона тоже */
  #stat-cards .sk-card { min-height: 178px; }
  .chart-card.sk-card .sk-block { height: 370px; }
  #accuracy-box.sk-card { min-height: 259px; }
  /* Палец, а не курсор: сегменты дорастают до 40px. Горизонтальный паддинг остаётся
     --space-3 — на 375px ряд из семи кнопок и так на грани переноса. */
  .seg button { height: var(--control-h); }

  .chart-head { gap: var(--space-2) var(--space-3); }
  .fc-row { gap: var(--space-2); padding: var(--space-2) var(--space-3); }
  .conf-bar.sm { width: 30px; }
  .legend-note { gap: var(--space-2) var(--space-3); }

  .admin-tabs a { padding: 0 var(--space-3); font-size: var(--fs-sm); }
  .kv { grid-template-columns: 1fr; gap: 2px 0; }
  .kv dt { margin-top: var(--space-2); }
  .kv dd { margin-bottom: 2px; }
  .actions-cell .btn { flex: none; }
  .auth-card .card { padding: var(--space-6) var(--space-4); }
  pre.log { font-size: var(--fs-xs); padding: var(--space-3); }
}

/* Портфель: срок владения, альтернативы и температура входа (аудит E1-F19, E3-6, E3-22).
   Блок альтернатив — вложенная поверхность на --radius-s, как .al-sample: он сравнивает
   три величины и должен читаться группой, а не тремя абзацами подряд. */
.pf-vs { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--line-soft); }
.pf-vs .section-title { margin-bottom: var(--space-2); }
.pf-vs .sub { margin-top: var(--space-2); }
#pf-held, #pf-temp, #pf-derived { margin-top: var(--space-3); }
#pf-temp b { color: var(--ink); font-weight: 700; }

/* ---------- Админка: раскладка и ритм ----------
   Приватные экраны отстали от публичных на одну ревизию дизайн-системы: в семи вьюхах
   жил 101 инлайн-стиль с магическими px (margin-top: 12/14/10/6/2, gap: 10, width: 90/110)
   и один хардкод-хекс #f0f0f3 на превью письма, который в тёмной теме оставался светлой
   плашкой. Ниже — набор классов, которым они заменены.

   Набор намеренно маленький и утилитарный: это раскладка семи служебных экранов, а не
   компоненты продукта, и разводить под каждый блок семантический класс было бы дороже,
   чем полезнее. Значения вне шкалы 4px схлопнуты к ближайшей ступени: 10 и 12 → 12,
   14 и 16 → 16, 2 / 6 / 8 → 8. Ширины полей заданы в ch — это ширина цифры текущего
   шрифта, то есть поле измерено содержимым (4 цифры, HH:MM, короткий хэндл), а не
   подобрано на глаз. */
[data-page^="admin-"] .adm-h    { margin: 0 0 var(--space-4); }
[data-page^="admin-"] .adm-m0   { margin: 0; }
[data-page^="admin-"] .adm-mt-xs { margin-top: var(--space-1); }
[data-page^="admin-"] .adm-mt-s { margin-top: var(--space-2); }
[data-page^="admin-"] .adm-mt   { margin-top: var(--space-3); }
[data-page^="admin-"] .adm-mt-l { margin-top: var(--space-4); }
[data-page^="admin-"] .adm-mb   { margin-bottom: var(--space-4); }
[data-page^="admin-"] .adm-my   { margin: var(--space-3) 0; }
[data-page^="admin-"] .adm-my-s { margin: var(--space-2) 0; }

[data-page^="admin-"] .adm-row  { display: flex; flex-wrap: wrap; gap: var(--space-3); }
[data-page^="admin-"] .adm-mid  { align-items: center; }
[data-page^="admin-"] .adm-end  { align-items: flex-end; }
[data-page^="admin-"] .adm-between { justify-content: space-between; }
[data-page^="admin-"] .adm-col  { display: flex; flex-direction: column; gap: var(--space-3); }
/* Столбец действий: кнопки во всю ширину — иначе четыре разной длины дают рваный правый
   край. Селектор потомка, а не ребёнка: каждая кнопка обёрнута в собственную <form> с CSRF. */
[data-page^="admin-"] .adm-col .btn { width: 100%; }
[data-page^="admin-"] .adm-grow { flex: 1; min-width: 11rem; }

[data-page^="admin-"] .adm-w-num  { width: calc(7ch + var(--space-6)); }
[data-page^="admin-"] .adm-w-time { width: calc(9ch + var(--space-6)); }
[data-page^="admin-"] .adm-w-hand { max-width: calc(21ch + var(--space-6)); }

[data-page^="admin-"] .adm-note { color: var(--ink2); font-size: var(--fs-base); }
[data-page^="admin-"] .adm-warn { color: var(--warn); }
[data-page^="admin-"] .adm-list {
  margin: var(--space-3) 0 0; padding-left: var(--space-5);
  color: var(--ink2); font-size: var(--fs-base); line-height: 1.7;
}
[data-page^="admin-"] .adm-steps { margin: var(--space-2) 0 0; padding-left: var(--space-5); }
/* Итоговая строка таблицы (<tfoot>): линия отделяет её от ряда дней, полужирный говорит,
   что это сумма, а не ещё один день. Правило админское, а не в общем слое: в /learn/
   у .prose таблиц свой tfoot из редактора WP, и утяжелять его нам незачем. */
[data-page^="admin-"] .adm-total > td { font-weight: 600; border-top: 1px solid var(--line); }
/* Вывод команды: вложенная поверхность на --radius-s, как .al-sample и .pf-vs */
[data-page^="admin-"] .adm-out {
  white-space: pre-wrap; margin: var(--space-2) 0 0; padding: var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-s);
  /* Превью соцпоста несёт сырой HTML со ссылками: неразрывный 64-символьный токен
     распирал страницу вбок на 375 (pre-wrap ломает строки, но не слова). */
  overflow-wrap: anywhere;
}
/* Чекбокс внутри .field не подчиняется общему правилу «ширина 100%, высота контрола»:
   растянутый на всю строку, он выглядел безымянной серой плашкой, а подпись уезжала вниз. */
[data-page^="admin-"] .field input[type="checkbox"] { width: auto; height: auto; flex: 0 0 auto; }
[data-page^="admin-"] .adm-sep { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--line); }
[data-page^="admin-"] .adm-chart { display: block; margin-top: var(--space-3); }
/* Превью соц-карточки в очереди ручной публикации: владелец видит ровно ту картинку,
   которую приложит к посту, поэтому ширину ограничиваем колонкой, а не растягиваем. */
[data-page^="admin-"] .adm-card-img {
  display: block; max-width: min(480px, 100%); height: auto;
  margin-top: var(--space-2); border: 1px solid var(--line); border-radius: var(--radius-s);
}
/* Превью письма: 640px — высота окна просмотра, а не контрола, шкала --control-h к ней
   не относится. Фон — var(--bg): здесь стоял хекс #f0f0f3 (светлый --bg), и в тёмной
   теме внутри тёмной страницы оставалась светло-серая плашка. */
[data-page^="admin-"] .adm-preview {
  width: 100%; height: 640px; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
