/* ═══════════════════ ОФОРМЛЕНИЕ (v0.9.0) ═══════════════════
   Три палитры × три режима темы = девять сочетаний.

   Устройство слоя (важно при правке):
   1. Палитра задаёт ПАРЫ значений `--l-*` (светлое) и `--d-*` (тёмное).
      Палитра про темы ничего не знает.
   2. Отображение пары в рабочий токен (`--bg`, `--ink`, …) написано
      НИЖЕ и ровно один раз на режим. Оно про палитры ничего не знает.
   Поэтому четвёртая палитра — это один блок пар, и больше ничего.

   Рабочие токены (`--bg`, `--accent`, …) — единственное, что читают
   правила ниже. Хекс в коде компонента = дефект: он переживёт смену
   палитры и в тёмной теме станет нечитаемым.

   Кремово-бирюзовая палитра до v0.9.0 (`#f7f6f2` / `#0f766e`) снята
   осознанно: так оформлен вольт Obsidian, и одинаковый вид мешал
   переключать контекст между инструментами. */

:root, :root[data-p="indigo"] {
  /* ── Индиго: холодный белый, сине-фиолетовый акцент ── */
  --l-bg:#f4f6fa;      --d-bg:#12141c;
  --l-card:#ffffff;    --d-card:#1a1d28;
  --l-field:#ffffff;   --d-field:#232734;
  --l-sunken:#eaeef6;  --d-sunken:#232734;
  --l-ink:#151a24;     --d-ink:#e7e9f0;
  --l-muted:#5a6376;   --d-muted:#979db0;
  --l-faint:#8b93a6;   --d-faint:#6e7488;
  --l-line:#e2e6ef;    --d-line:#2a2e3c;
  --l-line-2:#cdd4e2;  --d-line-2:#3a3f51;
  --l-accent:#4f46e5;  --d-accent:#8b87ff;
  --l-acc-ink:#ffffff; --d-acc-ink:#0d0f16;
  --l-acc-soft:#eef0fe;--d-acc-soft:#23253a;
  --l-acc-2:#7c74f0;   --d-acc-2:#a9a5ff;
  --l-good:#15803d;    --d-good:#4ade80;
  --l-good-bg:#f0fdf4; --d-good-bg:#16281d;
  --l-good-ink:#166534;--d-good-ink:#7ee6a4;
}
:root[data-p="steel"] {
  /* ── Сталь: приглушённый синий, самый тихий ── */
  --l-bg:#f3f5f7;      --d-bg:#11161a;
  --l-card:#ffffff;    --d-card:#182027;
  --l-field:#ffffff;   --d-field:#212b33;
  --l-sunken:#e8ecef;  --d-sunken:#212b33;
  --l-ink:#141b21;     --d-ink:#e4e9ed;
  --l-muted:#5a6873;   --d-muted:#929ea8;
  --l-faint:#8b98a2;   --d-faint:#6b7780;
  --l-line:#dfe4e9;    --d-line:#28323a;
  --l-line-2:#c8d1d8;  --d-line-2:#38444d;
  --l-accent:#2f6690;  --d-accent:#66a9d6;
  --l-acc-ink:#ffffff; --d-acc-ink:#08111a;
  --l-acc-soft:#e7eff5;--d-acc-soft:#1b2c39;
  --l-acc-2:#4a87b5;   --d-acc-2:#8cc3e6;
  --l-good:#15803d;    --d-good:#4ade80;
  --l-good-bg:#f0fdf4; --d-good-bg:#16281d;
  --l-good-ink:#166534;--d-good-ink:#7ee6a4;
}
:root[data-p="lime"] {
  /* ── Уголь и лайм: акцент у пика чувствительности глаза (~555 нм).
     `--good` здесь СОВПАДАЕТ с акцентом: отдельный зелёный рядом с
     салатовым не различается, а «рекорд» и «акцент» — одна мысль. ── */
  --l-bg:#f5f6f3;      --d-bg:#14171a;
  --l-card:#ffffff;    --d-card:#1c2024;
  --l-field:#ffffff;   --d-field:#252a2e;
  --l-sunken:#eaece5;  --d-sunken:#252a2e;
  --l-ink:#171a16;     --d-ink:#e8eae5;
  --l-muted:#5c6359;   --d-muted:#969c93;
  --l-faint:#8c948a;   --d-faint:#6e756b;
  --l-line:#e3e6de;    --d-line:#2a2f33;
  --l-line-2:#cdd2c6;  --d-line-2:#3a4045;
  --l-accent:#4d7c0f;  --d-accent:#a3e635;
  --l-acc-ink:#ffffff; --d-acc-ink:#10160a;
  --l-acc-soft:#edf3e3;--d-acc-soft:#263021;
  --l-acc-2:#6b9e22;   --d-acc-2:#c4f162;
  --l-good:#4d7c0f;    --d-good:#a3e635;
  --l-good-bg:#edf3e3; --d-good-bg:#263021;
  --l-good-ink:#3f6a0b;--d-good-ink:#bced6b;
}

/* ── Отображение пар в рабочие токены. Три блока, и это весь механизм тем.
   Порядок обязателен: явный выбор «Тёмная» идёт ПОСЛЕ медиазапроса —
   у них одинаковая специфичность, и решает исходный порядок. ── */
:root {
  --bg:var(--l-bg); --card:var(--l-card); --field:var(--l-field); --sunken:var(--l-sunken);
  --ink:var(--l-ink); --muted:var(--l-muted); --faint:var(--l-faint);
  --line:var(--l-line); --line-2:var(--l-line-2);
  --accent:var(--l-accent); --accent-ink:var(--l-acc-ink);
  --accent-soft:var(--l-acc-soft); --accent-2:var(--l-acc-2);
  --good:var(--l-good); --good-bg:var(--l-good-bg); --good-ink:var(--l-good-ink);

  /* Смысловые цвета от палитры не зависят: «внимание» обязано читаться
     как внимание в любой из них. Меняются только под тему. */
  --review-bg:#fff7ed; --review-line:#f59e0b; --review-ink:#92400e;
  /* текст НА янтарной плашке. Одинаков в обеих темах и потому объявлен
     только здесь: сама плашка янтарная и в светлой, и в тёмной. */
  --review-on:#2a1e08;
  --error:#b91c1c; --error-bg:#fef2f2; --error-line:#fecaca;
  --info-bg:#ecfeff; --info-ink:#0e7490;
  --warm-bg:#fef3c7; --warm-ink:#92400e;
  --atlas-bg:#fbf6ee; --atlas-line:#c2410c;
  --goal:#94a3b8; --deload:#c2410c; --return:#6366f1;
  /* Категориальные — для графиков (тип дня). Различаются между собой,
     а не «оттенками акцента»: иначе два типа дня сольются в один. */
  --cat-1:var(--l-accent); --cat-2:#c2410c; --trend:#334155; --connect:var(--l-line-2);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:var(--d-bg); --card:var(--d-card); --field:var(--d-field); --sunken:var(--d-sunken);
    --ink:var(--d-ink); --muted:var(--d-muted); --faint:var(--d-faint);
    --line:var(--d-line); --line-2:var(--d-line-2);
    --accent:var(--d-accent); --accent-ink:var(--d-acc-ink);
    --accent-soft:var(--d-acc-soft); --accent-2:var(--d-acc-2);
    --good:var(--d-good); --good-bg:var(--d-good-bg); --good-ink:var(--d-good-ink);
    --review-bg:#2a2113; --review-line:#f0b429; --review-ink:#f0b429;
    --error:#f87171; --error-bg:#2a1616; --error-line:#4a2020;
    --info-bg:#12303a; --info-ink:#67d9e8;
    --warm-bg:#322815; --warm-ink:#e9b949;
    --atlas-bg:#26211a; --atlas-line:#e07a46;
    --goal:#6b7a8c; --deload:#f08a5d; --return:#a5a3fb;
    --cat-1:var(--d-accent); --cat-2:#f08a5d; --trend:#94a3b8; --connect:var(--d-line-2);
  }
}
:root[data-theme="dark"] {
  --bg:var(--d-bg); --card:var(--d-card); --field:var(--d-field); --sunken:var(--d-sunken);
  --ink:var(--d-ink); --muted:var(--d-muted); --faint:var(--d-faint);
  --line:var(--d-line); --line-2:var(--d-line-2);
  --accent:var(--d-accent); --accent-ink:var(--d-acc-ink);
  --accent-soft:var(--d-acc-soft); --accent-2:var(--d-acc-2);
  --good:var(--d-good); --good-bg:var(--d-good-bg); --good-ink:var(--d-good-ink);
  --review-bg:#2a2113; --review-line:#f0b429; --review-ink:#f0b429;
  --error:#f87171; --error-bg:#2a1616; --error-line:#4a2020;
  --info-bg:#12303a; --info-ink:#67d9e8;
  --warm-bg:#322815; --warm-ink:#e9b949;
  --atlas-bg:#26211a; --atlas-line:#e07a46;
  --goal:#6b7a8c; --deload:#f08a5d; --return:#a5a3fb;
  --cat-1:var(--d-accent); --cat-2:#f08a5d; --trend:#94a3b8; --connect:var(--d-line-2);
}
/* ═══════════════════ ТИПОГРАФИКА (Ф3) ═══════════════════
   Ряд Apple iOS (Large): 11 · 12 · 13 · 15 · 17 · 20 · 22. Приложение рендерится
   SF Pro в Safari, и ряд выверен под этот шрифт. До Ф3 в файле было 22 записанных
   значения кегля в 80 объявлениях — на одной сводке 18 разных кеглей.

   ⭐ Основной кегль 17 px, а НЕ 16, и это результат замера, а не вкус. Дмитрий
   смотрит на телефон с 40 см (померил рулеткой 28.07.2026); при этом расстоянии
   порог читаемости 12′ даёт 17 px, то есть прежние 16 px были уже под полом.
   У Hevy основной текст тоже 17 pt. Экран разгружается удалением содержимого и
   `line-height` 1,5 → 1,3, а НЕ уменьшением шрифта — исход записан до замера.

   Куда какой токен (правило, а не вкус — держать при добавлении новых блоков):
   · payload (числа, названия упражнений, проза, поля ввода) → не мельче --fs-md;
   · подписи и служебное (микро-подписи полей, даты, метки) → --fs-sm;
   · микро (плашки, uppercase-надписи, единицы измерения) → --fs-xs / --fs-2xs.

   Ступень размера текста (экран настроек, Ф10) — множитель `--fs-k`. Литеральные
   px не масштабируются ничем, поэтому каждый токен объявлен через calc; значение
   ставится атрибутом `data-fs` — как палитра, и в `_theme_boot.html` тоже, иначе
   кегль мигал бы при каждом заходе.
   ⚠️ Пол 11 px и порог поля 16 px из-под множителя ВЫВЕДЕНЫ через max(): ступень
   «компактный» существует, чтобы Дмитрий проверил на себе ОСНОВНОЙ кегль, а не
   чтобы увести служебный текст под физиологический порог и включить зум iOS.

   Исключений больше нет: Ф8 вынесла подписи графика из холста в HTML-слой, и теперь
   они подчиняются той же шкале. Пока они жили внутри SVG, их px были единицами viewBox
   и ехали вместе с шириной контейнера — 6 px на iPhone против 16 px на Mac. */
:root {
  --fs-2xs: max(11px, calc(11 * var(--fs-k, 1) * 1px));
  --fs-xs:  max(11px, calc(12 * var(--fs-k, 1) * 1px));
  --fs-sm:  calc(13 * var(--fs-k, 1) * 1px);
  --fs-md:  calc(15 * var(--fs-k, 1) * 1px);
  --fs-lg:  calc(17 * var(--fs-k, 1) * 1px);
  --fs-xl:  calc(20 * var(--fs-k, 1) * 1px);
  --fs-metric: calc(22 * var(--fs-k, 1) * 1px);

  --lh-tight: 1.2;   /* крупные числа и заголовки */
  --lh-ui: 1.3;      /* интерфейс; было 1.5 на весь документ */
  --lh-read: 1.5;    /* абзацы, которые читают подряд */
}
:root[data-fs="compact"] { --fs-k: .9; }
:root[data-fs="large"]   { --fs-k: 1.15; }

