/*
 * Трекер тренировок — стили (§30 ТЗ).
 *
 * Все цвета объявлены переменными в одном месте и нигде не дублируются:
 * ни в других правилах, ни в JavaScript. Графики (§27) берут цвета отсюда
 * же через getComputedStyle.
 */

:root {
    --bg:        #14110f;
    --panel:     #1d1916;
    --panel-2:   #241f1b;
    --line:      #332c26;
    --accent:    #ff7a33;
    --accent-dim:#c9572f;
    --text:      #f3ede4;
    --text-dim:  #a89a8a;
    --good:      #6fae6f;
    --bad:       #e5604c;
    --bad-solid: #b3311f;
    --blue:      #5b9bd5;
    --purple:    #a87fd1;

    /*
     * Ступени тепловой карты (Р-118, Р-119).
     *
     * Переменными, а не числами по месту: те же цвета показывает шкала под
     * картой, и записанные дважды они разошлись бы при первой же правке —
     * шкала объясняла бы карту неверно, а выглядела бы объяснением.
     *
     * Лестница ровная: каждая ступень светлее предыдущей в 1,58 раза, и
     * первая — во столько же светлее пустого дня. Прежняя первая ступень
     * (#3a2418) отличалась от пустой клетки на 1,12 — это не различие, это
     * шум, а в неё попадает обычная тренировка. Год стабильных занятий
     * читался как год простоя.
     */
    --heat-1:    #5d3721;
    --heat-2:    #8f4c27;
    --heat-3:    #c4612d;

    /*
     * Два шрифта вместо одного.
     *
     * Раньше всё приложение было набрано Courier New — наследство версии 1.
     * Для чисел моноширинный хорош: цифры одной ширины, колонки в таблицах
     * выравниваются сами, вес и время не пляшут при каждом изменении. Для
     * русского текста он плох: буквы разрежены, читается тяжело, а на iPhone
     * Courier подменяется чем придётся.
     *
     * Поэтому текст — системным шрифтом устройства, числа и таблицы —
     * моноширинным. Заодно системный шрифт делает приложение похожим на
     * остальные приложения телефона, а не на терминал.
     */
    --font-text: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
                 'Helvetica Neue', Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas,
                 'Courier New', monospace;

    --radius:    10px;
    --radius-sm: 8px;
    --content:   520px;

    /* Запасное значение: подменяется на 100svh или из viewport.js */
    --app-height: 100vh;
    --keyboard-inset: 0px;

    --safe-top:    env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);

    /* Высота нижнего меню: на неё отступает содержимое */
    --nav-height: 62px;
}

* { box-sizing: border-box; }

/*
 * Скрытое остаётся скрытым.
 *
 * Браузер прячет [hidden] своим правилом display: none, но любое наше
 * правило с display перебивает его — оно сильнее по происхождению. Из-за
 * этого кнопка «Создать» в окне выбора, объявленная скрытой, всё равно
 * висела под списком с пустым названием в кавычках.
 */
[hidden] { display: none !important; }

/*
 * Цвет фона держит и html, а не только body (Р-80).
 *
 * Тело ровно в высоту окна и не прокручивается, но Safari при фокусе в поле
 * всё равно сдвигает окно, чтобы показать поле над клавиатурой. Из-под тела
 * в этот момент видно html — а он без фона белый. Человек видит белую
 * полосу и прокрутку там, где ни того ни другого нет.
 */
html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);

    /*
     * Тема тёмная — и системные части браузера тоже (Р-98). Календарь у
     * `input[type=date]` рисует браузер, и в светлой теме он приезжает белым
     * листом посреди тёмного экрана, со значком, которого не видно.
     */
    color-scheme: dark;

    /* Тяга за край не должна раскачивать страницу: тянется список внутри */
    overscroll-behavior: none;
}


/*
 * Размеры шрифта в rem, а не в пикселях (Р-112).
 *
 * Пиксель не слушает человека. Тот, кто увеличил текст в настройках телефона
 * или браузера — а это первое, что делают в сорок с лишним, — не получал в
 * приложении ничего: все сто сорок пять размеров были заданы намертво, и
 * подписи в десять пикселей, без которых числа ничего не значат, оставались
 * десятью пикселями.
 *
 * Один rem — это размер, назначенный самим человеком. Коробки при этом
 * остаются в пикселях: растёт текст, а не отступы, — иначе увеличение шрифта
 * раздувало бы экран вдвое и прятало половину под сгиб.
 */
body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-text);
    font-size: 0.9375rem;
    line-height: 1.45;
    /* Страница не прокручивается — прокручивается содержимое каркаса (Р-70) */
    height: var(--app-height);
    overflow: hidden;

    -webkit-tap-highlight-color: transparent;
    -webkit-text-size-adjust: 100%;

    /* На тёмном фоне сглаживание по умолчанию делает текст жирнее, чем надо */
    -webkit-font-smoothing: antialiased;
}

/*
 * Числа набираются моноширинным и одинаковой ширины цифрами: иначе вес
 * и время дёргаются при каждом изменении, а колонки в таблицах не
 * выстраиваются.
 */