* { box-sizing: border-box; }
html {
  /* 100%, а не none: `none` выключил бы и пользовательское увеличение — это нарушение
     WCAG 1.4.4. Нужно ровно одно: запретить Safari самому раздувать текст в альбомной
     ориентации, иначе шкала кеглей на телефоне перестаёт что-либо значить. */
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: var(--fs-lg); line-height: var(--lh-ui);
}
/* Числа в списках и таблицах — моноширинные, и объявляется это на КОНТЕЙНЕРЕ, а не на
   каждой ячейке: равнять надо весь столбец, а поштучные объявления разъезжаются, как
   только в блок добавят новую ячейку (ровно так гуляла колонка веса в «Рекордах»). */
.hcard, .records, .mbar, .matrix, .detail-table, .manual-row,
.journal-list, .coach-card, .stat, .chart-head { font-variant-numeric: tabular-nums; }

main { max-width: 900px; margin: 0 auto; padding: 1rem 1.2rem 4rem; }
h1 { font-size: var(--fs-xl); margin: 1rem 0; }
h3 { margin: 0 0 .5rem; font-size: var(--fs-lg); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.topbar {
  display: flex; align-items: center; gap: 1.5rem;
  /* safe-area: в standalone-режиме iPhone шапка иначе уезжает под «чёлку» */
  padding: calc(.7rem + env(safe-area-inset-top)) 1.2rem .7rem;
  padding-left: max(1.2rem, env(safe-area-inset-left));
  padding-right: max(1.2rem, env(safe-area-inset-right));
  background: var(--card); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
.brand { font-weight: 700; font-size: var(--fs-lg); white-space: nowrap; }
/* часы в сквозной полосе (п.17): моноширинные цифры, чтобы строка не дёргалась каждую секунду */
.now {
  margin-left: auto; color: var(--muted); font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Выход стоит на экране настроек (Ф10), а не в шапке: замер показал, что жёсткий ряд шапки
   вылезал за 320 px при любой ступени текста и за 375 px при крупной, а нажимают выход
   примерно никогда. Цель 44 px — как у прочих кнопок, теперь ей есть где развернуться. */
.logout-form { margin: 0; }
.logout-btn { min-height: 44px; padding: .5rem 1rem; }

/* Разделы — колонкой слева под «Lift» (п.19): у разделов есть подвкладки, в строку не влезают */
.shell { display: grid; grid-template-columns: 170px minmax(0, 1fr); align-items: start; }
.sections {
  display: flex; flex-direction: column; gap: .15rem;
  padding: 1rem .6rem; border-right: 1px solid var(--line);
  position: sticky; top: 3.4rem;
}
.sections a {
  padding: .5rem .7rem; border-radius: 8px; color: var(--ink); font-size: var(--fs-md);
}
.sections a:hover { background: var(--card); text-decoration: none; }
.sections a.on { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
/* Шире прежних 900px: раньше страница была центрирована на всю ширину окна, теперь слева
   стоит меню. 1080 позволяет пяти плиткам сводки встать в один ряд, а не оставлять пятую одну. */
.shell main { max-width: 1080px; margin: 0; padding: 1rem 1.2rem 4rem; }

/* Страница входа (v0.5.0) — отдельный экран без меню разделов */
.login-main { max-width: 380px; margin: 0 auto; padding: 3rem 1.2rem; }
.login-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 1.6rem 1.4rem 1.2rem;
}
.login-brand { font-size: var(--fs-xl); font-weight: 700; }
.login-submit { width: 100%; margin-top: 1rem; }
.login-note { margin-top: 1rem; }

label { display: block; margin: .6rem 0 .2rem; color: var(--muted); font-size: var(--fs-sm); }
input, select, textarea, button { font: inherit; }
/* ⛔ Поле ввода не мельче 16 px: при меньшем кегле iOS Safari зумит вьюпорт на фокусе,
   и обратно страница уже не выходит — в зале это приговор экрану ввода. `max()` держит
   порог и на ступени «компактный» (0,9 × 17 = 15,3). Отдельные поля свой кегль не
   объявляют: это правило — единственное место, где он задаётся. */
input, select, textarea { font-size: max(16px, var(--fs-lg)); }
/* Поле выбора файла (скриншот весов) ширину задаёт САМО — по кнопке и имени файла, то есть
   по кеглю; общего `width: 100%` у него нет, оно не в списке типов ниже. Раньше оно
   наследовало кегль ПОДПИСИ (14,4 px), теперь получает общий порог поля 17 px — собственная
   ширина стала 350 px, больше содержимого сводки на 375 px, и распёрла ВСЮ страницу:
   документ 403 px при окне 375, горизонтальная прокрутка на каждом экране сводки.
   ⚠️ Распор латентный: замером проверено, что и на прежних 16 px документ был 386 px —
   его прятал только мелкий кегль подписи. `max-width: 100%` не лечит (проценты при
   неопределённой ширине родителя во внутреннюю ширину не считаются), `overflow: hidden`
   на подписи тоже (гасит автоминимум гибкого элемента, но не min-content содержимого).
   Лечит явная ширина самого поля — и заодно это правильный вид на телефоне. */
.bw-form .file-ctl input[type=file] { width: 100%; min-width: 0; }
/* `input[type=time]` в списке обязателен: без него пустое поле времени на iOS схлопывается
   в огрызок шириной в placeholder — на снимке Дмитрия 28.07.2026 оно выглядело обрывком
   рядом с «Тип». Ширина решается здесь, а не раскладкой. */
input[type=number], input[type=text], input[type=date], input[type=time], select, textarea {
  width: 100%; max-width: 100%; padding: .5rem .6rem;
  border: 1px solid var(--line); border-radius: 8px; background: var(--field); color: var(--ink);
}
textarea { resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.btn-primary {
  margin-top: 1rem; padding: .6rem 1.2rem; border: 0; border-radius: 9px;
  background: var(--accent); color: var(--accent-ink); font-weight: 600; cursor: pointer;
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost {
  padding: .45rem .9rem; border: 1px solid var(--line); border-radius: 9px;
  background: var(--field); color: var(--ink); cursor: pointer;
}
.btn-del { border: 0; background: none; color: var(--muted); cursor: pointer; font-size: var(--fs-lg); }
/* ⛔ Необратимое действие: контур и цвет ошибки, но НЕ заливка. Сплошная красная кнопка на
   экране, где рядом стоят «Скопировать» и «Сохранить как шаблон», перетягивала бы взгляд на
   себя — а удаление это не то, к чему ведут. Держится поодаль от остальных действий
   (`.danger-form`), цель 44 px, как у прочих кнопок низа. */
.btn-danger {
  min-height: 44px; padding: .45rem .9rem; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--error-line); background: var(--error-bg); color: var(--error);
}
.danger-form { margin-top: 2rem; }

.hint { color: var(--muted); font-size: var(--fs-sm); }
.error { color: var(--error); background: var(--error-bg); border: 1px solid var(--error-line);
         padding: .6rem .8rem; border-radius: 8px; }
.flag { color: var(--review-line); font-size: var(--fs-sm); margin-left: .4rem; }

/* вкладки */
/* ⚠️ Ф9: подвкладок «Прогресса» стало три, и лента перестала помещаться — надписи начали
   переноситься ВНУТРИ кнопок, лента выросла с 40,1 до 62,2 px (замер стендом на 375 и 402).
   Считано по замеру, а не на глаз: трём вкладкам нужно ~366 px при доступных 343, боковые
   поля по 1rem давали 38,4 px лишнего — 0,6rem закрывают недостачу с запасом (328 px).
   Кегль при этом не тронут (⛔ «не уменьшать», замер с 40 см), высота ленты не выросла.
   `flex-wrap` — страховка на ступень «крупный» из Ф10: там текст вырастет на 15%, и лента
   обязана перенестись целой вкладкой, а не разорвать слова ([[new-parameter-breaks-old-assumptions]]). */
.tabs { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0; }
.tab { padding: .5rem .6rem; border: 1px solid var(--line); border-radius: 9px;
       background: var(--field); cursor: pointer; color: var(--muted); }
.tab.active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.row-inline { display: flex; align-items: flex-end; gap: 1rem; flex-wrap: wrap; }

/* ═══════ Ручной ввод карточками (Ф7) ═══════
   Было таблицей `min-width: 700px` с прокруткой вбок: на 375 px за экраном оставалось
   62% ширины и 165 элементов, а до полей кардио надо было прокрутить мимо всех прочих.
   Карточка на упражнение, свёрнутая по умолчанию: заголовок отвечает «что записано»
   без раскрытия, а поля показываются те, что относятся к снаряду. */
/* ⚠️ Оба класса, а не один: карточка ввода берёт вид у `.set-card` (рамка, фон, радиус),
   но её отступы задаёт сама — а `.set-card` объявлен НИЖЕ по файлу и при равной
   специфичности побеждал, добавляя 25,6 px каждой из одиннадцати карточек. */
.set-card.manual-row { margin: .5rem 0; padding: 0; }
.mr-head { display: flex; align-items: center; gap: .5rem; padding: .45rem .6rem;
           cursor: pointer; list-style: none; }
.mr-head::-webkit-details-marker { display: none; }   /* маркер `<details>` тут только шумит */
.mr-pos { flex: none; min-width: 1.1rem; color: var(--muted); font-size: var(--fs-sm);
          font-variant-numeric: tabular-nums; }
/* Имя переносится, но не больше двух строк: полное название важнее — 18 из 29 не помещаются
   в строку, а различаются как раз хвостом («…в блоке» / «…на верхнем блоке»). Три строки и
   больше сделали бы список из 11 карточек длиннее исходной таблицы. */
.mr-name { flex: 1 1 auto; min-width: 0; font-weight: 600; line-height: var(--lh-tight);
           display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
           overflow: hidden; }
.mr-name:empty::before { content: 'упражнение не выбрано'; color: var(--muted); font-weight: 400; }
.mr-digest { flex: none; color: var(--ink); white-space: nowrap; font-size: var(--fs-md);
             font-variant-numeric: tabular-nums; }
.mr-digest.empty { color: var(--faint); font-size: var(--fs-sm); }
/* ⚠️ Раскрытую карточку обязано быть чем закрыть, и это должно быть ОЧЕВИДНО.
   Первая версия прятала в раскрытой шапке название и отдавала всю строку под «Разминку»
   и ✕ — оба с отменённым умолчанием, чтобы не складывать карточку. Получилось, что
   свернуть её нельзя вообще: живой экран поймал за минуту то, чего не увидели ни тесты,
   ни снимки (Дмитрий: «я раскрыл все и назад свернуть не могу»).
   Теперь шапка в обоих состояниях — зона сворачивания: название остаётся на месте,
   справа стрелка состояния, а «Разминка» и удаление живут в теле карточки. */
.mr-head::after { content: '⌄'; flex: none; color: var(--muted); font-size: var(--fs-md);
                  line-height: 1; padding: 0 .2rem; }
.manual-row[open] > .mr-head::after { content: '⌃'; }
.manual-row[open] > .mr-head { border-bottom: 1px solid var(--line); }
/* в раскрытой название — одной строкой: числа рядом уже не нужны, они в полях ниже */
.manual-row[open] > .mr-head > .mr-name { -webkit-line-clamp: 1; }
.manual-row[open] > .mr-head > .mr-digest { display: none; }
.mr-foot { display: flex; align-items: center; justify-content: space-between;
           gap: .8rem; margin-top: .5rem; }
.mr-body { padding: .5rem .6rem .6rem; }

/* Выбор упражнения: кнопка во всю ширину с ПОЛНЫМ названием (оно переносится — 18 названий
   из 29 не помещаются в строку на 375 px). Сам `<select>` лежит поверх прозрачным: он
   остаётся значением формы и обязательным полем, а `display: none` сделал бы сообщение
   браузера о незаполненном поле невидимым, и отправка срывалась бы молча. */
.mr-pick { position: relative; }
.mr-pickbtn { position: relative; display: block; width: 100%; text-align: left; min-height: 44px;
              padding: .4rem 1.6rem .4rem .6rem; border: 1px solid var(--line); border-radius: 9px;
              background: var(--field); color: var(--ink); font: inherit; cursor: pointer;
              /* 15 px, а не основные 17: название на кнопке переносится, и вторая строка
                 стоит 22 px высоты раскрытой карточки — а её бюджет ограничен остатком
                 экрана при открытой клавиатуре. 15 — пол для названий упражнений (⛔). */
              font-size: var(--fs-md); line-height: var(--lh-tight); }
/* Стрелка — отдельным слоем, а не хвостом текста: при длинном названии («Сгибание рук на
   нижнем блоке (бицепс)») она переносилась на СВОЮ строку и стоила карточке 22 px высоты.
   Место под неё держит `padding-right` кнопки выше — правило одно, а не два одинаковых
   по весу: «не помогло» в этом файле уже однажды означало «не применилось». */
.mr-pickbtn::after { content: '▾'; position: absolute; right: .6rem; top: 50%;
                     transform: translateY(-50%); color: var(--muted); }
.mr-sel-hidden { position: absolute; inset: 0; width: 100%; height: 100%;
                 opacity: 0; pointer-events: none; }
.mr-fields { display: flex; gap: .5rem; margin-top: .4rem; }
.mr-f { flex: 1 1 0; min-width: 0; margin: 0; color: var(--muted); font-size: var(--fs-sm);
        line-height: var(--lh-tight); }
.mr-f[hidden] { display: none; }
/* Обе кнопки низа — цели 44 px (⛔ плана эргономики §3.4). Раньше `.btn-ghost` давал 38,5:
   в карточке это был единственный контрол мельче цели. На бюджет высоты они не влияют —
   при наборе низ карточки уезжает за сгиб, и это нормально (см. «что должно быть видно»). */
.mr-foot .btn-ghost { min-height: 44px; }
.mr-foot .btn-del { flex: none; }
/* «+ упражнение» стоит СНАРУЖИ карточки и потому под правило выше не попадала — осталась
   единственной целью мельче 44 px (38,5). Замер 29.07.2026: рядом с ней в липкой полосе
   в тот же момент появляется «⇅ Порядок» (она возникает, когда карточек становится две),
   а промах по ней прячет «+ упражнение» совсем (`.reordering #add-row`). Мелкая цель
   делает этот промах вероятнее — правило распространено на кнопку. */
#add-row { min-height: 44px; }

/* ═══════ Строки подходов (правки приёмки §2) ═══════
   Подход = строка: «первый подход 15 раз, второй и третий по 12» иначе не занести.
   Ширина внутри карточки на 375 px — 321,8 (замерено): № 44 + ✕ 44 = 88 фиксированных,
   зазоры по 12 (⛔ плана эргономики §3.4), остальное делят вес и повторы. Подписи колонок
   стоят ОДИН раз над списком — на каждой строке они стоили бы по 20 px высоты. */
.mr-cols, .mr-set { display: flex; align-items: center; gap: 12px; }
.mr-cols { margin: .6rem 0 .2rem; color: var(--muted); font-size: var(--fs-sm);
           line-height: var(--lh-tight); }
.mr-cols span[hidden] { display: none; }
.mr-cols .c-num, .mr-cols .c-done, .mr-cols .c-del { flex: none; width: 44px; }
.mr-cols .c-done { text-align: center; }
.mr-cols .c-weight, .mr-set .f-weight { flex: 1.4 1 0; }
.mr-cols .c-reps, .mr-cols .c-min, .mr-cols .c-km,
.mr-set .f-reps, .mr-set .f-min, .mr-set .f-km { flex: 1 1 0; }
.mr-set + .mr-set { margin-top: .3rem; }
.mr-in { min-width: 0; margin: 0; }
.mr-in[hidden] { display: none; }
/* Ячейка номера — она же переключатель «разминка» (как в Hevy: тап по номеру задаёт тип
   подхода). Состояние ГРОМКОЕ — плашка и буква «р», а не оттенок: приглушение строки
   этому флагу не отдаём, оно занято галочкой «сделал».
   ⚠️ Вид переключается соседним селектором `input:checked + …`, а НЕ `:has()`: без `:has()`
   у контрола не осталось бы вообще никакой обратной связи, и разметка ставилась бы
   невидимо. А она меняет ФАКТ в базе — разминочный подход не входит в рабочий вес. */
.mr-num { position: relative; flex: none; width: 44px; margin: 0; padding: 0; cursor: pointer; }
.mr-num input { position: absolute; opacity: 0; width: 0; height: 0; }
.mr-num-t { display: flex; align-items: center; justify-content: center; min-height: 44px;
            border-radius: 9px; color: var(--muted); font-size: var(--fs-sm);
            font-variant-numeric: tabular-nums; }
.mr-num-t::before { content: attr(data-n); }
.mr-num input:checked + .mr-num-t { background: var(--accent-soft); color: var(--accent);
                                    font-weight: 600; }
.mr-num input:checked + .mr-num-t::after { content: 'р'; margin-left: .1rem; }
.mr-num input:focus-visible + .mr-num-t { outline: 2px solid var(--accent); outline-offset: 2px; }
/* «Сделал» — приглушение строки, и это средство отдано ТОЛЬКО ему: разминка помечается
   плашкой на номере. Одно и то же оформление на два разных состояния означало бы, что
   приглушённая строка не значит ничего. */
.mr-done { position: relative; flex: none; width: 44px; margin: 0; padding: 0; cursor: pointer; }
.mr-done input { position: absolute; opacity: 0; width: 0; height: 0; }
.mr-done-t { display: flex; align-items: center; justify-content: center; min-height: 44px;
             border-radius: 9px; border: 1px solid var(--line); background: var(--field);
             color: transparent; }
.mr-done input:checked + .mr-done-t { background: var(--accent); border-color: var(--accent);
                                      color: var(--accent-ink); }
.mr-done input:focus-visible + .mr-done-t { outline: 2px solid var(--accent); outline-offset: 2px; }
.mr-set:has(.mr-done input:checked) .mr-in,
.mr-set:has(.mr-done input:checked) .mr-num-t { opacity: .55; }
.btn-set-del { flex: none; width: 44px; min-height: 44px; padding: 0; border: 0;
               border-radius: 9px; background: none; color: var(--faint); font: inherit;
               cursor: pointer; }
.btn-set-del:hover { color: var(--ink); background: var(--sunken); }
/* Свайп влево = удалить (подход и упражнение целиком). Пока палец тащит — карточка
   едет за ним; утащил дальше порога — красная кайма справа говорит, что отпускать
   сейчас значит удалить. Оба движения обратимы: не дотащил — вернулась на место. */
.mr-set, .manual-row, .journal-list li { touch-action: pan-y; }
.mr-set.swipe-armed, .manual-row.swipe-armed, .journal-list li.swipe-armed {
  box-shadow: inset -4px 0 0 var(--error, #b3261e);
  border-radius: 9px;
}
.mr-tip { margin: .4rem 0 0; color: var(--faint); font-size: var(--fs-xs);
          line-height: var(--lh-tight); }
.mr-prev { margin: .4rem 0 0; color: var(--muted); font-size: var(--fs-sm);
           line-height: var(--lh-tight); font-variant-numeric: tabular-nums; }
.mr-prev[hidden] { display: none; }

/* Карточка тренировки (Ф7): та же карточка, что на вводе, но правит уже записанное —
   поэтому разметка «разминка/рабочий» идёт на КАЖДЫЙ подход. С З4 в ней есть и правка
   СОСТАВА: «Убрать упражнение» в подвале карточки и блок «Дописать упражнение» под списком. */
.set-card.sess-row, .set-card.add-ex { margin: .5rem 0; padding: 0; }
.sess-row.review { background: var(--review-bg); border-color: var(--review-line); }
/* Кнопка убирания — у ПРАВОГО края, как «Удалить упражнение» на экране ввода: там она стоит
   справа (`space-between` со «+ подход»), и место кнопки уже выучено рукой. Разное место для
   одного действия на двух экранах — это промах по соседнему контролу. */
.sess-row .mr-foot, .add-ex .mr-foot { justify-content: flex-end; }
/* Блок дописывания — тот же вид карточки, но подпись шапки не число, а «+»: он не входит
   в состав тренировки и в нумерации упражнений не участвует. */
.add-ex > .mr-head > .mr-pos { color: var(--accent); font-weight: 600; }
.sess-warm { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .5rem; }
.sess-warm-t { color: var(--muted); font-size: var(--fs-sm); }
.sess-warm .warmup-sel { flex: 1 1 6rem; margin: 0; }

/* Список упражнений с поиском */
.pick-dlg { width: min(94vw, 460px); max-height: 80vh; padding: 0; border: 0;
            border-radius: 14px; background: var(--card); color: var(--ink);
            overflow: hidden; }
.pick-dlg::backdrop { background: rgba(0, 0, 0, .45); }
.pick-top { display: flex; gap: .5rem; padding: .8rem; border-bottom: 1px solid var(--line); }
.pick-top .btn-ghost { flex: none; }
.pick-list { list-style: none; margin: 0; padding: .4rem; overflow-y: auto;
             max-height: calc(80vh - 4.5rem); }
.pick-item { display: block; width: 100%; text-align: left; min-height: 44px;
             padding: .55rem .7rem; border: 0; border-radius: 9px; background: none;
             color: var(--ink); font: inherit; cursor: pointer; }
.pick-item:hover { background: var(--sunken); }
.pick-item.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* «Сохранить» липнет к низу: форма длиннее экрана, и в зале кнопку искали прокруткой.
   Счётчик рядом отвечает на единственный вопрос, который возникает по ходу занятия. */
.save-bar { position: sticky; bottom: 0; z-index: 3; display: flex; align-items: center;
            justify-content: space-between; gap: .8rem; margin-top: 1rem;
            padding: .6rem 0 calc(.6rem + env(safe-area-inset-bottom));
            background: var(--bg); border-top: 1px solid var(--line); }
.save-bar .btn-primary { margin-top: 0; }
.save-count { color: var(--muted); font-size: var(--fs-sm); }
/* Полоса карточки тренировки: три колонки вместо `space-between`, чтобы «Сохранить»
   стояло на одном месте при любых соседях. Крайние колонки равны — кнопка ровно по центру
   независимо от длины дат и от того, есть ли соседняя тренировка вообще. */
.save-bar.nav-bar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; }
.nav-sess { justify-self: start; min-height: 44px; display: inline-flex; align-items: center;
            padding: 0 .4rem; white-space: nowrap; font-size: var(--fs-sm);
            font-variant-numeric: tabular-nums; }
.nav-sess.right { justify-self: end; }
/* Неактивная стрелка: видна, но не кликается. `--muted`, а не `--faint` — у второго
   контраст 2,88:1, ниже порога, и с 40 см стрелки просто нет. */
.nav-sess.off { color: var(--muted); }

/* ═══════ Режим порядка: «взять → положить сюда» ═══════
   Перетаскивание отвергнуто осознанно (план 30.07, §2): жест нельзя проверить нигде,
   кроме телефона Дмитрия, а `setPointerCapture` не переживает перемещения самого узла.
   Два тапа переносят карточку на любое расстояние, прокрутка между ними — штатная.

   ⭐ Карточки в режиме сворачиваются СТИЛЕМ, а не снятием `open`: тогда на выходе из
   режима возвращать нечего — раскрытой останется та, что была раскрыта. Состояние,
   которое не меняли, не может разъехаться. */
.reordering .mr-body { display: none; }
.reordering .manual-row > .mr-head::after { content: '⇅'; }
.reordering #add-row { display: none; }
.reorder-tip { color: var(--muted); font-size: var(--fs-sm); line-height: var(--lh-tight); }
/* ⚠️ Полоса стала на кнопку длиннее, и первое, что она сделала, — перенесла «Заполнено 11
   из 11» и саму кнопку на две строки (замер: высота полосы 61 → 93 px). Подписи в полосе
   не переносятся: она липкая, и каждый её лишний пиксель отнимается у карточек. */
.save-bar .btn-ghost, .save-bar .btn-primary, .save-count { white-space: nowrap; }
/* «Запомнить в шаблоне» — своей строкой под полосой, подпись под кнопкой: рядом с
   «Сохранить» её легко нажать вместо него, а спутать эти две кнопки нельзя. */
.tpl-remember { margin: .8rem 0 0; }
.tpl-remember .hint { display: block; margin-top: .2rem; }
/* Взятая карточка помечена ГРОМКО: в режиме на экране одновременно и «что я держу»,
   и «куда кладу», и спутать их нельзя — иначе следующий тап уедет не туда. */
.manual-row.taken { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent) inset; }
.manual-row.taken > .mr-head::after { content: 'несу'; font-size: var(--fs-xs); color: var(--accent); }
/* Цель «сюда» — полноценная цель нажатия (44 px), а не полоска в пару пикселей:
   промах в зале стоит второго круга «взял — положил». */
.drop-slot { display: flex; align-items: center; justify-content: center; min-height: 44px;
             margin: .3rem 0; border: 1px dashed var(--accent); border-radius: 9px;
             background: var(--sunken); color: var(--accent); font-size: var(--fs-sm);
             cursor: pointer; }
.drop-slot:active { background: var(--card); }

/* Пока пишут в поле, липкие шапка и лента разделов уступают место: при открытой
   клавиатуре видно около 410 px, и 116 из них занимала навигация, которую в этот
   момент не трогают. Полосы не прячем, а отпускаем — они просто уезжают с прокруткой. */
/* ⚠️ Полоса «Сохранить» отпускается вместе с ними, и это не косметика: липкой она либо
   съедает ~60 px из тех 350, в которые обязана поместиться раскрытая карточка, либо
   уезжает под клавиатуру — и что из двух, на стенде не проверить (клавиатуры там нет).
   Отпущенная полоса снимает вопрос: во время набора её просто нет на экране. */
body.typing .topbar, body.typing .sections, body.typing .save-bar { position: static; }
/* Разминка (Волна 4): узкая колонка-галочка. Палец на телефоне мимо чекбокса 13 px попадает
   плохо, поэтому цель увеличена — колонка всё равно самая узкая в строке. */

/* предпросмотр */
.preview-grid { display: grid; grid-template-columns: 300px 1fr; gap: 1.2rem; align-items: start; }
.raw-pane { position: sticky; top: 4rem; background: var(--card); border: 1px solid var(--line);
            border-radius: 12px; padding: .9rem; }
.raw-pane pre { white-space: pre-wrap; word-break: break-word; font-size: var(--fs-sm);
                background: var(--sunken); padding: .6rem; border-radius: 8px; margin: 0; }
.warn { margin-top: .8rem; background: var(--review-bg); border: 1px solid var(--review-line);
        border-radius: 8px; padding: .5rem .7rem; font-size: var(--fs-sm); }
.warn ul { margin: .3rem 0; padding-left: 1.1rem; }

.date-field { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
              padding: .6rem .8rem; }
.set-card { background: var(--card); border: 1px solid var(--line); border-radius: 12px;
            padding: .8rem 1rem; margin: .8rem 0; }
.set-card.review { background: var(--review-bg); border-color: var(--review-line); }
.set-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; }
.set-head .pos { color: var(--muted); font-variant-numeric: tabular-nums; }
.set-head .raw-name { font-weight: 600; }
.badge { background: var(--review-line); color: var(--review-on); font-size: var(--fs-xs); padding: .1rem .5rem;
         border-radius: 999px; }
.set-fields { display: flex; gap: .8rem; }
.set-fields label { flex: 1; margin-top: 0; }
/* «разминка» в предпросмотре — галочка, а не поле ввода: она не должна тянуть на себя
   ширину числовых полей, поэтому из общего flex: 1 выключена */
.warmup-field { flex: 0 0 auto !important; display: flex; align-items: center; gap: .35rem;
                align-self: flex-end; padding-bottom: .45rem; font-size: var(--fs-sm); }
.warmup-field input { width: 1.15rem; height: 1.15rem; }
.reasons { margin: .4rem 0 0; padding-left: 1.1rem; color: var(--review-ink); font-size: var(--fs-sm); }
.canon-field { margin-top: .6rem; border-top: 1px dashed var(--line); padding-top: .6rem; }
.canon-lock { font-weight: 600; color: var(--accent); }
.canon-select { max-width: 420px; }
.new-canon { display: flex; gap: .8rem; margin-top: .4rem; }
.new-canon[hidden] { display: none; }        /* атрибут hidden должен побеждать display:flex */
.new-canon label { flex: 1; }
.learn-field { display: flex; align-items: center; gap: .4rem; color: var(--ink); margin-top: .5rem; }
.learn-field input { width: auto; }
.actions { display: flex; align-items: center; gap: 1rem; margin-top: 1.2rem; }

/* журнал */
.journal-list { list-style: none; padding: 0; }
.journal-list li { margin: .4rem 0; }
/* Строка ленты — сетка из трёх слотов: дата+время · пометки · сводка (Ф6).
   Было `flex` + `flex-wrap`: при пометках строка переносилась и становилась вдвое выше
   (замер на 402 px: высоты 48 и 87), а метка источника переменной длины двигала левый
   край сводки от строки к строке. Теперь дата всегда прижата влево, сводка вправо,
   а середина отдана редким пометкам — и переносить нечего, `nowrap` держит одну строку. */
.journal-list a { display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
                  align-items: center; column-gap: .6rem;
                  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
                  padding: .7rem 1rem; color: var(--ink); }
.journal-list a:hover { text-decoration: none; border-color: var(--accent); }
.j-date { font-weight: 600; font-size: var(--fs-md); white-space: nowrap; }
/* пометки значками; `overflow: hidden` — предохранитель: если их когда-нибудь станет
   больше, лишнее срежется, а строка останется одной высоты */
/* `line-height: 1` — иначе строка с пометками на 2 px выше остальных: у эмодзи строчный
   бокс выше текстового, и одна строка из 135 выбивалась из ритма (замер: 44 и 46 px) */
.j-marks { display: flex; align-items: center; gap: .35rem; min-width: 0;
           overflow: hidden; white-space: nowrap; line-height: 1; }
.j-sum { color: var(--muted); font-size: var(--fs-md); text-align: right; white-space: nowrap; }

.detail-table { width: 100%; border-collapse: collapse; }
.detail-table th { text-align: left; font-size: var(--fs-sm); color: var(--muted); border-bottom: 1px solid var(--line); padding: .4rem; }
.detail-table td { padding: .5rem .4rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.detail-table tr.review { background: var(--review-bg); }
.detail-table .src { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-xs); color: var(--muted); margin-top: .2rem; }
.detail-table .pos { color: var(--muted); font-variant-numeric: tabular-nums; }

.table-scroll { width: 100%; overflow-x: auto; }     /* горизонтальный скролл внутри, не всей страницы */
.sets-count { display: inline-block; min-width: 1.5rem; text-align: center;
              font-variant-numeric: tabular-nums; padding-top: .4rem; color: var(--ink); }
.tag { background: var(--info-bg); color: var(--info-ink); font-size: var(--fs-xs); padding: .1rem .45rem; border-radius: 6px; margin-left: .4rem; }
/* Разминка (Волна 4) — та же пометка, но приглушённая: это не свойство снаряда, а «эта
   работа в рабочий вес не идёт». Цвет отличается от «гравитрона», чтобы две пометки
   в одной строке не сливались. */
.tag.warm { background: var(--warm-bg); color: var(--warm-ink); white-space: nowrap; }
/* по выпадающему списку на подход, друг под другом — как веса в соседней ячейке.
   Ширина задана явно: иначе браузер сжимал список до «размин▾», и выбранное состояние
   приходилось угадывать (видно на снимке экрана в 1100px). */
.warmup-sel { display: block; margin-bottom: .25rem; padding: .25rem .3rem;
              min-width: 6.4rem; }
.warmup-sel:last-child { margin-bottom: 0; }
.note { background: var(--good-bg); border-left: 3px solid var(--accent); padding: .5rem .8rem; border-radius: 6px; }
.raw-details { margin-top: 1rem; }
.raw-details pre { white-space: pre-wrap; background: var(--card); border: 1px solid var(--line);
                   border-radius: 8px; padding: .8rem; }

/* прогресс и графики (Ф3) */
.progress-block { background: var(--card); border: 1px solid var(--line); border-radius: 12px;
                  padding: 1rem 1.1rem; margin: 1rem 0; }
.progress-block h2 { font-size: var(--fs-lg); margin: 0 0 .6rem; }
/* шапка блока: заголовок слева, переключатель справа (на узком экране — в столбик) */
.block-head { display: flex; align-items: baseline; justify-content: space-between;
              gap: .6rem; flex-wrap: wrap; }
.block-head h2 { margin-bottom: .4rem; }
.chip.sm { padding: .2rem .6rem; font-size: var(--fs-sm); }
.progress-controls { display: flex; gap: 1.2rem; align-items: flex-end; flex-wrap: wrap; margin-bottom: .6rem; }
.progress-controls .ctl { margin: 0; }
.progress-controls select { min-width: 260px; max-width: 100%; }
.metric-radios { display: flex; align-items: center; gap: .7rem; color: var(--muted); font-size: var(--fs-md); }
.metric-radios .radio { display: inline-flex; align-items: center; gap: .25rem; margin: 0; color: var(--ink); }
.metric-radios input { width: auto; }

.chart-head { display: flex; justify-content: space-between; align-items: baseline;
              flex-wrap: wrap; gap: .6rem; margin: .2rem 0 .4rem; }
.legend { display: flex; gap: 1rem; flex-wrap: wrap; }
.legend-item { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--fs-sm); color: var(--muted); }
.swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.stat { font-size: var(--fs-md); }
.stat b { font-variant-numeric: tabular-nums; }

/* ⭐ Ф8 — график: пиксель значит пиксель.
   Подписи вынесены из холста в HTML-слой, поэтому их кегль живёт в общей шкале токенов
   и НЕ масштабируется шириной контейнера (раньше одна и та же подпись рисовалась 6 px на
   iPhone и 16 px на Mac — холст растягивался, а с ним и всё внутри).
   Поля под подписи держит эта коробка: место под текст меряет браузер, а не сервер. */
.chart-wrap { width: 100%; }
.chart-box { width: 100%; }
.chart-plot {
  position: relative;
  height: 200px;                 /* высота графика — здесь, а не в единицах холста */
  margin: 0 12px 22px 40px;      /* поля = место под подписи осей */
}
.chart { width: 100%; height: 100%; display: block; overflow: visible; }
.chart-grid { stroke: var(--line); stroke-width: 1; }
.chart-line { stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { stroke: var(--card); stroke-width: .5; }
/* подписи оси Y — в левом поле, по правому краю к линии сетки */
.chart-ylab {
  position: absolute; right: 100%; padding-right: 6px; transform: translateY(-50%);
  font-size: var(--fs-xs); color: var(--muted); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* подписи оси X — под холстом; у краёв выключка внутрь, иначе вылезают за коробку */
.chart-xlab {
  position: absolute; top: 100%; padding-top: 5px;
  font-size: var(--fs-xs); color: var(--muted); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.chart-xlab.is-mid { transform: translateX(-50%); }
.chart-xlab.is-end { transform: translateX(-100%); }
/* значение у последней точки — payload графика, поэтому крупнее подписей осей */
.chart-val {
  position: absolute; transform: translate(-100%, -140%);
  font-size: var(--fs-md); font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  background: var(--card); padding: 0 3px; border-radius: 4px;   /* линия под текстом мешает читать */
}
/* у верхней кромки подпись уходит ПОД точку: сверху её сажает на заголовок блока */
.chart-val.is-below { transform: translate(-100%, 40%); }
.chart-gap {
  position: absolute; top: 2%; transform: translateX(-50%);
  font-size: var(--fs-xs); color: var(--muted); white-space: nowrap;
  background: var(--card); padding: 0 4px; border-radius: 4px;
}
/* заголовок диапазона — слева: справа всегда стоит подпись последнего значения */
.chart-span { font-size: var(--fs-xs); color: var(--muted); margin: 0 0 .2rem 40px; }
.chart-box.is-empty { display: grid; place-items: center; min-height: 120px; }

/* ⭐ Ф9 — радар баланса нагрузки («паутина»).
   ⚠️ Коробка обязана быть КВАДРАТНОЙ: положение лучей сервер задаёт процентами, а процент
   по X меряется от ширины, по Y — от высоты. В прямоугольнике правильный шестиугольник
   поехал бы эллипсом — при этом ни один тест на строку HTML этого бы не увидел.
   Ширина ограничена: в колонке на Mac радар растянулся бы на пол-экрана, а читается он
   формой, а не размером. */
.radar-plot { position: relative; width: 100%; max-width: 320px; aspect-ratio: 1; margin: 0 auto; }
.radar-ring { fill: none; stroke: var(--line); stroke-width: 1; }
.radar-spoke { stroke: var(--line); stroke-width: 1; }
.radar-area { fill-opacity: .18; stroke-width: 2; stroke-linejoin: round; }
/* подписи лучей — HTML-слоем (то же правило Ф8, что и у осей графика): их кегль живёт
   в общей шкале и не масштабируется вместе с холстом */
.radar-lab {
  position: absolute; transform: translate(-50%, -50%);
  font-size: var(--fs-xs); color: var(--muted); white-space: nowrap;
}
.chart-empty { color: var(--muted); font-size: var(--fs-md); margin: 0; }

.assist-note { background: var(--review-bg); border: 1px solid var(--review-line);
               border-radius: 8px; padding: .5rem .7rem; font-size: var(--fs-sm); color: var(--review-ink); margin: .3rem 0 .6rem; }

.bw-form { display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap; margin-top: 1rem;
           border-top: 1px solid var(--line); padding-top: 1rem; }
.bw-form .ctl { margin: 0; }
.bw-form input { min-width: 110px; }
.bw-form .btn-primary { margin-top: 0; }

/* дашборд «Прогресс» (Ф6) */
/* ⭐ `wrap`, а не прокрутка: шесть кнопок периода при крупной ступени текста (Ф10) не
   помещаются в 320 px и вылезали за экран вместе со всей страницей — замер 29.07.2026
   давал документ 336 при окне 320 на «Сводке», «Прогрессе», «Нагрузке» и матрице.
   Перенос второй строкой стоит ~30 px высоты только на узком экране и только на крупной
   ступени; горизонтальная прокрутка стоила бы того же на КАЖДОМ экране сразу. */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1.1rem; }
.chip { padding: .35rem .9rem; border: 1px solid var(--line); border-radius: 999px;
        background: var(--field); color: var(--muted); font-size: var(--fs-sm); }
.chip:hover { text-decoration: none; border-color: var(--accent); }
.chip.on { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
/* ── Шесть плиток «сразу видно» (10.08.2026) ─────────────────────────────────────
   ⭐ Сетка ЖЁСТКАЯ 2×3, а не auto-fit, как у прежних карточек. Причина замерена, а не
   выбрана вкусом: auto-fit(minmax(185px)) на 402 px даёт ОДНУ колонку, и плитки встают
   этажами — ровно то, что Дмитрий назвал «одной большой плиткой». Весь смысл блока в том,
   чтобы числа читались одним взглядом, то есть стояли решёткой на любой ширине телефона.
   ⛔ Кегль числа — общий `--fs-metric`: 17 px базового текста куплены рулеткой (40 см
   до экрана в зале) и уменьшению не подлежат. Компактность взята УДАЛЕНИЕМ содержимого,
   а не сжатием шрифта, — тот же метод, которым шли Ф3–Ф10. */
.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0 0 1.1rem; }
.tiles .t { background: var(--card); border: 1px solid var(--line); border-radius: 14px;
            padding: 10px 12px; min-width: 0; }
.tiles .t h3 { font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
               text-transform: uppercase; letter-spacing: .03em; margin: 0 0 2px; }
.tiles .t .big { font-size: var(--fs-metric); font-weight: 700; line-height: var(--lh-tight);
                 font-variant-numeric: tabular-nums; }
.tiles .t .big .u { font-size: var(--fs-sm); font-weight: 500; color: var(--muted); }
.tiles .t .delta { font-size: var(--fs-xs); font-weight: 600; color: var(--accent); margin-top: 1px; }
.tiles .t .hempty { font-size: var(--fs-sm); color: var(--muted); margin-top: 4px; }
/* Полоса к цели — волосяная: 4 px против 7 px у прежней карточки. Цель рабочая, терять её
   при переезде в плитку нельзя, но и половину плитки она занимать не должна. */
.tiles .t .bar { height: 4px; border-radius: 3px; background: var(--sunken);
                 overflow: hidden; margin-top: 6px; }
.tiles .t .bar > i { display: block; height: 100%;
                     background: linear-gradient(90deg, var(--accent), var(--accent-2)); }
/* На широком экране — шесть в ряд: решётка нужна телефону, на Mac она растянула бы каждую
   плитку на треть экрана ради одного числа. */
@media (min-width: 720px) { .tiles { grid-template-columns: repeat(6, 1fr); } }

/* ── Тепловая карта «когда я работал» ────────────────────────────────────────────
   ⭐ Клетки квадратные через aspect-ratio, а ширина колонки — доля от контейнера (1fr).
   Поэтому карта сама влезает в любую ширину телефона, и горизонтальной прокрутки,
   запрещённой стендом `check_layout`, не возникает ни на 320 px, ни на крупном кегле.
   ⚠️ Подписи месяцев и клетки — ДВЕ сетки с одинаковым числом колонок. Так подпись держит
   браузер, а не арифметика на сервере: любое изменение ширины двигает обе разом. */
.heat-block { margin-top: 1.1rem; }
.heat { --heat-cols: 53; }
/* ⛔ `minmax(0, 1fr)`, а не `1fr`. Стенд 10.08.2026 покрасил ровно это: `1fr` — сокращение
   от `minmax(auto, 1fr)`, и «auto» ПОЗВОЛЯЕТ содержимому раздуть колонку. Подпись месяца
   с `nowrap` шире своей колонки (26 px против 5,5 px на 320 px) растягивала сетку и давала
   горизонтальную прокрутку страницы — при этом на снимке всё выглядело нормально. */
/* ⛔ Кегль подписей — `--fs-2xs`, ЕДИНЫЙ для обоих режимов карты. Это существующий токен
   микро-подписей («плашки, uppercase-надписи, единицы измерения»), а не новая шкала, так что
   инвариант «кегль не уменьшать» (он про payload — числа и названия) не задет.
   Замер 10.08.2026, дневной режим на 402 px: при `--fs-xs` подпись 21 px при шаге 26,9 px,
   и на коротких месяцах между соседями оставалось 1,0 px — глазами одно слово. При `--fs-2xs`
   подпись ~19 px, минимальный зазор уходит за 3 px. Держит проверка стенда о зазоре. */
.heat-labs { display: grid; grid-template-columns: repeat(var(--heat-cols), minmax(0, 1fr));
             font-size: var(--fs-2xs); color: var(--muted); line-height: 1.1;
             margin-bottom: 3px; }
.heat-labs span { white-space: nowrap; }
.heat-grid { display: grid; grid-template-columns: repeat(var(--heat-cols), minmax(0, 1fr));
             gap: 2px; }
/* ⭐ Запасной вариант, который назвал сам Дмитрий («хотя бы четыре квартальные»), живёт
   ЗДЕСЬ, а не на сервере: помещаются ли двенадцать подписей, знает только браузер — это
   зависит от ширины экрана и от ступени кегля, а сервер не знает ни того, ни другого.
   Порог 380 px: на 402 px все двенадцать стоят свободно (30,7 px на месяц), на 320 px
   на месяц остаётся 24 px и подписи начинают слипаться. */
@media (max-width: 380px) {
  /* Квартальный откат — в ОБОИХ режимах карты, и в обоих он заведён замером, а не на глаз.
     ⚠️ Ловушка, пойманная стендом 10.08.2026: сперва класс `q` был только у дневных подписей,
     и это правило гасило у месячной карты ВСЕ двенадцать разом. Нашлось лишь после того, как
     в стенд добавили страницу «сводка (всё)» — до этого проверки гонялись только на периоде
     по умолчанию и второй режим карты не видели ни разу.
     На 402 px (телефон Дмитрия) откат НЕ включается: там все двенадцать стоят свободно. */
  .heat-labs span:not(.q) { display: none; }
  .heat:not(.heat-mo) .heat-grid { gap: 1px; }
  .heat:not(.heat-mo) .heat-wk { gap: 1px; }
}
.heat-wk { display: grid; grid-template-rows: repeat(7, 1fr); gap: 2px; }
/* ⚠️ Ступени цвета висят на `.heat-block i`, а НЕ на `.heat i`, чтобы образцы в легенде
   красились теми же правилами, что и клетки. Пока правила были привязаны к `.heat`,
   легенда лежала снаружи и её квадратики оставались ПРОЗРАЧНЫМИ — «реже … чаще» без
   единого образца (найдено на крупном снимке 10.08.2026).
   ⛔ Опечатка в имени токена не даёт ни ошибки, ни варнинга: объявление с неизвестной
   переменной просто становится недействительным, и клетка получает ПРОЗРАЧНЫЙ фон — на
   светлой карточке это выглядит как «клетки нет». Так пропали все месяцы 2022–2025 со
   ступенью 1. Ловит проверка стенда «клетки карты непрозрачны».

   ⛔ Ступени — ОДИН цвет с растущей плотностью, а не набор разных токенов. Первая редакция
   брала на ступень 1 `--accent-soft` (#eef0fe), и шкала оказалась ПЕРЕВЁРНУТОЙ внизу: этот
   токен светлее `--sunken` (#eaeef6), то есть месяц, в котором Дмитрий ТРЕНИРОВАЛСЯ, выходил
   белее пустого. На карте это читалось как «в 2022–2024 не было ничего», хотя занятия были —
   у них просто нет тоннажа (веса в записях появились лишь в ноябре 2025). Найдено ГЛАЗАМИ
   на крупном снимке 10.08.2026: все проверки до того мерили геометрию и цвета не касались.
   Теперь порядок держит проверка стенда «ступени идут от светлого к тёмному» — она сравнивает
   яркость фактически отрисованной клетки, а не имена токенов.
   ⚠️ Плотность через `opacity`, а не `color-mix`: одинаково работает в обеих темах и не
   зависит от поддержки браузером свежего CSS. */
.heat i, .heat-lgd i { display: block; aspect-ratio: 1; border-radius: 2px;
                       background: var(--sunken); }
.heat i.pad { background: transparent; }
.heat-block i.l0 { background: var(--sunken); }
.heat-block i.l1 { background: var(--accent); opacity: .25; }
.heat-block i.l2 { background: var(--accent); opacity: .45; }
.heat-block i.l3 { background: var(--accent); opacity: .7; }
.heat-block i.l4 { background: var(--accent); }
/* Крупная карта: клетка — месяц, строка — год. Включается на окнах длиннее года, где
   дневная дала бы 221 колонку по 1,7 px (замер 10.08.2026). */
/* ⚠️ Колонка года ужата с 42 до 32 px, а подписи месяцев набраны `--fs-2xs` вместо
   `--fs-xs`. Обе правки — из замера, а не из вкуса: при прежних размерах на месяц
   приходилось 27,7 px при ширине подписи ~26 px, и между «мар» и «апр» оставалось 0,2 px —
   формально не наложение, а глазами одно слово «марапр». Стенд это пропускал, потому что
   требовал лишь отсутствия пересечения; теперь он требует зазор не меньше 3 px.
   ⛔ Это не нарушение правила «кегль не уменьшать»: `--fs-2xs` — существующий токен
   микро-подписей («плашки, uppercase-надписи, единицы измерения»), а не новая шкала. */
.heat-mo { --heat-cols: 12; }
.heat-mo .heat-labs { margin-left: 32px; }
.heat-mo .heat-grid { margin-left: 32px; }
.heat-yr { display: flex; align-items: center; gap: 4px; margin-bottom: 3px; }
.heat-yr > b { width: 28px; flex: none; font-size: var(--fs-2xs); color: var(--muted);
               font-weight: 600; font-variant-numeric: tabular-nums; }
.heat-yr .heat-grid { flex: 1; margin-left: 0; grid-auto-rows: 1fr; }
.heat-lgd { display: flex; align-items: center; gap: 4px; margin-top: 8px;
            font-size: var(--fs-xs); color: var(--muted); }
.heat-lgd i { width: 11px; height: 11px; flex: none; aspect-ratio: auto; }
/* Низкая панель под графиком веса: у талии шаг сантиметровый, ей хватает меньшей высоты. */
.bw-sub { font-size: var(--fs-sm); color: var(--muted); font-weight: 600; margin: .9rem 0 .2rem; }
.chart-low .chart-plot { height: 120px; }

/* auto-fit: число плиток менялось дважды (пятая «дней с последней» в Ф6, минус «Объём
   (тоннаж)» в Ф9 — сейчас их четыре), и жёсткая сетка каждый раз оставляла последнюю плитку
   одну на всю ширину. Раскладка сама подбирает число колонок под ширину контента. */
.dash-hero {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(185px, 1fr));
  gap: 14px; margin-bottom: 1.4rem;
}
.hcard { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 14px 16px; }
.hcard h3 { font-size: var(--fs-xs); font-weight: 600; color: var(--muted); text-transform: uppercase;
            letter-spacing: .03em; margin: 0 0 6px; }
.hcard .big { font-size: var(--fs-metric); font-weight: 700; font-variant-numeric: tabular-nums; line-height: var(--lh-tight); }
.hcard .big .u { font-size: var(--fs-sm); font-weight: 500; color: var(--muted); }
.hcard .delta { font-size: var(--fs-sm); font-weight: 600; margin-top: 2px; }
.hcard .delta.good { color: var(--accent); }
.hcard .delta.up { color: var(--accent); }
.hcard .hnote, .hcard .hempty { font-size: var(--fs-sm); color: var(--muted); margin-top: 8px; }
.hcard .bar { height: 7px; border-radius: 6px; background: var(--sunken); overflow: hidden; margin-top: 12px; }
.hcard .bar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); }
.hcard .goalrow { display: flex; justify-content: space-between; font-size: var(--fs-xs); color: var(--muted); margin-top: 5px; }
.hspark { margin-top: 10px; }
.spark { width: 100%; height: 34px; display: block; }