.num,
table.log,
.tile strong,
.info-row strong,
.prog-count,
.rec-value,
.pr-value,
.ex-hist-sets,
.rest-bar strong,
.sess-meta strong,
input.big-input {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

h1, h2, h3 { margin: 0; font-weight: 700; letter-spacing: -0.01em; }
p { margin: 0 0 10px; }
p:last-child { margin-bottom: 0; }

/*
 * Высота окна, не зависящая от адресной строки (Р-69).
 *
 * На айфоне адресная строка при прокрутке сворачивается и разворачивается, и
 * окно вместе с ней то выше, то ниже. Пока высота бралась из
 * window.innerHeight, разметка ездила следом: экран будто дышал.
 *
 * svh — высота при развёрнутой адресной строке, то есть наименьшая из
 * возможных. Она не меняется, а значит, не дёргается и разметка. Меньшее из
 * двух зол осознанное: внизу может остаться полоса фона высотой в адресную
 * строку, но нижнее меню прибито к низу окна и его это не касается.
 *
 * Не dvh: dvh — это ровно то, что дышит.
 */
@supports (height: 100svh) {
    :root { --app-height: 100svh; }
}

/* ================== КАРКАС ================== */

/*
 * Каркас держит высоту окна, прокручивается только содержимое (Р-70).
 *
 * Раньше прокручивалась страница целиком, а меню держалось на
 * position: fixed. На айфоне такое меню не стоит на месте: при инерционной
 * прокрутке и при сворачивании адресной строки Safari пересчитывает
 * положение прикреплённого элемента с запозданием, и меню едет вслед за
 * пальцем. Ни один порядок свойств этого не лечит — лечит только то, что
 * меню перестаёт быть прикреплённым.
 *
 * Поэтому каркас ровно в окно, содержимое прокручивается внутри себя, а меню
 * — обычный сосед по колонке. Оно не может уехать: ему некуда.
 *
 * Клавиатура вычитается из высоты каркаса: она перекрывает окно, не уменьшая
 * его (§31), и без вычитания низ содержимого оказался бы под ней.
 */
.shell {
    height: calc(var(--app-height) - var(--keyboard-inset));
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/*
 * Меню в разметке идёт первым — на компьютере оно слева, и читалкам экрана
 * разумно встречать его до содержимого. На телефоне его место внизу, и
 * говорит об этом раскладка, а не порядок в разметке.
 */
.nav { order: 2; }

.content {
    order: 1;
    flex: 1;
    width: 100%;
    padding: calc(16px + var(--safe-top)) 14px 24px;

    /* Прокрутка живёт здесь, и дальше себя она не уходит: иначе тяга за
       краем списка раскачивала бы страницу целиком */
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

/*
 * Колонка чтения задаётся детям, а не самой прокрутке.
 *
 * Прокрутка обязана занимать всю ширину: полоса рисуется по краю того, что
 * прокручивается, и у колонки в 520 точек она встаёт посреди окна — там, где
 * полосе взяться неоткуда. Ширину держат дети, полоса остаётся у края окна,
 * как ей и положено.
 */
.content > .banners,
.content > .screen {
    width: 100%;
    max-width: var(--content);
    margin: 0 auto;
}



/*
 * Полосы прокрутки нет нигде (Р-78).
 *
 * Раньше прокручивалась страница целиком, и полосу рисовала система: на
 * телефоне она всплывала на время движения и гасла, на компьютере стояла у
 * края окна, где ей и место. С прокруткой внутри элемента её рисует браузер —
 * и она встала внутри содержимого, поверх карточек, постоянной серой чертой.
 *
 * Сначала её убрали только на сенсорных экранах, оставив на компьютере как
 * единственный признак того, что ниже что-то есть. Владелец попросил убрать и
 * там: в приложении, где экран всегда длиннее окна, этот признак не сообщает
 * ничего — прокручивают тут всегда.
 *
 * Прокрутка от этого не пропадает: колесо, тачпад, клавиши и перетаскивание
 * работают по-прежнему. Пропадает только черта.
 */
.content {
    scrollbar-width: none;
}

.content::-webkit-scrollbar {
    width: 0;
    height: 0;
}


.screen { display: block; }

.loading {
    color: var(--text-dim);
    text-align: center;
    padding: 40px 0;
    font-size: 0.8125rem;
}

/*
 * Приложение не запустилось (Р-95).
 *
 * Не полоса и не окно: и то и другое рисуется модулями, а их в этот момент
 * нет. Обычный текст на месте «Загрузка…» — единственное, что точно покажется.
 */
.boot-failed {
    padding: 40px 16px;
    text-align: center;
    color: var(--text);
    font-size: 0.875rem;
    line-height: 1.5;
}

.boot-failed p { margin: 0 0 10px; }

.boot-failed .boot-quiet {
    color: var(--text-dim);
    font-size: 0.75rem;
    word-break: break-word;
}

.noscript {
    padding: 20px;
    color: var(--text);
    text-align: center;
}

/* ================== МЕНЮ ================== */

.nav {
    /* Не fixed: прикреплённое меню на айфоне едет вслед за пальцем (Р-70) */
    flex: none;
    background: var(--panel);
    border-top: 1px solid var(--line);
    padding-bottom: var(--safe-bottom);
    z-index: 40;
}

.nav-inner {
    width: 100%;
    max-width: var(--content);
    margin: 0 auto;
    display: flex;
    gap: 4px;
    padding: 6px 8px;
}

/* Название приложения нужно только боковой колонке на компьютере */
.nav-brand { display: none; }

.nav-btn {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-family: inherit;
    font-size: 0.6875rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    padding: 7px 2px;
    cursor: pointer;
    min-height: 44px;
}

.nav-btn svg {
    width: 22px;
    height: 22px;
    fill: currentColor;
}

/*
 * У открытой вкладки два признака, а не один (§31, Р-126).
 *
 * Менялся цвет и подкладывался фон — но фон отличался от меню в 1,07 раза,
 * то есть второго признака не существовало: на снимке экрана его нет. Ответ
 * на вопрос «где я» держался на одном оттенке, а меню из четырёх вкладок —
 * единственное место, где приложение на него отвечает.
 *
 * На ярком солнце, при ночном режиме экрана или при слабом цветоразличении
 * все четыре вкладки выглядели одинаково. Теперь подложка видна, и начертание
 * подписи тоже другое: любого из двух хватило бы, но вместе они держатся и
 * там, где цвет не читается вовсе.
 */
.nav-btn.is-active {
    color: var(--accent);
    background: var(--line);
}

.nav-btn.is-active span { font-weight: 700; }

/*
 * Клавиатура перекрывает нижнюю часть окна, а не уменьшает его (§31).
 * Меню под ней всё равно не видно — убираем, чтобы оно не занимало место
 * над клавиатурой и не отжимало вниз кнопку «Выполнено».
 */
.keyboard-open .nav { display: none; }
.keyboard-open .content { padding-bottom: 24px; }

/* ================== ЗАГОЛОВКИ ЭКРАНА ================== */

.screen-head { margin-bottom: 18px; }
.screen-head h1 { font-size: 1.375rem; }

.screen-sub {
    color: var(--text-dim);
    font-size: 0.75rem;
    margin-top: 4px;
}

.section { margin-top: 24px; }

/*
 * Раздел экрана крупнее заголовка карточки (Р-124).
 *
 * Было шесть видов заголовка, и все одного размера: раздел экрана, заголовок
 * карточки, подзаголовок, заголовок графика, счётчик истории, подзаголовок
 * справки — все прописными, все с разрядкой, различались цветом и одной
 * точкой кегля.
 *
 * Экраны длинные — профиль больше двух тысяч точек, статистика под три
 * тысячи, — и глаз, пролистывая их, ищет, за что зацепиться, и не находит:
 * «КАК ИДЁТ ПРОГРАММА» и «ПО ДНЯМ НЕДЕЛИ» выглядели одинаково, хотя первое
 * это раздел экрана, а второе — подпись к картинке внутри карточки.
 *
 * Разведено два уровня, не больше: раздел экрана — тринадцать точек,
 * всё, что внутри карточки, остаётся на одиннадцати. Трёх уровней этим
 * экранам не нужно, а разница в две точки при разной разрядке видна сразу.
 */
.section-title {
    color: var(--accent);
    font-size: 0.8125rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    margin-bottom: 10px;
}

/*
    Подзаголовок внутри раздела (§29.1).

    Приглушён, в отличие от «НАЧАТЬ»: тот называет раздел целиком, а этот
    лишь разграничивает внутри него. Цветом спорить с ним нечего — иначе на
    экране три равных заголовка вместо одного с уточнениями.
*/
.sub-title {
    color: var(--text-dim);
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    margin: 2px 0 8px;
}

/* ================== КАРТОЧКИ ================== */

.card {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 14px;
}

.card-title {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 14px;
}

.empty-note {
    color: var(--text-dim);
    font-size: 0.8125rem;
    padding: 6px 0;
}

/*
 * Пояснения крупнее подписей (Р-121).
 *
 * Подписи подняты до одиннадцати точек, и пояснения, стоявшие на тех же
 * одиннадцати, слились с ними. А это не украшение: каждая строка объясняет
 * себя сама, и ровно этим приложение отличается от блокнота. Объяснение,
 * которое не хочется читать из-за кегля, всё равно что его нет — человек
 * снова тычет наугад.
 */
.hint {
    font-size: 0.75rem;
    color: var(--text-dim);
    margin-top: 6px;
}

.info-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: 1px solid var(--line);
    font-size: 0.8125rem;
}

.info-row:last-of-type { border-bottom: none; }
.info-row span { color: var(--text-dim); }

/* ================== ЗАГЛУШКИ ================== */

/*
 * Нереализованный раздел выглядит намеренно незаконченным: пунктир, никакой
 * заливки. Так его нельзя перепутать с готовым экраном, у которого просто
 * нет данных.
 */
.stub {
    border: 1px dashed var(--line);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 14px;
    color: var(--text-dim);
}

.stub-stage {
    display: inline-block;
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-dim);
    border: 1px solid var(--accent-dim);
    border-radius: 20px;
    padding: 2px 9px;
    margin-bottom: 10px;
}

.stub-title {
    color: var(--text);
    font-weight: 700;
    font-size: 0.875rem;
    margin-bottom: 6px;
}

.stub p { font-size: 0.75rem; }

/* ================== КНОПКИ ================== */

.btn {
    display: block;
    width: 100%;
    border: none;
    border-radius: var(--radius-sm);
    padding: 13px 16px;
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    cursor: pointer;
    margin-bottom: 10px;
    min-height: 44px;
}

.btn:last-child { margin-bottom: 0; }

/*
 * Выключенная кнопка выглядит выключенной (Р-115).
 *
 * У `.btn` не было ни одного правила `:disabled`, а выключаются они в семи
 * местах — на время обращения к сети и когда отправлять нечего. Человек жал
 * «Спросить», ничего не происходило, жал ещё раз и решал, что приложение
 * сломалось: кнопка здорова, она уже работает, но сказать об этом ей было
 * нечем — тот же цвет, тот же текст, та же высота.
 */
.btn:disabled {
    opacity: 0.45;
    cursor: default;
}

.btn-lg { font-size: 1rem; padding: 16px; }

.btn-accent { background: var(--accent); color: #1a1310; }
.btn-accent:active { background: var(--accent-dim); }

.btn-done { background: var(--good); color: #102310; }
.btn-done:active { filter: brightness(0.9); }

.btn-ghost {
    background: transparent;
    border: 1px solid var(--line);
    color: var(--text-dim);
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
}

.btn-ghost:active { border-color: var(--text-dim); }

.btn-danger {
    background: transparent;
    border: 1px solid var(--bad);
    color: var(--bad);
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
}

.btn-danger-solid { background: var(--bad-solid); color: #fff; }

/* ================== ПОЛЯ ================== */

.field { margin-bottom: 16px; }
.field:last-child { margin-bottom: 0; }

label {
    display: block;
    font-size: 0.75rem;
    color: var(--text-dim);
    margin-bottom: 6px;
    letter-spacing: 0.03em;
}

label strong { color: var(--accent); }

input[type=text],
input[type=number],
input[type=search],
input[type=password],
input[type=date],
select,
textarea {
    width: 100%;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--text);
    font-family: inherit;
    /* 16px обязательны: при меньшем размере Safari масштабирует страницу
       при фокусе в поле, и вернуть масштаб обратно нечем */
    font-size: 1rem;
    padding: 10px 12px;
}

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent-dim);
}

/*
 * Поле сводки (§55): текст в нём читают, а не пишут.
 *
 * Строки не переносятся по словам, а идут как есть, с прокруткой вбок —
 * сводка выстроена строками «упражнение: столько-то», и мягкий перенос
 * разбил бы их пополам, отчего список перестал бы читаться списком.
 */
/*
 * Крупные текстовые поля — сводка, план, вопрос тренеру.
 *
 * Шрифт 16px, как и у всех полей ввода, и по той же причине: Safari на
 * iPhone масштабирует страницу при фокусе в поле с меньшим размером, и
 * вернуть масштаб обратно нечем. Ширина окна после этого «плывёт» до конца
 * работы с приложением.
 */
.report-text {
    font-size: 1rem;
    line-height: 1.5;
    white-space: pre;
    overflow-x: auto;
    margin-bottom: 12px;
}

input[type=range] {
    width: 100%;
    accent-color: var(--accent);
    height: 32px;
}

/*
    Длительность отдыха: подпись слева, время справа.

    Время — кнопка, а не надпись, и выглядеть должно нажимаемым: пунктир под
    ним и цвет. Иначе о том, что его можно тронуть, никто не догадается — а
    ползунком одним до восьми минут не добраться.
*/
.rest-set {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 4px;
}

.rest-exact {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: 1.125rem;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    border-bottom: 1px dashed var(--accent-dim);
    cursor: pointer;
}

input[type=checkbox] {
    width: 20px;
    height: 20px;
    accent-color: var(--accent);
    flex: none;
}

.setting {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
    margin-bottom: 0;
    cursor: pointer;
}

.setting:last-child { border-bottom: none; }

.setting-text { display: block; }
.setting-label { display: block; font-size: 0.875rem; color: var(--text); }
.setting-hint { display: block; font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }

/* ================== СПРАВОЧНИК УПРАЖНЕНИЙ ================== */

.ex-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
}

.ex-row:last-child { border-bottom: none; }

.ex-main { flex: 1; min-width: 0; }

/*
 * Вся левая часть строки справочника — одна кнопка (§5.2, Р-120).
 *
 * Цель вырастает с двадцати точек до пятидесяти, а строка остаётся прежней
 * высоты: поля наружу, отступ обратно. Приём в приложении уже есть — им
 * сделан значок типа в истории.
 */
button.ex-main {
    display: block;
    width: 100%;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    padding: 7px 0;
    margin: -7px 0;
}

/*
 * Название внутри кнопки — блоком и полужирным: многоточие у длинного имени
 * работает только на блочном элементе, а вес был у прежней кнопки.
 */
button.ex-main .ex-name { display: block; font-weight: 700; }

/*
 * Сводка знакомства: строка целиком — кнопка, значение переносится (Р-123).
 *
 * Обрезка многоточием у .ex-name нужна в справочнике, где строк семьдесят и
 * ровный край важнее полного имени. Здесь наоборот: строк десять, и просят
 * проверить как раз то, что обрезалось.
 */
.ex-name.is-wrap {
    display: block;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    -webkit-line-clamp: none;
}

button.ex-row.intro-sum {
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    border: none;
    border-bottom: 1px solid var(--line);
}

.intro-sum .ex-main { display: block; }
.intro-sum .ex-name { font-weight: 700; }

/* Карандаш — знак, а не кнопка: нажимается вся строка */
.intro-sum-mark { flex: none; color: var(--text-dim); font-size: 0.9375rem; }

button.ex-main:active .ex-name { color: var(--accent); }