.progress-two { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
/* Строка рекорда — сетка из трёх колонок: название · вес · дата.
   Было `flex` + `space-between`: правый край строки выровнен, а число и дата вставали
   каждый раз в новом месте (замер на 402 px: левый край числа гулял на 59 px, высоты
   строк 39/60/61). Сетка держит колонки на месте независимо от длины названия.
   `minmax(0, 1fr)` обязателен: без него длинное название распирает колонку, и вся
   таблица уезжает вправо — те же грабли, что уже ловились на чипах шаблонов. */
/* ⚠️ Сетка объявлена на КОНТЕЙНЕРЕ, а не на строке. Колонки грида выравниваются только
   внутри одного грида: если гридом сделать `.rec`, получится десять независимых сеток,
   и числа снова встанут каждое на своём месте (проверено замером — разброс остался 46 px).
   Отсюда `display: contents` у строки: она перестаёт быть боксом, а её три ячейки
   становятся ячейками общей сетки. Цена — рамку и отступы носят сами ячейки. */
.records { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
           column-gap: .6rem; font-size: var(--fs-md); }
.records .rec { display: contents; }
/* ⚠️ Ячейки тянутся на высоту строки (`stretch` по умолчанию), а не садятся на базовую
   линию: при `align-self: baseline` ячейка названия в две строки была выше соседних, и
   линия-разделитель под названием оказывалась НИЖЕ линий под весом и датой — строка
   выглядела рваной даже при идеально ровных колонках (замечание Дмитрия 28.07.2026). */
.records .rec > * { padding: .5rem 0; border-bottom: 1px solid var(--line); }
/* колонка пометки: узкая и почти всегда пустая — абсолютный рекорд ставится редко */
.records .rb { text-align: center; }
.records .rec:last-child > * { border-bottom: 0; }
.records .rn { min-width: 0; }
.records .rv { text-align: right; white-space: nowrap; }
.records .rv b { font-variant-numeric: tabular-nums; }
/* дата — своей колонкой фиксированной ширины: моноширинные цифры не дают ей дышать
   от строки к строке, и глаз читает столбец, а не россыпь */
.records .rd { color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
               white-space: nowrap; text-align: right; }
.records .d { color: var(--muted); font-size: var(--fs-sm); }
/* Пометка — значок, а не слово: она стоит в своей колонке и не имеет права двигать
   соседей. Смысл сменился вместе с видом (28.07.2026): было «НОВЫЙ» = «превзошёл всё, что
   было до периода» — метка зависела от фильтра и на годе стояла у 10 записей из 10, то есть
   не различала ничего. Стало «лучший за всю историю»: от периода не зависит, врать не может. */
.pr { font-size: var(--fs-md); line-height: 1; }
.mbar { display: flex; align-items: center; gap: 10px; margin: 8px 0; font-size: var(--fs-md); }
.mbar .nm { width: 118px; color: var(--ink); line-height: var(--lh-tight); }
.mbar .tr { flex: 1; height: 12px; background: var(--sunken); border-radius: 6px; overflow: hidden; }
.mbar .tr > i { display: block; height: 100%; background: var(--accent); }
.mbar .vv { width: 30px; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Ф11 — «Эта неделя»: подходы на группу мышц за текущую календарную неделю.
   Сетка с `display: contents`, а не flex у каждой строки, — тот же приём и тот же довод,
   что у блока рекордов на этом же экране: числа обязаны стоять колонкой, иначе строка
   «скачет туда-сюда». Колонки `max-content`, а не фиксированные px: при ступени текста
   (Ф10) фиксированная ширина имени обрезала бы «Предплечья» на крупной ступени. */
.wk { display: grid; grid-template-columns: max-content minmax(0, 1fr) auto;
      column-gap: .6rem; font-size: var(--fs-md); }
.wk .wk-row { display: contents; }
.wk .wk-row > * { padding: .45rem 0; }
.wk .nm { color: var(--ink); white-space: nowrap; line-height: var(--lh-tight); }
/* Полосе вертикальные поля НЕ нужны: фон красит и padding-box, и с полями она стала бы
   вчетверо толще; высоту строки задаёт ячейка имени, полоса садится по центру. */
.wk .tr { padding: 0; align-self: center; height: 12px; background: var(--sunken);
          border-radius: 6px; overflow: hidden; }
.wk .tr > i { display: block; height: 100%; background: var(--accent); }
.wk .vv { text-align: right; font-variant-numeric: tabular-nums; }
.wk-sub { color: var(--muted); font-size: var(--fs-sm); margin: -.2rem 0 .8rem; }
.chart-goal { stroke: var(--goal); stroke-width: 1.5; stroke-dasharray: 5 4; }
.chart-goal-t { position: absolute; right: 0; transform: translateY(-120%);
                color: var(--goal); font-size: var(--fs-xs); white-space: nowrap; }
/* кольцо личного рекорда вокруг точки (полупрозрачное — не спорит с линией ряда) */
.chart-pr { stroke-width: 2; opacity: .45; }

@media (max-width: 1100px) {
  .progress-two { grid-template-columns: 1fr; }
}

@media (max-width: 820px) {
  /* На телефоне колонка разделов съела бы половину экрана — та же навигация лентой сверху.
     В зале Дмитрий с телефона, это основной сценарий, а не запасной. */
  /* minmax(0, 1fr), а не 1fr: у grid-элемента min-width по умолчанию auto, поэтому широкая
     таблица распирала колонку и вбок уезжала ВСЯ страница вместе с шапкой, а внутренний
     .table-scroll не срабатывал (на десктопе minmax стоял с самого начала) */
  .shell { grid-template-columns: minmax(0, 1fr); }
  .sections {
    flex-direction: row; overflow-x: auto; gap: .4rem;
    padding: .5rem .8rem; border-right: none; border-bottom: 1px solid var(--line);
    position: sticky; top: calc(3.2rem + env(safe-area-inset-top)); background: var(--bg); z-index: 4;
  }
  /* компактнее ровно настолько, чтобы все четыре раздела помещались в 375px без прокрутки:
     иначе «Прогресс» обрезан и его надо доскроллить, а это лишнее движение в зале */
  .sections a { white-space: nowrap; padding: .35rem .5rem; font-size: var(--fs-sm); }
  .shell main { padding: .8rem 1rem 4rem; }
  /* Шапка на 375px была занята под завязку (359 из 375), и шестерёнка (44px + зазор)
     не помещалась: документ разъезжался до 430px и КАЖДЫЙ экран получал горизонтальную
     прокрутку, а «Выйти» уходила за край. Ужимаем зазоры и поля — замер после правки
     даёт 347px. Часы и цели нажатия не трогаем: 44px обязаны остаться.
     ⭐ Ф10: этого запаса не хватало всё равно — на 320 px ряд вылезал при ЛЮБОЙ ступени
     текста (335 · 355 · 383), на 375 px — при «крупной». Вылечено не сжатием, а выносом
     «Выйти» в настройки; замер после: 307 на 320 px при крупной ступени. */
  .topbar { gap: .6rem;
            padding-left: max(.8rem, env(safe-area-inset-left));
            padding-right: max(.8rem, env(safe-area-inset-right)); }
}

@media (max-width: 680px) {
  .preview-grid { grid-template-columns: 1fr; }
  .raw-pane { position: static; }
  .set-fields, .new-canon { flex-wrap: wrap; }
  /* Волна 4: галочка «разминка» встала в тот же ряд, и на 375px поля начали сжиматься —
     «20,0» показывалось как «20,». Минимальная ширина заставляет ряд ПЕРЕНОСИТЬСЯ,
     а не сдавливать числа (проверено снимком экрана в 375px). */
  .set-fields label { min-width: 4.6rem; }
  .progress-controls select { min-width: 0; width: 100%; }
  .bw-form { gap: .7rem; }
}

/* Карточка упражнения (фаза 2b): описание техники из открытого справочника */
.ex-card { margin-top: 1.1rem; padding: .9rem 1rem; background: var(--card);
           border: 1px solid var(--line); border-radius: 10px; }
.ex-card h3 { margin: 0 0 .5rem; font-size: var(--fs-lg); }
.card-warn { margin: 0 0 .6rem; padding: .45rem .6rem; border-radius: 6px;
             background: var(--review-bg); color: var(--review-ink); font-size: var(--fs-md); }
.card-desc { margin: 0 0 .6rem; line-height: var(--lh-read); }
.card-steps { margin: 0 0 .7rem; padding-left: 1.2rem; line-height: var(--lh-read); color: var(--ink); }
.card-steps li { margin-bottom: .25rem; }
.card-muscles { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .6rem; }
/* .mtag, а не .tag — класс .tag уже занят пометками в журнале (переопределение сломало бы их вид) */
.mtag { padding: .15rem .55rem; border-radius: 999px; font-size: var(--fs-sm);
        background: var(--sunken); color: var(--muted); }
.mtag.main { background: var(--accent); color: var(--accent-ink); }
.card-src { margin: 0; font-size: var(--fs-sm); }
/* личная заметка Дмитрия — важнее описания из справочника, поэтому выделена и стоит первой */
.card-mine { margin: 0 0 .7rem; padding: .55rem .7rem; border-radius: 8px; line-height: var(--lh-read);
             background: var(--accent-soft); border-left: 3px solid var(--accent); }
/* примечание атласа — показания и предостережения (что можно повредить, кому упражнение подходит).
   Стоит после личной заметки: сначала как делаю Я, потом что об этом пишет источник. */
.card-atlas { margin: 0 0 .7rem; padding: .55rem .7rem; border-radius: 8px; line-height: var(--lh-read);
              font-size: var(--fs-md); background: var(--atlas-bg); border-left: 3px solid var(--atlas-line); }
/* статика (какие мышцы работают) и клип (как двигаться) — рядом на десктопе, в столбик на телефоне */
.card-media { display: flex; gap: .6rem; flex-wrap: wrap; margin: 0 0 .5rem; }
.card-media img, .card-media video { flex: 1 1 260px; max-width: 100%; border-radius: 8px;
                                     background: var(--sunken); }
.card-media-src { margin: 0 0 .7rem; font-size: var(--fs-sm); }

/* Пометки тренировки (Ф3, пп. 4/8/10) */
.meta-row { display: flex; flex-wrap: wrap; gap: .8rem; margin: .6rem 0 1rem; }
.meta-row .ctl { margin: 0; }
.meta-row select, .meta-row input[type=time] { min-width: 9rem; }
/* ═══ Строка пометок: дата · время (и «тип · место» той же сеткой) ═══
   Пять попыток за один день, поэтому правила собраны в одном месте и каждое подписано
   тем, что его купило. Порядок внутри блока значим: у части правил одинаковая
   специфичность, и «не помогло» здесь однажды означало «не применилось».

   ⛔ ГЛАВНАЯ АРИФМЕТИКА, из которой всё следует. На 402 px внутренняя ширина строки —
   363 px, колонки 2fr / 1fr → примерно 234 и 117. Длинное русское значение даты
   («28 июля 2026 г.») занимает ≈170 px, нативному контролу времени в Safari нужно
   ≈70 px чистого места (выведено из снимков Дмитрия: при 46 px отступов внутри поле
   помещалось, при 51 — уже нет). Свободного места в строке почти не остаётся, поэтому
   каждый лишний отступ здесь стоит поломки, а не красоты. */

/* 1. ⭐ Поля даты и времени сжимаются ПО СОДЕРЖИМОМУ, а не растягиваются на колонку.
   Это не косметика: растянутое поле требует от нативного контрола сжаться, а он
   отказывается и вылезает за край экрана. Сжатое требования не создаёт вовсе.
   ⚠️ `display: block` обязателен рядом с `width: auto`: поле по умолчанию строчное, и,
   перестав занимать всю ширину, встаёт в одну строку с подписью (поймано в Safari).
   Высота — общая с соседними списками, чтобы строка читалась как одна. */
.meta-row .ctl-date input, .meta-row .ctl-time input {
  display: block; width: auto; max-width: 100%;
}
/* 2,7rem = 43,2 px — выше собственной высоты и поля даты (42,1), и списка (41,6), поэтому
   оба садятся ровно на это число. При 2,6rem список упирался в порог, а поле нет. */
.meta-row .ctl input, .meta-row .ctl select { min-height: 2.7rem; }

/* 2. ⛔ Предохранитель от РАСПОРА СЕТКИ. Ячейка по умолчанию не сжимается уже своего
   содержимого, а минимальная ширина поля даты равна длине значения — колонка расширялась
   и толкала соседнюю. Замер (402 px, длинное значение имитировано кеглем): без правила
   документ 425 px и 8 элементов за краем, с ним — 402 и 0. Закрыто тестом. */
.meta-row .ctl, .meta-row .fld,
.meta-row .ctl input, .meta-row .ctl select { min-width: 0; }

/* 3. ⛔ Предохранитель последней надежды. `width: 100%` для нативного контрола даты и
   времени в Safari не приказ, а пожелание: если внутренностям тесно, поле отказывается
   сжиматься и вылезает за колонку — на iPhone оно уехало за правый край экрана. Обёртка
   обрезает такой перелив: худшее, что может случиться, — подрезанное значение вместо
   сломанной строки. Она же служит якорем значку (см. 5). */
.meta-row .fld { position: relative; display: block; overflow: hidden; }

/* 4. Значение — к левому краю, как текст в соседних списках.
   ⚠️ Одного `text-align` на поле НЕ хватает: в Safari значение лежит во внутреннем
   элементе, и выравнивание с родителя туда не наследуется. Chrome кладёт значение слева
   сам, поэтому правило для него холостое — разницу видно только на устройстве. */
.meta-row input[type=date], .meta-row input[type=time] { text-align: left; }
.meta-row input[type=date]::-webkit-date-and-time-value,
.meta-row input[type=time]::-webkit-date-and-time-value { text-align: left; }
.meta-row input[type=date]::-webkit-datetime-edit,
.meta-row input[type=time]::-webkit-datetime-edit { text-align: left; }

/* 5. Значок ВНУТРИ поля — как «💪 силовая» у соседа по строке. Рисуется слоем: содержимое
   поля даты это сама дата, вписать туда нечего. Три условия держатся вместе: значку нужно
   место (отступ поля), он не перехватывает нажатие (иначе тап по нему не откроет системный
   выбор даты) и центрируется по САМОМУ ПОЛЮ через обёртку — пока вертикаль считалась
   числом от низа подписи, значок вставал криво.
   ⚠️ Отступ считается из трёх слагаемых, а не подбирается: 6 px от края + 19 px значок
   (эмодзи рендерится ШИРЕ своего кегля — это сбивало три расчёта подряд) + 7 px зазор
   до значения = 32 px. Со сжатием по содержимому эти 32 px никому не мешают: поле просто
   становится на них шире, а не отбирает место у контрола. */
.meta-row .ctl-date input, .meta-row .ctl-time input { padding-left: 2rem; padding-right: .5rem; }
.meta-row .ctl-date .fld::after, .meta-row .ctl-time .fld::after {
  position: absolute; left: .38rem; top: 50%; transform: translateY(-50%);
  font-size: var(--fs-sm); line-height: 1; pointer-events: none;
}
.meta-row .ctl-date .fld::after { content: '📅'; }
.meta-row .ctl-time .fld::after { content: '⏰'; }

/* Самочувствие (Волна 4, Ф1) — сегментированные кнопки вместо выпадающего списка.
   Список за 134 тренировки не отметили ни разу: в зале это два действия вместо одного.
   Кнопка занимает целую строку формы — заметная, но по-прежнему необязательная. */
.wb-seg { border: 0; padding: 0; margin: 0; min-width: 0; }
.wb-seg legend { padding: 0 0 .3rem; color: var(--muted); font-size: var(--fs-sm); }
.wb-opts { display: flex; flex-wrap: wrap; gap: .4rem; }
/* радио прячем от глаз, но НЕ от клавиатуры и скринридера: `display:none` выкинул бы их
   из порядка обхода, а выбор самочувствия должен оставаться доступным с клавиатуры */
.wb-opts input[type=radio] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.wb-opts label {
  display: inline-flex; align-items: center; min-height: 2.5rem;   /* палец, не курсор */
  padding: .35rem .8rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); cursor: pointer; font-size: var(--fs-md); white-space: nowrap;
}
.wb-opts input[type=radio]:checked + label {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
}
/* «снять отметку» отмечено по умолчанию — и не должно выглядеть как сделанный выбор:
   иначе пустое поле читалось бы с экрана как осознанный ответ.
   ⚠️ Пока отметки нет, кнопку прячем совсем: снимать нечего, а полоса под тремя кнопками
   читалась как четвёртый ответ. Условие написано «спрятать, когда выбрано ничего», а не
   «показать, когда выбрано что-то», намеренно: браузер без `:has()` правило пропустит,
   и кнопка останется видимой — то есть отметку по-прежнему будет чем снять. */
.wb-opts label.wb-none { color: var(--muted); font-size: var(--fs-sm); border-style: dashed; }
.wb-opts:has(input#wb-none:checked) label.wb-none { display: none; }
.wb-opts input[type=radio]:checked + label.wb-none {
  background: transparent; color: var(--muted); border-color: var(--line);
}
.wb-opts input[type=radio]:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }
/* маркеры в ленте журнала: читаются взглядом, без захода внутрь тренировки */
.marker {
  display: inline-block; padding: .1rem .45rem; border-radius: 999px;
  background: var(--sunken); color: var(--ink); font-size: var(--fs-sm); white-space: nowrap;
}
.marker.place { background: var(--accent-soft); }
.marker.well { background: transparent; padding: .1rem .2rem; font-size: var(--fs-md); }
.j-time { color: var(--muted); font-weight: 400; font-size: var(--fs-sm); }
.h-time { color: var(--muted); font-weight: 400; font-size: var(--fs-md); }
/* темп — производная величина, поэтому подписью под полем, а не как вводимое значение */
.pace { font-size: var(--fs-xs); color: var(--muted); margin-top: .15rem; white-space: nowrap; }

/* Произвольный период (п.15) — свёрнут, чтобы не мешать обычным быстрым фильтрам */
.custom-period { margin: -.4rem 0 1rem; }
.custom-period summary { color: var(--muted); font-size: var(--fs-sm); cursor: pointer; }
.period-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .8rem; margin-top: .6rem; }
.period-form .ctl { margin: 0; }
.period-form input[type=date] { min-width: 9.5rem; }
.period-form button { margin: 0; }

/* Пометки тренировки на телефоне. (Прежняя прокрутка таблицы ввода отсюда убрана вместе
   с самой таблицей: с Ф7 ручной ввод — карточки, и прокручивать вбок нечего.) */
@media (max-width: 680px) {
  /* пометки тренировки над таблицей занимали четыре строки и выдавливали первое упражнение
     за нижний край — на телефоне они в две колонки */
  /* Две колонки в пропорции 2:1 — дата и тип длиннее своих соседей (замечание Дмитрия
     28.07.2026: «дата и время на одной строке, место и тип тоже; сейчас криво»). */
  .meta-row { display: grid; grid-template-columns: 2fr 1fr; gap: .5rem .8rem; }
  .meta-row select, .meta-row input[type=time] { min-width: 0; }
  /* самочувствие — во всю ширину: три кнопки в половине экрана 375px переносились бы
     по одной в строку и выдавливали таблицу упражнений за нижний край.
     Три состояния — тремя равными долями строки (радио вынесены из потока `position:absolute`
     и ячеек сетки не занимают), «не отмечено» — отдельной строкой под ними: это не четвёртый
     ответ, а способ снять отметку. */
  .meta-row .wb-seg { grid-column: 1 / -1; }
  .wb-opts { display: grid; grid-template-columns: repeat(3, 1fr); gap: .35rem; }
  .wb-opts label { padding: .35rem .3rem; justify-content: center; }
  .wb-opts label.wb-none { grid-column: 1 / -1; }
  /* ⭐ Ф10: на ступени «крупный» три кнопки самочувствия в строку НЕ помещаются — подписи
     стоят `nowrap`, а `1fr` это `minmax(auto, 1fr)` и ниже своего содержимого не ужимается.
     Замер 29.07.2026: «🔴 слабость» уходила за правый край на 9 px в Chromium и на 15 в
     WebKit, и вместе с ней горизонтальную прокрутку получала вся карточка тренировки —
     последнее нарушение критерия Ф10 на 375 px.
     Две колонки, а не `minmax(0, 1fr)` на три: ужатая колонка обрезала бы слово, то есть
     ступень «крупный» делала бы текст МЕНЕЕ читаемым — ровно наоборот тому, зачем её
     включают. Базовая ступень не тронута: там три в строку помещаются и это замерено. */
  :root[data-fs="large"] .wb-opts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Ф5 — копия тренировки и шаблоны (пп. 1, 2) */
.session-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
                   margin: 1.4rem 0 .4rem; }