/*
 * Имя переносится, а не обрезается (§5, Р-125).
 *
 * Была одна строка с многоточием — и в справочнике на семьдесят одно
 * упражнение как раз живут семейства с общим началом: подтягивания,
 * отжимания, жимы. «Австралийские подтягивания» и «Подтягивания обратным
 * хватом» обрывались в одинаковые огрызки, и человек не мог сказать, какое из
 * них какое, пока не откроет карточку.
 *
 * Приложение само объясняет, почему так нельзя, — в правиле строки плана:
 * «Тяга штанги в наклоне…» и «Тяга штанги в наклоне обратным хватом»
 * обрезаются в одинаковое. В справочнике то же самое, только строк там
 * семьдесят.
 *
 * Прежний довод за обрезку — «не уводить кнопки за край» — снят другими
 * средствами: у левой части строки min-width: 0, у кнопки ширина в сто
 * процентов, а действия свёрнуты в одну кнопку (Р-111), так что ряд больше не
 * гуляет от строки к строке.
 *
 * Две строки, не больше: у самого длинного имени в списке их как раз две, а
 * потолок держит ровный ритм списка.
 */
.ex-name {
    font-size: 0.875rem;
    overflow-wrap: anywhere;

    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

.ex-meta { display: block; font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }

.ex-actions { display: flex; gap: 4px; flex: none; }

/*
 * Разрушительное не стоит вплотную к безопасному (§31, Р-119, Р-125).
 *
 * Правило в приложении уже принято — у таблицы подходов ✎ и × разведены на
 * шестнадцать точек, и там же записано почему: «цена ошибки у них разная».
 * Оставались три места, до которых его не донесли: список замеров веса,
 * ряды значков в списках и ссылки под кнопкой «Продолжить» на главном —
 * «Завершить как есть» и «Удалить» в четырёх точках друг от друга, обе
 * подчёркнутым текстом одного начертания.
 *
 * Вопрос перед удалением есть, поэтому данные не теряются. Но окно надо
 * прочитать и закрыть, и именно из таких промахов складывается ощущение, что
 * приложение всё время переспрашивает.
 */
.ex-actions .icon-btn.is-danger { margin-left: 16px; }
.row-links .link-btn.is-danger { margin-left: 16px; }

.icon-btn {
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 0.9375rem;
    line-height: 1;
    width: 34px;
    height: 34px;
    cursor: pointer;
}

.icon-btn:active { border-color: var(--text-dim); }
.icon-btn.is-danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, transparent); }

/* Поле, не прошедшее проверку в диалоге */
.is-invalid { border-color: var(--bad) !important; }

/*
 * Чего не хватает — словами, а не только цветом рамки (Р-115).
 *
 * Цвет — единственное свидетельство, и он не говорит, что именно не так; а
 * когда полей два, приходится угадывать. Дальтонику же он не говорит ничего.
 */
.field-error {
    color: var(--bad);
    font-size: 0.75rem;
    margin-top: 4px;
    text-align: center;
}

/* ================== ПЛАН ТРЕНИРОВКИ ================== */

.plan-row {
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 12px;
    margin-bottom: 10px;
}

.plan-row-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 10px;
}

/*
 * Колонка с названием обязана уметь сжиматься.
 *
 * Гибкий элемент по умолчанию не становится уже своего содержимого, а
 * содержимое здесь — строка без переносов. «Тяга штанги в наклоне обратным
 * хватом с паузой» распирала шапку на 343 px при доступных 288, и кнопки
 * «выше — ниже — убрать» уезжали за правый край карточки. Многоточие у
 * названия при этом не срабатывало: обрезать было нечего, места хватало.
 */
.plan-row-title { min-width: 0; }

/*
 * Здесь название переносится, а не обрезается многоточием.
 *
 * В справочнике строка одна на упражнение, и там многоточие уместно. В
 * плане под названием и так идут поля подходов и веса — строка занимает
 * несколько сантиметров, и вторая строка ничего не ломает. А узнать
 * упражнение в собранном плане важнее, чем сэкономить высоту: «Тяга штанги
 * в наклоне…» и «Тяга штанги в наклоне обратным хватом» обрезаются в
 * одинаковое.
 */
.plan-row-name {
    font-size: 0.875rem;
    font-weight: 700;
    overflow-wrap: anywhere;
}

/*
 * Разрушительная кнопка отодвинута (Р-119).
 *
 * ↑, ↓ и × стояли подряд через 4 px, а порядок правят так: жмут ↓ несколько
 * раз подряд. Промах на четыре пикселя вправо — и строка исчезала вместе с
 * подходами, повторениями и весом, вписанными минуту назад.
 *
 * То же правило в приложении уже записано у таблицы подходов: «цена ошибки
 * у них разная». Здесь она выше — у подхода перед удалением есть хотя бы
 * вопрос, у строки плана не было ничего.
 */
.plan-row-tools { display: flex; gap: 4px; flex: none; }
.plan-row-tools .icon-btn.is-danger { margin-left: 16px; }

.plan-row-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
    gap: 8px;
}

.plan-row-fields .field { margin-bottom: 0; }
.plan-row-fields input { padding: 8px 10px; }

.icon-btn:disabled { opacity: 0.35; cursor: default; }

/* ================== ВЫПОЛНЕНИЕ ================== */

.sess-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.sess-type { font-size: 1.125rem; font-weight: 700; }
.sess-meta { font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }
.sess-meta strong { color: var(--accent); }
.sess-head .chips { margin-bottom: 0; }

.session-card { text-align: center; }

/*
 * Название упражнения — самое заметное на карточке.
 *
 * Оно было подписью: тринадцать пикселей, приглушённый цвет, разрядка. Так
 * набирают заголовок раздела, а не то, что ищут глазами, подняв голову от
 * штанги: карточка заполнена числами, полями и кнопками, и название в ней
 * терялось. Рамка нужна по той же причине — отделить его от всего
 * остального, а не только сделать крупнее.
 *
 * Перенос по словам обязателен: «Тяга горизонтального блока» в одну строку
 * не встаёт даже на широком телефоне, а вылезшее за рамку название хуже
 * мелкого.
 */
.sess-name {
    font-size: 1.375rem;
    font-weight: bold;
    line-height: 1.2;
    color: var(--text);
    background: rgba(255, 122, 51, 0.08);
    border: 1px solid var(--accent-dim);
    border-radius: 10px;
    padding: 12px 14px;
    margin-bottom: 10px;
    overflow-wrap: anywhere;
}

.sess-set { font-size: 0.875rem; color: var(--accent); margin-bottom: 16px; }

/*
 * Селектор начинается с input не для красоты: правило input[type=number]
 * выше по весу, чем один класс, и без уточнения поле растянулось бы на всю
 * ширину карточки вместо крупного квадрата по центру.
 */
input.big-input {
    display: block;
    width: 140px;

    /* Повторяется здесь намеренно: правило для input[type=number] идёт
       ниже общего списка моноширинных и перебивает его при равном весе */
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    margin: 0 auto 6px;
    font-size: 2.75rem;
    text-align: center;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--text);
    padding: 6px 0;
}

input.big-input:focus { outline: none; border-color: var(--accent); }

/*
 * Ряд «− число +» (§12, Р-120).
 *
 * Кнопки по 48 точек: этим экраном пользуются стоя и мокрой рукой, и цель
 * меньше сорока четырёх здесь особенно дорога. Переносится по строкам ради
 * строки ошибки — она вставляется рядом с полем и в ряду стала бы третьим
 * соседом кнопок.
 */
.big-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-bottom: 6px;
}

.big-row input.big-input { margin: 0; }

.big-row .field-error { flex-basis: 100%; text-align: center; }

.step-btn {
    flex: none;
    width: 48px;
    height: 48px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--text);
    font-family: inherit;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}

.step-btn:active { border-color: var(--accent); color: var(--accent); }

.big-label {
    font-size: 0.75rem;
    color: var(--text-dim);
    letter-spacing: 0.08em;
    margin-bottom: 10px;
}

/*
 * Полоска состояния плана (§56.1).
 *
 * Не карточка: карточка на этом экране означает «нажми меня», а здесь
 * сообщение. Отличается от фона левой чертой — тем же приёмом, что у
 * предупреждений, только спокойнее.
 */
.plan-note {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 14px;
    margin-bottom: 16px;
    border-left: 3px solid var(--accent);
    background: var(--panel-2);
    border-radius: 0 var(--radius) var(--radius) 0;
}

.plan-note-title { font-weight: 700; }
.plan-note-body { font-size: 0.8125rem; color: var(--text-dim); }

.plan-note .btn { align-self: flex-start; margin-top: 6px; }
.plan-note .row-links { margin-top: 6px; }

/* Кончившийся план ничем не ведёт — и черта у него не зовёт, а закрывает */
.plan-note.is-over { border-left-color: var(--line); }

/*
 * Отсчёт подхода на время (§57).
 *
 * Число крупнее поля ввода вдвое: на него смотрят из планки одним глазом и
 * с метра, а не с тридцати сантиметров, как на клавиатуру. Рамки нет —
 * рамка обещает, что сюда можно вписать, а вписывать во время подхода
 * нечего.
 */
.hold { text-align: center; }

.hold .hold-number {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 5.5rem;
    line-height: 1;
    font-weight: 700;
    color: var(--accent);
    margin-bottom: 8px;
}

/* Подготовка гасится: считается ещё не подход, а время на то, чтобы лечь */
.hold.is-lead .hold-number { color: var(--text-dim); }

.inline-field {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-bottom: 18px;
}

.inline-field input { width: 100px; padding: 8px; text-align: center; }
/*
 * Подпись в строке — наравне со словом рядом (Р-122).
 *
 * Часть подписей стала настоящими label ради экранного диктора, а у label по
 * общему правилу есть нижний отступ: в ряду, выровненном по центру, он сдвигал
 * бы слово вверх относительно поля.
 */
.inline-field span,
.inline-field label { font-size: 0.75rem; color: var(--text-dim); margin: 0; }

/* Что уже сделано в этом упражнении — одной строкой вместо таблицы */
.sess-done {
    display: flex;
    align-items: baseline;
    justify-content: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 12px;
    font-size: 0.8125rem;
}

.sess-done-label {
    font-size: 0.6875rem;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

/* Изменение к прошлому разу (§15.1) */
.sess-delta {
    font-size: 0.75rem;
    color: var(--text-dim);
    margin-top: 4px;
}

.sess-delta.is-up { color: var(--good); }
.sess-delta.is-down { color: var(--bad); }
.sess-delta.is-same { font-style: italic; }

.sess-tools { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.sess-tools .btn { flex: 1; margin-bottom: 0; min-width: 150px; }
.btn-sm { padding: 9px 12px; font-size: 0.8125rem; min-height: 38px; }

/* Список упражнений с прогрессом «Бицепс 2/3» */
.prog-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }

.prog-row {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 10px;
    width: 100%;
    text-align: left;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    padding: 10px 12px;
    cursor: pointer;
    min-height: 44px;
}

.prog-row.is-current { border-color: var(--accent); }
.prog-row.is-done    { border-left-color: var(--good); }
.prog-row.is-active  { border-left-color: var(--accent); }
.prog-row.is-skipped { opacity: 0.5; }
.prog-row.is-extra   { border-left-color: var(--blue); }

.prog-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.875rem; }
.prog-count { font-size: 0.875rem; font-weight: 700; color: var(--accent); }
.prog-state { font-size: 0.6875rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }

/* Прошлый и лучший результат (§15) */
.rec-block { margin-bottom: 16px; }

/*
 * Ориентир перед подходом — крупнее подписей (§15, Р-125).
 *
 * «Последний раз 60 кг × 10, 9, 55 × 8» и «Лучший результат» шли двенадцатью
 * точками — мельче всего на карточке, кроме служебных подписей. Над ними
 * название упражнения двадцатью двумя, под ними поле ввода сорока четырьмя.
 * Крупным оказалось то, что и так известно, и то, что и так под пальцем, а
 * единственное, чего человек не помнит, — мельче всего.
 *
 * Смотрят на эту строку стоя у штанги, телефон лежит на скамье в полуметре.
 * С этого расстояния двенадцать точек не читаются: надо нагибаться или брать
 * телефон в руки — между подходами, мокрой рукой.
 *
 * Места хватает: самая длинная строка ориентира около двадцати знаков, при
 * пятнадцати точках моноширинного это меньше двухсот из трёхсот тринадцати.
 */
.rec-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: 4px 8px;
    font-size: 0.9375rem;
    padding: 3px 0;
}

.rec-label { color: var(--text-dim); }
.rec-value { color: var(--text); font-weight: 700; }
.rec-when { color: var(--text-dim); font-size: 0.75rem; }
.rec-empty { color: var(--text-dim); font-style: italic; }

.record-mark { color: var(--accent); }
tr.is-record td { color: var(--accent); }

.record-line {
    font-size: 0.75rem;
    color: var(--accent);
    margin-bottom: 8px;
}

/* Полоса отдыха (§16) */
.rest-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 8px;
    background: var(--panel-2);
    border: 1px solid var(--accent-dim);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin-top: 10px;
}

.rest-label { font-size: 0.6875rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.08em; }
.rest-bar strong { font-size: 1.125rem; color: var(--accent); min-width: 64px; }
/*
 * Кнопки полосы отдыха — тоже сорок четыре точки (§31, Р-122).
 *
 * Было тридцать две, и три кнопки стояли в ряд: «−15 с», «+15 с»,
 * «Пропустить». Промах по «Пропустить» обрывает паузу, и вернуть её нечем.
 */
.rest-bar .chip { min-height: 44px; padding: 5px 10px; }

/*
 * «Пропустить» отодвинуто от «+15 с» (Р-112).
 *
 * «+15 с» жмут и удерживают по нескольку раз подряд, глядя не в экран, а в
 * сторону, с телефоном на вытянутой руке. Восемь точек правее — и пауза, ради
 * которой её и жали, кончилась совсем: отдых заводится только после
 * записанного подхода, обратно его не вернуть.
 *
 * Тем же приёмом, что и в таблице подходов: цена ошибки у соседей разная, и
 * расстояние между ними должно это говорить. «−15» и «+15» стоят вплотную
 * намеренно — промах между ними стоит тридцати секунд, и они видны на табло.
 */
.rest-bar [data-action="rest-skip"] { margin-left: 16px; }

/* Заметки (§20) */
.note-row { margin-bottom: 14px; }

/*
 * Ссылка — цель в сорок четыре точки (§31, Р-122).
 *
 * Правило было записано только у ссылок в ряду кнопок, а ссылки экрана
 * выполнения в него не попадали и оставались двадцатичетырёхточечными:
 * «Ещё…», «＋ дополнительный вес», «＋ заметка к подходу», «считать в
 * минутах». Между тем это самый занятый экран приложения: нажимают стоя,
 * одной рукой, пальцами, которые после подхода дрожат.
 *
 * Цена промаха здесь наибольшая в приложении. «＋ заметка» стоит в четырнадцати
 * точках над зелёной «ВЫПОЛНЕНО»: промах вниз записывает подход с тем, что
 * было в поле, и запускает отдых. А отмена подхода лежит под «Ещё…» — то есть
 * дорога к исправлению ошибки шла через такую же мелкую цель.
 *
 * Запас невидимый вдвойне: кегль подписи не тронут, а поля уходят наружу
 * отрицательным отступом — строка занимает в разметке столько же, сколько
 * занимала. Без этого высота карточки вырастала на полсотни точек, и на
 * экране в 812 зелёная «ВЫПОЛНЕНО» уезжала под нижнее меню: цель выросла бы
 * у трёх ссылок ценой главной кнопки экрана.
 *
 * Тот же приём применён к названию упражнения в справочнике (Р-120).
 */
.link-btn {
    background: none;
    border: none;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 0.75rem;
    text-decoration: underline;
    cursor: pointer;

    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0 8px;
    margin: -11px 0;
}

input.note-input {
    width: 100%;
    margin-top: 8px;

    /* 16px, как у всех полей: меньший размер заставляет Safari на iPhone
       масштабировать страницу при фокусе, и обратно она не возвращается */
    font-size: 1rem;
    padding: 8px 10px;
}

.note-shown {
    font-size: 0.75rem;
    color: var(--text-dim);
    border-left: 2px solid var(--line);
    padding-left: 10px;
    margin-top: 10px;
    text-align: left;
}

tr.log-note td {
    font-size: 0.6875rem;
    color: var(--text-dim);
    padding-top: 0;
    padding-bottom: 8px;
}

.plan-row-last { font-size: 0.6875rem; color: var(--text-dim); margin-top: 2px; }
/*
 * Просьба вмешаться набрана заметнее факта (Р-126).
 *
 * Обычная строка под упражнением сообщает, что было в прошлый раз, — её можно
 * пробежать глазами. Эти две просят поправить: «Вес прикинут от веса тела —
 * поправьте под себя», «Подставлено по чередованию — в прошлый раз было
 * иначе». Приложение честно признаётся, что число выдумано, и зовёт его
 * исправить.
 *
 * А набраны они были тусклее обычной строки: 4,06 к панели против 5,95.
 * Просьба о внимании оформлена как то, что внимания не требует. Тон остаётся
 * тем же тёплым, только читаемым — 6,72.
 */
.plan-row-last.is-guess { color: var(--accent); }

/* ================== ИТОГИ ================== */

table.log { width: 100%; border-collapse: collapse; font-size: 0.875rem; }

table.log th {
    text-align: left;
    color: var(--text-dim);
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 6px 4px;
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}

table.log td { padding: 7px 4px; border-bottom: 1px solid var(--line); }
table.log tr:last-child td { border-bottom: none; }

/* Широкая таблица прокручивается внутри себя, а не распирает страницу */
.table-scroll { overflow-x: auto; }

.ex-block {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 12px;
}

.ex-block-title {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    font-size: 0.875rem;
    font-weight: 700;
    margin-bottom: 8px;
}

.ex-block-title .sub { color: var(--text-dim); font-weight: 400; font-size: 0.75rem; }

/*
 * Плитки показателей.
 *
 * Раньше их было по три в ряд на телефоне: цифры по 19 точек, подписи по
 * 10. Читалось, но не с расстояния вытянутой руки — а смотрят на них
 * именно так, между подходами. Теперь на телефоне их две в ряд, цифра
 * заметно крупнее, и у каждой своя подложка: так они читаются как
 * отдельные показатели, а не как сплошной текст.
 */
.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 8px;
}

.tile {
    text-align: center;
    background: var(--panel-2);
    border-radius: var(--radius-sm);
    padding: 12px 8px;
}

.tile strong {
    display: block;
    color: var(--accent);
    font-size: 1.5rem;
    line-height: 1.15;
}

.tile span {
    display: block;
    font-size: 0.6875rem;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 4px;
}

/* ================== ГЛАВНЫЙ ЭКРАН ================== */

.active-type { font-size: 1rem; font-weight: 700; }
.active-meta { font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; margin-bottom: 14px; }

.history-item {
    display: block;
    width: 100%;
    text-align: left;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    padding: 12px 14px;
    cursor: pointer;
}

.history-item .h-date {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: 0.75rem;
    color: var(--text-dim);
    margin-bottom: 4px;
}

/*
 * Тип — не подпись, а кнопка (Р-50): им правится очередь, и заметив в
 * истории неверный тип, поправить его было негде.
 *
 * Подложка и поля не для красоты. Строка целиком нажимается и открывает
 * карточку, поэтому у значка должна быть своя различимая площадь — иначе
 * каждое второе нажатие уводило бы не туда. Отрицательные поля возвращают
 * строку к прежней высоте: значок стал крупнее, дата рядом с ним — нет.
 */
.history-item .h-badge {
    color: var(--accent);
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 3px 8px;
    margin: -3px 0;
    flex: none;
}
.history-item .h-stats { display: block; font-size: 0.8125rem; }

/* ================== РИТМ (§26.2) ================== */

/*
 * Ритм — справка, а не действие, поэтому он ужат до двух строк под
 * заголовком. Карточкой он занимал 130 px между кнопкой продолжения и
 * быстрым стартом, то есть раздвигал два действия ради пояснения.
 */
.rhythm-strip {
    border-left: 3px solid var(--line);
    padding-left: 10px;
    margin-bottom: 18px;
}

.screen-head + .rhythm-strip { margin-top: -6px; }

.rhythm-strip.is-rest    { border-left-color: var(--good); }
.rhythm-strip.is-due     { border-left-color: var(--accent); }
.rhythm-strip.is-overdue { border-left-color: var(--bad); }

.rhythm-state  { display: block; font-size: 0.875rem; font-weight: 700; }
.rhythm-detail { display: block; font-size: 0.6875rem; color: var(--text-dim); margin-top: 2px; }

/* ================== НАЧАТЬ (§29) ================== */

/*
 * Повтор прошлой тренировки — самый частый способ начать, поэтому он стоит
 * первым. Но оформлен спокойно: яркое пятно на экране одно, и оно отдано
 * кнопке внизу, которая завершает перебор способов.
 *
 * Крупно — упражнения: тренировку узнают по ним, а тип и дата это лишь
 * уточняют. Отдельный блок «прошлая тренировка» после этого не нужен.
 */
.repeat-card {
    display: block;
    width: 100%;
    text-align: left;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: 12px 14px;
    font-family: inherit;
    cursor: pointer;
    margin-bottom: 12px;
    min-height: 44px;
}

.repeat-card:active { border-color: var(--accent); }