.session-actions .btn-ghost { text-decoration: none; }
.tpl-save { display: flex; gap: .5rem; margin: 0; }
.tpl-save input[type=text] { padding: .45rem .7rem; border: 1px solid var(--line);
                             border-radius: 9px; min-width: 11rem; }
/* Зелёная плашка успеха — того же цвета, что примечание к тренировке (`.note`) */
.ok-note { background: var(--good-bg); border-left: 3px solid var(--accent); color: var(--good-ink);
           padding: .6rem .8rem; border-radius: 6px; }

.tpl-list { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin: 0 0 1rem; }
.tpl-title { color: var(--muted); font-size: var(--fs-sm); }
/* `min-width: 0` обязателен: без него длинное название («Верх тела (грудь/плечи/руки)»)
   не сжималось и не переносилось — чип вылезал за экран и уносил вправо ВСЮ страницу,
   вместе с полем даты и текстом сообщения тренера. Дефект вносился данными, которые
   приложение само же разрешает ввести (80 символов). */
.tpl { display: inline-flex; align-items: center; gap: .5rem; min-width: 0; max-width: 100%; }
.tpl .btn-ghost { text-decoration: none; min-width: 0; overflow: hidden;
                  text-overflow: ellipsis; white-space: nowrap; }
.tpl-del { margin: 0; flex: none; }
/* ✕ стоит рядом с ежедневной «создать», и промах пальцем удаляет шаблон: цель не меньше 44px */
.tpl-del .btn-ghost { padding: .55rem .8rem; min-width: 44px; min-height: 44px; color: var(--muted); }