/*
    Цвет внимания разведён на два знака (§29.1, Р-123).

    Первый из очереди — единственное предложение экрана, у которого есть
    основание в цифрах, и это отмечено цветом подписи. А рамкой отмечено
    другое: что нажатие начнёт тренировку сразу, без второго экрана. Раньше
    рамка стояла у обеих карточек, и одинаковые прямоугольники делали разное.

    Рамка, а не заливка: залитое пятно на экране одно, и оно отдано кнопке
    внизу (Р-96).
*/
.repeat-card.is-queue .rep-label { color: var(--accent); }

.repeat-card.is-now {
    border-color: var(--accent);
}

.repeat-card.is-now .rep-label { color: var(--accent); }

.repeat-card .rep-label {
    display: block;
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-bottom: 5px;
}

.repeat-card .rep-names { display: block; font-size: 0.9375rem; font-weight: 700; line-height: 1.3; }
.repeat-card .rep-meta { display: block; font-size: 0.6875rem; color: var(--text-dim); margin-top: 4px; }

/* Второстепенные действия строкой ссылок — но с пальцевой площадью нажатия */
/*
 * Ряд кнопок переносится, а не уезжает за край (Р-102).
 *
 * Без переноса flex жмёт кнопки до нечитаемого и всё равно не помещает:
 * на узком экране семь чипов инвентаря уехали за правый край — четыре из
 * семи оказались недоступны вовсе, и со стороны это выглядело так, будто
 * приложение не даёт выбрать несколько.
 */
.row-links {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 4px;
    margin-top: 2px;
}

/*
 * Кнопка в ряду теряет нижний отступ.
 *
 * У .btn он есть, потому что кнопки обычно стоят столбиком. В ряду тот же
 * отступ превращается в разную высоту: последней кнопке его не достаётся
 * (:last-child), и она оказывается выше соседей ровно на десять пикселей.
 * Ряд от этого выглядит сломанным, хотя сломан не он, а наследство столбика.
 */
.row-links .btn { margin-bottom: 0; }

/*
 * Кнопка после ряда не прилипает к нему (Р-96).
 *
 * Ряд отдал свой нижний отступ строкой выше, и следующая кнопка вставала
 * вплотную. Пока они разного цвета, это просто тесно; когда обе яркие —
 * «Разобрать» и «Утвердить план» — они сливаются в одно оранжевое пятно
 * буквой Г, и человек не видит, где кончается одна и начинается другая.
 */
.row-links + .btn { margin-top: 10px; }

/*
 * В ряду кнопок запас лишний: высоту ряда задают соседние кнопки, и
 * отрицательный отступ только сдвинул бы ссылку относительно них.
 */
.row-links .link-btn { padding: 0 8px; margin: 0; }

.link-btn.is-danger { color: var(--bad); }

/* ================== ПОСЛЕДНИЕ СЕМЬ ДНЕЙ (§26) ================== */

/*
 * Скользящее окно, а не календарная неделя: в понедельник утром календарная
 * неделя пуста и экран показывал бы спад, которого нет. Сегодня — всегда
 * крайняя правая клетка.
 */
.week-strip {
    display: flex;
    gap: 5px;
    margin-bottom: 14px;
}

.wk-day {
    flex: 1;
    text-align: center;
    font-size: 0.6875rem;
    color: var(--text-dim);
    background: var(--panel-2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: 9px 0;
}

.wk-day.is-done {
    background: var(--accent);
    color: #1a1310;
    font-weight: 700;
}

.wk-day.is-today { border-color: var(--text-dim); }
.wk-day.is-done.is-today { border-color: var(--text); }

/* Три плитки в ряд на телефоне: обычные 140 px переносили бы третью */
/*
 * Три плитки в ряд на главном — но читаемые (§29.1, Р-124).
 *
 * Общее правило плиток прямо говорит, почему их развели по две: «раньше их
 * было по три в ряд на телефоне: цифры по 19 точек, подписи по 10. Читалось,
 * но не с расстояния вытянутой руки». А на главном стояли ровно три в ряд —
 * и смотрят на главный как раз мельком, чаще, чем на статистику.
 *
 * По две в ряд здесь не выходит: третья плитка осталась бы одна во второй
 * строке, а место на главном посчитано. Поэтому три, но с прежним кеглем
 * цифры и без разрядки в подписи: разрядка съедает в колонке шириной
 * девяносто точек как раз те буквы, которых не хватает, а «ТОННАЖ, КГ» без
 * неё встаёт в строку целиком.
 */
.tiles.is-tight { grid-template-columns: repeat(3, 1fr); }
.tiles.is-tight .tile { padding: 10px 4px; }
.tiles.is-tight .tile span { letter-spacing: 0; }

/* ================== ВЕС ТЕЛА НА ГЛАВНОЙ (§25) ================== */

.weight-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
    width: 100%;
    text-align: left;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--text);
    font-family: inherit;
    padding: 14px 16px;
    cursor: pointer;
    min-height: 44px;
}

/*
 * Зазор между строками (Р-109).
 *
 * Две одинаковые панели вплотную читаются как одна: границы соседних строк
 * совпадают в одну линию, и вес с талией выглядели слипшимися. Та же беда,
 * что с двумя кнопками одного цвета подряд (Р-96), и лечится тем же.
 */
.weight-row + .weight-row { margin-top: 8px; }

.weight-row:active { border-color: var(--accent); }

.weight-row .w-value {
    font-family: var(--font-mono);
    font-size: 1.375rem;
    font-weight: 700;
    white-space: nowrap;
}

.weight-row .w-value small { font-size: 0.75rem; font-weight: 400; color: var(--text-dim); }
.weight-row .w-meta { font-size: 0.6875rem; color: var(--text-dim); }

/* ================== ШАБЛОНЫ ================== */

.tpl-head { margin-bottom: 8px; }
.tpl-name { font-size: 1rem; font-weight: 700; }
.tpl-meta { font-size: 0.6875rem; color: var(--text-dim); margin-top: 2px; }
.tpl-list { font-size: 0.75rem; color: var(--text-dim); margin-bottom: 14px; }

/* ================== ИСТОРИЯ ================== */

.hist-count {
    font-size: 0.6875rem;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 10px;
}

.history-item { margin-bottom: 10px; }
.history-item:last-child { margin-bottom: 0; }
.history-item .h-name { display: block; font-size: 0.875rem; font-weight: 700; margin-bottom: 4px; }

.history-item .h-note {
    display: block;
    font-size: 0.6875rem;
    color: var(--text-dim);
    border-left: 2px solid var(--line);
    padding-left: 8px;
    margin-top: 6px;
}

/* ================== КАЛЕНДАРЬ ================== */

.cal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 12px;
}

.cal-title { font-size: 0.9375rem; font-weight: 700; }

.cal-weekdays,
.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}

.cal-weekdays {
    margin-bottom: 4px;
    font-size: 0.6875rem;
    color: var(--text-dim);
    text-transform: uppercase;
    text-align: center;
}

.cal-cell {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 0.75rem;
    cursor: pointer;
}

.cal-cell:disabled { cursor: default; }
.cal-cell.is-empty { background: none; border: none; }

/*
 * Четыре ступени насыщенности вместо градиента: соседние оттенки на
 * маленькой клетке всё равно неразличимы, а ступени читаются сразу.
 */