/* Внимание к заготовке — той же палитрой, что «на проверку» (`--review-*`), а не своим
   хексом: иначе смена цвета внимания в `:root` этот блок не затронет */
.prefill-note { background: var(--review-bg); border: 1px solid var(--review-line);
                border-radius: 8px; padding: .6rem .8rem; margin: 0 0 1rem;
                font-size: var(--fs-md); line-height: var(--lh-read); }

@media (max-width: 680px) {
  /* На телефоне поле названия и кнопка в одну строку не влезают: поле сжималось до «Название ш».
     Поле — своей строкой во всю ширину, кнопка под ним. */
  .tpl-save { flex: 1 1 100%; flex-wrap: wrap; }
  .tpl-save input[type=text] { flex: 1 1 100%; min-width: 0; }
}

/* Ф6 — матричное представление журнала (п.5) */
/* Подвкладки бывают ссылками (журнал) и кнопками (ввод) — вид одинаковый. */
/* Подвкладки бывают ссылками (журнал) и кнопками (ввод) — вид одинаковый.
   `.tab.active` объявлен выше, второй раз здесь его не повторяем: одинаковый селектор
   дважды в одном файле делает верхнее правило мёртвым и правится вслепую. */
a.tab { text-decoration: none; display: inline-block; }
a.tab:hover { text-decoration: none; }