.cal-cell.is-l1 { background: #3a2418; color: var(--text); }
.cal-cell.is-l2 { background: #6b3a1d; color: var(--text); }
.cal-cell.is-l3 { background: #a8501f; color: #fff; }
.cal-cell.is-l4 { background: var(--accent); color: #1a1310; font-weight: 700; }

.cal-cell.is-today { border-color: var(--text-dim); }
.cal-cell.is-selected { border-color: var(--accent); border-width: 2px; }

.cal-summary {
    margin-top: 12px;
    font-size: 0.75rem;
    color: var(--text-dim);
    text-align: center;
}

/*
 * Правка и удаление подхода — в одну строку и на расстоянии.
 *
 * Ширины в 34 px на две кнопки не хватало, и они переносились одна под
 * другую. А стоя вплотную, кнопки одного размера легко перепутать пальцем —
 * при том, что цена ошибки у них разная: правку видно и можно повторить,
 * удаление приходится восстанавливать по памяти.
 */
td.cell-tools {
    width: 1%;
    text-align: right;
    white-space: nowrap;
}

td.cell-tools .icon-btn { width: 30px; height: 30px; font-size: 0.875rem; }
td.cell-tools .icon-btn + .icon-btn { margin-left: 16px; }

/* ================== ГРАФИКИ (§27) ================== */

svg.chart { width: 100%; height: auto; display: block; overflow: visible; }

/*
 * Подписи графиков — тем же кеглем, что подписи всего остального (Р-121).
 *
 * Было восемь и девять точек — вдвое мельче основного текста и самое мелкое
 * во всём приложении; вдобавок картинка шириной 320 единиц лежит в карточке
 * шириной 313, и каждая буква ужималась ещё на два процента. Восемь точек в
 * тридцати сантиметрах от глаз — это буква ростом меньше миллиметра.
 *
 * А смотрят на эти графики так же, как на плитки: с расстояния вытянутой
 * руки, между подходами. Столбцы было видно, а чей столбец и сколько в нём —
 * уже нет: график переставал быть графиком и оставался узором.
 */
.chart-value { font-size: 0.6875rem; fill: var(--text); font-weight: bold; }
.chart-label { font-size: 0.6875rem; fill: var(--text-dim); }

.chart-title {
    font-size: 0.6875rem;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: 16px 0 8px;
}

.chart-empty { color: var(--text-dim); font-size: 0.75rem; padding: 14px 0; text-align: center; }

.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; }
.legend-item { display: flex; align-items: center; gap: 5px; font-size: 0.6875rem; color: var(--text-dim); }
.legend .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.legend .dot.is-accent { background: var(--accent); }
.legend .dot.is-blue { background: var(--blue); }
.legend .dot.is-dim { background: var(--text-dim); }

/* Тепловая карта года: узкая и длинная, поэтому прокручивается вбок */
/*
 * Карта дней прокручивается вбок, а не ужимается под экран.
 *
 * Раньше год втискивался в ширину карточки целиком: клетка выходила в
 * девять пикселей, оттенки становились неразличимы, и вся история читалась
 * как серое пятно. Теперь клетка задана в пикселях, а не помещается —
 * листается пальцем или колесом.
 *
 * Колонка дней недели стоит вне прокрутки: внутри она уезжала бы вместе с
 * сеткой при первом же движении.
 */
.heat-wrap {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.heat-days {
    flex: none;
    padding-top: var(--heat-top);
    font-size: 0.6875rem;
    color: var(--text-dim);
}

.heat-days span {
    display: block;
    height: var(--heat-row);
    line-height: var(--heat-row);
}

.heatmap-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 6px;

    /* Прокрутка вбок пальцем не должна тянуть за собой всю страницу */
    overscroll-behavior-x: contain;
}

svg.heatmap { display: block; }

.heat-month { font-size: 0.6875rem; }

/*
 * Обводка одна на все клетки (Р-119).
 *
 * Была только у пустой — и пустой день выходил заметнее занятого: у него
 * есть край, а у заполненного нет. Теперь сетка нарисована одинаково, а
 * различает дни только заливка, как и задумано.
 */
.heat { fill: var(--panel-2); stroke: var(--line); stroke-width: 0.5; }
.heat-1 { fill: var(--heat-1); }
.heat-2 { fill: var(--heat-2); }
.heat-3 { fill: var(--heat-3); }
.heat-4 { fill: var(--accent); }

/* Клетка нажимается — и говорит об этом видом (Р-118) */
.heatmap [data-action] { cursor: pointer; }

.heat.is-picked { stroke: var(--text); stroke-width: 2; }

/*
 * Шкала под картой: квадратик — и рядом, какому числу подходов он отвечает.
 *
 * В строку, а не таблицей: пять ступеней помещаются в ряд даже на узком
 * экране, а не поместятся — перенесутся целыми парами.
 */
.heat-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    margin-top: 10px;
    font-size: 0.6875rem;
    color: var(--text-dim);
}

.heat-key {
    width: 11px;
    height: 11px;
    border-radius: 2px;
    display: inline-block;

    /* Рамка у всех — как и обводка у клеток карты: шкала обязана выглядеть
       ровно тем, что она объясняет */
    border: 1px solid var(--line);
}

.heat-key.heat-0 { background: var(--panel-2); }
.heat-key.heat-1 { background: var(--heat-1); }
.heat-key.heat-2 { background: var(--heat-2); }
.heat-key.heat-3 { background: var(--heat-3); }
.heat-key.heat-4 { background: var(--accent); }

/* Единица — в конец строки и отдельным словом: она одна на все пять ступеней */
.heat-legend-unit { margin-left: 2px; }

/* Строка выбранного дня держит высоту и пустой: иначе карта прыгает по нажатию */
.heat-pick { min-height: 1.2em; }

/* Лучший месяц за всё время (§23.1) */
.best-month {
    display: flex;
    flex-direction: column;
    gap: 2px;
    border-top: 1px solid var(--line);
    margin-top: 12px;
    padding-top: 12px;
    font-size: 0.75rem;
}

.best-label {
    color: var(--accent);
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* Изменение показателя к предыдущему периоду (§23.1) */
.tile-delta { display: block; font-size: 0.6875rem; margin-top: 2px; letter-spacing: 0; text-transform: none; }
.tile-delta.is-up { color: var(--good); }
.tile-delta.is-down { color: var(--bad); }

/* ================== ЛИЧНЫЕ РЕКОРДЫ (§24.1) ================== */

.pr-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-bottom: 1px solid var(--line);
    border-left: 3px solid transparent;
    color: var(--text);
    font-family: inherit;
    padding: 10px 0 10px 8px;
    cursor: pointer;
}

.pr-row:last-child { border-bottom: none; }
.pr-row.is-fresh { border-left-color: var(--accent); }

.pr-main { min-width: 0; }
.pr-name { display: block; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pr-meta { display: block; font-size: 0.6875rem; color: var(--text-dim); margin-top: 2px; }
.pr-value { flex: none; font-size: 0.8125rem; font-weight: 700; color: var(--accent); }

/* ================== КАРТОЧКА УПРАЖНЕНИЯ ================== */

.ex-hist-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--line);
    font-size: 0.8125rem;
}

.ex-hist-row:last-child { border-bottom: none; }
.ex-hist-date { flex: none; color: var(--text-dim); font-size: 0.75rem; }
.ex-hist-sets { text-align: right; }

/* ================== ЧИПЫ ================== */

.chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.chip {
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 18px;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 0.75rem;
    padding: 8px 14px;
    cursor: pointer;
    min-height: 36px;
}

.chip.is-active { border-color: var(--accent); color: var(--accent); }

/*
    Кнопки с удержанием (§16).

    На телефоне долгое нажатие — это системный жест: браузер начинает
    выделять текст и показывать своё меню, а заодно отменяет указатель
    (pointercancel). Повтор от этого срывался на первом же шаге, и удержание
    выглядело неработающим.

    touch-action убирает задержку и притязания жестов, user-select и
    touch-callout — выделение с меню.
*/
[data-hold] {
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;

    /* Вспышка подсветки на каждый повтор читается как мигание экрана */
    -webkit-tap-highlight-color: transparent;
}

/*
    Плашка во всю ширину — для тех разделов, где она одна (§29.1).

    В очереди плашки сравнивают друг с другом и потому ставят рядом. Здесь
    сравнивать не с чем: раздел показывает одно, и прижатая к левому краю
    плашка выглядит обрезком строки, а не разделом.
*/
.chip.is-full,
.chip.is-wide {
    display: flex;
    width: 100%;
    align-items: center;

    /* Высота у широких плашек общая: стоят они друг под другом, и разница
       в шесть пикселей читается как случайность, а не как замысел */
    min-height: 42px;
}

.chip.is-full { justify-content: center; }

/*
    Ежедневное: сделано сегодня или ещё нет (§29.1).

    Цвет отвечает на единственный вопрос к фоновой тренировке, и потому
    заменяет собой чтение подписи. Приглушённо, полупрозрачной рамкой: это
    отметка о состоянии, а не призыв — призыв на экране один, и он внизу.
*/
.chip.is-done {
    border-color: color-mix(in srgb, var(--good) 55%, transparent);
}

.chip.is-done .chip-count { color: var(--good); }

.chip.is-todo {
    border-color: color-mix(in srgb, var(--accent-dim) 65%, transparent);
}

.chip.is-todo .chip-count { color: var(--accent); }

/*
    Забытое упражнение (§29.1): такая же плашка, но пунктиром.

    Пунктир и говорит, что за ней не готовая тренировка, а сбор новой —
    одинаковый вид у разных действий обещал бы повтор там, где его нет.
    Раньше это была карточка в рамке рядом с повтором прошлой; после того
    как плашки стали очередью, она показывала одно упражнение, а места
    занимала как полноценный способ начать.
*/
.chip.is-draft {
    display: inline-flex;
    align-items: stretch;
    padding: 0;
    overflow: hidden;
    border-style: dashed;
    border-color: var(--accent);
    color: var(--accent);
}

.chip.is-draft .chip-main,
.chip.is-draft .chip-arrow {
    background: none;
    border: 0;
    color: inherit;
    font-family: inherit;
    font-size: inherit;
    cursor: pointer;
    padding: 10px 14px;
    min-height: 40px;
}

/*
    Забытое показывается по одному, и плашка занимает всю ширину: сравнивать
    её не с чем — соседей в ряду нет, — а широкая цель попадает под палец не
    глядя. Название посередине, стрелки по краям, где им и место.
*/
.chip.is-wide .chip-main {
    flex: 1;
    text-align: center;
}

/* Стрелки отделены чертой: это другое действие, а не часть названия */
.chip.is-draft .chip-arrow {
    padding: 10px 14px;
    font-size: 1.125rem;
    line-height: 1;
}

.chip.is-wide .chip-arrow:first-child { border-right: 1px dashed var(--accent); }
.chip.is-wide .chip-arrow:last-child { border-left: 1px dashed var(--accent); }

/*
    Сколько дней прошло с прошлого раза (§29.1).

    Приглушено и стоит после названия: отвечает на вопрос «почему это
    здесь», а выбирают всё-таки по названию. Сокращённо, «12 дн»: полное
    слово удлиняло плашку так, что в ряд их вставало на одну меньше.
*/
/*
 * Число на плашке тусклее слова, но не прозрачностью (Р-111).
 *
 * Прозрачность гасила заодно и цвет состояния: у плашки «ещё не сделано»
 * выходило 2,0 против фона — самое бледное место главного экрана. А очередь
 * (Р-32) построена ровно затем, чтобы эти числа сравнивать глазом; сравнить
 * можно только то, что видно.
 */
.chip-count {
    margin-left: 6px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

/* ================== ПОЛОСЫ УВЕДОМЛЕНИЙ ================== */

.banners { margin-bottom: 14px; }

.banner {
    background: var(--panel-2);
    border: 1px solid var(--accent-dim);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin-bottom: 10px;
    font-size: 0.8125rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.banner-btn {
    background: var(--accent);
    color: #1a1310;
    border: none;
    border-radius: 6px;
    padding: 8px 12px;
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
    min-height: 36px;
}

/* ================== ДИАЛОГИ ================== */

.dialog-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 16px;
    padding-bottom: calc(16px + var(--safe-bottom));
    z-index: 100;
}

.dialog {
    width: 100%;
    max-width: 420px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 18px;
}

.dialog-title { font-size: 1rem; font-weight: 700; margin-bottom: 8px; }
.dialog-text { font-size: 0.8125rem; color: var(--text-dim); margin-bottom: 16px; }

.dialog-options { margin-bottom: 14px; }

.dialog-option {
    display: block;
    width: 100%;
    text-align: left;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    padding: 12px 14px;
    margin-bottom: 8px;
    cursor: pointer;
}

.dialog-option.is-danger { border-color: var(--bad); color: var(--bad); }
.dialog-option-label { display: block; font-size: 0.875rem; }
.dialog-option-hint { display: block; font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }

.dialog-actions { display: flex; gap: 8px; }
.dialog-actions .btn { margin-bottom: 0; }

/*
 * Окно выбора занимает отведённую высоту сразу, а не растёт под содержимое.
 *
 * Раньше высоту задавал список: короткий отбор — низкое окно, прижатое к
 * низу экрана, и до него приходилось тянуться, приподнимая страницу
 * прокруткой. Место наверху при этом пустовало.
 *
 * Высота считается от --app-height, а не от vh: на телефоне vh включает
 * полосу адреса, которой на экране нет, и окно вылезало бы за край.
 */
.dialog-tall {
    display: flex;
    flex-direction: column;
    height: calc(var(--app-height) * 0.82);
    max-height: calc(var(--app-height) - 32px);
}

.dialog-tall .dialog-options { overflow-y: auto; flex: 1; min-height: 80px; }

.dialog-search {
    width: 100%;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--text);
    font-family: inherit;
    font-size: 1rem;
    padding: 10px 12px;
    margin-bottom: 10px;
}

.dialog-search:focus { outline: none; border-color: var(--accent-dim); }

/* ================== КОМПЬЮТЕР (§32) ================== */

@media (min-width: 900px) {
    /*
     * Колонка уже, чем была (900 px), и вот почему.
     *
     * На 900 карточка растягивалась на 844 px, а кнопка оставалась 220–280:
     * на одном экране получалось три разных правых края, и глазу не за что
     * зацепиться. Растянуть кнопку до 844 нельзя — такой ширины кнопок не
     * бывает; значит, узкой должна стать колонка.
     *
     * 760 выбрано по нижней границе: при ней списки карточек ещё встают в
     * две колонки (320 + 320 + отступ), а выполнение — в две колонки ввода
     * и списка упражнений. Меньше — и обе раскладки схлопнутся.
     */
    :root { --content: 760px; }

    .shell { flex-direction: row; }

    .nav {
        order: 0;
        align-self: stretch;
        width: 230px;
        flex: none;
        border-top: none;
        border-right: 1px solid var(--line);
        padding: 20px 12px;
    }

    .nav-inner {
        flex-direction: column;
        gap: 4px;
        padding: 0;
        max-width: none;
    }

    .nav-brand {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 0 10px 20px;
        font-size: 1.0625rem;
        font-weight: 700;
    }

    .nav-brand-mark { color: var(--accent); }

    .nav-btn {
        flex: none;
        flex-direction: row;
        justify-content: flex-start;
        gap: 12px;
        font-size: 0.8125rem;
        letter-spacing: 0;
        text-transform: none;
        padding: 11px 12px;
    }

    .content { padding: 32px 28px 60px; }

    /* Колонка прижата к меню, а не посреди свободного места */
    .content > .banners,
    .content > .screen { margin: 0; }

    .screen-head h1 { font-size: 1.625rem; }

    /*
     * Кнопки во всю ширину колонки — как на телефоне.
     *
     * Раньше они жались влево на 220–280 px, чтобы не растягиваться на все
     * 900. От этого на экране появлялось три правых края: у карточки, у
     * яркой кнопки и у обычной. Колонка стала уже, и растягивать их снова
     * можно: край теперь один на всё.
     */
    .dialog-actions .btn { flex: 1; }

    .dialog-backdrop { align-items: center; }

    /*
     * Экран выполнения в две колонки (§32): ввод подхода слева, список
     * упражнений справа. На телефоне список пришлось бы искать прокруткой,
     * здесь он всё время перед глазами.
     */
    .sess-layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(260px, 320px);
        gap: 16px;
        align-items: start;
    }

    .sess-layout .card { margin-bottom: 0; }
    .sess-layout + .btn { margin-top: 16px; }

    input.big-input { width: 180px; font-size: 3.25rem; }

    /* Поля плана шире: на широкой строке они иначе выглядят обрубками */
    .plan-row-fields { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

    /* На широком экране плиток помещается больше, и дробить их незачем */
    .tiles { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

    /*
     * Списки карточек в две колонки: на широком экране одна колонка
     * оставляет две трети ширины пустыми, а прокрутка становится длиннее
     * без всякой пользы.
     */
    .grid-2 {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
        gap: 14px;
        align-items: start;
    }

    .grid-2 > * { margin-bottom: 0; }

    /* Мелкие действия внутри карточки выполнения — по содержимому */
    .sess-tools .btn { width: auto; }

    /* Наведение мышью: на телефоне его не бывает, и подсветка там мешает */
    .prog-row:hover, .pr-row:hover, .history-item:hover,
    .chip:hover, .icon-btn:hover, .cal-cell:not(:disabled):hover {
        border-color: var(--text-dim);
    }

    .btn:hover { filter: brightness(1.08); }
}

/* Клавиатурная навигация: видимый фокус обязателен, иначе по Tab не понять,
   где находишься. На мыши он не появляется — :focus-visible про это знает */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.keys-hint { display: none; }

.prog-key {
    flex: none;
    display: none;
    min-width: 18px;
    height: 18px;
    line-height: 18px;
    text-align: center;
    border: 1px solid var(--line);
    border-radius: 4px;
    font-size: 0.6875rem;
    color: var(--text-dim);
}

/* Подсказки и номера клавиш — только там, где клавиатура вообще есть */
@media (min-width: 900px) and (pointer: fine) {
    .prog-key { display: inline-block; }
    .prog-row { grid-template-columns: auto 1fr auto auto; }
    .keys-hint { display: block; }
}

/* ================== ИНТЕРВАЛЬНАЯ ПРОГРАММА (§50) ================== */

/*
 * Экран без единого поля ввода: отсчёт должен читаться с вытянутой руки,
 * поэтому цифра здесь крупнее всего остального в приложении, а цвет полосы
 * говорит о фазе раньше, чем прочитается слово.
 */
.iv-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.iv-head h1 { font-size: 1.25rem; }
.iv-meta { font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }

.iv-clock {
    background: var(--panel);
    border: 1px solid var(--line);
    border-left: 4px solid var(--line);
    border-radius: var(--radius);
    padding: 20px 16px 24px;
    margin-bottom: 14px;
    text-align: center;
}

.iv-clock.is-lead  { border-left-color: var(--text-dim); }
.iv-clock.is-work  { border-left-color: var(--accent); }
.iv-clock.is-rest  { border-left-color: var(--blue); }
.iv-clock.is-round { border-left-color: var(--purple); }
.iv-clock.is-done  { border-left-color: var(--good); }

.iv-phase {
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.iv-time {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 4.25rem;
    line-height: 1.05;
    font-weight: 700;
    margin: 6px 0 10px;
}

.is-work .iv-time  { color: var(--accent); }
.is-rest .iv-time  { color: var(--blue); }
.is-round .iv-time { color: var(--purple); }
.is-done .iv-time  { color: var(--good); }

.iv-now { font-size: 1.25rem; font-weight: 700; }

.iv-how {
    font-size: 0.75rem;
    color: var(--text-dim);
    margin: 8px auto 0;
    max-width: 32em;
}

.iv-next { font-size: 0.8125rem; color: var(--text-dim); margin-top: 10px; }

.iv-where {
    font-size: 0.6875rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-top: 12px;
}

.iv-tools { margin-bottom: 14px; }

/* Список круга: где мы сейчас, видно без чтения */
.iv-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    font-size: 0.875rem;
}

.iv-row.is-now { background: var(--panel-2); color: var(--accent); font-weight: 700; }

.iv-num {
    flex: none;
    width: 22px;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--text-dim);
}

.iv-row.is-now .iv-num { color: var(--accent); }

/* На компьютере места больше — цифра крупнее, её видно от стойки */
@media (min-width: 900px) {
    .iv-time { font-size: 6rem; }
}

/*
 * Название упражнения нажимается — за ним описание техники (§5.2).
 *
 * width обязателен, и вот почему. Обрезание длинного названия многоточием
 * задано у .ex-name и работало, пока название было обычным текстом. Кнопка
 * же — поле формы: её ширина считается по содержимому даже при display:
 * block, растянуть её до колонки браузер сам не догадывается. «Тяга штанги
 * в наклоне обратным хватом с паузой» получала 368 px в колонке шириной
 * 156 и наезжала на кнопки правки и архива.
 */
button.ex-name {
    display: block;
    width: 100%;
    background: none;
    border: none;
    color: var(--text);
    font-family: inherit;
    font-size: inherit;
    font-weight: 700;
    text-align: left;
    cursor: pointer;

    /*
     * Поля наружу, отступ обратно (Р-120): цель растёт с двадцати точек до
     * тридцати шести, а строка остаётся прежней высоты. Там, где название —
     * единственное нажимаемое в строке, промах вниз на несколько пикселей не
     * делал ничего, и приложение выглядело зависшим.
     */
    padding: 8px 0;
    margin: -8px 0;
}

button.ex-name:active { color: var(--accent); }

/* Отбор по группам в окне выбора: над поиском, чипами */
.dialog-groups { margin-bottom: 10px; }

/* ================== КАК ПОЛЬЗОВАТЬСЯ ================== */

/*
 * Разделы справки — гармошкой на <details>.
 *
 * Собственная стрелка вместо браузерной: у Safari она своей формы и своего
 * места, и заголовки на разных устройствах вставали по-разному.
 */
.guide {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-bottom: 10px;
    overflow: hidden;
}

.guide > summary {
    padding: 15px 16px;
    font-size: 0.9375rem;
    font-weight: 700;
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 10px;
}

.guide > summary::-webkit-details-marker { display: none; }

.guide > summary::before {
    content: '';
    flex: none;
    width: 7px;
    height: 7px;
    border-right: 2px solid var(--accent);
    border-bottom: 2px solid var(--accent);
    transform: rotate(-45deg);
    transition: transform 0.15s;
    margin-left: 2px;
}

.guide[open] > summary::before { transform: rotate(45deg); }
.guide[open] > summary { color: var(--accent); }

.guide-body {
    padding: 0 16px 16px;
    font-size: 0.875rem;
    line-height: 1.55;
}

/*
 * Мера строки у связного текста (Р-124).
 *
 * Колонка чтения — 760 точек, и внутри карточки абзацу оставалось около
 * шестисот семидесяти: при кегле в четырнадцать точек это девяносто пять
 * знаков в строке. На такой длине глаз теряет начало следующей строки и
 * перечитывает одну и ту же. На телефоне всё хорошо само собой — ломается
 * только там, где экран шире.
 *
 * Ограничение стоит на абзацах, а не на карточке: плиткам, таблицам и
 * графикам ширина нужна вся.
 */
.guide-body p { margin: 0 0 12px; max-width: 34em; }
.guide-body b { color: var(--text); font-weight: 700; }

/* Подзаголовок внутри раздела: табата длинная и без разбивки не читается */
.guide-sub {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin: 18px 0 10px;
}

/*
 * Порядок действий. Номер вынесен в свою колонку, а не идёт строкой:
 * шаги длиной в две-три строки, и с обычным маркером вторая строка
 * подъезжала под цифру.
 */
.guide-steps {
    margin: 0 0 12px;
    padding: 0;
    list-style: none;
    counter-reset: guide;
}

.guide-steps li {
    counter-increment: guide;
    position: relative;
    padding-left: 30px;
    margin-bottom: 10px;
}

.guide-steps li::before {
    content: counter(guide);
    position: absolute;
    left: 0;
    top: 0;
    width: 20px;
    text-align: center;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: 50%;
    line-height: 18px;
}

/* Понятие и пояснение: имя строкой сверху, а не колонкой слева — на
   телефоне колонка оставляла пояснению полосу в двадцать знаков */
.guide-row { padding: 10px 0; border-bottom: 1px solid var(--line); }
.guide-row:last-child { border-bottom: none; padding-bottom: 0; }
.guide-rows { margin-bottom: 6px; }

.guide-row-name { font-weight: 700; margin-bottom: 2px; }
.guide-row-text { color: var(--text-dim); font-size: 0.8125rem; }

/* Пример — тем же цветом, что подсказки, но с полосой слева: это не
   указание, а иллюстрация, и путать их не надо */
.guide-example {
    border-left: 2px solid var(--line);
    padding-left: 12px;
    color: var(--text-dim);
    font-size: 0.8125rem;
}

.guide-sound {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
}

.guide-sound:last-child { border-bottom: none; }
.guide-sound-text { flex: 1; min-width: 0; }
.guide-sound .chip { flex: none; margin: 0; }

/* ================== АНКЕТА ТЕСТИРОВЩИКА (§52) ================== */

.sv-q {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 10px;
}

/* Незаполненное обязательное: рамка, а не текст с упрёком */
.sv-q.is-missing { border-color: var(--accent); }

.sv-label { font-weight: 600; font-size: 0.875rem; margin-bottom: 8px; }
.sv-req { color: var(--accent); }
.sv-hint { margin: -4px 0 10px; }
.sv-sec-hint { margin: -4px 0 10px; }

.sv-q .opts { margin-bottom: 0; }
.sv-q textarea, .sv-q input[type=text] { margin-bottom: 0; }

/* Шкала: цифры одной ширины, подписи концов по краям */
.sv-scale { align-items: center; }
.sv-scale .chip, .sv-scale .opt { min-width: 44px; text-align: center; }
.sv-end { color: var(--text-dim); font-size: 0.6875rem; }

.sv-about { margin-top: 10px; }
.sv-about .info-row strong { font-weight: 400; text-align: right; max-width: 60%; word-break: break-word; }

.sv-failed { border-color: var(--accent); }
.sv-failed .sv-text { margin: 10px 0; font-size: 0.8125rem; }

/*
 * Кнопки выбора в анкете.
 *
 * Отдельно от .chip: те живут в строке отбора и умышленно мелкие, а здесь
 * по ним попадают пальцем на бегу, и промах стоит потерянного ответа.
 */
.opt {
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--text);
    font-family: inherit;
    font-size: 0.875rem;
    padding: 9px 15px;
    cursor: pointer;
    min-height: 40px;
}