/* Своя высота у контейнера обязательна: `overflow-x: auto` делает `overflow-y` тоже auto,
   но без ограничения высоты контейнер не прокручивается — прокручивается страница, и
   `position: sticky` у шапки не срабатывает НИКОГДА. На периоде «Всё» это 26 строк без
   подписи даты перед глазами. */
.matrix-scroll { margin-top: .4rem; max-height: 72vh; overflow: auto; }
.matrix { border-collapse: collapse; font-size: var(--fs-md); }
.matrix th, .matrix td { border-bottom: 1px solid var(--line); padding: .4rem .6rem;
                         white-space: nowrap; }
.matrix thead th { position: sticky; top: 0; background: var(--card); z-index: 2;
                   font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
/* Колонка с названием держится на месте при свайпе — иначе, уехав вправо, не понять,
   в какой ты строке. Это главный смысл матрицы на широком экране. */
.matrix .mx-name { position: sticky; left: 0; background: var(--card); text-align: left;
                   font-weight: 500; max-width: 21rem; overflow: hidden;
                   text-overflow: ellipsis; z-index: 1; }
.matrix thead .mx-name { z-index: 3; }
.matrix .mx-name a { color: var(--ink); }
.matrix .mx-cell { font-variant-numeric: tabular-nums; color: var(--ink); }
/* Время столбца — второй строкой под датой и служебным кеглем: оно появляется только
   у пары тренировок одного дня, и вставать в строку с датой ему нельзя — шапка липкая,
   каждый её лишний пиксель отнимается у таблицы. */
.matrix .mx-time { display: block; color: var(--muted); font-size: var(--fs-xs);
                   font-weight: 400; font-variant-numeric: tabular-nums; }
.matrix .mx-split .mx-name { color: var(--muted); font-weight: 600; font-size: var(--fs-xs);
                             text-transform: uppercase; letter-spacing: .04em; }
.matrix .mx-split th, .matrix .mx-split td { border-top: 2px solid var(--line);
                                             padding-top: .7rem; }

@media (max-width: 680px) {
  .matrix th, .matrix td { padding: .35rem .45rem; }
  /* Названия НЕ обрезаем многоточием: на 375px «Жим от груди в …» — это и «наклон вверх»,
     и «наклон вниз», две разные строки выглядели одинаково. Подсказка `title` тут не
     помогает: на тачскрине наведения нет. Переносим в несколько строк — читается целиком. */
  .matrix .mx-name { max-width: 9.5rem; white-space: normal; overflow: visible;
                     text-overflow: clip; line-height: var(--lh-tight); }
  .matrix-scroll { max-height: 68vh; }
}

/* Ф7 — двуязычность (п.18): оригинал рядом с переводом, но тише его */
.card-names { margin: 0 0 .6rem; }
.card-names .en { color: var(--muted); font-weight: 400; font-size: var(--fs-sm); }
.mbar .en { display: block; color: var(--muted); font-weight: 400; font-size: var(--fs-xs); }

/* Аналитик (Волна 4, Ф3) — подвкладка «Прогресса». Карточка рекомендации читается сверху
   вниз: что за упражнение → что предлагается → почему → на каких записях это основано. */
.coach-actions { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap;
                 margin: 1rem 0 .6rem; }
.coach-actions .btn-primary, .coach-actions .btn-ghost { margin-top: 0; }
.coach-actions button[disabled] { opacity: .5; cursor: default; }
.coach-terms { line-height: var(--lh-read); margin: 0 0 1rem; }
.coach-run { background: var(--card); border: 1px solid var(--line); border-radius: 12px;
             padding: 1rem 1.1rem; margin-top: 1rem; }
.coach-summary { line-height: var(--lh-read); }
.coach-summary p { margin: 0 0 .7rem; }
.coach-cards { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: .6rem; }
.coach-card { border: 1px solid var(--line); border-left: 3px solid var(--muted);
              border-radius: 10px; padding: .6rem .8rem; background: var(--card); }
/* цвет левой кромки = намерение: «добавить» и «снять» не должны выглядеть одинаково,
   когда просматриваешь список глазами в зале */
.coach-card.progress { border-left-color: var(--accent); }
.coach-card.deload   { border-left-color: var(--deload); }
.coach-card.return   { border-left-color: var(--return); }
.cc-head { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.cc-intent { font-size: var(--fs-sm); color: var(--muted); }
.cc-nums { display: flex; gap: .5rem; align-items: baseline; flex-wrap: wrap;
           font-variant-numeric: tabular-nums; margin-top: .25rem; }
.cc-now { color: var(--muted); }
.cc-target { font-size: var(--fs-lg); }
.cc-reps { color: var(--accent); font-weight: 600; }
.cc-why { margin: .4rem 0 .2rem; line-height: var(--lh-read); }
.cc-ev { margin: 0; }
.coach-rejected { margin-top: .9rem; font-size: var(--fs-md); color: var(--muted); }
.coach-rejected ul { margin: .4rem 0; padding-left: 1.1rem; }
.coach-usage { margin: .9rem 0 0; }

/* Путь ② (Волна 4): рекомендация в форме ввода. Подстановка объясняется рядом с полями —
   вес без причины это назначение, а решение принимает Дмитрий. */
/* подпись не обрезаем и держим палец: .tpl .btn-ghost наследует ellipsis, и на 375px
   ссылка читалась как «🤖 с рекоменд…», а зона нажатия была ниже 44px */
/* `flex: none` — из-за роста основного кегля 16 → 17 (Ф3) пара «повторить <дата>» +
   «🤖 рекомендация» перестала помещаться в строку на 402 px, и подпись обрезалась краем
   экрана: сжимался бокс, а текст не обрезается намеренно (см. ниже). Теперь ссылка держит
   свою ширину, а уступает соседняя кнопка с датой — у неё многоточие и так есть.
   ⚠️ Замер элементов этого не ловит: бокс остаётся внутри вьюпорта, за край уезжают буквы. */
.rec-link { padding: .3rem .55rem; max-width: none; text-overflow: clip; overflow: visible;
            min-height: 44px; display: inline-flex; align-items: center; flex: none; }
.rec-note { background: var(--accent-soft); border-left: 3px solid var(--accent); border-radius: 8px;
            padding: .6rem .8rem; margin: 0 0 .8rem; line-height: var(--lh-read); font-size: var(--fs-md); }
.rec-note ul { margin: .4rem 0 0; padding-left: 1.1rem; }
.rec-note li { margin-bottom: .25rem; }
/* строка с подставленной рекомендацией — тонкая метка слева, чтобы в зале было видно,
   какие числа пришли от слоя, а какие с прошлого раза */
.manual-row.advised td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.coach-terms-box { margin: 0 0 1rem; }
.coach-terms-box > summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
/* Разбор — три блока с подписями (27.07): полотно в шесть предложений не читалось,
   и все упражнения в нём назывались второй раз, уже будучи карточками ниже. */
.coach-summary { margin: 0 0 .4rem; }
.coach-summary dt { font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
                    text-transform: uppercase; letter-spacing: .03em; margin-top: .7rem; }
.coach-summary dt:first-child { margin-top: 0; }
.coach-summary dd { margin: .2rem 0 0; line-height: var(--lh-read); }

/* ═══════════ Экран «Оформление» (v0.9.0) ═══════════ */
/* Шестерёнка в шапке — не пятый раздел: лента разделов на 375px рассчитана ровно
   на четыре, а вид настраивают раз в месяц против разделов каждый заход. */
.gear { font-size: var(--fs-lg); line-height: 1; padding: .45rem .5rem; text-decoration: none;
        min-width: 44px; min-height: 44px; display: inline-flex; align-items: center;
        justify-content: center; }
.gear:hover { text-decoration: none; background: var(--sunken); border-radius: 9px; }
.gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 9px; }
.gear.on { background: var(--accent-soft); border-radius: 9px; }

.set-h { font-size: var(--fs-lg); margin: 1.4rem 0 .5rem; }
/* .pal-card, а не .set-card: последний уже занят карточкой подхода в предпросмотре
   (`preview.html`). Одинаковая специфичность + позиция ниже = молчаливое переопределение
   экрана, через который проходит каждая тренировка. */
.pal-card { background: var(--card); border: 1px solid var(--line); border-radius: 14px;
            overflow: hidden; }

/* Строка палитры — цель для пальца во всю ширину, не только чекбокс */
.pal-row { display: flex; align-items: center; gap: .9rem; width: 100%; text-align: left;
           padding: .8rem 1rem; min-height: 60px; border: 0; background: none;
           color: var(--ink); cursor: pointer; border-bottom: 1px solid var(--line); }
.pal-card .pal-row:last-child { border-bottom: 0; }
.pal-row:hover { background: var(--sunken); }
.pal-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Образец: две половины палитры сразу — светлая и тёмная. «Уголь и лайм» тёмная
   по умолчанию, и одна светлая плашка врала бы про неё. */
.pal-prev { flex: none; display: flex; width: 54px; height: 38px; border-radius: 9px;
            overflow: hidden; border: 1px solid var(--line-2); }
.pal-prev i { flex: 1; display: flex; align-items: center; justify-content: center; }
.pal-prev b { display: block; width: 60%; height: 4px; border-radius: 2px; }
.pal-prev i:last-child b { width: 12px; height: 12px; border-radius: 999px; }

.pal-txt { flex: 1; min-width: 0; }
.pal-name { display: block; font-weight: 600; }
.pal-note { display: block; font-size: var(--fs-sm); color: var(--muted); }
/* галочка держит место всегда: иначе строка дёргается при выборе */
.pal-check { flex: none; width: 1.2rem; color: var(--accent); font-weight: 700; visibility: hidden; }
.pal-row[aria-checked="true"] .pal-check { visibility: visible; }
.pal-row[aria-checked="true"] .pal-name { color: var(--accent); }

/* Сегментированный переключатель темы */
/* `wrap` — из-за собственного сегмента Ф10: у flex-элемента `min-width` по умолчанию `auto`,
   поэтому `flex: 1` НЕ ужимает кнопку ниже ширины её слова, и «Компактный · Обычный · Крупный»
   при крупной ступени на 320 px распирали страницу до 339 px. Тот же подвох проект уже знает
   по grid (`minmax(0, 1fr)` вместо `1fr` у `.shell`). Перенос честнее сжатия: ужатая кнопка
   обрезала бы слово, а это ровно тот контрол, которым читаемость и настраивают. */
.seg { display: flex; flex-wrap: wrap; gap: .25rem; background: var(--sunken);
       border-radius: 999px; padding: .25rem; max-width: 420px; }
.seg-b { flex: 1; min-height: 44px; padding: .5rem .6rem; border: 0; border-radius: 999px;
         background: none; color: var(--muted); font-size: var(--fs-md); cursor: pointer; }
.seg-b[aria-pressed="true"] { background: var(--card); color: var(--ink); font-weight: 600;
                               box-shadow: inset 0 0 0 1px var(--line-2); }
.seg-b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.set-hint { margin-top: .7rem; max-width: 52ch; }