.opt.is-on {
    background: rgba(255, 122, 51, 0.14);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 600;
}

.opts { display: flex; flex-wrap: wrap; gap: 8px; }

/*
 * «Тренировка записана» на итогах (§19).
 *
 * Зелёным и с галочкой, а не обычной подсказкой: это не пояснение, а ответ
 * на вопрос, который человек задаёт себе, глядя на экран с кнопками правки
 * и удаления, — сохранилось ли уже.
 */
.saved-strip {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(111, 174, 111, 0.1);
    border: 1px solid rgba(111, 174, 111, 0.35);
    border-radius: var(--radius);
    color: var(--good);
    font-size: 0.8125rem;
    padding: 12px 14px;
    margin-bottom: 14px;
}

.saved-mark {
    flex: none;
    font-size: 0.9375rem;
    font-weight: 700;
}

/*
 * Развёртка плана (§56): строка на день.
 *
 * Дата слева узкой колонкой, занятие справа — так взгляд идёт по датам
 * сверху вниз, не спотыкаясь о разную длину названий. День отдыха приглушён:
 * он часть плана, но действия не требует.
 */
.plan-day {
    display: flex;
    gap: 10px;
    padding: 6px 0;
    border-bottom: 1px solid var(--line);
    font-size: 0.875rem;
}

.plan-day:last-child { border-bottom: none; }
.plan-day.is-rest { color: var(--text-dim); }

.plan-day-date { flex: none; width: 110px; color: var(--text-dim); font-size: 0.8125rem; }
.plan-day-body { flex: 1; min-width: 0; }

/*
 * Ограничение на экране «О себе» (§58).
 *
 * Список исключённого стоит внутри карточки ограничения, а не рядом с ней:
 * это его последствие, а не отдельная тема, и разнеси их — придётся объяснять
 * связь словами.
 */
.limit {
    padding: 10px 0;
    border-top: 1px solid var(--line);
}

.limit:first-of-type { border-top: none; }

.limit-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}

.limit-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 6px 0;
}

.limit-row-label {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--text-dim);
    letter-spacing: 0.04em;
    min-width: 76px;
}

.limit-row-body {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

/* Убираемая плашка: крестик внутри неё, а не отдельной кнопкой рядом */
.chip.is-removable { padding-right: 8px; }

/*
 * Запас в подходе (§59).
 *
 * Ряд стоит на виду, а не под «Ещё…»: он и есть то, ради чего затевалось
 * правило прогрессии, и спрятанный он не нажимался бы никогда. Но и не
 * кричит: подпись мелкая, чипы обычные — это уточнение к подходу, а не
 * второй способ его записать.
 */
.reserve {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin: 4px 0 12px;
}

.reserve-label {
    font-size: 0.6875rem;
    color: var(--text-dim);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Подсказка правила говорит о действии — и потому окрашена, а не сера */
.hint.is-accent { color: var(--accent); }

/* А эта говорит, что что-то не приехало: цвет плохой новости (Р-127) */
.hint.is-bad { color: var(--bad); }

/*
 * Переписка с тренером (§60).
 *
 * Свои сообщения прижаты вправо и подсвечены, ответы — во всю ширину слева.
 * Это единственная разница: рисовать «пузыри» мессенджера незачем, здесь не
 * болтают, а спрашивают по делу, и ответ читают целиком.
 */
.thread {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Ответ тренера — тот же связный текст в несколько абзацев (Р-124) */
.msg-body { white-space: pre-wrap; max-width: 34em; }

.msg.is-mine {
    align-self: flex-end;
    max-width: 85%;
    padding: 8px 12px;
    background: var(--panel-2);
    border-radius: var(--radius);
    color: var(--text-dim);
}

.msg .btn { margin-top: 10px; }

/* Ошибка обращения — полоска того же вида, но красной рамкой */
.banner.is-danger {
    border-color: var(--bad);
    color: var(--bad);
}

/* Правило программы (§56.2): строка списка, а не карточка — их читают подряд */
.plan-rule {
    padding: 6px 0;
    border-top: 1px solid var(--line);
    font-size: 0.875rem;
}

.plan-rule:first-of-type { border-top: none; }

/* Строка, которая сообщает не порядок дел, а осечку (Р-129) */
.plan-rule.is-bad,
.plan-rule.is-bad .plan-day-rest { color: var(--bad); }

/* Пауза дня стоит подписью под заданием, а не спорит с ним за строку */
.plan-day-rest {
    display: block;
    font-size: 0.75rem;
    color: var(--text-dim);
}

/* ================== ЗНАКОМСТВО (§61) ================== */

/*
 * Пройденный и отложенный шаг гаснут, а не исчезают.
 *
 * Исчезнувший шаг человек не найдёт, когда передумает, — а передумывают тут
 * постоянно: «потом» на ключе тренера это не отказ, это «не сейчас».
 * Погасший же виден, но не спорит с теми, что ещё ждут ответа.
 */
.card.is-quiet {
    opacity: 0.6;
}

.intro-mark {
    margin-left: 8px;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/*
 * Ход знакомства — полосками, а не числом (Р-99).
 *
 * Число «шаг 2 из 5» стоит в подзаголовке и отвечает точно; полоски отвечают
 * мгновенно, без чтения: видно, что пройдено больше половины, и что осталось
 * немного. Это тот случай, когда две подписи об одном не спорят, а работают
 * на разной скорости.
 */
.intro-bar {
    display: flex;
    gap: 4px;
    margin: 0 0 12px;
}

.intro-bar span {
    flex: 1;
    height: 3px;
    border-radius: 2px;
    background: var(--line);
}

.intro-bar span.is-done { background: var(--accent); }

/*
 * Поле «своё» и кнопка рядом, а не столбиком (Р-102).
 *
 * У полей ввода ширина во всю строку — в общем ряду это выталкивает кнопку
 * на следующую строку, и короткий вопрос занимает четыре ряда вместо двух.
 */
.intro-add {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-bottom: 14px;
}

.intro-add input {
    flex: 1;
    width: auto;
    margin-bottom: 0;
}

.intro-add .btn {
    margin-bottom: 0;
    white-space: nowrap;
}

/* ================== КАК ИДЁТ ПРОГРАММА (§63) ================== */

/*
 * Наблюдение окрашено полосой слева, а не текстом.
 *
 * Цветной текст читается как ошибка, а тут ошибки нет: «можно тяжелее» и
 * «запас потерян» — одинаково спокойные сведения, различающиеся направлением.
 * Полоса это направление и показывает, не мешая читать.
 */
.note-up,
.note-down,
.note-watch,
.note-ok {
    border-left: 3px solid var(--line);
    padding-left: 10px;
}

.note-up { border-left-color: var(--good); }
.note-down { border-left-color: var(--accent); }
.note-watch { border-left-color: var(--text-dim); }
.note-ok { border-left-color: var(--good); }

/*
 * Заголовок группы внутри шага знакомства (§61, Р-123).
 *
 * Обычного размера, не подписью: две группы шага — снаряды и помехи — раньше
 * различались только серой строкой помельче, и ряд частей тела под рядом
 * снарядов читался как «что качать».
 */
.intro-group {
    font-size: 0.9375rem;
    margin: 18px 0 0;
}

.card > .intro-group:first-child { margin-top: 0; }

/*
 * Карточка справки знакомства (§61, Р-125).
 *
 * Тем же кеглем и межстрочным, что связный текст в «Как пользоваться»: это
 * такое же объяснение, а не подпись к числу.
 */
.intro-card {
    border-left: 2px solid var(--line);
    padding-left: 12px;
    margin-bottom: 16px;
}

.intro-card strong { display: block; font-size: 0.9375rem; margin-bottom: 4px; }

.intro-card span {
    display: block;
    font-size: 0.875rem;
    line-height: 1.55;
    color: var(--text);
    max-width: 34em;
}

/*
 * Устаревшие данные с часов (§62.5, Р-125).
 *
 * Число гаснет до приглушённого, а под ним стоит строка о давности: пока
 * связка молчит, это не сегодняшний показатель, а последняя запись. Цель и
 * проценты при этом убираются вовсе — доля от цели у старого и обычно
 * неполного дня объявляла бы провал там, где просто оборвалась связь.
 */
.weight-row.is-stale { flex-wrap: wrap; }
.weight-row.is-stale .w-value { color: var(--text-dim); }

/* Отдельной строкой, а не третьей колонкой: это предупреждение, а не ещё
   одна подпись рядом с числом */
.w-stale {
    flex-basis: 100%;
    margin-top: 6px;
    font-size: 0.75rem;
    color: var(--bad);
}
