/* Информационная система — оформление.

   Дизайн-система взята у дашборда «IR: детали» и раскатана на все разделы:
   палитра, слои, типографика, радиусы. Исходные правила — в пакете передачи,
   `02_ПРИНЦИПЫ.md`; там же обоснование каждого решения.

   Цвет здесь несёт смысл, а не украшает. Три слоя поверхности (--page →
   --surface → --surface-2) разделяются фоном, а не тенью: тень в системе
   ровно одна, у всплывающей подсказки.

   Тёмная тема объявляется трижды — база, системная, явный выбор. Ни один
   цвет не должен существовать только внутри медиазапроса: если тёмная тема
   переопределяет токен, светлая обязана объявить его в голом :root. */

:root {
    color-scheme: light;

    /* Поверхности: от дальнего слоя к ближнему. */
    --page: #f4f2f7;
    --surface: #fbfafc;
    --surface-2: #eae7f0;
    --sunk: #f2f0f6;

    --ink: #17141f;
    --ink-2: #4f4a5e;
    --muted: #857f95;

    --grid: #e8e5ef;
    /* Отчерки-разделители — вполсилы от прежнего #d8d4e2 (владелец
       31.08.2026: «горизонтальные отчерки... сильно яркие, бледнее в 2
       раза. И везде кроме дашборда»). Полупрозрачность вместо готового
       светлого тона — тем же приёмом, что --hair ниже: линия одинаково
       легка на любой подложке. ОБВОДКИ управляющих элементов (плашки,
       чипы, счётчик) не бледнеют: их --rule-strong держится за
       --rule-solid. Дашборд не задет по построению — у него свой --rule
       внутри details.css. */
    --rule-solid: #d8d4e2;
    --rule: rgba(216, 212, 226, .5);
    /* Полупрозрачная намеренно: карточка ложится и на --page, и на
       --surface-2, и граница в обоих случаях выглядит одинаково лёгкой. */
    --hair: rgba(23, 20, 31, .11);

    --accent: #5f5288;
    --accent-soft: #eeecf6;
    --accent-ring: rgba(95, 82, 136, .35);
    /* Вуаль поверх акцентной заливки: обвести то, что легло на неё своим
       цветом. Полупрозрачная от цвета подписи, а не от белого, — в тёмной
       теме подпись тёмная, и белая обводка там светилась бы. */
    --accent-veil: rgba(255, 255, 255, .38);

    /* Ряды данных: перивинкль, винный, фиолет, слива, мов. */
    --s1: #c3c6ec;
    --s2: #4a1f42;
    --s3: #5f5288;
    --s4: #6b2954;
    --s7: #8a7a9b;

    /* Цвет подписи поверх заливки — свой на каждый ряд. Никогда не считать
       его по яркости фона в рантайме: при смене темы такой расчёт врёт. */
    --on-fill: #ffffff;
    --on-s1: #17141f;
    --on-s2: #ffffff;
    --on-s3: #ffffff;
    --on-s4: #ffffff;
    --on-s7: #17141f;
    --on-pink: #ffffff;

    --good: #5f5288;
    /* Настоящий красный, а не сливовый. До 20.08.2026 здесь стоял #6b2954 —
       он в палитру ложился, но «ошибка» от «просто акцента» на глаз не
       отличалась: владелец дважды сказал, что плашки выглядят фиолетовыми.
       Приглушён под остальную тему, как и единственный зелёный рядом. */
    --bad: #b0332f;
    --warn: #8a6a12;
    --pink: #6b2954;
    --pos: #5f5288;

    /* Единственный зелёный в системе. Палитра здесь фиолетово-сливовая, и
       «успешно» красилось цветом акцента — рамка выходила фиолетовой и от
       обычного акцента ничем не отличалась. Заведён отдельной переменной, а
       не подменой --good: тем же цветом отмечены ещё семь состояний в других
       разделах, и там смысл прежний. Приглушён под остальную палитру: чистый
       зелёный рядом с --bad и --warn читался бы вставкой из чужой темы.
       Оттенок сменён 27.08.2026 по прямому запросу владельца: #42925b
       (rgb 0.259/0.573/0.357 из его выдачи) вместо прежнего #3a7550.
       Только светлая тема — тёмная (#79b491 ниже, дважды) не тронута. */
    --online: #42925b;

    /* Единственный жёлтый в системе — заливка аватара агента. Он и здесь
       означает роль, а не принадлежность: в списке задач надо с одного
       взгляда отличать заведённое агентом от заведённого человеком, а
       восьмицветный ряд аватаров для этого сплошь фиолетовый.
       --warn на эту роль не годится: в светлой теме он тёмно-оливковый,
       кружок с ним выглядит грязным, а не жёлтым. */
    --bot: #e0a62c;
    --on-bot: #2b2005;

    /* Маркер статуса «В работе» — кружок с заполненной половинкой. Свой цвет,
       а не --warn: тот держит ещё шестнадцать правил в здоровье, коннекторах,
       процессах и сроках, и перекраска ради значка утянула бы их все. Причина
       та же, что у --bot выше: в светлой теме --warn тёмно-оливковый, и
       половинка с ним выглядит грязной, а не жёлтой. Одно значение на обе
       темы — маркер обязан читаться жёлтым и там, и там.
       Задан 01.09.2026 по прямому запросу владельца: rgb 0.925/0.753/0.349
       из его выдачи. */
    --status-doing: #ecc059;

    /* Маркер статуса «На ревью» — кружок на три четверти. До 01.09.2026 брал
       --accent, то есть цвет ссылок и кнопок; теперь у него свой оттенок, и
       статус перестаёт зависеть от правок акцента. Одно значение на обе темы,
       как и у --status-doing выше.
       Задан 01.09.2026 по прямому запросу владельца: rgb 0.365/0.184/0.521
       из его выдачи. */
    --status-review: #5d2f85;

    /* Семь ступеней хитмапа и парные цвета подписи к каждой. */
    --heat-0: #f3f2f9; --heat-1: #e2e0f0; --heat-2: #cbc6e2; --heat-3: #ada5cf;
    --heat-4: #8d82b8; --heat-5: #726597; --heat-6: #5f5288;
    --heat-ink-0: #17141f; --heat-ink-1: #17141f; --heat-ink-2: #17141f;
    --heat-ink-3: #17141f; --heat-ink-4: #ffffff; --heat-ink-5: #ffffff; --heat-ink-6: #ffffff;

    /* Мягкие подложки состояний — по образцу --accent-soft, которого система
       не давала для «плохо» и «внимание». Нужны уведомлениям: цветной текст
       без подложки на светлом фоне читается как опечатка, а не как статус. */
    --bad-soft: #fbecea;
    --warn-soft: #f7f1e3;
    --online-soft: #eaf3ed;
    --scrim: rgba(23, 20, 31, .35);

    /* Подъём плашки над страницей. Правило системы — слои разделяются фоном,
       а не тенью, и до 25.08.2026 тень была ровно одна, у подсказки. Вторая
       заведена по просьбе владельца для плашек ресурсов задачи. Отдельным
       токеном, а не числами по месту: тёмной теме нужна своя плотность —
       на почти чёрном фоне светлая тень не видна вовсе. */
    --lift: 0 1px 2px rgba(23, 20, 31, .06), 0 6px 16px rgba(23, 20, 31, .09);
    --lift-hi: 0 2px 4px rgba(23, 20, 31, .08), 0 10px 24px rgba(23, 20, 31, .13);

    --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Consolas, monospace;

    --gap: 16px;
    /* Чем крупнее контейнер, тем больше радиус; управляющие элементы всегда
       острее контейнеров, иначе кнопка выглядит плавающей внутри карточки. */
    --radius: 11px;
    --radius-ctl: 7px;

    /* Прежние имена системы — псевдонимы поверх токенов дашборда. Так старые
       правила продолжают работать и получают тёмную тему даром: var()
       вычисляется в момент использования, а не объявления.

       Площадки взяты по правилу дашборда (COL в трёх местах его кода):
       Ozon — акцент, Wildberries — винный, Яндекс — мов. Брендовые синий,
       малиновый и жёлтый ушли: в этой системе цвет означает роль, а не
       принадлежность, и три чужих тона ломали бы шкалу. */
    --paper: var(--page);
    --card: var(--surface);
    /* Сплошной белый для поверхностей, которые лежат ПОВЕРХ листа и обязаны
       от него отличаться: строки «Ресурсов» и «Подзадач», свёрнутые реплики
       обсуждения, включённые чипы-переключатели. Прежний --card (#fbfafc) от
       фона почти неотличим, и вместо ряда элементов читалось серое пятно.

       Владелец 01.09.2026, общим правилом после трёх частных просьб: «все
       такие плашки должны быть везде белыми». Стеклянная родня живёт своим
       --float-bg — он в светлой теме тоже белый.

       Только светлая тема: в тёмной белый лист невозможен, там токен
       возвращается к --card (оба тёмных блока ниже). */
    --solid-bg: #ffffff;
    --ink-3: var(--muted);
    /* За солид, а не за --rule: отчерки с 31.08.2026 полупрозрачные, а
       обводка контрола обязана остаться прежней плотности. */
    --rule-strong: var(--rule-solid);
    --track: var(--surface-2);
    /* «Успешно» — зелёным, а не акцентом. До 20.08.2026 здесь стоял --good,
       то есть тот же фиолетовый, что у обычного акцента: плашка «успех» в
       расписании ничем не отличалась от любой другой подписи. Зелёный в
       системе уже был — им коннекторы отмечают «на связи», и это ровно тот
       же смысл. Второго зелёного заводить не стали. */
    --ok: var(--online);
    /* Цвета площадок остались от карточек маркетплейсов на сводке. Карточки
       убраны 21.08.2026, но имена сохранены: `--ozon` разошёлся по системе
       как цвет фокуса и ссылки, а `--wb` и `--ym` ждут следующего места, где
       площадки понадобится различать цветом. */
    --ozon: var(--accent);
    --wb: var(--s2);
    --ym: var(--s7);
    /* СТАНДАРТНЫЕ ЦВЕТА ПЛОЩАДОК ДЛЯ ГРАФИКОВ (Антон, 24.09.2026): ВБ
       135/55/161, Ozon 41/99/204, Яндекс 243/225/86 — одни на всю систему,
       ряды и метки площадок красить только ими. Отдельные токены, а не
       подмена `--ozon`: тот разошёлся по системе как цвет фокуса и ссылок,
       и синим он бы перекрасил формы. Тёмная тема берёт те же значения. */
    --mp-wb: rgb(135, 55, 161);
    --mp-ozon: rgb(41, 99, 204);
    --mp-ym: rgb(243, 225, 86);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --page: #0c0a12;
        --surface: #17141f;
        --surface-2: #231f30;
        --sunk: #120f19;
        /* Сплошной белый — только светлая тема, см. :root. */
        --solid-bg: var(--card);

        --ink: #f6f4fa;
        --ink-2: #aaa4bb;
        --muted: #7a7490;

        --grid: #272233;
        --rule-solid: #362f45;
        --rule: rgba(54, 47, 69, .5);
        --hair: rgba(246, 244, 250, .11);

        --accent: #9688c2;
        --accent-soft: #241f33;
        --accent-ring: rgba(150, 136, 194, .4);
        --accent-veil: rgba(23, 20, 31, .3);

        --s1: #b8bbe0;
        --s2: #8f5e86;
        --s3: #9688c2;
        --s4: #ab6592;
        --s7: #ac9dbd;

        /* Переворачиваются не все сразу, а поштучно: контраст у каждой пары свой. */
        --on-fill: #17141f;
        --on-s1: #17141f;
        --on-s2: #ffffff;
        --on-s3: #17141f;
        --on-s4: #17141f;
        --on-s7: #17141f;
        --on-pink: #17141f;

        --good: #9688c2;
        --bad: #e8817c;
        --warn: #c9a13c;
        --pink: #ab6592;
        --pos: #9688c2;

        /* На тёмном фоне лесной зелёный уходит в грязь — светлее и мягче,
           тем же движением, что --bad и --warn выше. */
        --online: #79b491;

        /* На тёмном фоне жёлтый гасим: полная светлота светлой темы здесь
           светится. Подпись остаётся тёмной — инициалы на жёлтом читаются
           только так. */
        --bot: #cc9f31;
        --on-bot: #1c1506;

        --heat-0: #1c1926; --heat-1: #272236; --heat-2: #352e4b; --heat-3: #463c63;
        --heat-4: #5b4f81; --heat-5: #7466a1; --heat-6: #9688c2;
        --heat-ink-0: #ffffff; --heat-ink-1: #ffffff; --heat-ink-2: #ffffff;
        --heat-ink-3: #ffffff; --heat-ink-4: #ffffff; --heat-ink-5: #ffffff; --heat-ink-6: #17141f;
        --bad-soft: #33191a;
        --warn-soft: #2b2416;
        --online-soft: #16281d;
        --scrim: rgba(0, 0, 0, .55);
        --lift: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 16px rgba(0, 0, 0, .45);
        --lift-hi: 0 2px 4px rgba(0, 0, 0, .55), 0 10px 24px rgba(0, 0, 0, .5);

        /* Стекло левитирующей панели (IR-66) — тёмное. Светлые значения
           черновика здесь дали бы белую плиту поверх тёмного листа: на
           тёмном стекло держится не белизной, а тем, что пропускает фон,
           поэтому фон темнее непрозрачного --surface, а не светлее, блик
           сверху едва заметный, и тень глубже — на тёмном она единственное,
           что отрывает панель от фона. */
        /* Стеклом остаётся только тёмная тема — в светлой заливка сплошная
           белая, см. :root. */
        --float-bg: rgba(28, 24, 39, .72);
        --float-line: rgba(246, 244, 250, .1);
        --float-hair: rgba(246, 244, 250, .09);
        --float-shadow:
            0 9px 18px -7px rgba(0, 0, 0, .7),
            0 1px 5px rgba(0, 0, 0, .5),
            inset 0 1px 0 rgba(246, 244, 250, .06);
        --float-chip-shadow:
            0 3px 7px -3px rgba(0, 0, 0, .55),
            0 .5px 2px rgba(0, 0, 0, .4),
            inset 0 1px 0 rgba(246, 244, 250, .06);
        --float-chip-shadow-hi:
            0 4px 9px -3px rgba(0, 0, 0, .65),
            0 1px 2.5px rgba(0, 0, 0, .45),
            inset 0 1px 0 rgba(246, 244, 250, .06);
    }
}

:root[data-theme="dark"] {
    color-scheme: dark;

    --page: #0c0a12;
    --surface: #17141f;
    --surface-2: #231f30;
    --sunk: #120f19;
    /* Сплошной белый — только светлая тема, см. :root. */
    --solid-bg: var(--card);

    --ink: #f6f4fa;
    --ink-2: #aaa4bb;
    --muted: #7a7490;

    --grid: #272233;
    --rule-solid: #362f45;
    --rule: rgba(54, 47, 69, .5);
    --hair: rgba(246, 244, 250, .11);

    --accent: #9688c2;
    --accent-soft: #241f33;
    --accent-ring: rgba(150, 136, 194, .4);
    --accent-veil: rgba(23, 20, 31, .3);

    --s1: #b8bbe0;
    --s2: #8f5e86;
    --s3: #9688c2;
    --s4: #ab6592;
    --s7: #ac9dbd;

    --on-fill: #17141f;
    --on-s1: #17141f;
    --on-s2: #ffffff;
    --on-s3: #17141f;
    --on-s4: #17141f;
    --on-s7: #17141f;
    --on-pink: #17141f;

    --good: #9688c2;
    --bad: #e8817c;
    --warn: #c9a13c;
    --pink: #ab6592;
    --pos: #9688c2;
    /* Ручной выбор тёмной темы — та же пара значений, что и у системной. */
    --online: #79b491;

    --bot: #cc9f31;
    --on-bot: #1c1506;

    --heat-0: #1c1926; --heat-1: #272236; --heat-2: #352e4b; --heat-3: #463c63;
    --heat-4: #5b4f81; --heat-5: #7466a1; --heat-6: #9688c2;
    --heat-ink-0: #ffffff; --heat-ink-1: #ffffff; --heat-ink-2: #ffffff;
    --heat-ink-3: #ffffff; --heat-ink-4: #ffffff; --heat-ink-5: #ffffff; --heat-ink-6: #17141f;
    --bad-soft: #33191a;
    --warn-soft: #2b2416;
    --online-soft: #16281d;
    --scrim: rgba(0, 0, 0, .55);
    --lift: 0 1px 2px rgba(0, 0, 0, .5), 0 6px 16px rgba(0, 0, 0, .45);
    --lift-hi: 0 2px 4px rgba(0, 0, 0, .55), 0 10px 24px rgba(0, 0, 0, .5);

    /* Стекло левитирующей панели — те же значения, что в системной тёмной
       выше (файл объявляет тёмную дважды: по системе и по выбору). */
    /* Стеклом остаётся только тёмная тема — в светлой заливка сплошная
       белая, см. :root. */
    --float-bg: rgba(28, 24, 39, .72);
    --float-line: rgba(246, 244, 250, .1);
    --float-hair: rgba(246, 244, 250, .09);
    --float-shadow:
        0 9px 18px -7px rgba(0, 0, 0, .7),
        0 1px 5px rgba(0, 0, 0, .5),
        inset 0 1px 0 rgba(246, 244, 250, .06);
    --float-chip-shadow:
        0 3px 7px -3px rgba(0, 0, 0, .55),
        0 .5px 2px rgba(0, 0, 0, .4),
        inset 0 1px 0 rgba(246, 244, 250, .06);
    --float-chip-shadow-hi:
        0 4px 9px -3px rgba(0, 0, 0, .65),
        0 1px 2.5px rgba(0, 0, 0, .45),
        inset 0 1px 0 rgba(246, 244, 250, .06);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--page);
    color: var(--ink);
    font: 400 15px/1.5 var(--sans);
    -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

::selection { background: var(--accent-soft); color: var(--accent); }

:root {
    scrollbar-color: var(--rule) transparent;
    accent-color: var(--accent);
    caret-color: var(--accent);
}

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--rule);
    border-radius: 8px;
    border: 3px solid var(--page);
}
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Движение: переходы короткие, и общий выключатель обязателен. */
@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

/* Служебные типографские роли */

.eyebrow {
    font: 400 11px/1 var(--sans);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.figure {
    font: 600 29px/1 var(--sans);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.025em;
}

.num {
    font-variant-numeric: tabular-nums;
}

.muted { color: var(--ink-2); }

/* Номер задачи, копируемый щелчком. Внутри это кнопка — иначе не достать с
   клавиатуры, — но выглядеть она должна ровно как текст на её месте: номер
   остаётся подписью, а не действием страницы. Отсюда сброс до inherit и
   отказ от подчёркиваний и рамок; единственный признак — курсор и лёгкое
   потемнение под мышью.

   position/z-index нужны там, где строка списка кликается растянутой
   ссылкой: без подъёма щелчок по номеру открывал бы задачу. */

/* Шрифт и цвет — через :where, то есть с нулевым весом. Кнопке они нужны
   только чтобы отменить UA-стиль (Arial 13.33px и цвет кнопки), а дальше
   слово должно оставаться за тем классом, что стоит на этом же месте:
   .eyebrow в карточке (11px, разрядка, капитель) и .trow__key в списке
   (12px). Обычный .copykey перебивал их обоих — номер в карточке вырос до
   базовых 16px и потемнел. */
:where(.copykey) {
    font: inherit;
    color: inherit;
}

.copykey {
    background: none;
    border: 0;
    margin: 0;
    padding: 0;
    /* Кнопка в ячейке сетки растягивается на всю колонку, а текст в ней UA
       ставит по центру — номер уехал бы от левого края, где он и был. */
    text-align: left;
    cursor: pointer;
    position: relative;
    z-index: 1;
}

.copykey:hover { color: var(--ink); }

/* Подтверждение — всплывающая подпись над номером. Заменять текст самого
   номера нельзя: в списке под него отведена узкая колонка (--task-key-w), и
   «скопировано» туда не встаёт. Абсолютная подпись ничего не двигает и гаснет сама. */
.copykey--done::after {
    content: "скопировано";
    position: absolute;
    left: 0;
    bottom: calc(100% + 4px);
    z-index: 30;
    padding: 3px 6px;
    border-radius: 6px;
    background: var(--ink);
    color: var(--card);
    font: 400 11px/1.2 var(--sans);
    letter-spacing: 0;
    text-transform: none;
    white-space: nowrap;
    pointer-events: none;
}

/* Поле для запасного способа копирования (execCommand): оно должно быть в
   документе и доступно выделению, но не видно. Классом, а не style="" —
   инлайновые стили запрещены CSP. */
.offscreen {
    position: fixed;
    top: -1000px;
    left: -1000px;
    width: 1px;
    height: 1px;
    opacity: 0;
}

/* Каркас страницы: боковая панель слева, лист с содержимым справа.

   Панель фиксирована и не уезжает при прокрутке — разделы должны быть под
   рукой на длинных страницах вроде администрирования. Свёрнутое состояние —
   класс на <html>, его выставляет sidebar.js до первой отрисовки. */

:root {
    --side: 232px;
    --side-narrow: 60px;

    /* Панель держится на светлой стороне: чёрный блок во всю высоту экрана
       спорит с содержимым за внимание. Отделяет её линия, а не контраст. */
    --side-bg: var(--surface);
    --side-ink: var(--ink-2);
    --side-line: var(--grid);
    --side-hover: var(--surface-2);

    /* Левитирующая панель (IR-66). Панель — единственная плавающая
       поверхность системы: она отрывается от края экрана и ложится стеклом
       поверх общего фона. Лист содержимого при этом карточкой НЕ становится
       — иначе на экране две спорящие рамки вместо одной опоры.

       Цвета вынесены в токены, а не написаны по месту: тёмная тема
       переопределяет их ниже, как и всё остальное в этом файле. Белое стекло
       черновика годилось только для светлой. */
    --float-gap: 14px;
    --float-radius: 20px;
    /* Заливка семейства. В СВЕТЛОЙ теме она сплошная белая с 01.09.2026:
       владелец прошёл по разделам («строки ресурсов», «сайдбар», «фильтры
       сверху и плюсик») и закончил общим правилом — «все такие плашки должны
       быть везде белыми». Прежнее полупрозрачное rgba(251,250,252,.86) над
       листом давало rgb(250,250,251): от фона почти неотличимо, и панель
       читалась серым пятном вместо поверхности.

       Стеклом остаётся ТЁМНАЯ тема (оба блока ниже) — там полупрозрачность
       и работает, и выглядит стеклом. Поэтому же `backdrop-filter` у панелей
       не снят: под белым он просто ничего не делает, а в тёмной нужен.

       Форма семейства не менялась ни разу: рамка, радиус и тень прежние,
       меняется одна заливка. */
    --float-bg: #ffffff;
    --float-line: rgba(255, 255, 255, .6);
    /* Линии шапки и подвала внутри стекла: сплошной --side-line на
       полупрозрачном фоне читается грубее, чем нужно. */
    --float-hair: rgba(23, 20, 31, .08);
    --float-shadow:
        0 8px 16px -7px rgba(23, 20, 31, .22),
        0 1px 4px rgba(23, 20, 31, .07),
        inset 0 1px 0 rgba(255, 255, 255, .8);
    /* Та же тень в масштабе кнопки: панельная на элементе в 26px высоты
       читалась бы лужей. Слои те же — отрыв, ближняя мягкая, световой
       кант сверху. Носят её плашки фильтров трекера (см. блок у
       .tasks-filters ниже). */
    --float-chip-shadow:
        0 3px 6px -3px rgba(23, 20, 31, .18),
        0 .5px 1.5px rgba(23, 20, 31, .06),
        inset 0 1px 0 rgba(255, 255, 255, .8);
    /* Наведение на стеклянную строку: тот же контур, тень глубже — та же
       пара, что --lift/--lift-hi у плоского вида. */
    --float-chip-shadow-hi:
        0 4px 8px -3px rgba(23, 20, 31, .24),
        0 1px 2px rgba(23, 20, 31, .08),
        inset 0 1px 0 rgba(255, 255, 255, .8);
}

/* Каркас — сетка: колонка панели меряется по содержимому, лист забирает
   остальное. Фиксированная ширина отъедала место под самый длинный пункт,
   которого в меню нет. minmax(0,1fr) обязателен — без него широкая таблица
   в листе распирает сетку и панель уползает. */
.shell {
    min-height: 100vh;
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
}

.side {
    position: sticky;
    top: 0;
    z-index: 20;
    height: 100vh;
    /* Ширина постоянная, а не по содержимому. Колонка страницы объявлена как
       max-content, и панель, меряясь по содержимому, прыгала: раскрыл
       подразделы Settings — подписи длиннее, панель шире, весь лист поехал.
       Ширина рассчитана на самую длинную подпись («Инструкции агента»),
       поэтому раскрытие подменю больше ничего не двигает. */
    width: var(--side);
    max-width: var(--side);
    display: flex;
    flex-direction: column;
    overflow: hidden auto;
    overscroll-behavior: contain;
    background: var(--side-bg);
    color: var(--ink);
    border-right: 1px solid var(--side-line);
    transition: transform 0.16s ease;
}

.content { min-width: 0; }

/* Панель левитирует (IR-66, черновик проверен браузером агента 28.08.2026).

   До 01.09.2026 у вида была пара: левитирующий по умолчанию и прижатый
   «плоский» по выбору человека, а условие писалось отрицанием по атрибуту на
   корне (имя не приводим — его отсутствие в файле сторожит
   `tests/test_sidebar.py`). Владелец убрал плоский вместе с кнопкой
   переключения — вид остался один, и условие сняли. Селектор `:root` при
   этом оставлен намеренно: он держит вес, которым эти правила перебивают
   базовые `:hover` и `[open]` ниже по файлу, а без него они сравнялись бы и
   исход решал порядок строк.

   `border` целиком, а не только цвет: базовое правило рисует панели
   `border-right`, и без перекрытия старая граница осталась бы висеть
   поверх новой тени. */
:root .side {
    margin: var(--float-gap);
    top: var(--float-gap);
    height: calc(100vh - 2 * var(--float-gap));
    border: 1px solid var(--float-line);
    border-radius: var(--float-radius);
    background: var(--float-bg);
    /* Размытие остаётся, хотя в светлой теме под сплошной заливкой его не
       видно: стеклом живёт тёмная тема, и там оно работает. */
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    box-shadow: var(--float-shadow);
}

:root .side__top { border-bottom-color: var(--float-hair); }
:root .side__foot { border-top-color: var(--float-hair); }

/* Свёрнутое состояние ширину не назначает: подписи скрыты, и колонка сама
   становится равна ряду значков. Здесь снимаем и постоянную ширину — иначе
   свёрнутая панель осталась бы шириной с развёрнутую. */
.is-narrow .side { width: auto; max-width: none; }

/* Шапка панели: кнопка сворачивания и кто вошёл.

   Отступы подобраны под геометрию пунктов меню: значок пункта начинается на
   20 px от края панели (10 у списка + 10 у пункта), подпись — на 50 px. Здесь
   кнопка 28 px стоит с отступом 16, её значок попадает ровно на 20, а имя
   после промежутка в 6 — ровно на 50. Колонки значков и подписей сходятся. */

.side__top {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px 16px;
    min-height: 56px;
    border-bottom: 1px solid var(--side-line);
}

.side__toggle {
    flex: none;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--radius);
    color: var(--side-ink);
    cursor: pointer;
}

.side__toggle:hover { color: var(--ink); background: var(--side-hover); }

/* Пункты */

.side__nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px;
    flex: 1;
}

.side__item,
.side__link {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: var(--radius);
    background: none;
    font: 400 13px/1.2 var(--sans);
    color: var(--side-ink);
    text-decoration: none;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.side__item:hover,
.side__link:hover { color: var(--ink); background: var(--side-hover); }

/* Текущий раздел: белый лист с рамкой — тем же приёмом, что и карточки.
   Инверсия в чёрное здесь читалась бы как кнопка, а не как «вы тут». */
.side__item--on {
    color: var(--ink);
    background: var(--card);
    box-shadow: inset 0 0 0 1px var(--side-line);
    font-weight: 500;
}

.side__item--on:hover { background: var(--card); }

.ico { flex: none; }

/* Значок-эмодзи вместо контура. Ширина фиксирована по размеру контурных
   значков — иначе пункты меню разъезжаются: эмодзи рисуются шрифтом и в
   ширине не совпадают ни между собой, ни с SVG. Кегль чуть крупнее 20px,
   потому что рисунок внутри глифа занимает не всё поле и рядом с линиями
   контуров смотрится мельче. */
.ico--emoji {
    width: 20px;
    height: 20px;
    font-size: 15px;
    line-height: 20px;
    text-align: center;
    font-style: normal;
}

/* Подразделы — якоря внутри страницы, поэтому тише пунктов верхнего уровня */

.side__sub {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin: 2px 0 2px 19px;
    padding-left: 10px;
    border-left: 1px solid var(--side-line);
}

.side__link {
    padding: 6px 10px;
    font-size: 12.5px;
    color: var(--ink-3);
}

.side__link .ico { width: 16px; height: 16px; }
.side__link--on { color: var(--ink); background: var(--side-hover); }

/* Буллет вложенного пункта. Мельче значка раздела и заметно тише подписи:
   это метка «пункт списка», а не рисунок, который читают. В полную яркость
   ряд одинаковых точек лез в глаза сильнее самих названий.

   Приглушение прозрачностью, а не своим цветом: так точка следует за
   состоянием пункта — гаснет вместе с подписью и оживает на выбранном. */
.side__link .ico--bullet { width: 12px; height: 12px; opacity: .42; }
.side__link:hover .ico--bullet { opacity: .7; }
.side__link--on .ico--bullet { opacity: .85; }

/* Подвал панели: ряд значков-действий — входящие, тема, выход */

.side__foot {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 10px;
    border-top: 1px solid var(--side-line);
}

/* Выход отбит вправо: он не из одного ряда с повседневными действиями,
   и промах по соседнему значку здесь стоит дороже. */
.side__foot-end { margin-left: auto; }

/* Значок-действие: квадрат под палец, подпись живёт в title и aria-label.
   Подписи тут были бы шумом — действий наперечёт, и каждое опознаётся
   рисунком быстрее, чем читается. */
.side__act {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: none;
    border-radius: var(--radius);
    background: none;
    color: var(--side-ink);
    cursor: pointer;
}

.side__act:hover { color: var(--ink); background: var(--side-hover); }

/* Выход подписан словом, поэтому шире значковых соседей. */
.side__act--wide { width: auto; padding: 0 6px; }

.exit-tag {
    display: inline-flex;
    align-items: center;
    padding: 2px 5px;
    border: 1px solid currentColor;
    border-radius: 4px;
    font: 600 9.5px/1 var(--sans);
    letter-spacing: 0.1em;
}

.side__act--on {
    color: var(--ink);
    background: var(--card);
    box-shadow: inset 0 0 0 1px var(--side-line);
}

/* Облако — мишень для файлов (IR-194, часть Б, dropcloud.js). Пока файл
   тащат по окну, значок растёт и подсвечивается кольцом фокуса: 34px иначе
   трудно найти глазами, а промах мимо — это открытый вместо страницы файл.
   Над самим облаком — залит акцентом, как кнопка: отпускай. Во время
   отправки остаётся залитым, пока «Входящие» не откроются с итогом.

   Рост — по мере приближения файла (Антон тем же днём: «при приближении
   файла расширяться сильнее»): вдали ×1,3, вплотную ×2 — с 34 до 68px.
   Множитель считает скрипт и кладёт в `--drop-scale`. Растёт от левого
   нижнего угла: облако стоит у края панели, а у панели `overflow: hidden`,
   и рост от центра срезал бы его левый и нижний бок. Поверх соседей по
   подвалу — `z-index`. В свёрнутой панели (60px) места под ×2 нет: там рост
   от середины низа и не больше ×1,5. */
.side__act[data-drop] {
    position: relative;
    transform-origin: left bottom;
    transition: transform .12s;
}
.side__act--catch,
.side__act--over,
.side__act--busy {
    z-index: 2;
    outline: 2px solid var(--accent-ring);
    transform: scale(var(--drop-scale, 1.3));
}
.side__act--catch {
    color: var(--accent);
    background: var(--accent-soft);
}
.side__act--over,
.side__act--busy {
    color: var(--on-fill);
    background: var(--accent);
}
.side__act--busy { cursor: progress; }
/* Строка под итогом загрузки на «Входящих»: что ушло в буфер после броска
   на облако (dropcloud.js). */
.notice__more { margin: 4px 0 0; }
.is-narrow .side__act[data-drop] { transform-origin: center bottom; }
.is-narrow .side__act--catch,
.is-narrow .side__act--over,
.is-narrow .side__act--busy { transform: scale(min(var(--drop-scale, 1.3), 1.5)); }

/* Кто вошёл — в шапке панели, рядом с кнопкой сворачивания. */
.whoami {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
    padding: 0 2px;
}

/* Ширину панели задаёт меню, а не имя вошедшего: «Александр Симонов» шире любого
   пункта, и панель раздувалась бы под него. width: 0 с min-width: 100% выводит
   текст из расчёта ширины колонки, но оставляет его во всю доступную ширину —
   длинное имя обрежется многоточием. */
.whoami__name,
.whoami__role {
    width: 0;
    min-width: 100%;
}

.whoami__name {
    font: 500 13px/1.2 var(--sans);
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.whoami__role {
    font: 500 10px/1 var(--sans);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Бейдж роли — прямоугольник цветной заливкой, тот же приём, что у значков
   типа файла в «Ресурсах» (`file_icon` в _taskbits.html: цветной `rect` +
   белые буквы), только HTML/CSS вместо SVG — здесь это текст, а не рисунок,
   и щёлкнуть/выделить его можно. Прямоугольник, а не пилюля `.lbl` (та же
   идея заливки, но `border-radius: 999px`) — владелец просил именно
   «прямоугольничек» (27.08.2026).

   Свой небольшой радиус, не `var(--radius-ctl)` (7px): тот подобран под
   кнопки с этой же высоты вдвое, здесь при высоте строки в 10px он выглядел
   бы почти пилюлей — тем самым, чего просили избежать.

   Цвета — не из готовой палитры меток (`.lbl--c-*`): в системе нет
   калиброванного «голубого» с белым текстом (ближайший, `teal`, рассчитан
   на тёмный текст, там недостаточно контраста для белого). «admin» —
   небесный оттенок синего (правка владельца 27.08.2026: «голубой цвет
   более небесного оттенка» — первая попытка, `#155eef` (тот же синий,
   что превью .doc-файла в «Ресурсах»), читалась скорее индиго; вторая,
   `#0284c7`, продержалась один день — 28.08.2026 владелец дал точный
   цвет, `rgb(0.325, 0.714, 0.945)` = `#53b6f1`). «user» —
   тот же зелёный, что и `--online` (в системе он один). «manager» — свой
   оттенок, готового оранжевого с нужным контрастом в системе не было.

   `text-transform: none` — явным сбросом, а не по умолчанию: родитель
   `.whoami__role` красит все свои подписи капителью (`uppercase`), и без
   этой строки бейдж наследовал бы её (правка владельца 27.08.2026: «метка
   admin... не заглавными буквами»). */
.role-badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: none;
    color: #fff;
}
.role-badge--admin { background: #53b6f1; }
.role-badge--manager { background: #dc6803; }
.role-badge--user { background: var(--online); }

/* Свёрнутая панель: остаются одни значки, подписи убираются из потока и из
   дерева доступности — иначе скринридер читает то, чего не видно. */

.side__label {
    overflow: hidden;
    text-overflow: ellipsis;
}

.is-narrow .side__label { display: none; }
.is-narrow .side__top { justify-content: center; padding: 12px 6px; }
.is-narrow .side__toggle { width: 44px; }
/* В свёрнутом виде в шапке остаётся одна кнопка: на 60 пикселях ширины
   помещается что-то одно, а без кнопки панель нечем развернуть обратно. */
.is-narrow .side__nav { padding: 10px 6px; align-items: center; }

.is-narrow .side__item,
.is-narrow .side__link {
    justify-content: center;
    width: 44px;
    padding: 8px 0;
}

.is-narrow .side__sub {
    margin: 4px 0;
    padding: 4px 0 0;
    border-left: none;
    border-top: 1px solid var(--side-line);
    align-items: center;
}

.is-narrow .whoami { display: none; }
/* В свёрнутой панели ряд встаёт столбиком: три значка в 60 пикселей ширины
   не помещаются. */
.is-narrow .side__foot { flex-direction: column; justify-content: center; padding: 10px 6px; }
.is-narrow .side__foot-end { margin-left: 0; }

/* Полоса с кнопкой панели — нужна только на узком экране */

.handle { display: none; }

.page {
    max-width: 1080px;
    margin: 0 auto;
    /* Верхнее поле — тот же `--float-gap`, что отделяет сайдбар от левого
       края окна (Антон, 10.09.2026: «надо как от левой границы до
       сайдбара»). До этого стояло 28px, вдвое больше бокового зазора, и
       страница висела ниже, чем сайдбар рядом.

       Токеном, а не числом: зазор плавающих панелей уже объявлен один раз
       (`--float-gap`, `.side` берёт его же полем), и второе написание того
       же расстояния разошлось бы при первой правке. `.tgroup__head` (список
       задач, ниже по файлу) тоже держит `--float-gap` — теперь это буквально
       одно значение, а не два совпадающих. */
    padding: var(--float-gap) 24px 64px;
}

.page--bare { max-width: none; padding: 0; }

/* Лист под встроенное приложение. 1080 подобраны под текст — строка длиннее
   читается хуже, — но раздел с чужой страницей внутри состоит из таблиц, и
   этот предел зажимал их вдвое против того, на что они рассчитаны. Ширина
   не «на весь экран»: за 1400 таблицы начинают расползаться, а глазу нечем
   держать строку. */
.page--wide { max-width: 1400px; }

/* Первая попытка сузить карточку задачи (правка владельца 27.08.2026: «на
   1/6») уводила её ограничением `.page` — `max-width` на самом `.page`, у
   которого `margin: 0 auto`. Это работало только пока в строке не хватало
   места до предела: как только `.content` (грид-трек рядом с сайдбаром)
   становился шире потолка, `.page` центрировался в нём и уезжал ОТ сайдбара
   — ровно то, чего быть не должно (следующая правка того же дня: «должно
   быть такое же расстояние от сайдбара»). Ограничение переехало на сам
   `.tcard` ниже по файлу — у него нет `margin: auto`, левый край держит
   `padding-left: var(--content-x-icon)`, и `max-width` только подрезает
   правый край, не трогая левый. */

.skip {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 40;
    padding: 10px 14px;
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--ink);
}

.skip:focus { left: 8px; top: 8px; }

/* Подпись только для читалки с экрана: заголовок колонки, который зрячему не
   нужен, потому что колонка и так понятна по содержимому. Правила у класса не
   было, и слово «Действия» просто стояло в шапке таблицы коннекторов.

   Не `display: none` и не нулевой размер: и то, и другое читалка пропускает.
   Отсюда обрезка в один пиксель — приём общий для всех систем. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.scrim { display: none; }

/* Заголовок раздела: строка документа, а не украшение */

/* Шапка трекера примерзает к верху при прокрутке (просьба владельца
   26.08.2026): заголовок и строка фильтров остаются на месте, список задач
   уходит под них. Фон непрозрачный и в цвет страницы — иначе строки списка
   были бы видны сквозь прилипшую шапку.

   `top: 0`, а не отступ панели (`.page` даёт 28px сверху): без сдвига шапка
   и так стоит вплотную к верху видимой области — тот же приём, что у
   `.handle` ниже. На узком экране расчёт другой: там уже прилипла к верху
   мобильная плашка `.handle` (кнопка панели, 49px по высоте — 32px кнопки +
   16px паддингов + 1px рамки), и без своего отступа шапка трекера легла бы
   поверх неё, а не под.

   `box-shadow` продолжает фон вниз ещё на `--float-gap` (живой отзыв
   владельца 28.08.2026: «фон... не дотягивается снизу... видно контент,
   который под ним скроллится») — заголовок группы прилипает не вплотную
   под шапкой, а с зазором в тот же `--float-gap` (`.tgroup__head`,
   `top: calc(--topbar-h + --float-gap)`), и сама шапка красит фоном
   только свою собственную высоту.
   Зазор между ними ничьим фоном не был занят — сквозь него и было видно
   строку, идущую под ним. Тень без размытия и растекания (последние два
   нуля) красит прямоугольник тем же цветом страницы, не трогая ни высоту
   шапки, ни её разметку: `box-shadow` не участвует в layout, только в
   отрисовке. Число обязано совпадать с зазором у `.tgroup__head` —
   разойдись они, щель вернётся, только меньше или больше на разницу;
   потому оба и написаны одной переменной. */
.tasks-topbar {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--page);
    box-shadow: 0 var(--float-gap) 0 0 var(--page);
    /* Воздух над строкой фильтров — ВНУТРИ прилипающего блока, а не полем
       снаружи (владелец 31.08.2026: «фильтры сильно прижаты к верху...
       должно быть одинаково»). Снаружи он уже убирался — и возвращал
       «подъезжание» при первой прокрутке (см. комментарий ниже про
       схлопнутые поля); внутри же он едет вместе с шапкой и просто
       продолжает её фон. Ровно `--float-gap` — отступ левитирующего
       сайдбара от верха (вторая правка владельца тем же днём: «такую же
       высоту... от верха до фильтров и от фильтров до первой строки»);
       той же переменной набраны зазор под шапкой у `.tasks-topbar > .fbar`
       и место прилипания заголовка группы — все четыре числа обязаны
       совпадать, см. комментарий у тени выше и у `.tgroup__head`.
       `--topbar-h` считается из `offsetHeight` (`tasks.js`), паддинг в нём
       уже сидит — прилипшие заголовки групп сдвигать не надо. */
    padding-top: var(--float-gap);
}

@media (max-width: 900px) {
    .tasks-topbar { top: 49px; }
}

/* Кнопки фильтров — поштучно тем же видом, что панель: стекло, светлая
   обводка, тень семейства float в масштабе кнопки (владелец 31.08.2026:
   «не доп панель, а сами кнопочки фильтров по отдельности» — первая
   попытка оборачивала всю шапку карточкой и отменена revert'ом dea2878).
   Тот же выключатель, что у панели: «Плоский вид» в подвале сайдбара
   возвращает и панель, и эти кнопки — быстрый откат без кода; кодом —
   revert коммита этого блока, он самодостаточен вместе с токеном
   --float-chip-shadow.

   Только счётчик и оси отбора трекера (.tasks-filters): у лупы, «Вида» и
   плюса справа обводки в покое нет по замыслу — им стекло не вешается,
   а плашки тех же классов в «Лаборатории» и «Работах» не тронуты до
   отдельного решения. Акцентные состояния перекрыты следом: селектор со
   стеклом весомее базовых :hover/[open], без явного повтора рамка
   переставала бы отвечать на наведение. */
:root .tasks-filters .fmenu__sum,
:root .tasks-filters .tcount__pill,
:root .tasks-filters .circle--cta,
:root .tasks-filters .fbar__reset {
    background: var(--float-bg);
    border-color: var(--float-line);
    box-shadow: var(--float-chip-shadow);
}

/* Плюс (владелец 31.08.2026: «плюсик тоже») и крестик сброса (владелец
   тем же днём: в стиле самих плашек) — из «кружков» стеклятся эти два,
   лупа и «Вид» остаются голыми значками. Крестик в покое терял и обводку
   базового `.circle` — стекло даёт её вместе с подложкой, поэтому он
   виден постоянно, как соседние плашки, а не только под курсором.
   Подсветка наведения повторена поверх стекла — тот же довод, что у
   рамки плашек выше. */
:root .tasks-filters .circle--cta:hover,
:root .tasks-filters .fbar__reset:hover {
    background: var(--sunk);
}

:root .tasks-filters .fmenu__sum:hover,
:root .tasks-filters .fmenu[open] > .fmenu__sum {
    border-color: var(--accent);
}

:root .tasks-filters .tcount:hover .tcount__pill {
    background: var(--sunk);
}

/* Выпадающие меню тех же кнопок — тем же стеклом (владелец 31.08.2026,
   «и стиль выпадающих менюшек»). Тень здесь ПОЛНАЯ панельная, не
   кнопочная: поповер размером с панель, и уменьшенная тень его не
   отрывала бы. Размытие по делу — под открытым меню проходят строки
   списка. Список сортировки (.sortbox__pop) — такой же слой, просто без
   своего <details>, ему то же стекло. Радиус намеренно прежний
   (--radius): у панельных 20px пункты в 7px скругления резали бы углы.
   Выключатель и откат общие с кнопками выше. */
:root .tasks-filters .fmenu__pop,
:root .tasks-filters .sortbox__pop {
    background: var(--float-bg);
    border-color: var(--float-line);
    box-shadow: var(--float-shadow);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
}

/* Строки подзадач и ресурсов в карточке задачи — тем же стеклом (владелец
   31.08.2026: «строки ресурсов и подзадач давай тоже»). Оба блока сложены
   из одного .res__row. Наведение отвечает глубиной тени — той же парой,
   что --lift/--lift-hi у плоского вида, только в стеклянных значениях;
   размытия нет по той же причине, что у плашек фильтров: строки лежат на
   глухом фоне карточки, размывать нечего. Кружок «+» рядом со строками не
   стеклится — он из семьи голых значков. Выключатель и откат общие. */
:root .res__row {
    background: var(--float-bg);
    border-color: var(--float-line);
    box-shadow: var(--float-chip-shadow);
}

:root .res__row:hover {
    box-shadow: var(--float-chip-shadow-hi);
}

/* Строка фильтров должна стоять прилипшей сразу, а не «подъезжать» к верху
   при первой прокрутке (живой отзыв владельца 28.08.2026). До правки на
   спокойной странице перед ней стоял зазор в 42px: 28px верхнего поля
   `.page` (оно и не участвует в приклеивании — см. комментарий выше,
   `top: 0` рассчитан без него) плюс 14px верхнего поля самого `.fbar`
   (`margin`, ниже по файлу), которое из-за пустого `.tasks-topbar` без
   своих паддинга и рамки схлопывается с ним и всплывает выше него же —
   так падает ниже 28px `.page`, а не только в свои законные 14px. Пока
   зазор был на месте, полоса фильтров при загрузке страницы стояла на 42px
   ниже верха и «доезжала» до него первой же прокруткой — ровно то
   движение, которого не должно быть видно.

   `.page:has(> .tasks-topbar)` — по первому ребёнку, чтобы верхнее поле
   `.page` не пострадало у остальных страниц на том же классе, там оно
   по-прежнему держит отступ под заголовок. `.tasks-topbar > .fbar` — с
   более высокой удельной массой, чем голый `.fbar` ниже по файлу, чтобы
   победить его независимо от порядка правил. */
.page:has(> .tasks-topbar) { padding-top: 0; }
/* Низ — той же `--float-gap`, что и воздух над шапкой и отступ сайдбара
   (владелец 31.08.2026): сверху, снизу и у панели один зазор. Только на
   трекере: остальные `.fbar` держат общий `--gap`. */
.tasks-topbar > .fbar { margin-top: 0; margin-bottom: var(--float-gap); }

.sheet-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding-bottom: 10px;
    border-bottom: 2px solid var(--ink);
    margin-bottom: var(--gap);
    flex-wrap: wrap;
}

/* Заголовок без черты. Класс стоял в разметке трекера (`tasks.html`) без
   правила, и жирная линия под «Задачами» никуда не девалась; восстановлено
   25.08.2026 по слову владельца.

   Смысл тот же, что у карточки задачи ниже (`.task-back + .sheet-head`):
   черта разделяет шапку и содержимое там, где содержимое начинается сразу.
   На доске под заголовком идёт ряд фильтров, за ним полосы групп — и линия
   становилась третьей горизонталью подряд, разделяющей заголовок с фильтром,
   то есть с продолжением того же заголовка.

   Отступ снизу оставлен: воздух между названием и фильтрами нужен, а вот
   `padding-bottom` был воздухом над самой чертой и вместе с ней не нужен. */
.sheet-head--plain {
    padding-bottom: 0;
    border-bottom: none;
}

/* Ряд значков действий: поиск, сортировка, фильтры, справочник меток,
   заведение. Жил в углу заголовка обособленной обёрткой (24.08.2026,
   класс назывался `.sheet-head__acts`), 27.08.2026 переехал в строку
   фильтров — правее «Колонками», последним пунктом `.fbar__row`
   (`tasks.html`). Само правило почти не изменилось: выравнивание по
   центру, а не по базовой линии, — у значков базовой линии нет. */
.fbar__acts {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* «Плюс» сидел вплотную к правому краю листа — там же, где строки
       задач, — и его центр приходился правее центра кружка статуса в
       строке (правка владельца 25.08.2026: «сдвинуть, чтобы центр плюса
       был по центру кружков статусов»). Разница не подобрана на глаз, а
       считается из тех же чисел, что и раскладка строки: правое поле
       строки 12px, плюс половина кружка статуса (24px / 2), минус половина
       самого «плюса» (`--ctl-round` / 2). Строка фильтров делит с рядом
       задач ту же правую границу листа, так что расчёт не изменился при
       переезде. */
    margin-right: calc(12px + 24px / 2 - var(--ctl-round) / 2);
}

.sheet-head h1 {
    margin: 0;
    font-size: 24px;
    font-weight: 650;
    letter-spacing: -0.022em;
    min-width: 0;
    /* Заголовок задачи бывает URL-ом без пробелов — без переноса он тянул бы
       страницу в горизонтальную прокрутку. */
    overflow-wrap: anywhere;
}

/* Карточка с цветным корешком слева — как ярлык на коробке. Заведена была под
   маркетплейсы на сводке; те убраны 21.08.2026, разметка осталась в «Агентах».
   Вместе с карточками ушли `.cards`, `.card--ozon|wb|ym`, `.card__name`,
   `.metric__unit` и вся недельная лента `.week*`. */

.card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    border-left: 4px solid var(--spine, var(--rule));
    padding: 16px 17px 14px;
}

.card__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 16px 24px;
    margin-bottom: 20px;
}

.metric__label {
    display: block;
    margin-bottom: 6px;
}

.metric__value { display: block; }

.scale-note {
    margin-top: 10px;
    font-size: 11px;
    color: var(--ink-3);
}

/* Состояния */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: 20px;
    font: 600 11.5px/1.4 var(--sans);
    border: 1px solid currentColor;
}

.badge--ok { color: var(--ok); }
.badge--warn { color: var(--warn); }
.badge--bad { color: var(--bad); }
.badge--idle { color: var(--ink-3); }

/* Пара состояний коннектора. Отдельная от --ok/--bad не ради цвета, а ради
   смысла: «online / down» читается как про живую связь, и стоит она только
   там, где речь о внешнем источнике. Рамка берёт цвет из currentColor — как
   у всех бейджей, отдельного border-color здесь не нужно. */
.badge--online { color: var(--online); }
.badge--down { color: var(--bad); }

/* Имя источника в таблице коннекторов. Ячейки таблиц идут цветом --ink-2,
   поэтому имя и описание под ним читались одним серым абзацем — глазу не за
   что зацепиться при пролистывании полутора десятков строк. Имя выходит на
   основной цвет и в полужирный, описание остаётся прежним: иерархия внутри
   ячейки делается разницей, а не размером шрифта. */
.conn__name {
    color: var(--ink);
    font-weight: 650;
}

/* Имя-ссылка у таблиц Google: вес и цвет те же, что у обычного имени, иначе
   в одном столбце получилось бы два разных заголовка. */
a.conn__name { color: var(--ink); }
a.conn__name:hover { color: var(--accent); }

/* Откуда навык пришёл — имя плагина рядом с именем навыка. Стоит в строке с
   именем, а не под ним: это часть ответа «что это», а не пояснение к нему. */
.conn__from {
    margin-left: 7px;
    font-size: 11px;
    color: var(--ink-3);
}

/* Приписка «не в репозитории». Цвет предупреждения, но строка остаётся
   подписью: это не поломка, состояние навыка зелёное — просто копии нет. */
.conn__alarm { color: var(--warn); }

/* Каждая группа коннекторов — своя таблица, а ширину колонок браузер считает
   по содержимому каждой таблицы отдельно. Имена у маркетплейсов короткие, у
   прочих источников длинные с описанием — и «Состояние» с «Создан» вставали
   на разной высоте строки под разными вертикалями, хотя панель одна.
   Поэтому две правые колонки получают одинаковую ширину и прижимаются к
   правому краю: свободное место достаётся первой колонке, единственной без
   заданной ширины, а вертикали совпадают во всех группах.
   Ширина взята по самому длинному содержимому: заголовок «СОСТОЯНИЕ» шире
   бейджа, а дата идёт в коротком виде дд.мм.гг. */
.conn__state { width: 116px; text-align: right; }
.conn__when { width: 104px; text-align: right; }

.notice {
    padding: 12px 14px;
    border-radius: var(--radius);
    border: 1px solid var(--rule);
    background: var(--sunk);
    color: var(--ink-2);
    font-size: 13px;
}

.notice--bad {
    border-color: var(--bad);
    background: var(--bad-soft);
    color: var(--bad);
}

.notice--warn {
    border-color: var(--warn);
    background: var(--warn-soft);
    color: var(--warn);
}

/* Получилось — тем же зелёным, что «на связи» у коннекторов. Раньше удача и
   обычное сообщение выглядели одинаково серыми, и по цвету нельзя было
   отличить «остановил» от «не смог». */
.notice--ok {
    border-color: var(--online);
    background: var(--online-soft);
    color: var(--online);
}

/* Отступы задаются классом, а не атрибутом style: политика безопасности
   (style-src 'self') встроенные стили запрещает и молча их отбрасывает. */
.notice--spaced { margin-bottom: 16px; }

/* Таблицы административного раздела */

.panel {
    background: var(--surface);
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    margin-bottom: var(--gap);
    overflow: hidden;
}

.panel__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--rule);
}

.panel__head h2 {
    margin: 0;
    font-size: 15.5px;
    font-weight: 650;
    letter-spacing: -0.012em;
}

/* Переход по якорю из панели не должен упирать заголовок в самый верх окна. */
.panel[id] { scroll-margin-top: 16px; }

.panel__body { padding: 18px; }
.panel__body--divided { border-top: 1px solid var(--rule); }

.table-scroll { overflow-x: auto; }
.table-scroll--spaced { margin-top: 4px; }

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    color: var(--ink-2);
}

th {
    text-align: left;
    padding: 10px 18px;
    font: 600 11px/1 var(--sans);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}

td {
    padding: 10px 18px;
    border-bottom: 1px solid var(--grid);
    vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--sunk); }

.table--audit td { padding-top: 7px; padding-bottom: 7px; }

/* Шкалы лимитов: мера заполнения, а не график.
   Цвет несёт состояние, но процент подписан рядом — читается и без цвета. */

.meters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 20px 24px;
    margin-bottom: 24px;
}

.meter__value {
    display: block;
    margin: 6px 0 8px;
    font: 600 28px/1 var(--sans);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

.meter__unit {
    font-size: 15px;
    font-weight: 400;
    color: var(--ink-2);
    margin-left: 1px;
}

.meter__track {
    height: 12px;
    background: var(--track);
    border-radius: 6px;
    overflow: hidden;
}

.meter__fill {
    height: 100%;
    border-radius: 6px;
    min-width: 4px;
}

.meter__fill--ok { background: var(--ok); }
.meter__fill--warn { background: var(--warn); }
.meter__fill--bad { background: var(--bad); }

.meter__foot {
    margin-top: 7px;
    font-size: 11px;
    line-height: 1.5;
    color: var(--ink-3);
}

/* День сброса после стрелки. Стрелка значком, а не символом «→»: у шрифтов
   системы он разной ширины, и в ряду шкал даты вставали бы лесенкой.

   Обычный inline, а не inline-flex: у флекс-контейнера базовая линия берётся
   у первого элемента, а первый здесь значок — и «сброс через 5 д 14 ч»
   съезжало относительно «25 августа, ВТ». Обычные строчные элементы делят
   одну базовую линию сами.

   Цвет не задаётся: день сброса — такая же подпись, как остальной текст под
   шкалой, и выделять его незачем. */
.meter__to { margin-left: 5px; }

/* Значок приглушён и тонок: он тут разделитель, а не указатель. Полная
   яркость перетягивала внимание с самой даты. */
.meter__arrow {
    margin-right: 3px;
    vertical-align: baseline;
    opacity: .5;
}

/* Человек в таблице: имя крупно, техническая подпись под ним */

.person__name { display: block; }

.person__alt {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    color: var(--ink-3);
}

/* Состав отдела разложен по слоям: слева подпись слоя, справа имена. Слои
   стоят в столбик и всегда все три, поэтому строка отдела читается сверху
   вниз как срез, а не как перечисление через запятую. */

.dept__layer {
    display: grid;
    grid-template-columns: 62px 1fr;
    gap: 8px;
    align-items: baseline;
    padding: 2px 0;
}

.dept__layer + .dept__layer { border-top: 1px solid var(--grid); }

.dept__members {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    line-height: 1.5;
}

.dept__members a { color: var(--ink-2); }

.dept__members a:hover { color: var(--ink-1); }

/* Формы */

label { display: block; }

.field {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

input[type="text"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="search"],
textarea,
select {
    font: 400 14px/1.4 var(--sans);
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    padding: 9px 11px;
    width: 100%;
    min-width: 0;
}

input[type="password"] {
    font-family: var(--mono);
    letter-spacing: 0.18em;
}

textarea {
    resize: vertical;
    min-height: 74px;
    line-height: 1.55;
}

input:focus, select:focus, textarea:focus { border-color: var(--ozon); }

.button {
    font: 500 13px/1 var(--sans);
    color: var(--on-s3);
    background: var(--accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius-ctl);
    padding: 10px 16px;
    cursor: pointer;
    white-space: nowrap;
}

.button:hover { background: var(--s2); border-color: var(--s2); }
.button[disabled] { opacity: 0.5; cursor: default; }

/* Кнопка-ссылка: тот же вид, но ведёт на страницу, а не шлёт форму. */
a.button {
    display: inline-block;
    text-decoration: none;
    text-align: center;
}

.button--ghost {
    color: var(--ink);
    background: transparent;
    border-color: var(--rule-strong);
    padding: 6px 10px;
    font-size: 12px;
}

.button--ghost:hover { background: var(--paper); }

/* Кнопка-заливка в ряду обводок: главное действие строки. Правка владельца
   24.08.2026 — «Опубликовать сделать фиолетовой заливкой с белым текстом».
   Геометрия берётся от `--ghost`, чтобы строка не разъезжалась по высоте:
   отличается цветом, а не размером. */
.button--cta {
    padding: 6px 10px;
    font-size: 12px;
    color: var(--on-fill);
    background: var(--accent);
    border-color: var(--accent);
}

.button--cta:hover {
    background: var(--accent);
    border-color: var(--accent);
    filter: brightness(.93);
}
.button--danger { color: var(--bad); border-color: var(--bad); }
.button--danger:hover { background: var(--bad-soft); }

.inline-form {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    flex-wrap: wrap;
}

.inline-form .field { flex: 1 1 160px; }
.inline-form .field--narrow { flex: 0 1 180px; }

/* Вход */

.gate {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 24px;
}

/* Утверждённое стекло (просьба владельца 31.08.2026) — тот же рецепт из
   шести свойств, что у левитирующей панели выше по файлу, а не похожий на
   него: фон, волосяная обводка, панельный радиус, полная панельная тень и
   размытие подложки.

   Размытие здесь заслужено по правилу свода «размытие — только там, где под
   элементом реально проходит контент»: под карточкой ходит свет вестибюля.

   Прежняя чернильная полоса сверху (border-top: 3px solid --ink) снята: на
   стекле она читалась как приклеенная планка — у панелей системы обводка
   ровная по всему периметру.

   Общего выключателя «Плоский вид» здесь нет намеренно: он живёт в подвале
   боковой панели, а на входе панели ещё нет — sidebar.js подключается
   только вошедшему. Поэтому правило без обёртки `:not([data-side-style=
   "flat"])`, которая на этой странице никогда бы не сработала. */
.gate__card {
    width: 100%;
    max-width: 380px;
    padding: 28px;
    background: var(--float-bg);
    border: 1px solid var(--float-line);
    border-radius: var(--float-radius);
    box-shadow: var(--float-shadow);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
}

.gate__title {
    margin: 12px 0 6px;
    font-size: 18px;
    font-weight: 600;
}

.gate__text {
    margin: 0 0 20px;
    color: var(--ink-2);
    font-size: 13px;
}

.gate .button { width: 100%; }

.gate__steps {
    display: flex;
    gap: 6px;
    margin-bottom: 20px;
}

.gate__step {
    flex: 1;
    height: 3px;
    background: var(--rule);
    border-radius: var(--radius-ctl);
}

.gate__step--done { background: var(--ok); }
/* Заливку активного шага рисует не фон, а `::after` — см. «Свет за стеклом»
   ниже: полоска набирается слева направо и этим договаривает «шаг 1 из 2».
   При выключенном движении `::after` стоит на полной ширине, и вид тот же. */

/* Код набирают руками или диктуют — отсюда крупный кегль и разрядка */
.code {
    margin: 0 0 16px;
    padding: 14px 12px;
    text-align: center;
    font: 600 30px/1 var(--sans);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.22em;
    text-indent: 0.22em;
    color: var(--ink);
    background: var(--sunk);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    user-select: all;
}

/* Код — кнопка: щелчок кладёт его в буфер. Кнопка по умолчанию строчная и
   по ширине содержимого, поэтому рамка и центровка поехали бы; ширину
   возвращаем явно. Всё остальное — от `.code`, она объявлена ниже `.copykey`
   и перебивает её сброс фона и рамки. */
button.code {
    display: block;
    width: 100%;
    cursor: pointer;
}

button.code:hover { border-color: var(--accent); }

.gate__link {
    display: block;
    text-align: center;
    padding: 12px;
    margin-bottom: 16px;
    background: var(--ink);
    color: #fff;
    text-decoration: none;
    border-radius: var(--radius-ctl);
    font-weight: 500;
}

.gate__link:hover { background: var(--s2); border-color: var(--s2); }

/* В тёмной теме `--ink` светлый, и белая подпись на нём пропадала целиком
   (найдено 25.09.2026 при смене фона входа). Подпись берёт цвет страницы —
   он в тёмной теме тёмный; светлая тема не тронута. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .gate__link { color: var(--page); }
}

:root[data-theme="dark"] .gate__link { color: var(--page); }

.countdown {
    text-align: center;
    font: 500 12px/1 var(--sans);
    color: var(--ink-3);
}

.gate__foot {
    margin: 20px 0 0;
    padding-top: 16px;
    border-top: 1px solid var(--rule);
    font-size: 12px;
    color: var(--ink-3);
    text-align: center;
}

/* ── Вестибюль входа ─────────────────────────────────────────────────────
   Живой фон шагов входа — пароль и подтверждение (Антон, 25.09.2026).
   Сменил «Свет за стеклом» 31.08.2026: пять пятен палитры уступили месту
   сцене с первого слайда «Background Effect Hero Collection» Slider
   Revolution — бордово-оранжевый градиент, перетекающие пятна, две сферы с
   орбитами, стеклянные диски и звёздная пыль.

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

   Цвета сцены — буквальные, из настроек слайда, а не токены палитры: это
   просьба повторить конкретную картинку, и в тёмной теме она та же.
   Эталон `design-baseline.json` поэтому видит новые значения; сторож
   эталона и так горит и по слову Антона 22.09.2026 не подписывается.

   Разделение труда: сферы, орбиты, диски и градиент — элементы ниже;
   перетекающие пятна и пыль рисует canvas в gate-scene.js (CSS не умеет
   сливать шары в один контур). Там же — почему время берётся из часов.

   ГЛАВНОЕ ПРАВИЛО ЭТОГО БЛОКА: покой — конечный вид, а не начальный.
   Общий выключатель в шапке файла гасит `animation` целиком
   (`prefers-reduced-motion`), и правило вида `opacity: 0` в базовом
   состоянии оставило бы человека с выключенным движением перед пустой
   карточкой. Поэтому смещение и прозрачность живут ТОЛЬКО внутри
   @keyframes, а базовое состояние каждого элемента — это то, как он
   выглядит после анимации. */

.gate {
    position: relative;
    /* Свой контекст наложения: пятна не должны пролезать под соседние слои
       страницы, а карточка обязана лежать над ними. Он же держит и
       `mix-blend-mode` пятен — смешиваются они между собой и с этим фоном,
       а не со всей страницей. */
    isolation: isolate;

    /* Вестибюль тёмный в ЛЮБОЙ теме, и это главное решение всей правки.
       Цветной свет на почти белой странице (--page #f4f2f7) выцветает в
       серую дымку — как ни поднимай насыщенность, получается туман (две
       попытки, снимки от 31.08.2026). Светится он только от тёмного.

       Цвет записан буквально, а не токеном, именно поэтому: --ink в
       тёмной теме становится почти белым, и вестибюль перевернулся бы.
       Значение не новое — #17141f уже в палитре, это тот же чернильный
       тон, и `design-baseline.json` правкой не задет.

       В тёмной теме подложка и так темнее этой, поэтому там берём
       страничную: ниже два переопределения, по одному на каждый способ
       объявить тему. */
    background: #17141f;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .gate { background: var(--page); }
}

:root[data-theme="dark"] .gate { background: var(--page); }

/* Сцена. Единица `--u` — пиксель сетки образца (1240 × 900) на этом
   экране: на 1512 × 982 это 1 px, как у образца, на телефоне около 0,3,
   на большом мониторе до 1,6. Все размеры и смещения слоёв ниже — числа
   образца, умноженные на неё, поэтому раскладка сжимается целиком, а не
   разъезжается. Слои стоят от центра экрана, как в образце: сетка
   образца центрирована, «левые» и «правые» слои отсчитаны от её краёв.

   `--gx` / `--gy` — сдвиг за курсором (от −7 до 7, ставит скрипт), каждый
   слой умножает его на свою глубину `--depth` — числа тоже из образца.

   `fixed`, а не `absolute`: `.gate` живёт внутри листа с предельной
   шириной, и на широком экране сцена кончалась белыми полями по бокам.
   Сцена — фон экрана, а не блока. */
.gate__scene {
    --u: clamp(0.3px, min(100vw / 1240, 100vh / 900), 1.6px);
    --gx: 0;
    --gy: 0;
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    background: linear-gradient(45deg, #03071e 0%, #370617 25%, #6a040f 50%, #9d0208 75%, #e85d04 100%);
}

.gate__layer {
    --depth: 0;
    position: absolute;
    display: block;
    translate: calc(var(--gx) * var(--depth) * var(--u)) calc(var(--gy) * var(--depth) * var(--u));
}

/* Пыль — во весь экран, за всеми слоями и без параллакса. */
.gate__dust {
    inset: 0;
    width: 100%;
    height: 100%;
}

/* Перетекающие пятна. Размытие у каждого своё и тоже из образца (30, 15,
   3 px, у малого нет): большие читаются светом, малые — телом. Размывает
   сам скрипт, мягким краем пятна, а НЕ `filter: blur()` здесь: холст
   перерисовывается каждый кадр, и после заставки браузер (Safari
   особенно) размывал его плитками — по экрану шли квадраты со швами
   (Антон, 25.09.2026, в первый же час). */
.gate__morph--1 {
    --depth: 2;
    left: calc(50% - 500 * var(--u));
    top: calc(50% - 300 * var(--u));
    width: calc(1200 * var(--u));
    height: calc(800 * var(--u));
}

.gate__morph--2 {
    --depth: -2;
    left: calc(50% - 900 * var(--u));
    top: calc(50% - 500 * var(--u));
    width: calc(1200 * var(--u));
    height: calc(800 * var(--u));
}

.gate__morph--4 {
    --depth: -4;
    left: calc(50% + 461 * var(--u));
    top: calc(50% - 301 * var(--u));
    width: calc(200 * var(--u));
    height: calc(200 * var(--u));
}

.gate__morph--3 {
    --depth: 6;
    left: calc(50% - 480 * var(--u));
    bottom: calc(-70 * var(--u));
    width: calc(350 * var(--u));
    height: calc(350 * var(--u));
}

/* Сферы и орбиты. Орбита — круг с обводкой, положенный в перспективу:
   поворот на 45° по двум осям делает из него наклонный эллипс вокруг
   сферы. */
.gate__planet,
.gate__ring,
.gate__disc {
    border-radius: 50%;
}

.gate__ring {
    border-style: solid;
}

.gate__ring--2 {
    --depth: 2;
    left: calc(50% - 437 * var(--u));
    top: calc(50% + 3 * var(--u));
    width: calc(600 * var(--u));
    height: calc(600 * var(--u));
    border-width: calc(10 * var(--u));
    border-color: rgba(55, 6, 23, .35);
    transform: perspective(calc(600 * var(--u))) rotateX(45deg) rotateY(45deg) rotateZ(10deg);
}

.gate__planet--2 {
    --depth: 2;
    left: calc(50% - 435 * var(--u));
    top: calc(50% + 97 * var(--u));
    width: calc(500 * var(--u));
    height: calc(500 * var(--u));
    background: linear-gradient(40deg, rgba(3, 7, 30, 0) 0%, rgba(45, 6, 24, 1) 40%, rgba(55, 6, 23, 1) 50%, rgba(157, 2, 8, 1) 75%, rgba(220, 47, 2, 1) 90%);
}

/* Стеклянные диски: полупрозрачная заливка и размытие того, что под ними,
   — тот же приём, что у карточки входа, только без обводки. */
.gate__disc--2 {
    --depth: 4;
    left: calc(50% - 750 * var(--u));
    bottom: calc(-210 * var(--u));
    width: calc(500 * var(--u));
    height: calc(500 * var(--u));
    background: linear-gradient(45deg, rgba(3, 7, 30, 0) 0%, rgba(42, 6, 25, .15) 75%, rgba(55, 6, 23, .25) 100%);
    backdrop-filter: blur(calc(15 * var(--u)));
    -webkit-backdrop-filter: blur(calc(15 * var(--u)));
}

.gate__ring--1 {
    --depth: -2;
    left: calc(50% + 50 * var(--u));
    top: calc(50% - 525 * var(--u));
    width: calc(420 * var(--u));
    height: calc(420 * var(--u));
    border-width: 1px;
    border-color: rgba(255, 255, 255, .15);
    transform: perspective(calc(600 * var(--u))) rotateX(45deg) rotateY(45deg);
}

.gate__planet--1 {
    --depth: -2;
    left: calc(50% + 90 * var(--u));
    top: calc(50% - 440 * var(--u));
    width: calc(300 * var(--u));
    height: calc(300 * var(--u));
    background: linear-gradient(45deg, rgba(106, 4, 15, 0) 0%, rgba(157, 2, 8, .71) 25%, rgba(182, 20, 6, 1) 35%, rgba(220, 47, 2, 1) 50%, rgba(250, 163, 7, 1) 75%, rgba(255, 186, 8, 1) 100%);
}

.gate__disc--1 {
    --depth: -4;
    left: calc(50% + 318 * var(--u));
    top: calc(50% - 415 * var(--u));
    width: calc(100 * var(--u));
    height: calc(100 * var(--u));
    background: rgba(255, 255, 255, .15);
    backdrop-filter: blur(calc(10 * var(--u)));
    -webkit-backdrop-filter: blur(calc(10 * var(--u)));
}

/* Появление — только на первом шаге и только при чистом заходе (класс
   `gate--intro` ставит login.html). Второй шаг продолжает ту же картину,
   и проигрыш заставки заново читался бы как перезагрузка. Слои выходят
   лесенкой, как в образце: задержки и длительности — его.

   Сам градиент фона не проявляется, он есть с первого кадра. Проявление
   из прозрачности было и снято: пока сцена прозрачна, сквозь неё виден
   тёмный блок `.gate` шириной листа, а по бокам — белая страница. */
.gate--intro .gate__layer:not(.gate__dust) {
    animation: gate-emerge 2s cubic-bezier(.65, 0, .35, 1) both;
}

@keyframes gate-emerge {
    from {
        opacity: 0;
        scale: .9;
    }
}

.gate--intro .gate__morph--1 { animation-duration: 3s; animation-delay: .2s; }
.gate--intro .gate__morph--2 { animation-duration: 3s; animation-delay: .5s; }
.gate--intro .gate__planet--1 { animation-delay: .7s; }
.gate--intro .gate__morph--4 { animation-delay: .9s; }
.gate--intro .gate__ring--1 { animation-delay: 1s; }
.gate--intro .gate__morph--3 { animation-delay: 1.1s; }
.gate--intro .gate__planet--2 { animation-delay: 1.3s; }
.gate--intro .gate__disc--1 { animation-delay: 1.3s; }
.gate--intro .gate__disc--2 { animation-delay: 1.5s; }
.gate--intro .gate__ring--2 { animation-delay: 1.6s; }

/* Карточка приходит на свет: поднимается и проявляется. */
.gate__card {
    position: relative;
    z-index: 1;
    animation: gate-rise 620ms cubic-bezier(.22, .61, .36, 1) both;
}

@keyframes gate-rise {
    from {
        opacity: 0;
        transform: translateY(10px) scale(.99);
    }
}

/* Содержимое разбирается следом, лесенкой в 60 мс. Порядковый номер, а не
   имена классов: у карточки две ветки разметки (форма и плашка «слишком
   много попыток»), и лесенка обязана работать в обеих. */
.gate__card > * {
    animation: gate-in 520ms cubic-bezier(.22, .61, .36, 1) both;
}

.gate__card > *:nth-child(1) { animation-delay: 150ms; }
.gate__card > *:nth-child(2) { animation-delay: 210ms; }
.gate__card > *:nth-child(3) { animation-delay: 270ms; }
.gate__card > *:nth-child(4) { animation-delay: 330ms; }

@keyframes gate-in {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
}

/* Полоска шага набирается слева направо — «шаг 1 из 2» сказано ещё и
   движением, а не только подписью. Трек — фон самой полоски (--rule),
   заливка — этот слой. */
.gate__step--active {
    position: relative;
    overflow: hidden;
}

.gate__step--active::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ink);
    transform-origin: left center;
    animation: gate-fill 760ms cubic-bezier(.22, .61, .36, 1) 320ms both;
}

@keyframes gate-fill {
    from { transform: scaleX(0); }
}

/* Раздел Cron: карточки процессов, полоски расхода, строки расписания */

.procs {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
}

.proc {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.proc--stopped { background: var(--warn-soft); border-color: var(--warn); }

.proc__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.proc__who {
    display: inline-flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px 10px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-2);
}

/* Название работы — крупнее строки «кто и откуда»: сначала понимают, чья
   сессия, потом читают, чем она занята. */
.proc__name {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.45;
    color: var(--ink);
}

/* Строка происхождения: откуда сессия, чья и на какой модели. Источник —
   ярлык с цветным маркером, остальное тихим текстом: цвет здесь кодирует
   тип запуска, а не украшает. */

.proc__origin {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    margin: 0;
    font-size: 12px;
    color: var(--ink-2);
}

/* Не flex: у inline-flex базовая линия берётся от первого элемента, а первый
   здесь — кружок-маркер без текста. Из-за этого «Telegram-бот» съезжал
   относительно имени рядом. Маркер — inline-block со своим отступом. */
.proc__source {
    font-weight: 600;
    color: var(--ink);
}

.proc__source::before {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-right: 7px;
    border-radius: 50%;
    background: var(--mark, var(--muted));
    vertical-align: 0.12em;
}

.proc__source--telegram { --mark: var(--accent); }
.proc__source--job { --mark: var(--ok); }
.proc__source--terminal { --mark: var(--ink-3); }
/* Демон и ручной запуск раньше оставались без метки: у первого правила не
   было вовсе, второй вид только что появился. Без --mark кружок рисовался
   пустым, и строка выглядела сломанной. */
.proc__source--daemon { --mark: var(--ozon); }
.proc__source--shell { --mark: var(--ink-2); }
.proc__source--service { --mark: var(--warn); }
.proc__source--unknown { --mark: var(--bad); }

.proc__model::before,
.proc__cont::before {
    content: "·";
    margin-right: 10px;
    color: var(--muted);
}

.proc__author { color: var(--ink); font-weight: 600; }
.proc__model { font-family: var(--mono); font-size: 11.5px; }
.proc__cont { color: var(--muted); }

.proc__facts {
    margin: 0;
    font-size: 12px;
    color: var(--ink-3);
}

.proc__actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: auto;
}

/* Поручение целиком открывается в окне. Кнопка притворяется текстом: в
   карточке это по-прежнему предложение, а не элемент управления, и рамка с
   фоном ломали бы ровный ряд карточек. Подчёркивание — только при наведении,
   иначе каждая карточка выглядела бы ссылкой. */

.proc__full {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.proc__cont--link {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: var(--muted);
    cursor: pointer;
}

.proc__full:hover,
.proc__cont--link:hover { text-decoration: underline; }

/* Текст поручения в окне: моноширинный и с переносами — там списки пунктов,
   пути и куски команд, которым нужен свой ритм строки. */

.prompt__who { margin: 0 0 10px; }

.prompt__text {
    margin: 0;
    max-height: 60vh;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
    font-family: var(--mono);
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--ink);
}

.is-hidden { display: none; }

/* Полоски «куда ушли токены»: доля от суммы за сутки, полоски складываются
   в целое. Тоньше шкал лимитов — это ряд, а не одиночный прибор. */

.burn__head { margin: 0 0 10px; }

.burn {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) minmax(80px, 2fr) auto;
    align-items: center;
    gap: 12px;
    padding: 4px 0;
    font-size: 13px;
}

/* Строка расхода, которая раскрывается в карту дней. Сетку в этом случае
   держит `summary`, а сам `details` становится обычным блоком: оставь на нём
   grid — и раскрытая карта встала бы четвёртой колонкой, разломав ряд.

   Тот же класс `.burn` используется списком агентов на странице «Доступ»
   простым `div`, и там сетка обязана остаться на месте — поэтому правило
   адресное, по тегу. */
details.burn {
    display: block;
    padding: 0;
}

.burn__row {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) minmax(80px, 2fr) auto;
    align-items: center;
    gap: 12px;
    padding: 4px 0;
    cursor: pointer;
    border-radius: var(--radius-ctl);
}

/* Свой треугольник не рисуем и чужой не показываем: строка и так меняет фон
   под курсором, а маркер сдвинул бы первую колонку и сломал ряд имён. */
.burn__row::-webkit-details-marker { display: none; }
.burn__row { list-style: none; }
.burn__row:hover { background: var(--surface-2); }

/* Строки без человека за спиной («Служебные запуски», «Рабочая группа») не
   раскрываются — им и подсказки о клике не нужно. */
.burn--flat .burn__row { cursor: default; }
.burn--flat .burn__row:hover { background: none; }

.burn__name {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.burn__title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.burn__track {
    height: 8px;
    background: var(--surface-2);
    border-radius: 4px;
    overflow: hidden;
}

.burn__fill {
    height: 100%;
    border-radius: var(--radius-ctl);
    min-width: 2px;
    background: var(--accent);
}

.burn__value { color: var(--ink-2); white-space: nowrap; }

/* Карта дней. Столбец — неделя, клетка — сутки; оттенок задаётся классом,
   потому что инлайновый `style` запрещён политикой безопасности.

   Цвет один — `--accent`, уровни различаются прозрачностью. Новых красок
   здесь намеренно нет: карта должна читаться как часть панели, а не спорить
   с ней, и в тёмной теме токен светлеет сам.

   Раскладка — по образцу карты вкладов GitHub (IR-95): число сверху, месяцы
   над столбцами, дни недели слева, легенда справа внизу. Сетка плотная, как
   у образца: клетка 10px, зазор 3px, шаг столбца 13px.

   Ширина — вся панель, за счёт глубины (Антон, 05.09.2026): в разметке два
   года столбцов, а ряд `.cal__view` показывает столько, сколько влезло,
   прижав сетку к правому краю; лишнее слева отрезано, не прокручивается.
   Ширина ряда округлена вниз до целого шага столбца (`round()`), чтобы у
   левого края не висела половинка клетки; браузер без `round()` получает
   обычную ширину и половинку — карта не ломается. Обрезка только по
   горизонтали (`overflow-x: clip`): плашка подсказки выходит вверх и должна
   быть видна. Число столбцов в CSS не записано — сетка берёт их из
   разметки, поэтому ряд месяцев и ряд клеток совпадают столбец в столбец.
   Подпись месяца вылезает вправо из своего столбца, как у образца; над
   левым краем ряд месяцев растворяется маской, чтобы не показывать хвост
   подписи отрезанного столбца. */
.cal { padding: 8px 0 4px; }

.cal__head {
    margin: 0 0 8px;
    font-size: 13px;
    color: var(--ink-2);
}

.cal__box {
    display: block;
    padding: 0;
}

.cal__row {
    display: grid;
    grid-template-columns: 22px minmax(0, 1fr);
    column-gap: 3px;
}

.cal__view {
    justify-self: end;
    width: calc(round(down, 100% + 3px, 13px) - 3px);
    max-width: 100%;
    display: flex;
    justify-content: flex-end;
    overflow-x: clip;
    overflow-y: visible;
}

.cal__view--months {
    height: 14px;
    margin-bottom: 4px;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 16px);
    mask-image: linear-gradient(to right, transparent, #000 16px);
}

.cal__months {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 10px;
    gap: 3px;
    flex: none;
    height: 14px;
    font-size: 11px;
    line-height: 14px;
    color: var(--ink-3);
}

.cal__month {
    min-width: 0;
    white-space: nowrap;
}

/* Подпись над одним из двух последних столбцов вылезала бы за правый край
   ряда и резалась обрезкой. У неё текст выходит из столбца влево: в блоке
   с `direction: rtl` строка переполняется в левую сторону, сами буквы
   остаются слева направо. */
.cal__month:nth-last-child(-n+2) { direction: rtl; }

.cal__days {
    display: grid;
    grid-template-rows: repeat(7, 10px);
    gap: 3px;
    font-size: 10px;
    color: var(--ink-3);
}

.cal__days span {
    display: flex;
    align-items: center;
}

.cal__grid {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 10px;
    gap: 3px;
    flex: none;
}

.cal__col {
    display: grid;
    grid-template-rows: repeat(7, 10px);
    gap: 3px;
}

/* Клетка 10px (Антон, 05.09.2026: «помельче квадратики»), зазор 3px — как у
   образца. `position: relative` — под плашку подсказки. */
.cal__cell {
    display: block;
    position: relative;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--hair);
}

.cal__cell--l1 { background: var(--accent); opacity: .3; }
.cal__cell--l2 { background: var(--accent); opacity: .5; }
.cal__cell--l3 { background: var(--accent); opacity: .75; }
.cal__cell--l4 { background: var(--accent); }

/* Подсказка — та же стеклянная плашка, что выпадающие меню фильтров в
   трекере (.fmenu__pop): заливка, рамка, тень и размытие семейства float,
   радиус --radius. Появляется сразу по наведению, без задержки системного
   title. Текст — из data-tip, перенос строки — символ &#10; в атрибуте.
   Ряд обрезан по горизонтали, поэтому плашка обязана оставаться внутри
   него: у последних 26 столбцов (полгода — на любом настольном экране это
   меньше половины видимого) она прижата к правому краю клетки и растёт
   влево, у остальных — к левому и растёт вправо. Оттенок клетки задан
   `opacity` — при наведении клетка получает полную плотность, и плашка
   рисуется без полупрозрачности. */
.cal__cell[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    left: 0;
    bottom: calc(100% + 7px);
    z-index: 5;
    /* Ширина по содержимому, а не по клетке: у абсолютного блока внутри
       10-пиксельной клетки иначе переносилось бы каждое слово. */
    width: max-content;
    max-width: 340px;
    padding: 6px 10px;
    white-space: pre-line;
    font-size: 12px;
    line-height: 1.35;
    color: var(--ink);
    background: var(--float-bg);
    border: 1px solid var(--float-line);
    border-radius: var(--radius);
    box-shadow: var(--float-shadow);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    pointer-events: none;
    visibility: hidden;
}

.cal__cell[data-tip]:hover { opacity: 1; }
.cal__cell[data-tip]:hover::after { visibility: visible; }

.cal__col:nth-last-child(-n+26) .cal__cell[data-tip]::after {
    left: auto;
    right: 0;
}

.cal__foot {
    display: flex;
    justify-content: flex-end;
    margin-top: 8px;
    font-size: 11px;
    line-height: 12px;
    color: var(--ink-3);
}

/* В легенде клетка не резиновая — образец, а не часть сетки. */
.cal__legend {
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

.cal__legend .cal__cell {
    width: 10px;
    height: 10px;
}

.jobrow {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 4px 0;
    font-size: 13px;
}

.jobrow__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.jobrow__meta { margin-left: auto; color: var(--ink-3); white-space: nowrap; }

/* Строка таймера или службы: что — когда — итог */

.timer {
    display: grid;
    grid-template-columns: minmax(160px, 2fr) minmax(140px, 2fr) auto;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    font-size: 13px;
}

.timer + .timer { border-top: 1px solid var(--rule); }

.timer__name { display: block; }

.timer__when { color: var(--ink-2); }
.timer__when .person__alt { margin-top: 1px; }

@media (max-width: 600px) {
    .timer { grid-template-columns: 1fr auto; }
    .timer__when { grid-column: 1 / -1; }
}

/* Свёрнутые второстепенные блоки: системные таймеры, классический cron */

.fold { border-top: 1px solid var(--rule); }

.fold__summary {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 12px 18px;
    font-size: 13px;
    color: var(--ink-2);
    cursor: pointer;
    list-style: none;
}

.fold__summary::-webkit-details-marker { display: none; }

.fold__summary::before {
    content: "\25B8";
    color: var(--ink-3);
    font-size: 11px;
}

.fold[open] .fold__summary::before { content: "\25BE"; }
.fold__summary:hover { background: var(--sunk); }

/* Правка на месте: «Изменить» у отдела и «Открыть доступ» у пространства
   памяти. Класс был в разметке с самого начала, а стилей у него не было —
   браузер рисовал свой треугольник поверх кнопки и вываливал форму без
   отступов. Раскрытое содержимое отделено рамкой: иначе поля соседних
   строк таблицы читаются как продолжение одной. */

.glass > summary {
    list-style: none;
    cursor: pointer;
}

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

.glass[open] > summary { margin-bottom: 10px; }

.glass[open] > :not(summary) {
    margin-top: 10px;
    padding-left: 12px;
    border-left: 2px solid var(--rule);
}

/* Кнопка-значок: действие без подписи. Подпись живёт в title и aria-label —
   в строке таблицы на текст места нет, а смысл кнопки должен быть доступен. */

.iconbutton {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: var(--card);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    color: var(--ink-2);
    cursor: pointer;
}

.iconbutton:hover { background: var(--paper); color: var(--ink); }

.iconbutton--danger { color: var(--bad); border-color: var(--bad); }
.iconbutton--danger:hover { background: var(--bad-soft); color: var(--bad); }

.aico { display: block; }

/* Пустое место вместо отсутствующей кнопки: столбец значков в таблице должен
   стоять ровно, даже когда у строки нет всех действий. */
.iconbutton-gap {
    display: block;
    width: 30px;
    height: 30px;
}

/* Окно настроек. Нативный <dialog>: затемнение и перехват фокуса — от
   браузера, нам остаётся только вид. */

.modal {
    width: min(420px, calc(100vw - 32px));
    padding: 0;
    background: var(--card);
    color: var(--ink);
    /* Была ещё чёрная полоса в 3px сверху — акцент, какого нет ни у одной
       карточки в остальной системе: там граница везде ровная, в один цвет
       и одну толщину (правка владельца 25.08.2026: «какая-то чёрная граница
       сверху»). Обводка окна теперь та же, что у карточки задачи. */
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.modal::backdrop { background: var(--scrim); }

.modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--rule);
}

.modal__head h2 {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
}

.modal__body { padding: 18px; }

/* Окно картинки-вложения — лайтбокс (27.08.2026: «все картинки должны
   открываться в модалке, настолько большие, насколько можно вписать в
   экран»). #file-dialog теперь только для этого — переименование, удаление
   и скачивание переехали в меню строки (`.res__menu`), а общий `.modal`
   (420px) для картинки тесен, поэтому свой модификатор. */
.modal--lightbox { width: min(94vw, 1200px); }

.modal__body--lightbox {
    display: flex;
    justify-content: center;
    padding: 12px;
}

/* `object-fit: contain`, а не `cover`: обрезать чужую картинку никто не
   просил, пусть влезает целиком, даже если это значит поля по бокам.
   Высота ограничена долей окна экрана, а не пикселями — высокая картинка
   не должна вылезать за нижний край при любом размере монитора. */
.file-preview {
    display: block;
    max-width: 100%;
    max-height: 80vh;
    object-fit: contain;
    background: var(--sunk);
    border-radius: var(--radius-ctl);
}

.modal__foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 18px;
    border-top: 1px solid var(--rule);
    background: var(--sunk);
}

/* ── Групповые действия трекера (Антон, 23.09.2026) ──────────────────────
   Панель выбора — внизу экрана поверх всего, маленькая и скруглённая:
   сколько выбрано, «Действия», крестик. Оформление — семейство левитирующей
   панели целиком (рамка, радиус, заливка, размытие и тень `.side`), своего
   стиля у неё нет. Выше шапки списка (40), ниже листов меню на телефоне
   (80): открытое меню строки не должно прятаться под ней. */
.bulkbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: var(--float-gap);
    z-index: 40;
    display: flex;
    align-items: center;
    gap: 10px;
    width: max-content;
    margin: 0 auto;
    padding: 6px 6px 6px 16px;
    border: 1px solid var(--float-line);
    border-radius: var(--float-radius);
    background: var(--float-bg);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    box-shadow: var(--float-shadow);
    color: var(--ink);
    font-size: 13px;
    white-space: nowrap;
}
.bulkbar[hidden] { display: none; }
.bulkbar__count { color: var(--ink-2); }
.bulkbar__count .num { color: var(--ink); font-weight: 600; }

/* Палитра действий — по сути Spotlight: поле сверху, список под ним
   сужается набором. Окно — тот же `.modal` по рамке и подложке, ширина —
   `.modal--wide`. Стоит в верхней трети, а не по центру: при наборе список
   меняет высоту, и окно по центру прыгало бы вверх-вниз под рукой. */
.cmdk {
    width: min(680px, calc(100vw - 32px));
    margin: 12vh auto auto;
    padding: 0;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--card);
    color: var(--ink);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
}
.cmdk::backdrop { background: var(--scrim); }
.cmdk__head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--rule);
}
/* Поле без рамки, как в Spotlight: рамка — у всего окна. Селектор с
   обёрткой, иначе общее правило полей (`input[type="text"]`, рамка, поля,
   фон) перебивает — у него тот же вес, и стоит оно раньше лишь случайно. */
.cmdk .cmdk__input,
.cmdk .cmdk__input:focus {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    color: var(--ink);
    font: inherit;
    font-size: 16px;
    outline: none;
}
.cmdk__crumb {
    flex: none;
    padding: 1px 8px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    color: var(--ink-2);
    font-size: 12px;
    white-space: nowrap;
}
.cmdk__crumb[hidden] { display: none; }
.cmdk__count { flex: none; color: var(--muted); font-size: 12px; white-space: nowrap; }
.cmdk__list {
    margin: 0;
    padding: 6px;
    list-style: none;
    max-height: min(70vh, 560px);
    overflow-y: auto;
}
.cmdk__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    border-radius: var(--radius-ctl);
    font-size: 13px;
    cursor: pointer;
}
.cmdk__item[hidden] { display: none; }
.cmdk__item--on { background: var(--side-hover); }
/* Раздел перед действием — только в поиске по всем разделам сразу: там
   «В работе» без подписи не отличить от метки с тем же словом. */
.cmdk__path { flex: none; color: var(--muted); font-size: 12px; }
.cmdk__path::after { content: " ›"; }
.cmdk__path[hidden] { display: none; }
.cmdk__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk__label--bad { color: var(--bad); }
.cmdk__more,
.cmdk__hint { flex: none; margin-left: auto; color: var(--muted); font-size: 12px; }
.cmdk__empty { margin: 0; padding: 14px 18px; color: var(--muted); font-size: 13px; }
.cmdk__empty[hidden] { display: none; }
.cmdk__foot {
    margin: 0;
    padding: 8px 18px;
    border-top: 1px solid var(--rule);
    background: var(--sunk);
    color: var(--muted);
    font-size: 12px;
}

/* Знания и правила: слева перечень файлов, справа открытый файл.

   Лентой карточек над редактором это не работало: между выбранным файлом и
   полем правки вклинивались чужие группы, и выбор оказывался оторван от
   результата. Колонка слева держит весь список на виду и не растёт вниз. */

.docs {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr);
    gap: 18px;
    align-items: start;
}

.docs__nav {
    position: sticky;
    top: 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.docs__group {
    margin: 14px 0 2px;
    font: 500 11px/1 var(--sans);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.docs__nav > .docs__group:first-child { margin-top: 0; }

.docs__hint {
    margin: 0 0 6px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--ink-3);
}

.docs__item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 9px 11px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--ink);
}

.docs__item:hover { background: var(--sunk); }

/* Открытый файл — белый лист с рамкой, тем же приёмом, что текущий раздел
   в боковой панели: помечаем положение, а не рисуем кнопку. */
.docs__item--on {
    background: var(--card);
    border-color: var(--rule);
    box-shadow: inset 0 0 0 1px var(--rule);
}

.docs__name { font-size: 13px; font-weight: 500; }
.docs__role { font-size: 11.5px; color: var(--ink-2); }

.docs__marks {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 3px;
}

.docs__mark {
    font: 500 10px/1.4 var(--sans);
    letter-spacing: 0.04em;
    color: var(--ink-3);
}

.docs__mark--warn { color: var(--warn); }

.docs__main { min-width: 0; }

/* Узкий экран: перечень уходит наверх обычным списком, липкость снимается —
   иначе он занимал бы половину высоты. */
@media (max-width: 900px) {
    /* Та же ловушка, что у `.tsheet`: голое `1fr` не даёт колонке сжаться
       уже содержимого, и широкий блок кода в правилах уносит страницу вбок. */
    .docs { grid-template-columns: minmax(0, 1fr); }
    .docs__nav { position: static; }
}

.doc-note { margin-bottom: 14px; }

/* Пояснения одной системы: подпись пункта и обычный текст рядом. Раньше
   подпись была жирной внутри абзаца и читалась как «это важнее остального». */

.notes {
    display: grid;
    gap: 8px 16px;
    margin: 0 0 14px;
}

@media (min-width: 760px) {
    .notes { grid-template-columns: 160px minmax(0, 1fr); }
    .notes__label { padding-top: 1px; }
}

.notes__label {
    font: 400 11px/1.5 var(--sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

.notes__text {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--ink-2);
}

/* Подписи файла: кто пишет, когда читается, чем является. Без них редактор —
   девять одинаковых полей, и человек правит не тот файл. */
.docmeta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px 20px;
    padding: 14px 16px;
    margin-bottom: 14px;
    background: var(--sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.docmeta__item { display: flex; flex-direction: column; gap: 3px; }
.docmeta__value { font-size: 13px; font-weight: 500; }
.docmeta__note { font-size: 11.5px; line-height: 1.45; color: var(--ink-3); }

/* Кандидат в память: заголовок, сам факт, откуда взят, решение. */
/* Метка назначения — строкой выше заголовка, обе прижаты влево. */
.cand__head {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin-bottom: 8px;
}

.cand__title { font-weight: 500; font-size: 13.5px; }
.cand__body { margin: 0 0 6px; font-size: 13px; line-height: 1.6; }
.cand__meta { margin: 0 0 10px; font-size: 11px; color: var(--ink-3); }

/* Ряд решений: кнопки одного размера, выровненные по одной линии.
   .button и .button--ghost различаются кеглем и отступами, поэтому в этом
   ряду размеры задаются явно — иначе край получается ступенькой. */
.cand__actions {
    display: flex;
    align-items: stretch;
    gap: 8px;
    flex-wrap: wrap;
}

.cand__actions form { display: flex; }

.cand__act {
    min-width: 132px;
    padding: 9px 14px;
    font: 500 13px/1.2 var(--sans);
    text-align: center;
}

.cand--postponed { background: var(--sunk); }

/* Правят руками и читают глазами: моноширинный и высокая строка. */
.doc-edit {
    width: 100%;
    min-height: 420px;
    padding: 12px 14px;
    font: 400 13px/1.6 var(--sans);
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    resize: vertical;
    tab-size: 4;
}

.doc-edit:focus { border-color: var(--ozon); }
.doc-edit[readonly] { background: var(--paper); color: var(--ink-2); }

.doc-restore { display: inline-block; margin-left: 10px; }

/* Мелочи */

.stack > * + * { margin-top: 12px; }

.row-actions {
    display: flex;
    gap: 6px;
    justify-content: flex-end;
    flex-wrap: nowrap;
}

.empty {
    padding: 28px 18px;
    text-align: center;
    color: var(--ink-3);
    font-size: 13px;
}

/* Узкий экран: панель не ужимается до значков, а полностью уезжает за край и
   выезжает поверх листа по кнопке. Сворачивание там смысла не имеет — места
   нет ни под то, ни под другое. */

@media (max-width: 900px) {
    .shell { display: block; }

    .side {
        position: fixed;
        left: 0;
        bottom: 0;
        width: var(--side);
        max-width: var(--side);
        transform: translateX(-100%);
        box-shadow: 0 6px 20px rgba(0, 0, 0, .14);
    }

    /* Левитация — только на широком экране (IR-66). Здесь панель не стоит
       рядом с листом, а уезжает за край и выезжает поверх него: с отступом
       в 14px `translateX(-100%)` не увозит её целиком — у левого края
       остаётся торчать полоска ровно в этот отступ. Скругление и стекло
       снимаем заодно: панель тут во весь экран по высоте и ничего собой не
       накрывает. */
    :root .side {
        margin: 0;
        top: 0;
        height: 100vh;
        border: none;
        border-radius: 0;
        background: var(--side-bg);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        box-shadow: 0 6px 20px rgba(0, 0, 0, .14);
    }

    :root .side__top { border-bottom-color: var(--side-line); }
    :root .side__foot { border-top-color: var(--side-line); }

    .content { margin-left: 0; }

    /* Класс свёрнутости на узком экране не действует: он про широкий макет. */
    .is-narrow .side { width: var(--side); }
    .is-narrow .side__label { display: revert; }
    .is-narrow .side__top { justify-content: flex-start; padding: 12px 16px; }
    .is-narrow .side__nav { padding: 10px; align-items: stretch; }
    .is-narrow .side__item,
    .is-narrow .side__link { justify-content: flex-start; width: 100%; padding: 8px 10px; }
    .is-narrow .side__link { padding: 6px 10px; }
    .is-narrow .side__sub {
        margin: 2px 0 2px 19px;
        padding: 0 0 0 10px;
        border-left: 1px solid var(--side-line);
        border-top: none;
        align-items: stretch;
    }
    .is-narrow .whoami { display: flex; }
    /* Панель тут всегда полной ширины, поэтому подвал возвращается в ряд. */
    .is-narrow .side__foot { flex-direction: row; padding: 10px; }
    .is-narrow .side__foot-end { margin-left: auto; }

    .is-open .side { transform: translateX(0); }

    .is-open .scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 15;
        width: 100%;
        padding: 0;
        border: none;
        background: var(--scrim);
        cursor: pointer;
    }

    .handle {
        display: flex;
        align-items: center;
        gap: 10px;
        position: sticky;
        top: 0;
        z-index: 10;
        padding: 8px 16px;
        background: var(--side-bg);
        color: var(--ink);
        border-bottom: 1px solid var(--side-line);
    }

    .handle__button {
        display: grid;
        place-items: center;
        width: 32px;
        height: 32px;
        padding: 0;
        background: none;
        border: none;
        border-radius: var(--radius);
        color: var(--side-ink);
        cursor: pointer;
    }

    .handle__button:hover { color: var(--ink); background: var(--side-hover); }

    .handle__title {
        font: 500 12px/1 var(--sans);
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--ink-2);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

@media (max-width: 600px) {
    /* Кроме листа без обвязки: у него `padding: 0` стоит осознанно, а это
       правило перебивало его на телефоне — экран входа получал светлые
       поля по бокам, и тёмный вестибюль переставал доходить до края
       (снято 31.08.2026). Отступы внутри там задаёт сама `.gate`. */
    .page:not(.page--bare) { padding-left: 16px; padding-right: 16px; }
    .figure { font-size: 25px; }
    th, td { padding-left: 12px; padding-right: 12px; }
    /* iOS Safari принудительно зумит страницу при фокусе в поле со шрифтом
       меньше 16px — на телефоне поля укрупняются, вёрстка не страдает. */
    input[type="text"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    input[type="search"],
    textarea,
    select { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
}

/* Задачи.

   Список плотный и сгруппирован по статусу: в трекер приходят с вопросом
   «что сейчас в работе», и ответ должен читаться до всякой прокрутки.
   Цвет несёт состояние: значки статуса, горящий срок, срочность, аватары.
   Внутри группы сортировка детерминированная — приоритет, срок, номер. */

.row-actions--left { justify-content: flex-start; }


/* Строка действий под полем комментария: слева созидательное, справа
   удаление. Раздел «Удаление» внизу карточки убран — он обещал целый сюжет
   там, где одна кнопка, и уводил её от места, где решают судьбу задачи. */
.taskdo { align-items: center; }

/* Правая пара — «Отправить боту» и «Удалить задачу» (IR-36). Отступ висит на
   обёртке, а не на последней кнопке: состав пары зависит от прав смотрящего,
   и `margin-left: auto` пришлось бы переносить с одной кнопки на другую. */
.taskdo__side {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* Предупреждение живёт под своей кнопкой, а не под всей строкой. */
/* Пояснение под удалением прижато к той же стороне, что и сама кнопка:
   обе стоят справа, под строкой времён. */
.taskdo__note { margin: 8px 0 0; text-align: left; }

/* Удаление стоит под лентой, вне панели, а равняется на «Опубликовать»
   внутри неё (правка владельца 02.09.2026; до этого равнялось на «Отправить
   боту» справа). Отступа не нужно: блок и так начинается на левой границе
   кнопки. */
/* Удаление стоит под обсуждением и выровнено по левому краю «Опубликовать»
   (правка владельца 02.09.2026). Отступа нет: замер показал, что блок и так
   начинается там же, где кнопка публикации, — прежние 19px были поправкой на
   поле панели, но она нужна была прижатому вправо, а не выровненному влево. */
.taskkill {
    margin: 0 0 var(--gap);
    text-align: left;
}

.taskkill form { display: flex; justify-content: flex-start; }
.empty--tight { padding: 14px 18px; text-align: left; }

.panel--form { padding: 16px 18px; }

.field--date { flex: 0 1 150px; }

/* Фильтры: пресеты-чипы, состояние в URL. Активный чип — «нажат» тем же
   приёмом, что и текущий раздел панели: белый лист с рамкой. */

/* Дашборд внутри каркаса. Рамки вокруг нет намеренно: содержимое дашборда и
   так собрано из панелей со своими границами, и ещё одна коробка вокруг
   читалась как лишний слой.

   Высоту задаёт скрипт по содержимому (dash.js) — иначе внутри появлялась
   своя полоса прокрутки, и страница скроллилась дважды: сначала рамка, потом
   лист. Значение здесь — только на случай, если скрипт не отработал. */

/* Поля дашборда должны совпасть с полями других разделов. Считается так:
   у листа .page отступ по бокам 24px, а дашборд внутри добавляет свои 20px
   (.ird-shell: max-width 1200, padding 0 20px). Вместе выходило 44px — заметно
   больше, чем в остальных разделах.

   Вытягиваем рамку ровно на внутренний отступ дашборда: тогда его содержимое
   начинается там же, где текст на любой другой странице. Само содержимое при
   этом не трогается — только рамка вокруг него. */
.dashframe {
    display: block;
    --ird-pad: 20px;
    margin-left: calc(-1 * var(--ird-pad));
    margin-right: calc(-1 * var(--ird-pad));
}

/* У трекера экспериментов внутренний отступ листа не 20, а 28 — вытягиваем
   рамку ровно на него, иначе его текст стоит с двойным полем и не совпадает
   с остальными разделами. */
.dashframe--ab { --ird-pad: 28px; }

.dashframe__view {
    display: block;
    width: 100%;
    height: 1200px;
    border: 0;
    overflow: hidden;
}

/* Страница, собранная агентом (01.09.2026). Рамка своя, а не `.dashframe`: у
   той поля вытянуты под вёрстку дашборда, а здесь разметку пишет модель — она
   про наши отступы не знает и рисует от края. */
.toolframe {
    display: block;
    width: 100%;
    height: 900px;
    margin-top: 16px;
    border: 1px solid var(--line);
    /* Общий радиус, а не своё число: 12px стояло с 01.09 мимо эталона
       стилей, выровнено по решению Антона 05.09.2026. */
    border-radius: var(--radius);
    background: #fff;
}

/* Строка под чертой раздела: слева счётчики, справа поиск.
   Заведение задачи ушло наверх, в саму шапку.

   С 22.08.2026 оба свёрнуты до значка: плюс в шапке и лупа в строке. Размер
   у них один на двоих — они стоят в разных контейнерах, друг от друга ничего
   не наследуют, и заданные по отдельности разъедутся при первой правке.
   Раскрытый поиск по-прежнему растёт влево до `--task-search-w`. */
:root {
    --ctl-round: 30px;
    --task-search-w: 320px;
}

/* Ширина колонки дедлайна — одна на все состояния. Пустое поле по природе
   уже заполненного, и без общей ширины ряд задач без срока пилил бы колонку
   на две. Значение берут и сетка строки, и само поле.

   Считается по самому длинному виду и не больше: «20.08.26 14:30» — это
   четырнадцать знаков моноширинного 12px плюс отступы. С 22.08.2026 слева от
   даты стоит значок календаря (срок показан той же плашкой, что в карточке),
   и его ширина с зазором заложена сюда же — иначе дата обрезается
   многоточием на самом частом виде.

   01.09.2026 сужена со 142px: замер в браузере показал, что заполненная
   плашка занимает 125px, то есть семнадцать пикселей колонки стояли пустыми
   и читались лишним отступом между сроком и статусом (зазор сетки 10px там
   выглядел как 27). Число берётся из замера, а не на глаз: правите вид
   плашки — перемеряйте, иначе дата снова начнёт обрезаться. */
:root { --task-due-w: 126px; }

/* Колонка номера. Была 52px — ровно под старую подпись `IR-1234`: семь знаков
   табличными цифрами по 12px. С 22.08.2026 в списке короткая подпись `#1234`,
   а колонка осталась прежней, и лишние пиксели читались отступом до названия.

   Подгонять её числом бесполезно: под сегодняшний `#20` — развалится, когда
   номера подрастут; под будущий `#1234` — сегодня остаётся два десятка пустых
   пикселей. Поэтому ширину задаёт СПИСОК: `tasks.board` считает самый длинный
   показанный номер, шаблон вешает класс, класс подставляет ширину. Колонка
   по-прежнему фиксирована, строки выровнены между собой, но занимают место по
   факту и расширяются сами в тот день, когда номера станут длиннее.

   Величины две, и это важно. `--task-key-w` — место под сами знаки, 7px на
   знак: столько занимает табличная цифра при 12px. `--task-key-gap` — воздух
   после номера, и он ОДИН для всех длин: колонка растёт вместе с номером, а
   отступ до названия остаётся тем же. Крутить расстояние надо им.

   Полный зазор до заголовка = `--task-key-gap` плюс зазор сетки (10px, на
   узком экране 8). 23.08.2026 добавку подняли с трёх пикселей до шестнадцати:
   на трёх номер и название читались одним словом.

   Значение `--task-key-w` здесь — запасное, на случай списка без класса. */
:root {
    --task-key-w: 38px;
    --task-key-gap: 16px;
}

/* Двузначный номер занимает место трёхзначного намеренно: от ширины этой
   колонки считается левая вертикаль всей страницы (`--content-x`), и без
   закрепления она ползла бы каждый раз, когда номера прибавляют разряд
   (25.08.2026). */
.board--key2 { --task-key-w: 21px; }
.board--key3 { --task-key-w: 21px; }
.board--key4 { --task-key-w: 28px; }
.board--key5 { --task-key-w: 35px; }
.board--key6 { --task-key-w: 42px; }


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

   Ширину держит ФОРМА, а не поле внутри. Раньше 320 пикселей стояли на самом
   поле, а `max-width: 100%` считался от формы, которая сама была шириной в
   это поле, — то есть ограничение было само от себя и не ограничивало ничего.
   На телефоне раскрытый поиск вылезал за экран. Теперь размер у формы, она
   обычный сжимаемый элемент строки, и `min-width: 0` позволяет ей стать уже
   своего содержимого. */
.tsearch {
    flex: 0 1 auto;
    min-width: 0;
    width: var(--ctl-round);
    /* Анимацию раскрытия 31.08.2026 снимали как «задержку» и тем же днём
       вернули решением владельца: мгновенный скачок ширины хуже плавного. */
    transition: width 0.16s ease;
}

/* Набранный запрос держит поле раскрытым и без фокуса — иначе непонятно,
   почему список короткий. */
.tsearch:focus-within,
.tsearch:has(input[type="search"]:not(:placeholder-shown)) { width: var(--task-search-w); }

/* Лупа и поле — в одном `<label>`: щелчок по значку ставит курсор в поле,
   а фокус в поле раскрывает форму. Ни строки скрипта, и CSP ни при чём. */
.tsearch__box {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    height: var(--ctl-round);
    padding: 0 8px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill, 999px);
    background: none;
    cursor: text;
}

.tsearch__box:hover,
.tsearch:focus-within .tsearch__box,
.tsearch:has(input[type="search"]:not(:placeholder-shown)) .tsearch__box {
    border-color: var(--rule-strong);
    background: var(--card);
}

.tsearch__ico {
    display: inline-flex;
    flex: none;
    color: var(--muted);
}

.tsearch:focus-within .tsearch__ico { color: var(--ink); }

/* В покое поле шириной в ноль: видна одна лупа. Не `display: none` — иначе
   набранный запрос исчезал бы вместе с полем, а он держит форму раскрытой. */
.tsearch input[type="search"] {
    font: 400 13px/1.4 var(--sans);
    color: var(--ink);
    background: none;
    border: 0;
    padding: 0;
    width: 0;
    min-width: 0;
    flex: 1 1 auto;
}

.tsearch input[type="search"]:focus { outline: none; }

/* Значок «очистить» у type=search в покое занимал бы место в свёрнутом виде. */
.tsearch input[type="search"]::-webkit-search-cancel-button { display: none; }

@media (max-width: 640px) {
    .tsearch:focus-within,
    .tsearch:has(input[type="search"]:not(:placeholder-shown)) { width: 190px; }
    /* Общее правило про 16px на телефоне (см. `@media(max-width:600px)`
       выше по файлу, у `input[type="search"]` без класса) сюда не достаёт:
       это поле весит больше голым классом `.tsearch` и, стоя ПОЗЖЕ того
       правила в файле, молча его перебивает при равном весе — при чём тут
       порядок, а не медиа-условие. Открытое здесь, рядом со своим же
       правилом, побеждает без вопросов (живой случай 26.08.2026: «на
       смартфоне при клике в поле поиска — зум страницы»). */
    .tsearch input[type="search"] { font-size: 16px; }
}


/* Ряды «Кто / Срок / Метки» стоят плотнее друг к другу: это одна группа
   отбора, и воздух между ними читался как разрыв. Зато вся группа отодвинута
   от строки со счётчиками выше — там граница смысла, а не внутри. */
.fbar { margin: 14px 0 var(--gap); }

.fbar__row {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

/* Отбираемая часть строки — своим флекс-контейнером (правка владельца
   27.08.2026, срочный фикс). Держит три оси отбора (Исполнитель/Дедлайн/
   Метка) и крестик сброса; «Вид» и ряд действий (лупа, шестерёнка, плюс) —
   в `.fbar__acts` правее, вне этого контейнера (тем же днём «Вид» переехал
   туда из конца `#filters-body` — своей распорки-`.fbar__gap`, которая
   раньше толкала его к правому краю контейнера, здесь больше нет: крестику
   сброса, единственному, что осталось последним, толкать уже некого).

   `flex: 1` тянет его до самого ряда действий (`.fbar__acts`) справа —
   растёт контейнер, а не то, что внутри него, и на узком экране это тот же
   приём, что и раньше. `min-width: 0` — тот же приём, что у переноса
   длинных заголовков: без него флекс-элемент не сжимается меньше своего
   содержимого и распирает строку. */
.fbar__body {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
    flex: 1;
    min-width: 0;
}

/* Ряд фильтров задач сдвинут влево полем — раньше вслед за ТЕКСТОМ
   заголовка «Задачи» (убран целиком 27.08.2026, правка владельца: см.
   комментарий у `.tasks-topbar` в `tasks.html`), теперь напрямую вслед за
   строкой задачи, потому что первым пунктом ряда с того же дня стоит
   счётчик (`.tcount`) — а он метит не в заголовок, а в КОЛОНКУ НОМЕРА
   строки (`.trow__key`), той же шириной (`--task-key-w` + `--task-key-gap`,
   правило у `.tcount` выше). Слагаемые те же, что у номера в `.trow`
   (`grid-template-columns`) и у `--content-x` ниже по файлу: поле строки
   28 + значок статуса 16 + зазор 10 — не подбираются числом отдельно.

   Дальше в ряду («Исполнитель» и прочие оси отбора, `#filters-body`) цель
   другая — ТЕКСТ первой плашки под ТЕКСТОМ заголовка строки задачи
   (`--content-x`), а не под номером, — поэтому свой отступ у него, ниже.

   На узком экране (до 900px) `-9px` не трогали: он был откалиброван под
   заголовок «Задачи», которого больше нет, но проверки той же формулы на
   телефоне для нового счётчика-первым-пунктом ещё не было, а менять
   вслепую сразу оба диапазона рискованнее, чем оставить рабочий как есть.

   Только `.tasks-filters .fbar__row`, а не общий `.fbar__row`: у
   «Лаборатории», «Работ» и «Переработки» первый элемент строки — текстовая
   подпись `.fbar__caption` без рамки и своего заголовка с `--content-x`, и
   тот же сдвиг увёл бы её от места, а не приблизил. */
.tasks-filters .fbar__row { margin-left: -9px; }
@media (min-width: 900px) {
    .tasks-filters .fbar__row { margin-left: calc(28px + 16px + 10px); }

    /* `#filters-body` — своя поправка поверх `gap: 4px` ряда, а не вместо
       неё. Плашка «Исполнитель» несёт рамку и поле до текста в 9px (см.
       `.fmenu__sum`), и после колонки счётчика до текста заголовка строки
       должно остаться ровно `--content-x` минус то, что уже отработал
       `margin-left` ряда, минус эти 9px. Арифметика сама сводится к
       постоянной −3px (28+16+10 совпадает с хвостом `--content-x` после
       вычета своей же колонки счётчика — переменные `--task-key-w` и
       `--task-key-gap` в разности сокращаются), поэтому число здесь не
       переменная, а именно число — как и «−9px» рядом. */
    #filters-body { margin-left: -3px; }
}

.fbar__caption {
    flex: none;
    display: inline-flex;
    align-items: center;
    /* Тот же зазор значок→текст, что в заголовке группы. */
    gap: 8px;
    margin-right: 4px;
    /* 14 значок + 8 зазор + 44 подпись */
    width: 66px;
}

/* Слот значка есть у всех трёх подписей, у «Кто» и «Срока» он пуст. */
.fbar__ico {
    flex: none;
    display: inline-flex;
    width: 14px;
    height: 14px;
}

/* Ось отбора — выпадающим меню, тем же, что выбор исполнителя в строке.
   Раньше это был ряд чипов на строку: имена людей и словарь меток растут, ряды
   переносились, и панель занимала треть экрана до первой задачи. Системный
   `select` эту беду убрал, но рядом с меню строки читался чужим — своя
   стрелка, свой шрифт, свои отступы в каждой ОС. */

/* `display: flex` здесь, а не только на самой плашке, — иначе `<details>`
   заворачивает `<summary>` в анонимную строчную рамку по метрикам шрифта
   страницы (15px/1.5 ≈ 22,5px), а не по росту самой плашки: `.fmenu__sum`
   выравнивался по базовой линии этой невидимой строки и стоял на пару
   пикселей выше центра ряда — «на глаз почти незаметно», а числом видно
   сразу (правка владельца 26.08.2026, живая проверка — плашка и крестик
   сброса на разной высоте). Флекс-контейнер меряет высоту по реальному
   ребёнку, и разница пропадает. */
.fmenu { position: relative; flex: none; display: flex; align-items: center; }
.fmenu[open] { z-index: 30; }

/* Кегль и поля те же, что у метки и срока в строке задачи (правка
   владельца 26.08.2026: были заметно крупнее соседей на доске).

   Эталон высоты — не метка, а поле срока в строке (`.dline .dline__at`,
   26,2px: рамка 1 + поле 4 + строка 12×1,35). Первая попытка ровнялась по
   `.lbl` (20,5px) — тем же днём владелец указал на само поле как на образец,
   и метка тоже подгоняется под него, а не наоборот. `line-height` — числом,
   а не множителем: кегль здесь 12px, у `.lbl` 11px, и одна и та же «1.35»
   дала бы разные пиксели на разных кеглях. */
.fmenu__sum {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 8px;
    line-height: 16.2px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--card);
    color: var(--ink);
    font-size: 12px;
    list-style: none;
    cursor: pointer;
    white-space: nowrap;
}

.fmenu__sum::-webkit-details-marker { display: none; }
.fmenu__sum::marker { content: ""; }
.fmenu__sum:hover { border-color: var(--accent); }
.fmenu[open] .fmenu__sum { border-color: var(--accent); }

/* Аватар исполнителя внутри плашки — тем же приёмом, что у `.prop .av`:
   базовые 22px рассчитаны на строку задачи, а не на плашку в 20.5px, и
   портрет тянул бы её выше текста и метки рядом. 16px — крупнее не
   помещается без остатка. */
.fmenu__sum .av { width: 16px; height: 16px; font-size: 7px; }

/* Отбор по этой оси уже что-то выбрал (не «любой») — заливаем тем же серым,
   что полоса статуса («На ревью», «В работе»): отбор такое же состояние
   доски, и обе плашки должны читаться одним языком (правка владельца
   26.08.2026). `--surface-2` — та же переменная, что держит цвет полосы по
   умолчанию; точный оттенок светлой темы у полосы жёстко перекрашен ниже по
   файлу (см. «Перекраска светлой темы»), и плашка фильтра копирует ЕГО же
   числом, а не переменной — так оба остаются одного цвета в обеих темах. */
.fmenu__sum--on { background: var(--surface-2); }

/* Значение может быть длинным именем или меткой: обрезаем на кнопке, а не
   растягиваем строку фильтров. Целиком оно всё равно видно в самом меню. */
.fmenu__now { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }

/* Исключённое значение на плашке — человек или срок (Антон, 07.09.2026):
   зачёркнуто и приглушено, тем же знаком «кроме», что у исключённой метки
   (`.lbl--off`). Лицо рядом с зачёркнутым именем гаснет той же
   прозрачностью, что чип. */
.fmenu__now--off { color: var(--ink-2); text-decoration: line-through; }
.fmenu__face { display: inline-flex; }
.fmenu__face--off { opacity: .5; }

/* Метки на плашке отбора — внутри того же прямоугольника, что и текстовое
   значение соседних фильтров. Ряд, а не одна метка: несколько выбранных встают
   в строку через тот же зазор, каким метки разделены в строке задачи.

   Обрезки по ширине здесь нет намеренно, в отличие от `.fmenu__now`: имя
   человека можно оборвать многоточием и остаться понятым, а обрезанная метка
   выглядит сломанной вёрсткой. Плашка растёт, а ряд фильтров переносится —
   `.fbar__row` это умеет. */
.fmenu__chips {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.fmenu__pop {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 5;
    width: max-content;
    min-width: 100%;
    max-width: min(320px, 84vw);
    max-height: 280px;
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
    text-align: left;
}

/* Пункт — строка с местом под аватарку. Отбор по человеку узнают по лицу
   быстрее, чем по имени, и у пунктов без человека (срок, метка) место просто
   не занято: `gap` работает только между тем, что есть. */
.fmenu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    border-radius: var(--radius-ctl);
    color: var(--ink);
    font-size: 13px;
    text-decoration: none;
    white-space: nowrap;
}

.fmenu__item:hover { background: var(--hair); }
/* Выбранный пункт обязан читаться на любом фоне меню (владелец 31.08.2026:
   «если метка включена... она должна быть помечена выбранной» — отметка
   СТОЯЛА, но её `--paper` на стеклянном float-bg сливался в невидимость;
   на прежнем --card он был едва различим и раньше). Язык тот же, что у
   выбранного пункта панели (.side__item--on): заливка плюс внутреннее
   кольцо. Цвет — `--hair`, а не `--sunk`/`--rule-strong` (второй заход
   владельца тем же днём: «убери фиолетовый оттенок, используй светло-серый
   из уже существующих»): вся палитра поверхностей несёт фиолетовый подтон,
   а `--hair` — нейтральная альфа от чернил, светло-серый на любом фоне и
   в обеих темах; кольцо тем же цветом складывается с заливкой в более
   тёмную кромку само. Наведение — той же заливкой без кольца: кольцо и
   насыщенность отличают выбранное от наведённого. */
.fmenu__item--on {
    font-weight: 500;
    background: var(--hair);
    box-shadow: inset 0 0 0 1px var(--hair);
}

/* Исключённая метка (двойной щелчок, Антон 07.09.2026): та же отметка
   «выбрано», что у `--on`, — состояние оси такое же осознанное, — но текст
   зачёркнут и приглушён: «кроме этого» читается без слов. Тех же цветов,
   новых нет. */
.fmenu__item--off {
    background: var(--hair);
    box-shadow: inset 0 0 0 1px var(--hair);
    color: var(--ink-2);
    text-decoration: line-through;
}

/* Подсказка о жесте внизу меню меток — мельче пунктов и серым, чтобы не
   читалась как ещё один пункт. */
.fmenu__hint {
    padding: 6px 8px 2px;
    color: var(--ink-2);
    font-size: 11px;
    white-space: nowrap;
}

/* Пункт меню сортировки — та же плашка, но `<button>`, а не `<a>`: адреса у
   порядка внутри группы нет, ссылка вела бы в никуда. Сброс — то немногое,
   что браузер даёт кнопке от себя и чего нет у ссылки. */
button.fmenu__item {
    width: 100%;
    border: none;
    background: none;
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}
button.fmenu__item:hover { background: var(--paper); }

/* Меню «Подзадачи» (правка владельца 27.08.2026): вместо двух отдельных
   плашек-ссылок в ряду фильтров — один значок, а внутри поповера строка
   «подпись + тумблер», тем же приёмом clickable-row, что у `.fmenu__item`
   (вся строка — ссылка, а не только кружок справа). Второй тумблер
   («Вложенность подзадач») в разметке появляется, только когда первый
   включён (Jinja, tasks.html) — тот же порядок, что был у чипов. */
.fmenu__toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 5px 8px;
    border-radius: var(--radius-ctl);
    color: var(--ink);
    font-size: 13px;
    text-decoration: none;
    white-space: nowrap;
}
.fmenu__toggle:hover { background: var(--paper); }
.fmenu__toggle + .fmenu__toggle { margin-top: 1px; }

/* «В строке видно» (живой отзыв владельца 28.08.2026) — тот же ряд
   «подпись + тумблер», но кнопкой, а не ссылкой: адреса у видимости колонки
   нет, это отображение, не срез (тот же довод, что у кнопок сортировки,
   `button.fmenu__item` выше). Сброс — то немногое, что браузер даёт кнопке
   от себя и чего нет у ссылки. */
button.fmenu__toggle {
    width: 100%;
    border: none;
    background: none;
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}
button.fmenu__toggle:hover { background: var(--paper); }

/* Тумблер — тот же приём, что у остальных фильтров: состояние целиком в
   адресе, переключает обычная ссылка, а не скрипт. Форма — по образцу
   iOS/большинства систем: дорожка вдвое больше кружка, кружок с отступом 2
   по бокам, сдвиг на разницу.

   Размер вдвое меньше первой версии (32×18/14, живой отзыв владельца
   27.08.2026: «тумблеры в меню настроек отображения мельче в 2 раза») —
   в поповере «Вид» они соседствуют с 13px текстом строки, и дорожка
   ростом с саму строку читалась крупнее самого пункта меню. */
.tgl {
    position: relative;
    flex: none;
    width: 16px;
    height: 9px;
    border-radius: 999px;
    background: var(--rule-strong);
}
.tgl__thumb {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--card);
    transition: transform .15s;
}
.tgl--on { background: var(--accent); }
.tgl--on .tgl__thumb { transform: translateX(7px); }

/* Меню «Вид» — в ряду действий (`.fbar__acts`) правее лупы, у самого
   правого края строки; открытое от левого края поповера (умолчание
   `.fmenu__pop`) оно вылезало за границу экрана (живой случай 27.08.2026,
   тогда меню ещё стояло в `#filters-body`, но правило то же и после
   переезда в `.fbar__acts` тем же днём — оно всё так же последнее и всё
   так же крайнее). Тот же приём, что у `.res__menu > .fmenu__pop` ниже:
   открывается от правого края и растёт влево, а не вправо. Новый класс под
   это не заводили — единственный `<details class="fmenu">` в `.fbar__acts`,
   селектор находит его и без отдельной отметки. */
.fbar__acts details.fmenu > .fmenu__pop {
    left: auto;
    right: 0;
    /* Список сортировки всплывает НАД содержимым поповера отдельным слоем
       (`.sortbox__pop` ниже, живой отзыв владельца 28.08.2026: «должно
       выпадать новым слоем, а не растягивать родительское») — а не
       раздвигает поток, как было. Умолчание `.fmenu__pop` — `overflow-y:
       auto; max-height: 280px`, нужное «Метке» с длинным списком меток;
       здесь же обрезало бы всплывший слой точно так, как раньше обрезал
       поток. Снимаем безопасно: содержимое этого конкретного поповера
       (переключатель вида, три тумблера, строка сортировки) короткое и
       никогда не подпирает потолок в 280px. */
    overflow: visible;
    max-height: none;
    /* Ширина не гуляет от выбранного поля сортировки (владелец 31.08.2026:
       «меню скачет в зависимости от ширины... показывать сразу максимально
       необходимую»): у поповера width: max-content, самая широкая строка —
       строка сортировки, а её плашка меняет длину («ID» против
       «Исполнитель»), и правый край к тому же прибит — менялся левый.
       260px — самое широкое состояние (плашка «Исполнитель», замер
       браузером агента 31.08.2026: 250.6px) с запасом; min-, а не width:
       строке «Вложенность подзадач» и будущим пунктам расти не запрещено,
       их ширина от выбора поля не зависит. */
    min-width: 260px;
}

/* Переключатель вида (список/доска) внутри «Вид» — пара уже готовых чипов
   (`.chips`/`.chip`/`.chip--on`, тот же приём, что у «Метки» в отборе), но
   со значком перед словом: голому чипу такого сочетания раньше не
   требовалось, и своего выравнивания у него не было — текст и SVG вставали
   по базовой линии вразнобой. Правило узкое, только эта пара, а не все
   чипы сайта: остальные `.chip` — однословные, инлайновая строка их не
   портит.

   Обёртка растянута на ширину поповера, чипы делят её поровну, содержимое
   по центру (владелец 31.08.2026: «Списком и Доска должны быть по центру,
   кнопки во всю ширину доступного поля»). Умолчание `.chips` —
   inline-flex по содержимому, здесь оно оставляло пару прижатой влево на
   фоне 260px меню. */
.fbar__acts details.fmenu .chips {
    display: flex;
    /* Верхний воздух — оптический, а не боксовый (владелец 31.08.2026:
       «отступ сверху должен быть такой же, как с боков»). Боксовые зазоры
       и до правки были равны — по 7px со всех сторон, — но растянутая
       пилюля идёт длинной ПЛОСКОЙ кромкой параллельно краю меню и на 7px
       читается впритык, а её полностью скруглённые бока (радиус 999 при
       высоте ~31px) сами добавляют себе воздуха. +4px сверху (итого 11px
       против 7 боковых) выравнивают на глаз; замерено и снято браузером
       агента 31.08.2026. Менять паддинг всего поповера нельзя — поехали бы
       остальные строки. */
    margin-top: 4px;
}

.fbar__acts details.fmenu .chips .chip {
    display: inline-flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

/* Черта-разделитель перед «Сортировкой» и перед «В строке видно» — тремя
   разными по природе группами пунктов одного поповера (переключатель вида
   + тумблеры сверху, порядок, видимость колонок). Тем же приёмом, что у
   «Убрать исполнителя» в меню «Исполнитель» (`.asgmenu__item--clear`):
   граница и отступ на самом блоке, а не отдельный `<hr>`.

   Перед тумблерами (первый — «Закрытые задачи») черты больше нет (живой
   отзыв владельца 28.08.2026: «прилипает сильно рано» — то есть черта
   стояла сразу под переключателем вида, а тумблеры и так отделены от него
   собственным `gap` строки; было `.fmenu__toggle:first-of-type` в этом же
   правиле). */
.fbar__acts details.fmenu .sortbox,
.fbar__acts details.fmenu .rowvis {
    margin-top: 4px;
    border-top: 1px solid var(--rule);
    padding-top: 8px;
}

/* Строка «Сортировка» стоит между двумя отчерками, и зазоры до них обязаны
   совпадать (владелец 31.08.2026: «сверху и снизу отступы разные»): сверху
   её отделяли 8px паддинга своего блока, снизу — только 4px общего
   margin-top у «В строке видно», и 9 против 4 было видно глазом. У
   `.sortbox` те же 4px оставлены нарочно: над ЕГО отчерком стоит тумблер
   со своим внутренним полем, и воздух там складывается из двух частей. */
.fbar__acts details.fmenu .rowvis { margin-top: 8px; }

/* «В строке видно» плашками (Антон, 23.09.2026: «заменить тумблеры на
   скруглённые прямоугольники, примерно как фильтры над списком задач… идут
   в строку одна за одной, не входят в ширину — переносятся»). Плашка —
   размеры и рамка `.fmenu__sum`; включённая залита тем же серым, что отбор
   в фильтре (`.fmenu__sum--on`), выключенная пустая и с приглушённым
   текстом — колонки в строке нет, и плашка это говорит. Подпись раздела
   стоит над рядом, с тем же полем слева, что у пунктов меню. */
.rowvis .eyebrow { display: block; padding: 0 8px; }
.rowvis__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
    padding: 0 8px 4px;
}
:where(.rvchip) { font: inherit; }
.rvchip {
    display: inline-flex;
    align-items: center;
    padding: 4px 8px;
    line-height: 16.2px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: none;
    color: var(--muted);
    font-size: 12px;
    white-space: nowrap;
    cursor: pointer;
}
.rvchip:hover { border-color: var(--accent); }
.rvchip--on { background: var(--surface-2); color: var(--ink); }

/* `.sortbox__pop` (см. ниже) позиционируется абсолютно от этой обёртки —
   `.rowvis` того же не просит, у неё нет всплывающего слоя. */
.fbar__acts details.fmenu .sortbox {
    position: relative;
}

/* ── Сортировка — настоящий выпадающий список, а не всегда открытый ряд
   кнопок (живой отзыв владельца 28.08.2026, со скриншотом настроек
   Linear — «Ordering»: подпись слева, справа плашка с текущим значением
   и стрелкой, сам список раскрывается по клику). Раньше здесь стояла
   подпись `.eyebrow` и сразу под ней четыре всегда видных `.fmenu__item`
   — не читалось как список, пока не наткнёшься на первую кнопку.

   Разворачивание — скрытым чекбоксом с подписью, тем же приёмом, что у
   описания задачи (`.fold__box`/`desc-fold`) и правки реплики: `<details>`
   внутри уже открытого `<details class="fmenu">` пришлось бы городить
   заново весь свой JS закрытия по щелчку мимо (`tasks.js`, `closeMenus`
   считает вложенность по точному совпадению, а не по предкам) — чекбокс в
   эту систему не входит вовсе и ничего не ломает. Список — плашка
   `.sortbox__now` того же вида, что `.fmenu__sum` у осей отбора выше;
   текущее значение вписывает `tasks.js` (`mark()`), сам чекбокс сбрасывает
   он же при выборе — открытым список остаётся ровно до ответа. */
/* Строка из двух элементов: <label> открытия списка (растёт на всю ширину)
   и кнопка направления ПОСЛЕ него (владелец 31.08.2026). Кнопка вынесена из
   label намеренно: щелчок по интерактивному элементу внутри label браузеры
   к чекбоксу не пересылают, но полагаться на это тонко — сосед надёжнее. */
.sortbox__head {
    display: flex;
    align-items: center;
    gap: 4px;
}

.sortbox__row {
    display: flex;
    flex: 1;
    min-width: 0;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 5px 8px;
    border-radius: var(--radius-ctl);
    color: var(--ink);
    font-size: 13px;
    cursor: pointer;
}
.sortbox__row:hover { background: var(--paper); }

/* Направление сортировки: стрелка вниз — по убыванию, вверх — по
   возрастанию. Смысл направления у каждого поля свой (приоритет по
   умолчанию «важное сверху», то есть стрелка вниз; остальные — вверх),
   поэтому и стрелку, и подпись ставит tasks.js, а не разметка. Разворот —
   транформом класса `--asc`, вторая фигура не нужна. */
.sortbox__dir {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 4px;
    border: 0;
    border-radius: var(--radius-ctl);
    background: none;
    color: var(--muted);
    cursor: pointer;
}
.sortbox__dir:hover { background: var(--paper); color: var(--ink); }
.sortbox__dir--asc svg { transform: rotate(180deg); }

/* Убрана с глаз, но не `display: none` — так чекбокс остаётся доступен с
   клавиатуры (тот же приём, что `.fold__box`). */
.sortbox__input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.sortbox__now {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    color: var(--ink-2);
    font-size: 12px;
    white-space: nowrap;
}
.sortbox__row:hover .sortbox__now { border-color: var(--accent); }

/* Стрелка — та же тройка бордеров-треугольника, что у меню быстрых сроков
   (`.pmenu__caret`): заводить вторую такую фигуру ради разворота на 180°
   незачем, разворот делает сам чекбокс. */
.sortbox__input:checked ~ .sortbox__head .pmenu__caret { transform: rotate(180deg); }

/* Слоем поверх, а не раздвигая поток (было `flex-direction: column` в
   потоке — живой отзыв владельца 28.08.2026: «должно выпадать новым
   слоем, а не растягивать родительское»). Та же геометрия, что у
   `.fmenu__pop`/`.asgmenu__pop`: рамка, тень, скругление — список
   вариантов и есть маленькое меню, просто без своего `<details>` (см.
   комментарий у `.sortbox__row` выше, почему не вложенный `<details>`).
   Родителю (`.sortbox`, `position: relative`) есть куда его позиционировать
   абсолютно, а самому поповеру «Вид» пришлось снять `overflow-y: auto` —
   иначе всплывший слой обрезался бы точно так же, как раньше обрезал
   поток (правило чуть выше, `.fbar__acts details.fmenu > .fmenu__pop`).

   `z-index: 1` обязателен, а не украшение (живой случай 28.08.2026: список
   сортировки открывался под тумблерами «В строке видно»). Ниже по потоку
   после `.sortbox` идёт `.rowvis`, и её тумблер `.tgl` сам `position:
   relative` (для сдвига кружка внутри) — то есть тоже позиционированный
   элемент с z-index:auto. Оба они (наш попап и чужой тумблер) без
   собственного z-index красятся в один и тот же слой отрисовки, и внутри
   него порядок решает не то, что сверху на глаз, а порядок в разметке —
   `.rowvis` в ней позже `.sortbox`, значит его тумблеры и оказывались
   поверх. Число z-index не обязано быть большим — правило только выводит
   попап в отдельный, более поздний слой; та же ловушка, что у ключа и
   кнопки статуса строки (`.trow__key`, `.trow__do`) и у плашки подзадачи
   (`.trow__sub`) ниже по файлу. */
.sortbox__pop {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1;
    margin-top: 4px;
    flex-direction: column;
    padding: 6px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
}
.sortbox__input:checked ~ .sortbox__pop { display: flex; }

/* Подсказка во вложенном виде (живой случай 28.08.2026, `tasks.html`) — тот
   же вид, что у пустого результата поиска исполнителя (`.asgmenu__empty`):
   приглушённая строка вместо тишины там, где список ничего не сделает. */
.sortbox__hint {
    margin: 4px 0 0;
    padding: 5px 8px;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.35;
}

/* ── Меню готовых ответов ────────────────────────────────────────────────
   Родня `fmenu`, и намеренно: обе панели — это `<details>`, обе открывает
   браузер сам, и выглядеть они обязаны одним семейством. Разница в устройстве
   строки: у фильтра это «значок · подпись», здесь третьей колонкой встаёт
   приглушённое значение справа — во что превратится пункт. Без него «конец
   недели» приходится считать в уме, а с ним пункт объясняет сам себя.

   Кнопка узкая, только значок: поле ввода рядом никуда не делось и остаётся
   главным способом, а меню — приставка к нему. */

.pmenu { position: relative; flex: none; }
.pmenu[open] { z-index: 30; }

.pmenu__sum {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 5px 7px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    background: var(--card);
    color: var(--muted);
    list-style: none;
    cursor: pointer;
}

.pmenu__sum::-webkit-details-marker { display: none; }
.pmenu__sum::marker { content: ""; }
.pmenu__sum:hover { border-color: var(--accent); color: var(--ink); }
.pmenu[open] .pmenu__sum { border-color: var(--accent); color: var(--ink); }

.pmenu__caret {
    flex: none;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--muted);
}

/* Прижата вправо, а не влево, как у фильтров: кнопка стоит у правого края
   узкой боковой колонки, и панель, растущая вправо, вылезла бы за неё. */
.pmenu__pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 5;
    width: max-content;
    min-width: 232px;
    max-width: min(320px, 84vw);
    padding: 5px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 10px 28px rgba(0, 0, 0, .16);
    text-align: left;
}

/* Три колонки: значок, подпись, значение. Подпись забирает остаток —
   `minmax(0, 1fr)`, потому что голое `1fr` не даёт ей сжаться и длинное
   значение вытолкнуло бы панель за край. */
.pmenu__item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: var(--radius-ctl);
    background: none;
    color: var(--ink);
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}

.pmenu__item:hover { background: var(--paper); }
.pmenu__item .sico { color: var(--muted); }
.pmenu__what { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Значение — справа и тише подписи: это следствие выбора, а не сам выбор. */
.pmenu__val { color: var(--muted); font-size: 12px; white-space: nowrap; }

/* Снятие срока — действие, а не дата: подпись приглушена, чтобы оно не
   читалось наравне с «завтра». */
.pmenu__item--off .pmenu__what { color: var(--ink-2); }

/* Поле и кнопка одной строкой. Поле забирает остаток ширины. */
.duepair { display: flex; align-items: center; gap: 6px; }
.duepair .dline { flex: 1; min-width: 0; }

/* Панель, внутри которой меню раскрыто, перестаёт обрезать содержимое и
   поднимается над соседними. Нужно ровно и то и другое, и ловится это только
   глазами. `overflow: hidden` стоит у панели ради скруглённых углов и резал
   меню по нижней границе — было видно два пункта из четырёх. Сняли обрезку —
   меню полезло под следующую панель: панели братья с непрозрачным фоном, и
   следующая красится поверх предыдущей вместе со всем, что из той торчит. */
.panel:has(.pmenu[open]) {
    position: relative;
    z-index: 30;
    overflow: visible;
}

/* Крестик сброса носит класс `circle` — тот же, что «плюс» в шапке группы
   (IR-36). Вид, размер и поведение при наведении приходят оттуда; здесь
   остаётся только то, чем он от него отличается, — место в ряду.

   Своего правила вида у него нет намеренно: круглые значки на одной
   странице выглядели похоже, но подсвечивались по-разному, и наведение
   читалось как признак «эта кнопка живая, а та нет». Шестерёнка справочника
   меток раньше стояла тут же и держалась того же приёма — с 25.08.2026 она
   в шапке раздела, рядом с лупой и «плюсом» (см. `.circle--head`). */

/* Крестик прошёл три места за один день. Сперва стоял абсолютно за левым
   краем ряда, в поле страницы, — и на узком поле (24px против кнопки 24px +
   зазор 6px) наезжал на боковую панель. Затем встал первым пунктом в потоке,
   перед «Исполнителем» — но в потоке появление кнопки сдвигает всё, что за
   ней, а на телефоне (панель уезжает шторкой, плашки и так стоят у самого
   края) сдвигало сами фильтры при каждом выборе (владелец 26.08.2026: «на
   смартфоне он смещает фильтры»).

   Теперь он последним пунктом группы, после «Метки» — и с 27.08.2026, когда
   «Вид» переехал из конца `#filters-body` в `.fbar__acts`, действительно
   последним: сдвигать в потоке за ним больше некого, а расстояние до края
   листа отдаёт `#filters-body` своим `flex: 1` (см. `.fbar__body` выше).
   Абсолютное позиционирование, приходившее и уходившее в двух прошлых
   версиях, здесь не нужно вовсе — простой margin в потоке решает то же
   самое без брейкпоинтов и точки отсчёта. */
.fbar__reset { margin-left: 8px; }


.fbar__gap { flex: 1; }

/* Чип бывает ссылкой и кнопкой. Кнопке надо отменить UA-стиль браузера, но
   с нулевым весом — иначе `background` из этого сброса перебил бы фон
   включённого состояния, и нажатый чип перестал бы отличаться от соседей. */
:where(button.chip) {
    font: inherit;
    background: none;
    color: inherit;
}

.chip {
    padding: 5px 11px;
    border-radius: 999px;
    font-size: 12.5px;
    color: var(--ink-2);
    text-decoration: none;
    border: 1px solid transparent;
}

button.chip { cursor: pointer; }

/* Пара чипов справа в шапке панели. Шапка равняет содержимое по базовой
   линии, поэтому у обёртки свой inline-flex: иначе кнопки разъезжаются по
   высоте между собой. */
.chips {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.chip:hover { background: var(--side-hover); color: var(--ink); }

/* Включённый чип — белым, как плашки фильтров трекера (владелец 01.09.2026:
   «все такие плашки должны быть везде белыми»). Класс общий, поэтому правка
   доходит разом до «Лаборатории», «Работ» и остальных разделов, где выбор
   показан чипами. */
.chip--on {
    background: var(--solid-bg);
    border-color: var(--rule-strong);
    color: var(--ink);
    font-weight: 500;
}

/* Тихие чипы и раскрывашка — всё же интерактив: --ink-2 ради контраста. */
.chip--quiet { font-size: 12px; color: var(--ink-2); }

/* Группа статуса */

/* Раньше здесь стоял отступ 14px — со своим нижним полем последней строки
   (7px) и верхним полем следующего заголовка (6px) давал 27px до следующей
   группы против 14px между обычными строками (7+7) внутри группы: переход
   между группами читался вдвое просторнее (просьба владельца 27.08.2026:
   сделать одинаково).

   Первая правка убрала margin целиком и подняла верхнее поле заголовка —
   и оказалась неверной: то поле красится собственным фоном заголовка
   (`.tgroup__head` — `background: var(--surface-2)`), а не остаётся
   пустым, как поле строки. Прибавка ушла ВНУТРЬ серой полосы и на отступ
   перед ней не работала — гость увидел, что строка почти вплотную
   подходит к следующей группе (отзыв владельца тем же числом 27.08.2026).

   Пустым может быть только то, что не красит ничей фон, — нижнее поле
   строки (7px, у строки фона нет) и внешний margin (фон не красит никогда).
   Значит margin обязан быть 7px, а не 0: 7 (поле строки) + 7 (margin) = те
   же 14px пустоты, что между обычными строками. Верхнее поле заголовка
   вернулось к прежним 6px — его роль внутренняя (отступ до текста в самой
   полосе), к отступу перед ней отношения не имеет. */
.tgroup { margin-bottom: 7px; }

/* Заголовок группы — полоса, а не подпись над карточкой. Так он читается как
   принадлежность строк под ним, а не как отдельный заголовок раздела: список
   идёт сплошняком, и полоса — единственное, что его делит.

   Примерзает под шапкой фильтров при прокрутке (просьба владельца
   26.08.2026: «На ревью, В работе... если задачи в верхнем списке
   кончились, он уезжает, и заменяется следующим статусом»). Смена
   заголовка — не скрипт, а штатное поведение sticky: заголовок — `<summary>`
   внутри своей же группы (`<details class="tfold">`), и его липкость
   ограничена рамками этой группы. Как только группа прокручивается мимо
   целиком, заголовок уезжает вместе с ней, а следующий, идущий сразу за
   ней, — тот же приём, тот же `top`, — занимает освободившееся место сам.

   `top` не голое число, а переменная плюс отступ: шапка фильтров
   (`.tasks-topbar`) сама переменной высоты — переносится на телефоне,
   переносится метка, — и её меряет и кладёт в `--topbar-h` `tasks.js`.
   Фолбэк `0px` — на случай, пока скрипт ещё не отработал или отключён:
   заголовок ляжет под самый верх, на кадр раньше срока, а не потеряется
   вовсе.

   `+ var(--float-gap)` — ровно тот воздух, что стоит между строкой
   фильтров и первой группой на спокойной странице (`.tasks-topbar > .fbar`
   держит снизу `--float-gap` — отступ левитирующего сайдбара, к которому
   владелец 31.08.2026 привязал оба зазора шапки). Третья итерация числа:
   `28px` (27.08.2026, «прилипает сильно близко»), `8px` (28.08.2026,
   «прилипает сильно рано») — а 31.08.2026 владелец поймал сам принцип:
   при начале прокрутки заголовок «На ревью» ПОДЪЕЗЖАЛ к фильтрам, потому
   что место прилипания (8px) не совпадало с местом покоя (16px), и полоса
   фильтров на глазах худела на разницу. Когда оба числа — одна и та же
   переменная, заголовок первой группы прилипает там же, где стоял, и не
   движется относительно шапки вовсе. Разойдись эти числа снова —
   вернётся либо щель, либо это подъезжание.

   `z-index: 5` — ниже шапки фильтров (та `10`, обязана перекрыть заголовок
   группы при передаче), выше обычной строки, но ниже открытого меню строки
   (`20`/`30`) — открытая раскрывашка обязана быть виднее декоративной
   полосы, если они как-то пересеклись. */
.tgroup__head {
    position: sticky;
    top: calc(var(--topbar-h, 0px) + var(--float-gap));
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border-radius: var(--radius-ctl);
    background: var(--surface-2);
    list-style: none;
    cursor: pointer;
}

.tgroup__head::-webkit-details-marker { display: none; }
.tgroup__head::marker { content: ""; }
.tgroup__head:hover { background: var(--grid); }

/* Треугольник сворачивания — рамками, как у остальных стрелок в системе.
   Повёрнут вниз у раскрытой группы и вправо у свёрнутой. */
.tgroup__fold {
    flex: none;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--muted);
    transition: transform .12s ease;
}

.tfold:not([open]) .tgroup__fold { transform: rotate(-90deg); }

.tgroup__name {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
}

/* Счётчик тише названия: он уточняет, а не называет. */
.tgroup__count { font-size: 12px; color: var(--muted); }

/* Счётчик задач — кружок, он же ссылка на «Нагрузку» (правка владельца
   25.08.2026). Разметка живёт в `tasks.html`. Стоял сразу за заголовком в
   его кегле; 27.08.2026 переехал в строку фильтров, первым пунктом перед
   «Исполнителем» — там ряд уже выровнен по центру (`.fbar__row`,
   `align-items: center`), и поправка под базовую линию заголовка (была
   `vertical-align: 4px`, `margin-left: 10px` от текста «Задачи») больше не
   нужна: `gap` строки сам разводит соседние плашки.

   Формой держится за `.circle`, но своим правилом, а не его классом: у
   `.circle` жёсткие 24×24 под значок, а здесь внутри цифры — кружок обязан
   растягиваться под три знака, а не под один. Общего у них ровно вид, и
   дублировать его дешевле, чем ломать размер `.circle` ради одного места. */
.tcount {
    display: inline-flex;
    align-items: center;
    /* `flex-start`: на общей вертикали с номерами строк стоит левый край
       пилюли (`.tcount__pill` ниже — там и история трёх калибровок этого
       выравнивания). */
    justify-content: flex-start;
    /* Ширина — не по числу знаков (было `min-width: 21px`, плавала от
       одной до трёх цифр), а та же колонка, что у номера строки: `.trow__key`
       занимает `--task-key-w` под сами знаки плюс `--task-key-gap` воздуха
       после.

       `21px`, а не `var(--task-key-w)`, — тем же приёмом, что у
       `--content-x` ниже по файлу: та переменная переопределяется классом
       `.board--keyN` НА САМОЙ доске, а счётчик стоит в `<nav>` рядом с ней,
       не внутри, и снаружи доски видит только запасное значение переменной
       в `:root` (38px, не 21) — не ту ширину, что у реальной колонки номера
       на этой странице. `--content-x` эту ловушку уже обходит хардкодом
       того же `21px`; здесь тот же обход, а не совпадение. */
    width: calc(21px + var(--task-key-gap));
    /* Высоту не ставим: её приносит пилюля, ростом с плашку фильтра. */
    /* Цвет — тот же, что у лупы и шестерёнки рядом: счётчик из их семьи
       значков, а не текста заголовка (правка владельца 26.08.2026, был
       заметно темнее и крупнее соседей). */
    color: var(--muted);
    /* Кегль и насыщенность — те же, что у номера строки под ним (было
       свои, 11px/600: правка владельца 27.08.2026, «цифра рядом с
       названием в 24px спорила бы с ним за внимание» — верно для того
       места, но счётчик тем же днём переехал из заголовка в строку
       фильтров, и рядом с ним давно не 24px заголовок, а строки списка с
       их 12px номером; владелец 28.08.2026 попросил тем же шрифтом, что
       и они). Насыщенность не выставляем вовсе — наследуется, как у
       `.trow__key`.

       Разрядку, в отличие от насыщенности, пришлось поставить явно, а не
       довериться наследованию: счётчик — ссылка `<a>`, и трекинг страницы
       (лёгкий отрицательный, `tracker-font.css`) доходит до него сам. Номер
       строки — кнопка `<button>`, а кнопки браузер по умолчанию не тянут
       внутрь ничего, кроме того, что явно вернул `:where(.copykey)` (шрифт
       и цвет, разрядку он не трогает) — довод тот же, что там. Без этой
       строки цифры отличались бы на десятые доли пикселя, замер браузером
       агента 28.08.2026 поймал разницу. */
    font-size: 12px;
    line-height: 1;
    letter-spacing: normal;
    text-decoration: none;
}

/* Рамка — не на самой ссылке, а на внутренней пилюле вокруг числа (владелец
   31.08.2026: «обводка у счётчика кривая, смещена вправо»). Ссылка держит
   ширину колонки номера (правило выше), и рамка по ней обнимала заодно и
   служебный зазор `--task-key-gap`: цифра стояла вплотную к левому краю
   кольца, а справа болталось до 23px пустоты.

   На общую вертикаль с номерами строк встаёт ЛЕВЫЙ КРАЙ КОЛЬЦА, а не
   начало цифры. Первый заход был наоборот — пилюля выступала на 8px влево,
   чтобы цифра осталась на вертикали калибровки 27–28.08.2026, — и владелец
   тем же днём поправил: «счётчик не выровнен по общей вертикали», глаз
   ведёт по видимой рамке, а не по цифре за ней. Та калибровка снята
   осознанно, это не откат по недосмотру.

   Метрики — РОВНО как у плашек фильтров правее («по высоте она меньше, чем
   у фильтров, должна быть такая же»): поля, межстрочник, рамка и радиус —
   копия `.fmenu__sum` выше, счётчик из их семьи. Разойдутся правила —
   разойдутся и кружки в одном ряду. */
.tcount__pill {
    display: inline-flex;
    align-items: center;
    padding: 4px 8px;
    line-height: 16.2px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--card);
    /* Оптическая поправка (владелец 31.08.2026: «счётчик чуть правее
       вертикали id»): кромка, поставленная РОВНО на вертикаль колонки
       номера, читалась правее — у полностью скруглённой пилюли левая
       граница касается вертикали одной точкой на середине высоты, выше и
       ниже кольцо уходит вправо, и глаз усредняет. −2px выводят видимую
       массу кромки на вертикаль; подобрано сравнением скриншотов браузером
       агента. */
    margin-left: -2px;
}

.tcount:hover { color: var(--ink); }

.tcount:hover .tcount__pill {
    background: var(--sunk);
}

/* Кольцо фокуса — тоже по пилюле: бокс самой ссылки шире видимого кружка
   на служебный зазор, и рамка фокуса по нему обводила бы пустоту. */
.tcount:focus-visible {
    outline: none;
}

.tcount:focus-visible .tcount__pill {
    outline: 2px solid var(--accent-ring);
    outline-offset: 1px;
}

/* «+» у правого края полосы: завести задачу сразу в этот статус. Прижат
   `margin-left: auto`, а не отдельной распоркой — распорка была бы пустым
   элементом ради выравнивания. Виден всегда, а не только под курсором: на
   планшете наведения нет, а кнопка не шумит — это один знак приглушённым. */
/* Круглая кнопка-обводка. Одна форма на три места: плюс в заголовке группы,
   статус в строке задачи, заведение в шапке раздела. Держать их порознь
   нельзя — разъедутся размером и радиусом при первой же правке.

   Значок центрируется сеткой, а не подбором отступов: раньше плюс был
   текстовым символом со своей высотой строки и садился выше центра, а
   круглая обводка вокруг делала это очевидным. */
.circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 50%;
    background: none;
    color: var(--muted);
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
}

.circle:hover,
.circle[open] > .circle,
details[open] > .circle {
    border-color: var(--rule-strong);
    background: var(--card);
    color: var(--ink);
}

.circle:focus-visible {
    outline: 2px solid var(--accent-ring);
    outline-offset: 1px;
}

/* Кружок во включённом состоянии: колокол «Мои подписки» (10.09.2026) должен
   отличаться от соседей и в покое, а не только под курсором — иначе срез
   списка выглядит как обычная кнопка, и человек не понимает, почему задач
   стало меньше. */
.circle--on {
    border-color: var(--accent);
    background: var(--card);
    color: var(--accent);
}

/* Кружок с чужим значком внутри (статус задачи): свой цвет значку задаёт
   `sico-wrap`, и перекрашивать его наведением нельзя — цвет статуса это
   и есть смысл. */
.circle--icon { color: inherit; }
.circle--icon .sico-wrap { display: inline-flex; }

/* Заведение в шапке раздела — тот же кружок, но заметный: это единственная
   точка входа в новую задачу, и в покое она обязана быть видна. */
.circle--cta {
    width: var(--ctl-round);
    height: var(--ctl-round);
    border-color: var(--rule-strong);
    background: var(--card);
    color: var(--ink);
}

.circle--cta:hover { background: var(--sunk); }

/* Шестерёнка справочника меток — в паре с лупой и «плюсом» в шапке раздела
   (25.08.2026). Размер тот же, что у соседей (30px, `--ctl-round`), а вид —
   базовый `.circle`: обводка прозрачна в покое и проявляется при наведении,
   как у лупы, а не постоянно, как у «плюса» — это не главное действие
   раздела, чтобы держать его на виду всегда. */
.circle--head { width: var(--ctl-round); height: var(--ctl-round); }

.tgroup__add { margin-left: auto; }

/* Список без карточки: ни рамки, ни фона, ни скруглений. Карточка вокруг
   каждой группы делила экран на коробки, а в списке задач важна сплошная
   колонка строк — глаз идёт по ней сверху вниз, не спотыкаясь о края.
   Отделяет группы теперь полоса заголовка. */
/* Верхнее поле — 7px, не 2 (27.08.2026): нижнее поле заголовка (6px)
   красится его же фоном (.tgroup__head, background: var(--surface-2)) и
   пустотой не читается — то же самое, из-за чего перед этим уже пришлось
   чинить отступ группа→группа (см. .tgroup). Реальная пустота от конца
   серой полосы до первой строки — это только поле `.tlist` (было 2px) и
   собственное верхнее поле строки (7px): 2+7=9 против 14 между обычными
   строками. Подняв поле `.tlist` до 7, получаем те же 7+7=14 везде — и
   после заголовка, и между строками, и после последней строки группы. */
.tlist {
    margin: 0;
    padding: 7px 0 0;
    list-style: none;
}

/* Колонки: срочность, номер, суть, метки, «автор → исполнитель», дедлайн,
   действие. Тянется только суть — у остального своя естественная ширина.

   Отдельной колонки «когда обновлено» больше нет: она отвечала на вопрос,
   которого никто не задаёт. Место занял дедлайн — и он же вобрал прежний
   чип относительного срока, потому что две колонки про один и тот же день
   читаются как ошибка. Относительная подпись («через 3 дн») ушла в
   подсказку поля. */
/* Строка кликается целиком, а не только по заголовку: попасть в текст из трёх
   слов мышью — отдельное упражнение, и промах открывает не задачу, а ничего.

   Сделано растянутой ссылкой, а не обработчиком на строке: CSP запрещает
   инлайновый onclick, а главное — ссылка остаётся ссылкой. Средняя кнопка
   мыши, «открыть в новой вкладке» и переход по Tab работают сами. */
.trow {
    position: relative;
    display: grid;
    /* значок · ключ · заголовок · метки · автор→исполнитель · дедлайн ·
       действие. Бейдж подзадач сюда больше не входит отдельной колонкой
       (было при заведении IR-53) — с 26.08.2026 он стоит сразу за
       заголовком, внутри `.trow__main` (просьба владельца: «по правому краю
       выравнивается» — колонка `auto` вслед за `1fr` начиналась там, где
       кончалась самая длинная строка списка, а не эта конкретная). Обрезка
       многоточием (`overflow: hidden`) с того же IR-53 стоит только на
       `.trow__title`, не на всём `.trow__main`, и больше не грозит обрезать
       раскрывашку бейджа изнутри. */
    /* Ширина каждой колонки — переменной, а не голым числом: «В строке
       видно» (владелец 28.08.2026) прячет колонку целиком, а не только её
       содержимое, и делает это на `.board`, объемлющем все строки разом
       (`.board--hide-prio` и подобные ниже по файлу переопределяют
       `--col-*` в 0, схлопывая трек). Умолчание у каждой переменной — её
       прежнее голое значение, так что без единого выключенного тумблера
       грид ровно тот же, что был. */
    /* Срок сразу ЗА названием, перед метками и парой — с 17.09.2026 (Антон:
       вертикаль под дедлайны пустует, когда сроков нет; метки — ближе к
       паре). Пара и кружок статуса стоят вплотную к правому краю, а колонка
       срока примыкает к воздуху после названия: там пустота читается как
       продолжение зазора, а не как дыра посреди правого края. Ширина
       колонки та же, `--task-due-w` — иначе плашка, показываемая по
       наведению, дёргала бы метки, пару и кружок на каждой строке. */
    grid-template-columns: var(--col-prio, 16px) var(--col-key, calc(var(--task-key-w) + var(--task-key-gap)))
                           minmax(0, 1fr) var(--col-due, var(--task-due-w)) var(--col-labels, auto)
                           var(--col-pair, auto) var(--col-do, auto);
    /* Высота строки не зависит от того, какие колонки видны (Антон,
       24.09.2026: «при включении/выключении „Статуса“ высота строк скачет
       немного»). Высоту давала самая высокая видимая ячейка: кружок статуса
       24px и плашка срока 24,8px, остальные ниже. Спрятал одну из них —
       строка садилась на 1–3px. Теперь трек не ниже кружка (24px), а плашка
       срока ровно 24px (см. `.trow__deadline .prop`), и строка одной высоты
       при любом наборе колонок. На узком экране правило то же, у канбана
       строка — флекс, и его это не касается. */
    grid-auto-rows: minmax(24px, auto);
    align-items: center;
    gap: 10px;
    /* Левое поле — не то же самое, что правое: строка выровнена не по краю
       листа, а по значку статуса в полосе группы над ней (просьба владельца
       26.08.2026: «сдвинуть строки задач вправо... чтобы они стали на одной
       вертикали с иконками кружками статусов в блоках»). У полосы группы
       (`.tgroup__head`) значок стоит после её же поля (12px) и треугольника
       сворачивания (8px = `.tgroup__fold`) через зазор в 8px — те же 12+8+8
       и здесь. Только `1fr`-колонка заголовка ужимается на эти 16px, все
       колонки правее (метки, пара, срок, действие) не двигаются вовсе —
       так устроен грид: не хватает места забирает только гибкий трек. */
    padding: 7px 12px 7px 28px;
    border-radius: var(--radius-ctl);
}

/* «В строке видно» (владелец 28.08.2026) — шесть тумблеров в меню «Вид»,
   каждый прячет одну колонку строки на самой доске. Переменная схлопывает
   трек в гриде (правило `.trow` выше), класс на элементе убирает то, что в
   нём стояло, — иначе нулевая по ширине колонка всё равно держала бы
   фокусируемую кнопку или раскрывашку. Заголовок задачи в список не входит
   вовсе — это не украшение строки, а сама задача, прятать нечем.

   Важность, ключ и срок — фиксированной ширины треки, их и схлопывает
   переменная. Метки, пара и действие — `auto`: у них content-based трек,
   и он и без переменной сжался бы в ноль, стоило спрятать содержимое
   (`display: none` не даёт `auto`-треку основы для размера) — переменная
   здесь для единообразия, а не по необходимости. `.board--hide-prio`
   целится в `.trow > .asgmenu`, а не в `.prio`: с 28.08.2026 значок стал
   меню, обёртка — прямой ребёнок строки (см. правки чуть выше по файлу и
   в @media).

   Каждому потомку — свой номер колонки явно (`grid-column`), а не то, что
   даст сам собой авторазмещение грида. Живой баг 28.08.2026: без этого
   спрятанный `display: none`-элемент выпадает из потока подряд идущих
   элементов грида целиком, а не оставляет по себе пустую колонку — грид
   впихивает СЛЕДУЮЩИЙ элемент в её место. Спрятали важность и ключ —
   заголовок задачи (следующий по разметке) занял их схлопнутые до 0px
   колонки вместо своей 1fr, и текст задачи попросту исчез с экрана. Номера
   те же, что порядок колонок в `grid-template-columns` (правило `.trow`
   выше): 1 важность, 2 ключ, 3 заголовок, 4 срок, 5 метки, 6 пара,
   7 действие (срок переехал из 6-й в 4-ю 17.09.2026, см. `.trow`). */
.trow > .asgmenu { grid-column: 1; }
/* Галочка пакетного решения в сводах (IR-102) занимает колонку важности:
   у правила важности нет, трек свободен. */
.trow__pick { grid-column: 1; justify-self: center; margin: 0; }
/* Обоснование под формулировкой в очереди Ереси — той же колонки, что
   заголовок, мелко и приглушённо. */
.trow__why { display: block; font-size: 12px; overflow-wrap: anywhere; }
.trow__key { grid-column: 2; }
.trow__main { grid-column: 3; }
.trow__deadline { grid-column: 4; }
.trow__labels { grid-column: 5; }
.trow__pair { grid-column: 6; }
.trow__do { grid-column: 7; }

/* Галочка выбора для групповых действий (Антон, 23.09.2026) — в левом поле
   строки, вне грида: поле в 28px и так пустует (строка выровнена по значку
   статуса в полосе группы), и колонки не сдвигаются. Место и центровка —
   те же, что у значка важности в том же поле на узком экране: левые 7px,
   половина высоты отступом, а не `transform` (см. `.trow > .asgmenu` в
   @media 760px — там же, почему не transform).

   Видна под курсором и всегда, пока хоть что-то выбрано: список в покое не
   должен превращаться в столбик пустых квадратиков. На экранах без мыши
   наведения нет — там видна всегда. Над растянутой ссылкой строки поднята,
   как ключ и кнопка статуса.

   Квадрат свой, а не системный (Антон тем же днём: «рамочку чекбокса чуть
   прозрачнее или как-то ещё сделать его менее акцентным»): системный
   рисовал тёмную рамку, и пустая галочка перетягивала взгляд с номера и
   названия. Рамка — светлой линией `--rule-strong`, как у плашек фильтра,
   темнеет под курсором; отмеченный залит чернилами второго уровня
   (`--ink-2`) с белой галкой — не акцентом: в отметках выбора фиолетовый
   не нужен (тот же ход, что `.lbl-check`). Галка — повёрнутый уголок
   псевдоэлемента: картинку `data:` страница задач не пропустит (CSP). */
/* Зона щелчка — подпись вокруг галочки (Антон тем же днём: «сделать поле
   кликабельности больше, чем она сама, сложно попасть»): всё левое поле
   строки до значка важности, на всю высоту строки. Над растянутой ссылкой
   строки поднята подпись, а не квадрат — щелчок в любую точку поля
   выбирает задачу, а не открывает её. */
.trow__hit {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: 1;
    width: 28px;
    cursor: pointer;
}
.trow__sel {
    appearance: none;
    -webkit-appearance: none;
    position: absolute;
    left: 7px;
    top: 50%;
    width: 14px;
    height: 14px;
    margin: -7px 0 0;
    border: 1px solid var(--rule-strong);
    border-radius: 4px;
    background: var(--card);
    cursor: pointer;
    opacity: 0;
    transition: opacity .1s ease;
}
.trow__sel:not(:checked):hover,
.trow__hit:hover .trow__sel:not(:checked) { border-color: var(--muted); }
.trow__sel:checked { border-color: var(--ink-2); background: var(--ink-2); }
.trow__sel:checked::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 1px;
    width: 3px;
    height: 7px;
    border: solid var(--card);
    border-width: 0 1.5px 1.5px 0;
    transform: rotate(45deg);
}
.trow__sel:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 1px; }
.trow:hover .trow__sel,
.trow--ctx .trow__sel,
.trow__sel:focus-visible,
.trow__sel:checked,
.board--picking .trow__sel { opacity: 1; }
@media (hover: none) {
    .trow__sel { opacity: 1; }
}
/* `trow--ctx` — строка, для которой открыто меню по правой кнопке
   (24.09.2026). Она остаётся такой, какой была под курсором (Антон тем же
   днём: «при вызове меню не снимать подсветку со строки»): курсор уходит
   в меню, а строка держит всё, что давало наведение, — фон (в светлой теме
   свой тон, см. `.trow:hover` в @media light), галочку, кольцо кружка
   статуса, пустые плашки срока и меток. Класс стоит рядом с каждым
   `.trow:hover` в этом файле. */
.trow--picked,
.trow--ctx { background: var(--surface); }
/* Воздух вокруг галочки (Антон тем же днём: «от сайдбара до строки отступ
   чуть меньше, а от выделения строки до чекбокса — чуть больше, и слева, и
   справа»). Подсветка строки и полоса группы отходят к сайдбару на 6px
   (38 → 32), поле строки растёт на те же 6px (28 → 34), и всё содержимое —
   важность, номер, значок статуса в полосе — остаётся ровно там, где было:
   вертикали со счётчиком над списком и со значком статуса не едут. Галочка
   встаёт в 10px от края подсветки и в 10px до значка важности (было 7 и 7).
   Полоса группы едет вместе со строками, иначе подсветка под курсором
   торчала бы из-под неё уступом. Только широкий экран: на узком левое поле
   делят галочка и важность (@media 760px), лист там и так прижат. */
@media (min-width: 761px) {
    .board:not(.board--kanban) .trow { margin-left: -6px; padding-left: 34px; }
    .board:not(.board--kanban) .tgroup__head { margin-left: -6px; padding-left: 18px; }
    .board:not(.board--kanban) .trow__sel { left: 10px; }
    .board:not(.board--kanban) .trow__hit { width: 34px; }
}
/* В колонке канбана строка — карточка в потоке, поле слева у неё своё:
   галочка встаёт первым элементом ряда. */
/* Зона щелчка там — поле в 6px вокруг квадрата, съеденное отрицательным
   отступом: попасть легче, а ряд карточки не сдвигается. */
.board--kanban .trow__hit {
    position: relative;
    top: auto;
    bottom: auto;
    display: inline-flex;
    order: 0;
    width: auto;
    margin: -6px;
    padding: 6px;
}
.board--kanban .trow__sel { position: relative; left: auto; top: auto; margin: 0; }

.board--hide-prio { --col-prio: 0px; }
.board--hide-key { --col-key: 0px; }
.board--hide-labels { --col-labels: 0px; }
.board--hide-pair { --col-pair: 0px; }
.board--hide-due { --col-due: 0px; }
.board--hide-do { --col-do: 0px; }

.board--hide-prio .trow > .asgmenu,
.board--hide-key .trow__key,
.board--hide-labels .trow__labels,
.board--hide-pair .trow__pair,
.board--hide-due .trow__deadline,
.board--hide-do .trow__do { display: none; }

/* Разделителей между строками нет. Линия под каждой строкой при двух десятках
   задач превращает список в разлинованную таблицу, и глаз читает линии, а не
   названия. Строки разделяет воздух, а подсветка при наведении — скруглённая
   полоса: она же показывает, что строка целиком кликается. */

/* Доска: список или колонки (IR-352).

   Разметка у обоих видов одна, разница только здесь. Иначе два представления
   разъехались бы при первой же правке строки — а строка правится чаще всего. */

.board--kanban {
    display: grid;
    grid-auto-flow: column;
    /* Колонка не уже 260: в ней стоит заголовок задачи, и на 200 он рвётся
       на три строки. Не шире 320 — иначе на экране помещается три колонки
       вместо пяти, и смысл доски теряется. */
    grid-auto-columns: minmax(260px, 320px);
    gap: var(--gap);
    overflow-x: auto;
    padding-bottom: 8px;
    align-items: start;
}

/* В колонке строка становится карточкой.

   Сеткой это не выражается. Раскладка списка — семь колонок в один ряд, и
   попытка пересобрать её в три колонки давала не карточку, а кашу: элементов
   семь, колонок три, лишние переносятся куда придётся. Ключ вставал в колонку
   заголовка и рвался на «IR-» и «294», а сам заголовок уходил в третью, узкую,
   и вылезал за край карточки. Так доска и уехала в бой — разметку тесты
   проверили, а картинку не посмотрел никто.

   Поэтому здесь свой поток: строка становится гибкой, элементы расставляются
   `order`, а заголовок и кнопка занимают всю ширину. Порядок в карточке —
   важность, номер, участники, название, метки, срок, действие. */
.board--kanban .trow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
    padding: 9px 10px;
    border: 1px solid var(--hair);
    border-radius: var(--radius-ctl);
    background: var(--card);
}

.board--kanban .trow:hover,
.board--kanban .trow--ctx { background: var(--surface); border-color: var(--rule); }

/* Важность в колонке-карточке — тем же приёмом, что раньше, но целится в
   обёртку `.asgmenu`, а не в сам `.prio`: с 28.08.2026 значок стал меню
   (щелчок открывает выбор, `bits.priority_menu(bare=True)`), и прямым
   ребёнком строки стала обёртка, а сам `.prio` теперь на уровень глубже. */
.board--kanban .trow > .asgmenu { order: 1; }
.board--kanban .trow__key { order: 2; }
/* Участники прижаты к правому краю первой строки — как в списке. */
.board--kanban .trow__pair { order: 3; margin-left: auto; }

/* Название с новой строки и во всю ширину. В карточке ему можно перенестись
   на вторую строку: обрезать многоточием там, где место есть, значит прятать
   единственное, ради чего в карточку смотрят. */
.board--kanban .trow__main {
    order: 4;
    flex: 0 0 100%;
    overflow: visible;
    white-space: normal;
}
/* Обрезка многоточием теперь на `.trow__title`, а не на `.trow__main` (см.
   его правило выше) — колонке отключать её тут придётся тем же свойствам,
   но уже на новом месте, иначе в карточке канбана заголовок обрежется. */
.board--kanban .trow__title {
    overflow: visible;
    white-space: normal;
}

.board--kanban .trow__labels { order: 5; }
.board--kanban .trow__deadline { order: 6; width: auto; margin-left: auto; }
.board--kanban .trow__do { order: 7; flex: 0 0 100%; }

/* В карточке пустое место не просто молчит, а не занимает строку. В списке у
   срока своя колонка постоянной ширины, и прозрачности там достаточно —
   ничего не съезжает. В колонке же пустой срок и плашка «+» оставляли под
   названием пустой ряд, и кнопка отрывалась от карточки. */
.board--kanban .trow .dline__at:placeholder-shown,
.board--kanban .trow .lbl--add { display: none; }

.board--kanban .trow:hover .dline__at:placeholder-shown,
.board--kanban .trow--ctx .dline__at:placeholder-shown,
.board--kanban .trow:focus-within .dline__at:placeholder-shown,
.board--kanban .trow:hover .lbl--add,
.board--kanban .trow--ctx .lbl--add,
.board--kanban .trow:focus-within .lbl--add { display: inline-block; }

/* Пустая группа остаётся на экране: в неё перетаскивают. Высота нужна, чтобы
   было куда целиться — полоска в один пиксель промахом и кончится. */
.board--kanban .tlist--empty,
.tlist--empty { min-height: 44px; }

/* Строка, которую тащат, и группа, над которой держат. Оба состояния —
   классами: CSP запрещает style="", и без них отклика не было бы вовсе. */
.trow--dragging { opacity: 0.45; }

.tgroup--over {
    outline: 2px dashed var(--accent);
    outline-offset: -2px;
}

.trow[draggable="true"] { cursor: grab; }
.trow--dragging { cursor: grabbing; }

.trow__title::after {
    content: "";
    position: absolute;
    inset: 0;
}

/* Кнопка перевода статуса стоит над растянутой ссылкой — иначе «В работу»
   открывала бы задачу вместо перевода. Ключу нужно то же самое, и по той же
   причине: без подъёма касание по номеру попадает в ссылку и открывает
   задачу вместо копирования. На мыши это было незаметно — там номер и так
   почти не промахивается, — а пальцем промахнуться легко.

   Важности нужен тот же подъём с 28.08.2026, когда значок стал меню
   (`bits.priority_menu(bare=True)`): без него щелчок по значку попадал в
   растянутую ссылку заголовка (живая проверка браузером агента — меню не
   открывалось вовсе, клик перехватывал `.trow__title`). Раньше `.prio` был
   декоративным `<span>` без своего клика, подъём был не нужен. */
.trow__do,
.trow__key,
.trow > .asgmenu { position: relative; z-index: 1; }

/* Меню важности растёт вправо, а не влево, как у остальных `.asgmenu` без
   `--pill` (умолчание `.asgmenu__pop` — `right: 0`, годится статусу и
   подобным в правом краю строки). Значок важности стоит в самом начале
   строки, у левого края листа, и открытие «от правого края влево» уводило
   поповер — минимум 208px, `min-width` — под боковую панель, где ему было
   не поместиться (живая проверка браузером агента 28.08.2026: список
   вариантов виднелся одной обрезанной галочкой). Своего модификатора
   `--pill` сюда не добавили нарочно — он ещё и красит `summary` плашкой
   с рамкой, а тут нужен только голый значок; поэтому направление правится
   отдельным правилом, не переключением на готовый класс. */
.trow > .asgmenu > .asgmenu__pop { right: auto; left: 0; }

/* Раскрытое меню важности поднимает вес выше заголовка следующей группы —
   тот же случай, что у `.trow__labels:has(.lblmenu[open])` чуть выше (живая
   проверка браузером агента 28.08.2026 нашла его же: на последней строке
   группы «Низкий» и «Без приоритета» уходили под серую полосу «В работе»,
   у обоих ровно тот же вес — 5). Бьём по самому `.asgmenu`, не через
   `:has()` на обёртке: обёртки под важностью нет, `.asgmenu` и есть прямой
   ребёнок строки. */
.trow > .asgmenu[open] { z-index: 20; }

/* Подсветка строки — светлее фона, а не темнее. Полоса заголовка группы
   темнее страницы, и подсвети мы строку тем же ходом, наведённая строка
   читалась бы как ещё один заголовок. Так они расходятся: группы вниз по
   светлоте, строки вверх. */
.trow:hover { background: var(--surface); }

/* Пустое место молчит, пока на строку не навели.

   Пустой срок рисовался пунктирной рамкой с подписью «нет срока», а строка без
   меток — плашкой «+». По отдельности это подсказки, но в списке из двух
   десятков задач, где срок стоит у двух, вся правая половина экрана
   превращалась в столбик одинаковых пустых рамок — заметнее, чем сами задачи.
   Проставленный срок и настоящие метки видны всегда: они данные, а не
   приглашение.

   Наведение — не единственный способ до них добраться: `focus-within` держит
   их открытыми при обходе с клавиатуры, а на узком экране правая часть строки
   и так скрыта целиком.

   Срок с 22.08.2026 показан плашкой, и молчит именно она (`prop--empty`), а
   не поле ввода: поле уехало внутрь её меню, где пряталось бы не к месту. */
.trow__deadline .prop--empty,
.trow .lbl--add { opacity: 0; transition: opacity .1s ease; }

.trow:hover .trow__deadline .prop--empty,
.trow--ctx .trow__deadline .prop--empty,
.trow:focus-within .trow__deadline .prop--empty,
.trow__deadline details[open] > .prop--empty,
.trow:hover .lbl--add,
.trow--ctx .lbl--add,
.trow:focus-within .lbl--add { opacity: 1; }

.prio { display: flex; color: var(--ink); }
.trow__key { font-size: 12px; color: var(--ink-3); }

.trow__main {
    display: flex;
    align-items: center;
    min-width: 0;
}

/* Обрезка многоточием переехала с контейнера на сам заголовок (было на
   `.trow__main`) — маркер подзадачи, когда он стоит после заголовка
   (`.trow__sub--after`, IR-53), не должен пропадать вместе с обрезанным
   хвостом длинного названия: он `flex: none` и остаётся видимым, а
   сжимается и обрезается только заголовок. */
.trow__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
}
/* Подчёркивания по наведению нет (владелец 26.08.2026): строка и так вся
   кликается растянутой ссылкой (`.trow__title::after`), a вся строка
   подсвечивается фоном при наведении (`.trow:hover`) — подчёркивание
   только у названия было лишним, вторым сигналом того же самого. */

/* align-items: center — без него кружок статуса (высота 24px, задана явно)
   не растягивается по кросс-оси и садится к верхнему краю обёртки (та выше
   24px из-за общего выравнивания строки по сетке), а не по центру вместе с
   остальными элементами строки — на глаз почти незаметно (меньше 1px), но
   строка выровнена по одной горизонтали не до конца (проверка владельца
   27.08.2026). */
.trow__do { display: flex; align-items: center; }

/* Статус в строке — кружок с меню, а не кнопка следующего шага.

   Кнопка вела ровно в один статус: «В работу», «Готово», «Вернуть» — и за
   остальными пятью приходилось открывать карточку. Ширину под самую длинную
   подпись держать больше не надо: у кружка она одна по определению, и правый
   край строки не гуляет от задачи к задаче.

   Обводка появляется при наведении на строку — тогда она подсказка, а не
   украшение: ряд одинаковых колец на правом краю перевешивал бы названия
   задач. Сам значок статуса виден всегда, включая планшет без наведения. */
/* vertical-align: middle — без него `<summary class="circle">` участвует в
   инлайновой раскладке своего `<details>` по базовой линии, и высота строки
   вокруг 24-пиксельного кружка раздувается до 25.5px line-box'ом (даже без
   текста рядом — строка получает высоту от шрифта-«распорки»). Из-за этого
   кружок сидел на 0.75px выше центра строки, чем остальные элементы
   (заметил владелец 27.08.2026, проверяя выравнивание по горизонтали). */
.trow__do .circle { border-color: transparent; vertical-align: middle; }

.trow:hover .trow__do .circle,
.trow--ctx .trow__do .circle,
.trow__do details[open] > .circle {
    border-color: var(--rule-strong);
    background: var(--card);
}

/* Подзадачи (IR-53).

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

   Номер родителя внутри виден только в плоском виде («Подзадачи» без
   «Вложенных») — во вложенном родитель и так стоит строкой выше, вторая
   подпись того же номера была бы шумом.

   Сторона тоже разная, и перевешивалась дважды за 26.08.2026. Сначала
   переставили плашку ПОСЛЕ заголовка во вложенном виде: слева её соседом
   была строка НАД ней, чужая задача, и плашка читалась как принадлежащая
   ей. Прямым разбором в чате вернули на 180° в тот же день: во вложенном
   виде родитель и так виден строкой выше, плашке надёжнее стоять ПЕРЕД
   заголовком, как и было изначально. Плоский вид, наоборот, — там рядом
   может стоять посторонняя задача без общего родителя, и плашка с номером
   держит внимание лучше, если она ПОСЛЕ заголовка (`.trow__sub--after`), а
   не перед ним, где её легко принять за что-то из строки выше.

   `position: relative; z-index: 1` — та же ловушка, что у ключа и кнопки
   статуса (`.trow__do, .trow__key`): без подъёма щелчок по плашке уходил бы
   в растянутую ссылку строки и открывал бы саму подзадачу, а не родителя. */
/* Контурная, не заливкой — тем же приёмом, что нейтральная метка (`.lbl`) и
   тот же размер: padding и радиус переняты у неё один в один (просьба
   владельца 26.08.2026: маркер и бейдж подзадач «очень яркие», «ближе к
   Linear, только с обводкой, по габаритам как метка в строке»). Заливка
   осталась только у наведения — как подсказка, а не постоянный цвет. */
.trow__sub {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px 8px;
    margin-right: 4px;
    border-radius: 999px;
    border: 1px solid var(--rule-strong);
    color: var(--ink-2);
    /* Кегль — на самой плашке, не только на `.num` внутри: без номера
       (вложенный вид, `sub_arrow(num=False)` — одна стрелка) в плашке нет
       текста вовсе, и высоту флекс-контейнер по кеглю не берёт — без
       `min-height` плашка с одной стрелкой мельчала до 16px, пока соседняя,
       с номером, честно садилась на 20,5 (те же, что у `.lbl`). Число ниже —
       не поле над кеглем, а сама итоговая высота: `box-sizing: border-box`
       (общее правило файла) считает `min-height` вместе с рамкой и полем, а
       не в придачу к ним, поэтому это 20,5, а не 16,5. Живая проверка в
       браузере агента 26.08.2026 (первая попытка молча просела до те же
       16,5 — рамка и поле съели их у контента). */
    font-size: 11px;
    font-weight: 500;
    min-height: 20.5px;
    text-decoration: none;
    flex: none;
}
.trow__sub:hover { background: var(--surface-2); color: var(--ink); }
.trow__sub--after { margin-right: 0; margin-left: 4px; }

/* Без рамки — только у угловой стрелки вложенного вида (просьба владельца
   27.08.2026): `.trow__sub` там всегда без номера (`sub_arrow(num=False)`,
   см. макрос) — родитель и так виден строкой выше, обводка вокруг одной
   стрелки выглядела лишним акцентом. Плоский вид (плашка с номером,
   `.trow__sub--after`) рамку не теряет — она там единственная подсказка,
   что строка вообще подзадача. `border-color`, не `border: none` —
   `box-sizing: border-box` уже считает рамку в `min-height` (20,5px), и
   так размер плашки не меняется, меняется только видимость кольца. */
.board--nested .trow__sub { border-color: transparent; }

/* Отступ у вложенного вида — только там: в плоском виде подзадача остаётся
   обычной строкой своей группы, отступ ей не нужен, хватает стрелки.

   Отступ на `.trow__main` (колонка заголовка), а не на всей строке
   (`padding-left` на `.trow--child`, как было раньше): та сдвигала грид
   целиком, и номер с важностью у подзадачи съезжали от общей вертикали
   родителя — ровно то, что владелец заметил вживую 26.08.2026. Номер и
   значок важности стоят в своих колонках грида перед `.trow__main`, и
   сдвиг внутри его собственной колонки их не касается. */
/* 20px, не 30 (на 1/3 меньше — просьба владельца 27.08.2026): подзадачи во
   вложенном виде сидели заметно правее, чем нужно. */
.board--nested .trow--child .trow__main { margin-left: 20px; }

/* Бейдж «N/M» — тот же формат, что у плашек фильтра (`.fmenu__sum`), только
   мельче: он стоит рядом с названием в тесном ряду строки, а не один в
   панели. Контурный, размера метки в строке (`.lbl`) — та же правка, что у
   `.trow__sub` чуть выше, и по той же просьбе (26.08.2026).

   С 26.08.2026 стоит внутри `.trow__main`, сразу за заголовком, а не своей
   колонкой грида: `overflow: hidden` (обрезка многоточием) с того же
   IR-53 переехала на голый `.trow__title`, и раскрывашку бейджа больше не
   обрежет — колонка была нужна как раз затем, чтобы уйти из-под этой
   обрезки, а теперь достаточно того, что обрезает только сам заголовок.

   `position: relative; z-index: 1` на бейдже — та же ловушка, что у ключа и
   кнопки статуса чуть выше (`.trow__do, .trow__key`): строка кликается
   целиком растянутой ссылкой (`.trow__title::after`), и без подъёма щелчок
   по бейджу попадал бы в неё, открывая карточку вместо раскрывашки. */
.subpop { position: relative; z-index: 1; display: inline-flex; flex: none; margin-left: 4px; }
.subpop__sum {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border-radius: 999px;
    border: 1px solid var(--rule-strong);
    color: var(--ink-2);
    font-size: 11px;
    font-weight: 500;
    list-style: none;
    cursor: pointer;
}

/* Кольцо заполнения бейджа (референс — Linear, upload id 1140, 26.08.2026):
   трек тем же приглушённым цветом, что рамки строки, дуга — акцентом, тем
   же, что у ссылок и статуса «Ревью» чуть ниже — своего цвета под одну дугу
   заводить незачем. Доля — в разметке (`stroke-dasharray`, см. `sub_ring` в
   `_taskbits.html`), здесь только цвет. */
.subring { flex: none; }
.subring__track { stroke: var(--rule-strong); }
.subring__fill { stroke: var(--accent); }
.subpop__sum::-webkit-details-marker { display: none; }
.subpop__sum::marker { content: ""; }
.subpop__sum:hover,
.subpop[open] .subpop__sum { background: var(--surface-2); color: var(--ink); }
/* Прокрутки у списка подзадач нет (23.09.2026): в нём не больше десяти
   строк (`sub_preview`), а меню исполнителя подзадачи всплывает из него
   вторым слоем — умолчание `.fmenu__pop` (`overflow-y: auto`, 280px)
   обрезало бы этот слой по краю списка. */
.subpop__pop {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 220px;
    max-height: none;
    overflow: visible;
}
.subpop__item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 7px;
    border-radius: var(--radius-ctl);
    font-size: 12.5px;
}
.subpop__item:hover { background: var(--side-hover); }
/* Ссылкой — левая часть строки, аватар справа — своё меню. */
.subpop__link {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    gap: 6px;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}
.subpop__who .asgmenu { display: inline-flex; }
/* Раскрытое меню исполнителя подзадачи — над соседними строками списка,
   тот же подъём, что у меню исполнителя в строке (`.trow__who:has(…)`). */
.subpop__item:has(.asgmenu[open]) { position: relative; z-index: 20; }
.subpop__num { color: var(--ink-3); }
.subpop__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
/* Аватар исполнителя — правым краем, столбиком по строкам; пустая ячейка
   держит ширину у задач без исполнителя. */
.subpop__who { flex: none; width: 22px; height: 22px; margin-left: auto; display: inline-flex; }
.subpop__more { padding: 5px 7px; color: var(--muted); font-size: 12px; text-decoration: none; }
.subpop__more:hover { color: var(--ink-2); }

/* Значок статуса красится от обёртки — один рисунок годится и строке списка,
   и переключателю в карточке. */

.sico { flex: none; }
.sico-wrap { display: inline-flex; align-items: center; color: var(--ink-3); }
.sico-wrap--todo { color: var(--ink-2); }
.sico-wrap--doing { color: var(--status-doing); }
.sico-wrap--done { color: var(--ok); }
/* Ревью — цветом, а не серым по умолчанию. Серым красятся «Бэклог» и
   «Отменено», то есть состояния, где ничего не происходит; ревью же ждёт
   человека, и в одном ряду с ними читалось отложенным. До 01.09.2026 брал
   --accent; свой токен заведён по прямой просьбе владельца. */
.sico-wrap--review { color: var(--status-review); }

.pico { flex: none; display: block; }
.pico__bar { fill: var(--rule-strong); }
.pico__bar--on { fill: var(--ink-2); }
/* Свой цвет, не `--bad`: тот делят ошибки и предупреждения по всей системе,
   а значок срочности — не про ошибку. Замер владельца 26.08.2026:
   0,945 0,482 0,278 (0–1) = #f17b47. */
.pico--urgent { color: #f17b47; }

/* Срок: относительный текст, цвет — насколько горит. Красный — сегодня и
   раньше, оранжевый — в пределах недели, серый — дальше и у закрытых. */

.due {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    white-space: nowrap;
    color: var(--ink-2);
}

.due svg { flex: none; }
.due--overdue, .due--today { color: var(--bad); font-weight: 500; }
.due--soon { color: var(--warn); }

/* Значок календаря у просроченной задачи — свой цвет, ярче текста срока.
   Замер владельца 03.09.2026: 0,882 0,357 0,345 (0–1) = #e15b58. Только
   `overdue`: «сегодня» красится тем же `--bad`, но просрочкой ещё не стало.

   Два селектора, потому что срок рисуют два разных макроса: `due` (витрина
   элементов на /design) и `due_pill` — плашка в списке и в карточке задачи.
   Держать их вместе: разъедутся при первой правке цвета. */
.due--overdue svg,
.prop--overdue .sico { color: #e15b58; }

/* Аватар-инициалы. Палитра тёмная и глухая: белые инициалы читаются на всех
   восьми фонах, а человек остаётся одного цвета во всех местах системы. */

.av {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    font: 600 9.5px/1 var(--sans);
    letter-spacing: 0.02em;
    color: #fff;
    user-select: none;
}

/* Восьмицветный ряд кружков с инициалами — своя палитра, а не выборка из
   семейств «s» и «heat», как было до 01.09.2026. Прежний ряд лежал в фиолетовом
   секторе (система фиолетово-сливовая, и аватары брали из неё же): восемь
   кружков подряд читались одним цветом, а различать людей в строке надо с
   одного взгляда. Просьба владельца: «контрастные, но читаемые».

   Тона разведены по кругу — синий, тил, терракота, пурпур, малиновый,
   зелёный, охра, серый, — и набор проверен тремя мерками разом, иначе
   «контрастные» и «читаемые» тянут в разные стороны:

   * белые инициалы на кружке — от 4,8:1 (терракота) до 6,4:1, все выше
     порога 4,5:1 для мелкого текста. Потому подпись у всех одна, белая,
     а не своя на каждый цвет;
   * сам кружок на фоне страницы — не ниже 3:1 и в светлой теме, и в тёмной
     (у прежнего ряда три цвета из восьми давали на тёмном 2,4–2,6 и
     сливались с фоном);
   * попарная различимость — минимальная ΔE в наборе 27,7 при пороге
     заметности около 25. Первая версия ряда этого не проходила: синий, сталь
     и грифель стояли в 20 единицах друг от друга и на кружке 22 px читались
     одним цветом.

   Тёмная тема ряд не переопределяет: цвета средней светлоты держат и белый
   текст, и оба фона, а закреплённый за человеком цвет не должен меняться от
   темы — по нему его и узнают.

   Номер лежит в `users.avatar_tone` и выдаётся `avatars.tone()`; кружок живёт
   ровно до того дня, когда подтянется фото из Telegram, — тогда на его место
   встаёт `img.av`. */
:root {
    --av-0: #4a68a8;
    --av-1: #1f7a76;
    --av-2: #b5563a;
    --av-3: #914a9d;
    --av-4: #a8416a;
    --av-5: #3f7d4a;
    --av-6: #8a6a1f;
    --av-7: #6b6b72;
}

.av-0 { background: var(--av-0); color: #fff; }
.av-1 { background: var(--av-1); color: #fff; }
.av-2 { background: var(--av-2); color: #fff; }
.av-3 { background: var(--av-3); color: #fff; }
.av-4 { background: var(--av-4); color: #fff; }
.av-5 { background: var(--av-5); color: #fff; }
.av-6 { background: var(--av-6); color: #fff; }
.av-7 { background: var(--av-7); color: #fff; }

/* Агент — вне восьмицветного ряда: его цвет означает роль, а не номер. */
.av--bot { background: var(--bot); color: var(--on-bot); }

/* Фото из Telegram. Размер тот же, что у кружка с инициалами: меняется
   только содержимое. object-fit нужен потому, что Telegram отдаёт квадрат
   160×160, а место круглое, — без него портрет сжимался бы по вертикали.
   Тонкая внутренняя обводка держит край, когда аватарка светлая и
   сливается со строкой. */

img.av {
    display: inline-block;
    object-fit: cover;
    background: var(--rule);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

/* Обводки роли у фотографии нет намеренно (пробовали 20.08.2026, убрали в
   тот же день): она съедала два пикселя картинки, и бот выглядел мельче
   людей в одном ряду. Аватарка бота узнаётся и без рамки, а жёлтая заливка
   осталась там, где фотографии нет. */

.av--none {
    background: none;
    border: 1.5px dashed var(--rule-strong);
}

/* Метки: цвет заливки задаётся в окне управления и приезжает классом.
   Инлайнового style здесь быть не может — его запрещает CSP, — поэтому
   палитра конечна и живёт готовыми парами ниже.

   Подпись поверх заливки берётся из --on-*: они подобраны вручную под обе
   темы. Считать контраст по яркости фона в рантайме нельзя, в тёмной теме
   такой расчёт врёт. */

.lbl {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    border: 1px solid var(--rule-strong);
    font: 500 11px/1.5 var(--sans);
    color: var(--ink-2);
    vertical-align: 1px;
}

/* Метка внутри плашки «Метка» в ряду фильтров — своя, более МЕЛКАЯ высота,
   не крупнее. Первая попытка (26.08.2026, тем же разговором) взяла для чипа
   ту же высоту, что у самой плашки (26,2px) — но чип сидит ВНУТРИ отступов
   плашки (`.fmenu__sum`: рамка 1 + поле 4 с каждой стороны), и плашка
   раздувалась вокруг него до 36px, вместо того чтобы остаться той же
   высоты, что «Исполнитель»/«Дедлайн» рядом (живой случай: «раздувается»).
   Здесь чип должен УМЕЩАТЬСЯ в свободные 16,2px (26,2 минус рамка и поле
   плашки), а не задавать высоту сам. `line-height` внутри шорткода `font`,
   а не отдельной строкой — тот же подвох, что уже ловили у общего правила:
   `font: ...` стирает всё, что задано раньше него в том же правиле. */
.fmenu__chips .lbl {
    padding: 0 8px;
    font: 500 11px/14px var(--sans);
}

/* Исключённая метка на плашке отбора: тот же чип, зачёркнут и приглушён.
   Цвет заливки остаётся — по нему метку узнают, — а зачёркивание говорит
   «кроме». Прозрачность та же, что у выключенной кнопки (`.button[disabled]`). */
.lbl--off {
    text-decoration: line-through;
    opacity: .5;
}

/* Палитра меток. Здесь она и задаётся — Python знает только имена.
   Цвета обычные, а не оттенки оформления: метки различают на глаз, и ряд
   близких тонов этой работы не делает.

   Каждому цвету прописана пара для подписи. Она посчитана один раз по
   яркости и записана сюда: в рантайме считать нельзя, а на глаз ошибиться
   легко — на жёлтом белый текст пропадает. */

.lbl--c-gray   { --fill: #a5a8ae; --fill-ink: #17141f; }
.lbl--c-slate  { --fill: #9aa4b4; --fill-ink: #17141f; }
.lbl--c-indigo { --fill: #656ecd; --fill-ink: #ffffff; }
.lbl--c-teal   { --fill: #5db4cc; --fill-ink: #17141f; }
.lbl--c-green  { --fill: #6eb789; --fill-ink: #17141f; }
.lbl--c-amber  { --fill: #e9c336; --fill-ink: #17141f; }
.lbl--c-orange { --fill: #dc9755; --fill-ink: #17141f; }
.lbl--c-rose   { --fill: #c29f99; --fill-ink: #17141f; }
.lbl--c-red    { --fill: #db6561; --fill-ink: #ffffff; }

/* Нейтральная метка остаётся контурной: заливка на каждой второй метке
   превращает список в пестроту, а выделять нужно немногие. */
.lbl[class*="lbl--c-"] {
    background: var(--fill);
    border-color: var(--fill);
    color: var(--fill-ink);
}

/* Справочник меток. Ряд на метку: образец, имя, цвет, счётчик, кнопки.
   Ряды выровнены в колонки общей сеткой, а не каждый сам по себе — иначе
   поля разной ширины гуляют по вертикали и список читается как свалка.

   Удаление вынесено отдельной формой (внутрь формы редактирования её не
   вложить), поэтому кнопка живёт в своей ячейке последней колонки. */

.lblman {
    display: grid;
    grid-template-columns: minmax(70px, auto) minmax(120px, 1fr) auto auto auto auto;
    align-items: center;
    gap: 8px 10px;
    margin-bottom: 16px;
}

.lblman__hint { margin: 0 0 14px; font-size: 12px; }

/* Формы — не сетка внутри сетки: их ячейки должны встать в колонки родителя,
   поэтому каждая форма раскрывается display: contents. */
.lblman__row, .lblman__kill { display: contents; }

.lblman__chip { justify-self: start; }

/* Поле растягивалось на всю свободную колонку (1fr) — для «ym» или «wb» это
   пустая рамка на две трети окна. Потолок в 260px оставляет полю расти под
   длинное имя, но не превращает его в пустошь под коротким (правка
   владельца 25.08.2026). */
.lblman__name { width: 100%; max-width: 260px; }
.lblman__count {
    min-width: 2ch;
    text-align: right;
    font-size: 12px;
}

/* Выбор цвета: свёрнутый кружок, по клику — полоска со всей палитрой.
   Полоска всегда развёрнутой занимала бы полстроки на каждую метку. */

.cpick { position: relative; }

.cpick__sum {
    display: flex;
    list-style: none;
    cursor: pointer;
}

.cpick__sum::-webkit-details-marker { display: none; }
.cpick__sum::marker { content: ""; }

.cpick__pop {
    position: absolute;
    top: calc(100% + 6px);
    /* Раскрывается влево: вправо полоска из десяти кружков упиралась в край
       окна и добавляла ему горизонтальную прокрутку. */
    right: 0;
    z-index: 5;
    padding: 8px 10px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
    /* Одиннадцать кружков в ряд — это ~333 px, и в окне справочника (680 px)
       они помещаются одной строкой. Ограничение ставим только по краю экрана:
       предел в 320 px заставлял полоску переноситься вниз всегда. */
    max-width: calc(100vw - 48px);
}

.cpick__pop .swatches { flex-wrap: nowrap; }

/* Кружок в свёрнутом виде показывает цвет и ничего не принимает: сам выбор
   живёт в раскрытой полоске. */
.swatch--flat { cursor: pointer; }

.swatch--flat.swatch--neutral { border: 1.5px dashed var(--rule-strong); }

/* Выбор цвета кружками. Радиокнопка не прячется в display: none — так она
   выпала бы из обхода с клавиатуры; вместо этого растянута прозрачной поверх
   кружка, поэтому кликом ловится он весь. */

.swatches {
    display: flex;
    align-items: center;
    gap: 6px;
}

.swatches__sep {
    width: 1px;
    height: 18px;
    margin: 0 2px;
    background: var(--rule);
}

.swatch {
    position: relative;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    cursor: pointer;
    background: var(--fill, var(--surface-2));
}

.swatch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: none;
    opacity: 0;
    cursor: pointer;
}

/* Отметка выбранного — галочка поверх заливки, как в примере. Цвет её берётся
   из пары к заливке, иначе на светлом кружке она пропадает. */
.swatch:has(input:checked)::after {
    content: "✓";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    color: var(--fill-ink, var(--ink));
    pointer-events: none;
}

.swatch:has(input:focus-visible) {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
}

/* Нейтральный — не цвет, а его отсутствие: пустой контурный кружок. */
.swatch--neutral {
    --fill: transparent;
    --fill-ink: var(--ink-2);
    border: 1.5px dashed var(--rule-strong);
}

/* Кружки берут те же значения, что и метки: одно объявление на цвет. */
.swatch--gray   { --fill: #a5a8ae; --fill-ink: #17141f; }
.swatch--slate  { --fill: #9aa4b4; --fill-ink: #17141f; }
.swatch--indigo { --fill: #656ecd; --fill-ink: #ffffff; }
.swatch--teal   { --fill: #5db4cc; --fill-ink: #17141f; }
.swatch--green  { --fill: #6eb789; --fill-ink: #17141f; }
.swatch--amber  { --fill: #e9c336; --fill-ink: #17141f; }
.swatch--orange { --fill: #dc9755; --fill-ink: #17141f; }
.swatch--rose   { --fill: #c29f99; --fill-ink: #17141f; }
.swatch--red    { --fill: #db6561; --fill-ink: #ffffff; }

/* Свой цвет — радужный кружок, как в привычных палитрах. Под ним нативный
   пикер: он даёт и градиентную площадку, и поле HEX, и системный выбор. */
.swatch--custom {
    --fill-ink: #fff;
    background: conic-gradient(#f56565, #ecc94b, #48bb78, #4299e1, #9f7aea, #f56565);
}

/* Пикер лежит поверх радиокнопки: клик по кружку должен открывать выбор
   цвета, а не просто отмечать «свой». Отметку выставит скрипт. */
.swatch__hex { z-index: 1; }

.lblman__add {
    display: flex;
    gap: 8px;
    align-items: center;
    padding-top: 14px;
    border-top: 1px solid var(--rule);
}

.lblman__add .lblman__name { flex: 1; }

/* Окно справочника шире обычного: в ряду поле имени, выбор цвета и кнопки,
   в 420 px они складываются друг на друга. */
.modal--wide { width: min(680px, calc(100vw - 32px)); }

@media (max-width: 620px) {
    /* На узком экране колонки схлопываются в две: подпись и поле. */
    .lblman { grid-template-columns: auto 1fr; }
    .lblman__count { display: none; }

    /* Здесь ряд кружков в строку уже не встаёт — только тут и переносим. */
    .cpick__pop .swatches { flex-wrap: wrap; }
    .cpick__pop { max-width: min(320px, calc(100vw - 48px)); }
}

/* Метки в строке задачи: своя колонка у правого края, перед аватаром.
   Слева они плыли — у длинного заголовка уезжали за обрез, у короткого
   липли к тексту, и ряд не читался сверху вниз. */

.trow__labels {
    /* Строка кликается целиком растянутой ссылкой; колонка поднята над ней,
       иначе клик по метке открывал бы задачу вместо меню. */
    position: relative;
    z-index: 1;
    display: flex;
    justify-content: flex-end;
}

/* Открытое меню поднимает всю колонку, а не только себя.
   z-index внутри .lblmenu считается в контексте этой колонки, поэтому меню
   целиком весит ровно 1 — столько же, сколько колонки и кнопки соседних
   строк. При равном весе побеждает порядок в разметке, и строки ниже
   ложились поверх раскрытого меню. Поднимаем саму колонку. */
.trow__labels:has(.lblmenu[open]) { z-index: 20; }

/* Меню меток на <details>: открытие, закрытие, Esc и обход с клавиатуры
   достаются от браузера даром, а инлайновый обработчик CSP не пропустит. */

.lblmenu { position: relative; }
.lblmenu[open] { z-index: 5; }

.lblmenu__sum {
    display: flex;
    align-items: center;
    gap: 4px;
    list-style: none;
    cursor: pointer;
}

/* Треугольник-раскрывашка здесь лишний: роль кнопки несут сами метки. */
.lblmenu__sum::-webkit-details-marker { display: none; }
.lblmenu__sum::marker { content: ""; }

/* Пустая задача: приглушённый плюс — иначе первую метку поставить не с чего. */
.lbl--add {
    border-style: dashed;
    color: var(--ink-3);
    padding: 1px 7px;
}

.lblmenu__sum:hover .lbl--add { color: var(--ink-2); border-color: var(--ink-3); }

.lblmenu__pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 5;
    width: 232px;
    padding: 10px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
    text-align: left;
    white-space: normal;
}

.lblmenu__list {
    display: flex;
    flex-direction: column;
    /* По содержимому, а не во всю ширину: в колонке flex по умолчанию тянет
       элементы, и метка-таблетка растянулась бы в полосу с текстом у левого
       края — на метку это уже не похоже. */
    align-items: flex-start;
    gap: 6px;
    max-height: 220px;
    overflow-y: auto;
}

/* Дедлайн прямо в строке. Поле, а не текст со ссылкой на карточку: срок
   переставляют чаще всего остального, и ради этого открывать задачу — три
   лишних перехода на каждую правку.

   Колонка стоит над растянутой ссылкой строки: без этого клик по календарю
   открывал бы задачу. */
.trow__deadline {
    position: relative;
    z-index: 1;
    width: var(--task-due-w);
}

.dline { display: flex; align-items: center; }

/* Поле срока — обычный текст, и никаких выпадающих выборов рядом. Родное
   datetime-local отсюда убрано: формат в нём задаёт язык браузера, и при
   английском возвращается «02:30 PM». Свои календарь и список часов тоже
   убраны — они работали хуже родных. Остался набор с клавиатуры по маске.
   Причина и цена решения — в tasks.py, у DUE_PLACEHOLDER.

   Селектор с родителем, а не один класс, — и это не косметика. Базовое
   правило форм записано как `input[type="text"]`: селектор по атрибуту
   весит столько же, сколько класс, поэтому пара «элемент + атрибут»
   перебивала одинокий `.dline__at`. Поле молча брало общие 14px и padding
   9px и вырастало до 40px против 26px у кнопки рядом. Добавленный предок
   поднимает вес, и компактный вид наконец применяется.

   Предок здесь `.dline` — обёртка самого макроса, а не колонка списка.
   Пока в селекторе стоял `.trow__deadline`, компактный вид доставался
   только строке списка, а в карточке, на заведении задачи и в меню
   исполнителя то же самое поле выглядело чужим: крупный текст, плотная
   рамка, двойная высота.

   Высота считается ровно под кнопку строки (.button--ghost): 1 + 4 + 16.2
   + 4 + 1 ≈ 26px. Правите padding у кнопки — правьте и здесь, иначе поле и
   кнопка в строке разъедутся по нижнему краю. */
.dline .dline__at {
    width: var(--task-due-w);
    min-width: 0;
    padding: 4px 6px;
    border: 1px solid transparent;
    border-radius: var(--radius-ctl);
    background: none;
    font: 400 12px/1.35 var(--mono);
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

/* В списке ширину задаёт колонка, а не само поле: колонка уже посчитана под
   тот же размер, и два источника ширины разъезжались бы при правке одного. */
.trow__deadline .dline__at { width: 100%; }

/* Срок в строке — та же плашка, что в карточке (`bits.due_pill`), ужатая под
   колонку. Держать здесь второй вид поля больше нельзя: голое поле умело
   только набор руками, а «к пятнице» человек знает сразу, число — нет. */
/* Высота плашки — ровно кружок статуса, 24px: строка 16px + поля 3+3 +
   рамка 1+1. Строка задана пикселями, а не множителем шрифта: с 1.3 плашка
   выходила 24,8px и одна держала высоту всей строки — спрячь срок, и
   строка садилась (24.09.2026, см. `grid-auto-rows` у `.trow`).
   `vertical-align: middle` — тот же случай, что у кружка в `.trow__do`:
   плашка стоит внутри `<details>`, и по базовой линии строчная «распорка»
   раздувала ячейку до 25px. */
.trow__deadline .prop {
    max-width: 100%;
    padding: 3px 8px;
    border-color: transparent;
    background: none;
    line-height: 16px;
    vertical-align: middle;
}

.trow__deadline .prop__val { font-size: 12px; }

.trow:hover .trow__deadline .prop,
.trow--ctx .trow__deadline .prop,
.trow__deadline details[open] > .prop {
    border-color: var(--rule-strong);
    background: var(--card);
}

/* Раскрытое меню поднимается над соседними колонками и над растянутой ссылкой
   строки — тем же приёмом, что у меток. */
.trow__deadline:has(details[open]),
.trow__do:has(details[open]) { z-index: 20; }

/* Плашки в карточке привязывают меню к своему ЛЕВОМУ краю: там они стоят
   слева от широкой строки. В списке та же плашка прижата к правому краю, и
   меню шириной 208px уезжало за экран. Возвращаем правую привязку — общую
   для всех меню строки. */
.trow__deadline .asgmenu--pill > .asgmenu__pop { right: 0; left: auto; }

/* В карточке — вся отданная ширина, до кнопки меню. Своя ширина у поля от
   строки списка, где место считанное; в панели она оставляла между полем и
   кнопкой пустую полосу в треть ширины, и два элемента читались как не
   связанные. Правило стоит здесь, а рядом с `.duepair` не сработало: сила у
   селекторов одна, а `.dline .dline__at` ниже по файлу и потому побеждал. */
.duepair .dline .dline__at { width: 100%; }

/* Рамка появляется по наведению и фокусу. Постоянная превратила бы список в
   таблицу форм: полей в строке столько же, сколько задач, и все они кричали
   бы «заполни меня». */
.dline__at:hover { border-color: var(--rule); }
.dline__at:focus { border-color: var(--accent); outline: none; }

/* Цвет по тому, насколько горит: те же состояния, что были у чипа срока. */
.dline__at.due--overdue { color: var(--bad); }
.dline__at.due--today { color: var(--warn); }
.dline__at.due--quiet { color: var(--ink-3); }

/* Пустой срок — пунктирная рамка и приглушённая подпись «нет срока». Тот же
   язык, что у пустого кружка исполнителя: место есть, значения нет.

   Держится на :placeholder-shown, то есть на настоящей пустоте поля, а не на
   классе от сервера: набрал человек дату — рамка ушла тут же, не дожидаясь
   ответа страницы. */
.dline__at:placeholder-shown {
    border-style: dashed;
    border-color: var(--rule-strong);
}

.dline__at::placeholder { color: var(--ink-3); opacity: 1; }

/* В фокусе рамка снова сплошная: человек уже правит, и пунктир сообщал бы
   про пустоту, которой сейчас занимаются. */
.dline__at:focus:placeholder-shown { border-style: solid; }

/* Пара «автор → исполнитель» одной колонкой сетки. Тремя отдельными
   колонками было бы честнее по разметке, но между ними встал бы общий gap
   строки в 10px, и стрелка отъехала бы от обоих кружков одинаково далеко —
   связка перестала бы читаться как связка. Внутри свой зазор в 5px.

   Ширина набирается фиксированными частями (22 + 5 + 8 + 5 + 22), поэтому
   кружки стоят на одной вертикали во всех строках без grid-подпорок. */
.trow__pair {
    display: flex;
    align-items: center;
    gap: 5px;
}

/* Автор поднят над растянутой ссылкой строки: под ней у span'а не всплывает
   title, а подпись «Автор: …» — единственное, чем кружок себя называет. */
.trow__from {
    position: relative;
    z-index: 1;
    display: flex;
}

/* Открытое меню автора — над соседями. С 21.08.2026 автор меняется таким же
   меню, как исполнитель, но подъёма ему не досталось: колонка так и стояла на
   z-index: 1, а каждая следующая строка списка рисуется поверх предыдущей.
   Поповер уходил под аватары нижних строк и под метки — видно было верхние
   пару пунктов, остальное под чужой строкой. */
.trow__from:has(.asgmenu[open]) { z-index: 20; }

.tflow { flex: none; color: var(--ink-3); }

/* Меню исполнителя на аватаре — тем же приёмом, что меню меток: <details>,
   поповер у правого края, колонка поднята над растянутой ссылкой строки. */

.trow__who {
    position: relative;
    z-index: 1;
    display: flex;
}

.trow__who:has(.asgmenu[open]) { z-index: 20; }

.asgmenu { position: relative; }
.asgmenu[open] { z-index: 5; }

.asgmenu__sum {
    display: flex;
    align-items: center;
    list-style: none;
    cursor: pointer;
}

.asgmenu__sum::-webkit-details-marker { display: none; }
.asgmenu__sum::marker { content: ""; }

/* Наведение подсказывает, что аватар кликается: сам по себе он читается
   как картинка, а не как кнопка. */
.asgmenu__sum:hover .av { box-shadow: 0 0 0 2px var(--rule-strong); }

/* Ширина по содержимому, а не 208 пикселей на всё. Внутри меню исполнителя
   лежит форма поручения агенту — имя, «Старт», поле времени и кнопка, — и в
   фиксированную ширину она не влезала: `overflow-y: auto` по спецификации
   делает и горизонтальную прокрутку, поэтому «Поручить» просто уезжала за
   край. Нижняя граница оставлена прежней, чтобы список людей не сужался до
   ширины самого короткого имени, верхняя — чтобы меню не выходило за экран. */
.asgmenu__pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 5;
    width: max-content;
    min-width: 208px;
    max-width: min(340px, 84vw);
    /* Высота по содержимому. 260 пикселей не хватало уже на пятерых: список
       людей, форма поручения агенту и «снять исполнителя» не влезали, и меню
       пролистывалось там, где могло показаться целиком. Потолок остаётся, но
       он про край экрана, а не про длину списка. */
    max-height: min(70vh, 560px);
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
    text-align: left;
    white-space: normal;
}

/* Поиск по имени (живой отзыв владельца 28.08.2026) — первой строкой в
   меню исполнителя/автора, прилипает к верху при прокрутке списка: людей
   бывает больше пятёрки (см. комментарий у `.asgmenu__pop` про потолок в
   560px), и поле обязано оставаться под рукой, а не уезжать вместе со
   списком. Отрицательные поля — тот же приём растяжки «на весь поповер»,
   что у `.lblmenu__new` ниже: `.asgmenu__pop` несёт `padding: 6px`, и
   ровно на них строка выходит к самому краю, а `padding` на ней самой
   возвращает те же 6 (плюс воздух под текст) визуально. */
.asgmenu__search {
    position: sticky;
    top: -6px;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 6px;
    margin: -6px -6px 4px;
    padding: 7px 8px;
    background: var(--card);
    border-bottom: 1px solid var(--rule);
    color: var(--muted);
}
.asgmenu__search:focus-within { color: var(--ink); }
.asgmenu__search .sico { flex: none; }

/* Поле разобрано до нитки: рамку, фон и поля даёт обёртка `.asgmenu__search`
   выше, самому input мешать нечем. Кегль — тот же, что у пункта списка
   (`.asgmenu__item`, 13px), чтобы поиск не выглядел чужим шрифтом над
   собственными результатами.

   Селектор с `input` спереди — не для красоты: общее правило форм
   (`input[type="search"]` среди прочих, в начале файла) весит ровно
   столько же по типу+атрибуту, и без своего `input` голый класс проиграл
   бы ему рамку, фон и поля обратно. */
input.asgmenu__search-input {
    flex: 1;
    min-width: 0;
    border: none;
    background: none;
    padding: 0;
    font-size: 13px;
    color: inherit;
}
.asgmenu__search-input:focus { outline: none; }

.asgmenu__empty {
    margin: 0;
    padding: 5px 8px;
    color: var(--muted);
    font-size: 13px;
}

.asgmenu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 5px 6px;
    border: 0;
    border-radius: var(--radius-ctl);
    background: none;
    font: 400 13px/1.2 var(--sans);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}

.asgmenu__item:hover { background: var(--paper); }
.asgmenu__item--on { font-weight: 500; background: var(--paper); }

/* Галочка у выбранного, справа. Заливка и полужирный отвечают на вопрос «что
   сейчас» только в сравнении с соседями: посмотрев на одну строку, ответа не
   получить, а под курсором заливка вообще у другой. Галочка отвечает сама по
   себе.

   Псевдоэлементом, а не разметкой: правило одно и накрывает сразу все меню —
   статус, важность, человека, — а разметки в каждом из них прибавилось бы по
   строке. `margin-left: auto` прижимает её вправо, отдельной распорки не
   нужно. */
.asgmenu__item--on::after {
    content: "✓";
    margin-left: auto;
    padding-left: 10px;
    color: var(--accent);
    font-weight: 600;
}

/* Разделитель перед «снять»: это не ещё один человек в списке. */
.asgmenu__item--clear { margin-top: 4px; border-top: 1px solid var(--rule); padding-top: 8px; }

/* Меню по правой кнопке на строке списка (Антон, 24.09.2026) — тот же
   поповер и те же пункты, что у меню строки (`.asgmenu__pop`,
   `.asgmenu__item`), только стоит не под кнопкой, а у курсора: положение
   ставит tasks.js через CSSOM (атрибут style CSP не пропустит). Подменю
   сбоку — те же меню строки (`.asgmenu__pop`, у меток `.lblmenu__pop`),
   вынутые из-под своих кнопок тем же правилом.

   Правило стоит ниже обоих поповеров намеренно: сила селекторов одна, и
   `position: absolute` с привязкой к кнопке иначе победил бы. z-index —
   над панелью выбора (40) и шапкой, под листами меню на телефоне (80). */
/* Появляется и гаснет по прозрачности, быстро (Антон тем же днём: «плавно
   по прозрачности, но очень быстро, так же как исчезновение по клику мимо
   и Esc») — та же десятая секунды, что у остальных проявлений в строке
   (`.trow__sel`, пустые плашки). Класс «видно» ставит и снимает tasks.js;
   угасающее меню щелчков уже не ловит. */
.rowmenu,
.rowmenu__sub {
    position: fixed;
    top: 0;
    left: 0;
    right: auto;
    z-index: 50;
    opacity: 0;
    pointer-events: none;
    transition: opacity .1s ease;
}
.rowmenu--on,
.rowmenu__sub--on { opacity: 1; pointer-events: auto; }

/* Кольца фокуса у меню нет (Антон тем же днём: «обведено уродливой
   фиолетовой рамкой, она не нужна»). Фокус в меню кладёт скрипт, чтобы
   сразу ходить стрелками, и общее `:focus-visible` (2px `--accent`)
   обводило им весь поповер. Где стоит клавиатура, показывает заливка
   пункта ниже. */
.rowmenu:focus,
.rowmenu:focus-visible { outline: none; }

/* Пункт под клавиатурой выглядит как под курсором, а не кольцом: в меню
   ходят стрелками, и кольцо на каждом шаге читалось бы как ещё одна
   рамка. Раскрытый пункт держит заливку, пока открыто его подменю. */
.rowmenu .asgmenu__item:focus,
.rowmenu .asgmenu__item:focus-visible,
.rowmenu__sub .asgmenu__item:focus,
.rowmenu__sub .asgmenu__item:focus-visible,
.rowmenu .asgmenu__item[aria-expanded="true"] { outline: none; background: var(--paper); }

/* Слот значка одной ширины у всех пунктов: у одних лицо (22px), у других
   значок 13–16px, и без слота подписи стояли бы лесенкой. */
.rowmenu__ico {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 22px;
}
/* Лицо в слоте — размером с плашку фильтра (`.fmenu__sum .av`): полное
   22px делало пункты «Исполнитель» и «Автор» выше соседних. */
.rowmenu__ico .av { width: 16px; height: 16px; font-size: 7px; }

.rowmenu__rule {
    display: block;
    height: 1px;
    margin: 4px 2px;
    background: var(--rule);
}

/* Переименование на месте — поле на месте названия. Текст в поле стоит
   ровно там, где стоял в ссылке: отрицательное поле слева съедает свой
   отступ и рамку. Селектор с предком — чтобы перебить общее правило полей
   (`input[type="text"]`: 14px, поля 9px), как у `.dline .dline__at`. */
.trow__main input.trow__rename {
    flex: 1;
    min-width: 0;
    margin: -2px 0 -2px -7px;
    padding: 1px 6px;
    border: 1px solid var(--accent);
    border-radius: var(--radius-ctl);
    background: var(--card);
    font: inherit;
    color: var(--ink);
}
.trow__main input.trow__rename:focus { outline: none; }

/* Агент в меню — та же строка списка, только со временем старта и кнопкой.
   Своих правил у формы не было вовсе, и она шла общими: аватар вставал на
   шесть пикселей левее людей над ним, а имя бралось шрифтом форм — крупнее
   и плотнее соседей. Отступы и шрифт здесь те же, что у `asgmenu__item`. */
.asgmenu__bot {
    display: grid;
    justify-items: start;
    gap: 6px;
    margin-top: 4px;
    padding: 8px 6px 2px;
    border-top: 1px solid var(--rule);
}

.asgmenu__who {
    display: flex;
    align-items: center;
    gap: 8px;
    font: 400 13px/1.2 var(--sans);
    color: var(--ink);
}

/* Перенос — на случай узкого экрана: «Старт» и поле времени рядом читаются
   как одно, но упереться в верхнюю границу ширины они не должны. */
.asgmenu__start { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }

.lblmenu__new { width: 100%; margin: 8px 0 6px; }

/* Метка в меню — кнопка, а не подпись при галочке. Состояние показывает сама
   метка: выбранная выглядит ровно так, как будет выглядеть на задаче,
   невыбранная — одним контуром в своём цвете. Галочка рядом отвечала бы на
   тот же вопрос второй раз, и смотреть пришлось бы не на метку, а на
   квадратик слева от неё.

   Цель под палец крупнее базовой метки: по чипу в 11 пикселей попадают не с
   первого раза, а тут по нему щёлкают постоянно. */
.lbl--pick {
    padding: 3px 10px;
    cursor: pointer;
    font-family: var(--sans);
    background: none;
}

/* Невыбранная цветная: заливку снимаем, цвет метки остаётся в контуре —
   так видно, какой она станет, но она не спорит с уже поставленными. */
.lbl--pick[class*="lbl--c-"]:not(.is-on) {
    background: none;
    border-color: var(--fill);
    color: var(--ink-2);
}

/* Нейтральной цвета нет вовсе, поэтому выбранной ей достаётся плотность:
   подложка и контур заметнее, подпись выходит на основной цвет текста. */
.lbl--pick.is-on:not([class*="lbl--c-"]) {
    background: var(--sunk);
    border-color: var(--ink-3);
    color: var(--ink);
}

.lbl--pick:hover { border-color: var(--accent); }

.lbl-check {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}

.lbl-check input { accent-color: var(--ink); }


/* Карточка задачи: содержание слева, свойства справа — та же сетка, что у
   `.tsheet__side` на других страницах (agent.html, task_new.html,
   spaces_new.html). На узком экране свойства уезжают под содержание. */

.tsheet {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: var(--gap);
    align-items: start;
}

/* Одна колонка. Свойства карточки задачи (`.props` ниже) рядом плашек под
   заголовком, а не в этой колонке, — а на телефоне именно вторая колонка
   уводила карточку вбок. Правило снова получает исключение на широком
   экране, см. `.tcard .tsheet--single` в конце файла: правка владельца
   27.08.2026, «есть место, чтобы перенести туда полоску свойств». */
.tsheet--single { grid-template-columns: minmax(0, 1fr); }

/* ── Свойства плашками ───────────────────────────────────────────────────
   Узкий экран: ряд под заголовком — статус, важность, исполнитель, срок,
   метки, автор. Переносится на вторую строку, когда не влезает, — как в
   образце. На широком экране этот же блок становится правой колонкой
   `.tsheet` и меняет раскладку на вертикальную — см. `.tcard .props` в
   конце файла. */

.props {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0 0 18px;
}

/* Раскрытое меню плашки поднимает весь блок свойств, а не только себя —
   тот же приём, что у `.trow__labels:has(.lblmenu[open])` в списке (живой
   случай там же: строки ниже ложились поверх раскрытого меню). Здесь сосед
   не строка, а `.tsheet__main` — колонка описания и обсуждения: `.props`
   стоит ДО неё в разметке (нужно для узкого экрана, плашки сразу под
   заголовком), а grid красит элементы по их положению в дереве, если ни у
   кого нет своего веса, — значит содержимое карточки ложится поверх
   раскрытого меню (живой случай 28.08.2026: «выпадающее меню метки лежит
   ниже, чем слой контента»). Список менюшек тот же, что несут все плашки
   `.props` разом — статус, важность, исполнитель, срок, автор (`.asgmenu`)
   и метки (`.lblmenu`), а не только метки: причина у всех пяти одна и та
   же, и почему баг заметили именно на метках — метки чаще остальных
   остаются раскрытыми после выбора (плашка не закрывается сама, см. её
   правку в tasks.html). */
.props:has(.asgmenu[open], .lblmenu[open]) { z-index: 20; }

/* Кто и когда завёл — первой строкой блока свойств (правка владельца
   31.08.2026; раньше стояло под описанием классом `.tdesc__by`, ещё раньше —
   в шапке панели, справа от заголовка). Шрифт тот же, что был под описанием:
   приглушённый, 12px. Дата — своей строкой: `.num` блочный. Ширина 100%
   работает на узком экране, где свойства лежат рядом: flex-wrap самого
   `.props` отправляет подпись на собственную строку над плашками. */
.props__by {
    width: auto;
    margin: 0;
    color: var(--muted);
    font-size: 12px;
}
.props__by .num { display: inline; }

/* Обе строки времени переехали под свойства 02.09.2026 (правка владельца).
   Отступ сверху — только у первой из них: между плашками свойств и текстом
   нужен воздух, а между самими строками нет, они читаются парой.

   Обёртка занимает всю ширину блока свойств: без неё строки вставали в
   хвост последней плашки, и «слева» начиналось не от края.

   По умолчанию строки идут ЛЕНТОЙ: обновление слева, заведение справа, а не
   помещаются — перенос. Столбиком они встают только там, где свойства сами
   стали колонкой (min-width: 1400px, в конце файла): порог один на двоих,
   поэтому вид меняется ровно в тот момент, когда свойства уходят из-под
   заголовка в правую полосу (правка владельца 02.09.2026). */
.props__times {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 2px 12px;
}

/* Сама плашка — она же `summary` раскрывашки, поэтому маркер снимаем. */
.prop {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 5px 11px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--card);
    color: var(--ink);
    font-size: 13px;
    line-height: 1.3;
    list-style: none;
    cursor: pointer;
    white-space: nowrap;
}

.prop::-webkit-details-marker { display: none; }
.prop::marker { content: ""; }
.prop:hover { border-color: var(--accent); }
[open] > .prop { border-color: var(--accent); }

/* Незаполненное показывает название поля приглушённым: «Приоритет» серым —
   это и подпись, и приглашение. Пустое место на его месте не сообщало бы ни
   того, ни другого. */
.prop--empty { color: var(--muted); }
.prop--empty .sico { color: var(--muted); }

.prop__val { overflow: hidden; text-overflow: ellipsis; }

/* Подпись поля внутри плашки — тише значения: она уточняет, что это, а
   читают всё равно значение. */
.prop__cap { color: var(--muted); font-size: 12px; }

/* Заголовок группы внутри меню — на месте прежней подписи `optgroup`. */
.asgmenu__cap {
    margin: 8px 6px 4px;
    padding-top: 8px;
    border-top: 1px solid var(--grid);
    color: var(--muted);
    font-size: 11px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* Аватар в плашке мельче, чем в строке списка: там он сам себе значок, здесь
   стоит рядом с именем и не должен перевешивать его. */
.prop .av { width: 18px; height: 18px; font-size: 9px; }

/* Метка внутри плашки «Метки» на карточке — та же мелкая высота, что у чипа
   в плашке «Метка» в ряду фильтров главной (`.fmenu__chips .lbl` выше, тот
   же приём и по той же причине): просьба владельца 27.08.2026 — плашка
   «кризис» была 32,5px против 28,9 у соседних («На ревью», «Средний»,
   срок) — общий `.lbl` (20,5px) внутри полей самой плашки (рамка 1 + поле 5
   с каждой стороны) раздувал её вокруг себя, вместо того чтобы остаться
   тем же размером, что текст рядом. Чип умещается в свободные 16,9px (сама
   плашка держит высоту своим шрифтом 13px/1.3), а не задаёт её сам. */
.prop .lbl {
    padding: 0 8px;
    font: 500 11px/14px var(--sans);
}

/* Подписчики карточки (10.09.2026). Лица идут в ряд с узким зазором, а НЕ
   внахлёст, как в Linear: наложение читается только с кольцом цвета подложки
   вокруг каждого лица, а это новое значение тени — стили заморожены, и новые
   заводятся по прямой просьбе владельца (см. `design-baseline.json`). Лиц на
   плашке всё равно не больше трёх, остальные считаются числом. */
.watch__faces { display: inline-flex; align-items: center; gap: 2px; }

/* Меню подписчиков раскрывается ВЛЕВО, в отличие от соседних плашек: эта
   стоит последней в ряду свойств, и панель, растущая вправо, уезжает за
   правый край окна — на широком экране ряд плашек прижат к нему вплотную
   (снято браузером агента 10.09.2026). */
.asgmenu--pill.watch > .asgmenu__pop { left: auto; right: 0; }

/* Своя подписка — первый пункт меню и единственный со значком слева вместо
   лица: остальные пункты про других людей, и путать их нельзя. */
.watch__self { font-weight: 500; }
.watch__self .sico { flex: none; }

.watch__note {
    margin: 2px 8px 4px;
    color: var(--muted);
    font-size: 11px;
    line-height: 1.4;
    white-space: normal;
}

/* Колокол в плашке: подписан — залит, не подписан — контур. Приглушение
   пустой плашки достаётся от `.prop--empty .sico`. */
.sico--bell-on { color: var(--accent); }

/* Меню под плашкой прижато влево: плашки идут слева направо, и панель,
   растущая вправо от левой плашки, — это то же направление. У аватара в
   строке списка наоборот, он у правого края. */
.asgmenu--pill { position: relative; }
.asgmenu--pill[open] { z-index: 30; }
.asgmenu--pill > .asgmenu__pop { right: auto; left: 0; }

/* Поле «своя дата» стоит первой строкой меню срока и отделено от готовых
   ответов — как строка ввода в образце. */
.duepop__own {
    padding: 4px 4px 8px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--grid);
}

.duepop .pmenu__item { min-width: 208px; }

/* Описание без карточки: текст идёт прямо под плашками. Панель с заголовком
   «Описание» подписывала очевидное — это первое, что после названия, — и
   делила карточку на коробки там, где читают подряд. */
/* Было на общем `--gap` (16px, меньше двух других отступов карточки —
   см. комментарий у `.tdesc__files` про «три отступа на одном числе»),
   подняли до 24px, вровень с ними (просьба владельца 27.08.2026: «побольше,
   примерно на 2/4»). Тем же вечером — ещё дважды крупнее (24→32→40): этот
   переход, единственный из трёх, ведёт не в описание, а в другую панель
   («Обсуждение», `.panel--feed`), и заметно большего воздуха перед ней не
   жалко — единообразие трёх отступов сюда не распространяется. */
.tdesc { margin-bottom: 40px; }
.tdesc__body { max-width: 74ch; }

/* Файлы под описанием. Полосы сверху больше нет (правка владельца
   25.08.2026): блок отделяют заголовок и воздух, а линия резала карточку ещё
   одной коробкой — ровно тем, от чего избавились, убрав панель «Описание».

   24px — та же величина, что у `.subblock` и `.res` ниже: три отступа
   карточки (описание→подзадачи, подзадачи→ресурсы, ресурсы→обсуждение)
   держатся на одном числе (владелец 26.08.2026: «должны быть одинаковыми,
   меньше на четверть, чем отступ от подзадач до ресурсов сейчас» — было
   32px через margin-bottom `.res`, 32 * 3/4 = 24). */
.tdesc__files { margin-top: 24px; }

/* Подзадачи (IR-53).

   Строка родства под заголовком — приглушённой ссылкой-раскрывашкой, тем же
   рисунком стрелки, что и в списке. Меню открывает щелчок по всей строке —
   правой кнопки на телефоне нет, а `<details>` работает и с клавиатуры.

   Отрицательное поле сверху подтягивает строку под заголовок вплотную
   (владелец 26.08.2026: «ближе к заголовку, сразу под ним») — `.sheet-head`
   карточки даёт от себя 26px (padding-bottom 10 + margin-bottom
   var(--gap)=16), общее правило для всех шапок системы, трогать его отсюда
   нельзя. Селектор через соседа, а не переписанный `.parentline`, — правка
   касается только карточки с родителем, а не любой строки под любым
   заголовком. */
.sheet-head + .parentline { margin-top: -24px; }
.parentline { margin: 6px 0 0; }
.parentline__menu { display: inline-flex; }
.parentline__sum {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--muted);
    font-size: 13px;
    cursor: pointer;
    list-style: none;
}
.parentline__sum::-webkit-details-marker { display: none; }
.parentline__sum::marker { content: ""; }
.parentline__sum:hover { color: var(--ink-2); }

/* Родитель и его кольцо прогресса — скруглённым прямоугольником, как метка
   в строке (владелец 26.08.2026: «Статус родительской задачи потом её
   название... кольцо прогресса тоже в скруглённом прямоугольнике»), а не
   подчёркнутой ссылкой, как было. Обводка сама говорит «кликается» — здесь
   и подчёркивание было бы лишним. */
.parentline__chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    color: var(--ink-2);
}
.parentline__link { color: inherit; }
.parentline__pop {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 220px;
}
.parentline__item {
    display: block;
    width: 100%;
    padding: 6px 8px;
    border-radius: var(--radius-ctl);
    background: none;
    border: none;
    color: inherit;
    text-align: left;
    text-decoration: none;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}
.parentline__item:hover { background: var(--side-hover); }
.parentline__item--danger:hover { background: var(--side-hover); color: var(--bad); }
.parentline__reparent {
    display: flex;
    gap: 6px;
    margin-top: 4px;
    padding-top: 8px;
    border-top: 1px solid var(--rule);
}
.parentline__num { width: 100%; min-width: 0; }

/* Блок «Подзадачи» — тот же ряд, что у «Ресурсов» (`.res__head`, ниже), и
   строки того же вида (`.res`, `.res__row`): две панели карточки, устроенные
   одинаково, не должны выглядеть двумя разными системами.

   24px — см. комментарий у `.tdesc__files` выше: три отступа карточки
   держатся на одном числе. */
.subblock { margin-top: 24px; }

/* Ресурсы — строками одна под другой, невысокими (правка владельца
   25.08.2026). Плитками в два столбца они были ровно один заход: половина
   текстовой колонки режет и имя файла, и подпись, а список файлов читают
   сверху вниз, а не разглядывают.

   Ширина — вся текстовая колонка, те же 74ch, что у описания: в строку
   помещаются имя, размер и автор без переносов. */
.res {
    display: flex;
    flex-direction: column;
    gap: 6px;
    /* Ширина и нижний отступ — ниже, в сведённом разделе блока ресурсов. */
}

/* Строка — кнопка (у вложений «Входящих» ссылка), поэтому сбрасываем всё,
   что браузер даёт кнопке от себя: свой шрифт, свой цвет, выравнивание по
   центру и рамку. */
.res__row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-width: 0;
    /* Правое поле — 5px, не 10 (правка владельца 27.08.2026: «отступ от
       границы справа такой же, как сверху и снизу»). С общим полем 10px
       кружок (аватар/точки/плюс) стоял от правой рамки строки на 11px
       (10 поля + 1 рамка), а от верхней и нижней — на 6px (5 поля + 1
       рамка): вертикаль икон была ближе к рамке сверху и снизу, чем справа.
       Левое поле не трогаем — слева в строке текст, не кружок, и на глаз
       там сравнивать не с чем. */
    padding: 5px 5px 5px 10px;
    border: 1px solid var(--hair);
    border-radius: var(--radius-ctl);
    background: var(--solid-bg);
    box-shadow: var(--lift);
    font: inherit;
    color: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.res__row:hover { box-shadow: var(--lift-hi); }

.res__row:focus-visible {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
}

/* Строка подзадачи (task.html): значок и название — ссылка `.res__open`,
   `display: contents` не рисует своей рамки и не трогает флекс-раскладку
   строки, а аватар в `.res__meta` — отдельное меню поверх неё, не вложенное.
   Цвет и подчёркивание сбрасываем сами: UA-стиль ссылки достаётся дочерним
   значку и названию через наследование, даже когда у самой ссылки нет
   бокса. */
.res__open {
    display: contents;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}
.res__open:hover .res__name { text-decoration: underline; }

/* Строка ресурса открывается щелчком куда угодно, а не только по названию
   (просьба владельца 28.08.2026: «сама строка должна быть кликабельной для
   перехода по ссылке, не только текст ссылки/заголовок»).

   Приём — растянутая ссылка: своего бокса у `display: contents` нет, но
   псевдоэлемент его получает и накрывает всю строку (`.res__row` уже
   `position: relative`). Отдельным классом, а не всем `.res__open`: у строки
   подзадачи в правой части живёт меню исполнителя, накрыть его нельзя. */
.res__open--row::after {
    content: "";
    position: absolute;
    inset: 0;
}

/* Три точки — поверх этого покрывала, иначе щелчок по меню уходил бы в
   переход по строке, а меню осталось бы закрытым. Своё позиционирование у
   них уже есть от `.fmenu` (position: relative), не хватало только слоя:
   открытым меню поднимается до 30 само, а закрытое сидело вровень с
   покрывалом и проигрывало ему порядком в разметке. */
.res__row > .res__menu { z-index: 1; }

/* Открытое меню исполнителя — над соседними строками списка, тот же приём,
   что у `.trow__who`/`.trow__from` в общем списке: без подъёма поповер
   нижней строки рисуется под аватаром и фоном строки под ней. Меню на три
   точки (`.res__menu`, 27.08.2026) — та же ловушка, тот же приём. */
.res__row:has(.asgmenu[open]),
.res__row:has(.res__menu[open]) { z-index: 1; }

/* Три точки сидят у правого края строки — поповер растёт влево, а не
   вправо за край колонки (умолчание `.fmenu__pop` — left: 0, здесь
   наоборот). Тот же приём, что у меню срока в строке списка задач. */
.res__menu > .fmenu__pop { right: 0; left: auto; }

/* Сама кнопка ярче обычного мутного `.circle` (просьба владельца
   27.08.2026) — три точки едва читались на фоне строки. */
.res__menu > .circle { color: var(--ink-2); }

/* Наведение — заливкой, не обводкой (просьба владельца 27.08.2026: «как у
   наведения на ресурсы и подзадачи»). Две первые попытки промахнулись мимо
   образца: `var(--surface)` (намёк `.trow:hover`) и `var(--sunk)` с тенью
   (кнопка «+», `.res__add > .circle:hover`) — владелец отверг обе. Настоящий
   образец — сам заголовок-раскрывашка `.subfold-toggle:hover` («Подзадачи»,
   «Ресурсы» рядом): плоская заливка `var(--surface-2)`, без тени и без
   рамки, проверено вживую (computed background). Обычный `.circle:hover`
   рисует ещё и рамку — здесь она снята, точки видны заливкой круга, не
   кольцом вокруг него. */
.res__menu > .circle:hover {
    border-color: transparent;
    background: var(--surface-2);
    box-shadow: none;
}

/* Ширина колонки фиксирована по самому широкому значку (файловая плашка,
   20px, правка владельца 27.08.2026), а не по содержимому — иначе у строки
   подзадачи (status_icon, 14px) название съезжает левее, чем у строки
   ресурса, и общей вертикали между двумя блоками карточки не получается. */
.res__ico {
    display: inline-flex;
    justify-content: center;
    flex: 0 0 20px;
    width: 20px;
    color: var(--muted);
}

/* Длинное имя обрезается многоточием, целиком остаётся в подсказке и в окне
   файла: строка низкая по замыслу, переносить в ней нечего. */
.res__name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.res__meta {
    /* Раскладка, размер и цвет — ниже, в сведённом разделе блока ресурсов. */
    white-space: nowrap;
}

/* Когда карточку трогали. Одна строка вместо панели в четыре: три из них
   повторяли шапку описания. */
.task-times {
    margin: 4px 0 var(--gap);
    color: var(--muted);
    font-size: 12px;
}

.task-head__meta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.task-body {
    margin: 0;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Правка описания подменяет текст, а не встаёт под ним (правка владельца
   24.08.2026) — то же решение, что у реплик, и по той же причине: у длинного
   описания форма снизу оказывалась за краем экрана.

   Пока правка закрыта, от неё видна только подпись; открыли — текст, кнопка
   «Развернуть» и сама подпись уходят, на их месте поле с исходной разметкой. */
.task-edit { display: none; margin-top: 14px; }

.tedit__state {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.task-edit__open {
    display: inline-block;
    margin-top: 14px;
    cursor: pointer;
    font-size: 12.5px;
    color: var(--ink-2);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.task-edit__open:hover { color: var(--ink); }

.tedit__state:checked ~ .tdesc__body .task-edit { display: block; }

.tedit__state:checked ~ .tdesc__body .task-body,
.tedit__state:checked ~ .tdesc__body .fold__more,
.tedit__state:checked ~ .tdesc__body .task-edit__open { display: none; }

/* Клавиатура: обводка рисуется вокруг подписи, а не вокруг спрятанного флажка. */
.tedit__state:focus-visible ~ .tdesc__body .task-edit__open {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
}

.task-meta {
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 13px;
}

.task-meta .eyebrow { display: block; margin-bottom: 1px; }

/* Переключатель статуса: в карточке — колонка, текущий «нажат». */

.switch { display: flex; flex-wrap: wrap; gap: 6px; }
.switch--stack { flex-direction: column; }
.switch--stack form { display: contents; }

.switch__item {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    width: 100%;
    padding: 7px 12px;
    background: var(--card);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-ctl);
    font: 400 13px/1 var(--sans);
    cursor: pointer;
}

.switch__item span { color: var(--ink); }
.switch__item:hover { background: var(--paper); }

/* Тот же переключатель, но радиокнопками: на странице заведения статус
   уезжает общей отправкой, отдельной формы на каждый пункт там нет.
   Сама радиокнопка скрыта — рамку рисует состояние :checked. */
.switch__radio { position: absolute; opacity: 0; pointer-events: none; }

.switch__item:has(.switch__radio:checked) {
    border-color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--ink);
}

.switch__item:has(.switch__radio:checked) span { font-weight: 500; }

/* Лента: правки полей и комментарии одним потоком. Правки набраны мельче —
   они фон, а разговор людей важнее. */

/* Обсуждение без общего контейнера (владелец, 31.08.2026, вторым шагом той
   же правки): записи ленты сами карточки, и рамка панели вокруг них
   дублировала границы — рамка в рамке. Секция осталась `.panel` в разметке,
   чтобы не трогать шаблон и тесты; вид рамки снят здесь. Ширина — та же
   полоса, что у описания (`.tdesc__body`, 74ch): реплики читают как текст,
   и мера строки у них общая. */
.panel--feed {
    background: none;
    border: none;
    border-radius: 0;
    overflow: visible;
    max-width: 74ch;
}

/* Шапке и телу нечего отбивать от рамки, которой нет: боковые поля сняты,
   лента и поле ответа стоят в одной колонке с заголовком «Обсуждение». */
.panel--feed .panel__head { padding: 0 0 10px; border-bottom: none; }
.panel--feed .panel__body { padding-left: 0; padding-right: 0; }

/* Правая пара шапки — решётка и «Развернуть» — одной обёрткой: два прижатых
   соседа делили бы свободное место, и решётка повисла бы в середине. */
.feed__tools { display: flex; align-items: center; gap: 8px; }

/* Технические записи скрыты по умолчанию (владелец, 31.08.2026): мелкие
   строки движений, служебные подписи под репликами и журнал запусков.
   Прячет их скрытый флажок перед шапкой — решётка переключает его без
   скрипта, тем же приёмом, что правка описания (`.tedit__state`).
   «Задача заведена» и файлы — не техника, они видны всегда. */
.fsys__state {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.fsys__state:not(:checked) ~ .feed .feed__minor,
.fsys__state:not(:checked) ~ .feed .feed__change--sys,
.fsys__state:not(:checked) ~ .feed .feed__tech { display: none; }

/* Включённая решётка помечена мягкой заливкой: состояние переключателя
   должно быть видно и без открытой техники на экране. */
.fsys__state:checked ~ .panel__head .feed__sys {
    background: var(--accent-soft);
    color: var(--accent);
}

/* Клавиатура: обводка рисуется вокруг решётки — самого флажка на экране нет. */
.fsys__state:focus-visible ~ .panel__head .feed__sys {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
}

/* Лента — колонка карточек и мелких строк с воздухом между ними (владелец,
   31.08.2026, по образцу из «Входящих» 1217): реплика — карточка, движение
   по карточке — мелкая строка. Линейки-разделители убраны — записи отделяет
   сам зазор. */
.feed {
    margin: 0;
    padding: 0 0 12px;
    list-style: none;
    display: grid;
    gap: 10px;
}

.feed__item { padding: 2px 0; }

.feed__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}

.feed__who { font-size: 12.5px; font-weight: 500; }

/* Кому передали задачу — сразу за именем, стрелкой. Раньше это была отдельная
   запись ленты со своей шапкой и своим временем: два-три поста подряд про
   одно движение. Стрелка отвечает на тот же вопрос там же, где читают, кто
   пишет. */
.feed__moved { font-size: 12.5px; font-weight: 500; opacity: .85; }

/* Время прижато к правому краю: слева читают «кто и кому», справа «когда».
   `margin-left: auto`, а не распорка в разметке: порядок тегов должен
   оставаться смысловым. */
.feed__when { font-size: 11.5px; margin-left: auto; }

.feed__change {
    margin: 0;
    font-size: 12.5px;
    color: var(--ink-3);
}

.feed__value { color: var(--ink-2); }

/* Движение без слов — одна мелкая строка: имя, что переставили, время
   справа. Тише реплик намеренно: это след по карточке, а не разговор, и
   спорить с карточками за внимание ему нельзя (та же причина, по которой
   IR-36 прятал его совсем). Несколько перестановок одного движения стоят
   в той же строке через запятую-пробел. */
.feed__minor {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
    padding: 0 6px;
    font-size: 12px;
    color: var(--muted);
}

.feed__minor-who { font-weight: 500; color: var(--ink-2); }

/* Отдельной подписи «изменён …» под текстом нет (правка владельца
   24.08.2026): у правленой реплики время правки стоит в самой шапке, а
   исходное приходит подсказкой при наведении. */

/* Действия реплики и её время — одной правой группой в фиолетовой шапке.
   Правка владельца 24.08.2026: «перенести в строку заголовка, вправо, ближе к
   дате; порядок — Редактировать, Удалить, дата».

   Прижата группа, а не каждый элемент по отдельности: два соседа с
   `margin-left: auto` делят свободное место между собой, и кнопки повисли бы
   в середине строки вместо правого края. */
.feed__side {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
}

.feed__side .feed__when { margin-left: 0; }

/* Значки правки и удаления живут только у РАСКРЫТОЙ реплики и проявляются
   при наведении на неё — правка владельца 31.08.2026: «карандаш и корзина
   должны появляться и быть активны только при наведении на развёрнутый
   комментарий». Невидимый значок клика не ловит (`pointer-events: none`):
   раньше он был прозрачным, но живым, и щелчок по свёрнутой полоске в этом
   месте удалял или правил реплику вместо того, чтобы развернуть её.

   Прежняя правка 24.08.2026 «показывать без наведения» пришла от телефонов,
   где наведения нет вовсе, а тап его лишь изображает. Это осталось верным,
   поэтому на устройствах без курсора значки раскрытой реплики видны
   постоянно — блок @media (hover: none) ниже.

   Место значки занимают всегда, даже невидимые: иначе дата дёргалась бы
   влево-вправо при каждом раскрытии.

   Размер равен имени автора (13px в шапке реплики): значок крупнее спорил бы
   с ним за внимание, мельче — не попасть пальцем. */
.feed__act {
    background: none;
    border: 0;
    padding: 0;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    list-style: none;
    opacity: 0;
    pointer-events: none;
    transition: opacity .16s ease-out;
}

/* :focus-within — для клавиатуры: pointer-events не мешает табу дойти до
   значка, но без этого правила подсвеченный значок оставался бы немым. */
.fitem[open]:hover .feed__act,
.fitem[open]:focus-within .feed__act {
    opacity: .85;
    pointer-events: auto;
    transition: opacity .16s ease-in;
}

.fitem[open] .feed__act:hover,
.feed__act:focus-visible { opacity: 1; }

@media (hover: none) {
    .fitem[open] .feed__act {
        opacity: .85;
        pointer-events: auto;
    }
}

/* Поле, растущее по содержимому (IR-36). Высоту считает `tasks.js`; здесь
   только убрана ручка изменения размера — она обещает то, чем управляет уже
   не человек, — и спрятана прокрутка, пока текст помещается. Без JS поле
   остаётся обычным: высота приходит из `rows`. */
textarea[data-grow] {
    resize: none;
    overflow-y: hidden;
}

/* Форма правки реплики встаёт НА МЕСТО текста, подменяя его.

   Правка владельца 24.08.2026: раньше поле раскрывалось под репликой, и у
   длинной оно оказывалось за краем экрана — нажавший «Редактировать» видел,
   что ничего не произошло. Теперь показанный текст на время правки скрыт, а
   поле занимает его место: страница не прыгает, и правишь там, где читал.

   Состояние держит скрытый флажок перед раскрывашкой: `summary` в `summary`
   не вкладывается, а кнопка правки уехала в шапку. */
.fedit__state {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.fedit-form { display: none; }

.fedit__state:checked ~ .fitem .feed__text { display: none; }

.fedit__state:checked ~ .fitem .fedit-form {
    display: block;
    padding: 6px 16px 14px;
}

/* Отказ — справа, созидательное слева: тот же порядок, что у «Опубликовать» и
   «Отправить боту» под полем комментария. */
.inplace__side { margin-left: auto; }

/* Узкий экран: строке кнопок разрешено переноситься. Ряд `nowrap` на телефоне
   выталкивал «Отмену» за правый край, если подписи не помещались в две
   кнопки. */
@media (max-width: 600px) {
    .inplace .row-actions { flex-wrap: wrap; row-gap: 8px; }
}

.fedit-form textarea {
    display: block;
    width: 100%;
    margin: 8px 0;
    font: inherit;
}

/* Клавиатура: обводка рисуется вокруг подписи в шапке, а не вокруг спрятанного
   флажка — его на экране нет. */
.fedit__state:focus-visible ~ .fitem .feed__act[for] {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
    opacity: 1;
}

/* Перенос строк держит разметка (`taskdoc`), а не `white-space: pre-wrap`:
   с ним заголовки и таблицы получали бы сверху пустые строки от переносов,
   которые разметка уже съела. */
.feed__text {
    margin: 0;
    font-size: 13.5px;
    overflow-wrap: anywhere;
}

/* Комментарий — карточка: волосяная рамка, скругление темы, фон панели.
   Владелец, 31.08.2026, по образцу из «Входящих» 1217. До этого реплика
   была полосой с шапкой, залитой акцентом (21-25.08.2026): границу держала
   заливка. У карточки границу держит рамка, поэтому шапка вернулась к
   спокойному виду — фиолетовые полосы через всю ленту читались тяжелее
   самих реплик. `overflow: hidden` обязателен: без него заливка формы
   правки вылезала бы из скруглённых углов. */
.feed__item--said {
    margin: 0;
    padding: 0;
    /* Белый лист, как у строк «Ресурсов» и «Подзадач» (просьба владельца
       01.09.2026): реплика лежит на карточке, и --surface от неё почти не
       отличался. В тёмной теме токен возвращается к --card. */
    background: var(--solid-bg);
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    overflow: hidden;
}

/* Шапка реплики: аватар, имя, справа действия и время. Свёрнутая запись —
   карточка из одной этой строки, поэтому нижнее поле у шапки своё, а не
   от текста под ней. */
.feed__item--said .feed__meta {
    margin: 0;
    padding: 10px 16px 8px;
}

.feed__item--said .feed__who { font-size: 13px; }

.feed__item--said .feed__text { padding: 2px 16px 14px; }

/* Правки внутри реплики: та же левая вертикаль, что у текста. Вид строки не
   меняется — мелкая подпись о том, что заодно поменялось, — меняется только
   место: она переехала под текст из отдельного поста. */
.feed__item--said .feed__change { padding: 0 16px; }
.feed__item--said .feed__change:last-child { padding-bottom: 12px; }

/* Текст, за которым идёт такая строка, отдаёт ей свой нижний отступ: иначе
   между репликой и подписью получается провал в полтора межстрочья. */
.feed__item--said .feed__text:has(+ .feed__change) { padding-bottom: 6px; }

/* Аватар в ленте — метка начала реплики. Цвет тот же, что в списке задач:
   у агента жёлтый, у человека свой из восьмицветного ряда. Обводка с
   акцентной шапки карточке не нужна: на фоне панели все восемь цветов
   ряда видны сами. */
.feed__item--said .av {
    width: 20px;
    height: 20px;
    font-size: 9px;
}

/* ── Ветки обсуждения (IR-71, 07.09.2026) ──────────────────────────────────
   Отдельный блок ПОСЛЕ секции ленты нарочно: редизайн ленты 31.08.2026 на
   пробе, и при его откате ветки должны пережить его одной переименовкой
   классов, а не уйти вместе с ним. Только существующие токены — стили
   заморожены (tests/test_design.py).

   Ответ — строка внутри карточки корня: без своей рамки и скругления,
   отделён волосяной линией сверху и сдвинут влево полосой того же цвета.
   Форма ответа и кнопка — та же механика :checked ~, что у правки:
   работает без скрипта. */
.feed__thread {
    list-style: none;
    margin: 0;
    padding: 0 0 0 16px;
    border-top: 1px solid var(--hair);
}

.feed__item--reply {
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    border-left: 2px solid var(--hair);
    border-radius: 0;
}

.feed__item--reply + .feed__item--reply { border-top: 1px solid var(--hair); }

.feed__item--reply .feed__meta { padding: 8px 14px 6px; }
.feed__item--reply .feed__text { padding: 2px 14px 12px; }

.frepl__state {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.frepl {
    padding: 6px 16px 10px;
    border-top: 1px solid var(--hair);
}

/* «Ответить» — только у раскрытой реплики (Антон, 08.09.2026): у свёрнутой
   полоски видно одно имя, и кнопка под ней читалась как ответ неизвестно на
   что. Блок ответа стоит соседом ПОСЛЕ раскрывашки — как и форма правки, он
   обязан жить снаружи `details` (`summary` внутри `summary` не живёт, а форму
   открывает флажок), — поэтому прячем его от самой `details`, а не изнутри.
   Ветка ответов при свёрнутом корне остаётся видимой: просили спрятать
   кнопку, не разговор. */
.fitem:not([open]) ~ .frepl { display: none; }

.frepl-form { display: none; }

/* Кнопка — по размеру подписи, а не на всю ширину карточки: подпись к флажку
   растянулась бы блоком. */
.frepl__open { display: inline-flex; }

.frepl__state:checked ~ .frepl .frepl-form { display: block; }
.frepl__state:checked ~ .frepl .frepl__open { display: none; }

.frepl-form textarea {
    display: block;
    width: 100%;
    margin: 8px 0;
    font: inherit;
}

.frepl__state:focus-visible ~ .frepl .frepl__open {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
}

.mention { color: var(--ozon); font-weight: 500; }

.task-back { margin: 0 0 10px; font-size: 13px; }
/* Номер задачи — в той же строке, справа от «← Все задачи» (правка владельца
   31.08.2026; раньше стоял в шапке листа, правее заголовка). С 02.09.2026 он
   набран тем же кеглем, что и ссылка, а между ними стоит вторая стрелка:
   строка читается как путь, а не как ссылка с довеском. Правило только для
   карточки задачи — у остальных страниц с `.task-back` номера в строке нет. */
.tcard > .task-back { display: flex; align-items: baseline; gap: 8px; }
.task-back__num {
    padding: 0;
    border: 0;
    background: none;
    color: var(--ink-2);
    font-size: 13px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    cursor: pointer;
}
.task-back__num:hover { color: var(--ink); }
/* Указатели — обычная «←» шрифтом, как на странице новой задачи: рисованная
   стрелка с длинной чертой выглядела чужой рядом с остальным (две попытки,
   02.09.2026). Цвет — готовый `--muted`, тот же, каким набраны все
   приглушённые подписи: своего оттенка указателям не нужно, а смешение
   значений на месте — это ещё один цвет, которого нет в своде. Первая
   стрелка свешивается влево, чтобы «Все задачи» встали на вертикаль
   содержимого страницы. */
.task-back__arrow {
    flex: none;
    color: var(--muted);
}
.tcard > .task-back > .task-back__arrow:first-child { margin-left: -19px; }
.task-back a { color: var(--ink-2); text-decoration: none; }
.task-back a:hover { color: var(--ink); text-decoration: underline; }

/* Нагрузка по людям */

.person-cell {
    display: flex;
    align-items: center;
    gap: 10px;
}

.notify-state {
    margin: 0;
    flex: 1 1 260px;
    font-size: 13px;
    color: var(--ink-2);
}

/* Задачи в сводке: одна строка над карточками маркетплейсов. */

.tstrip {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    padding: 14px 18px;
    margin-bottom: var(--gap);
    background: var(--card);
    border: 1px solid var(--rule);
    border-left: 4px solid var(--rule-strong);
    border-radius: var(--radius);
}

.tstrip__figure {
    font: 600 20px/1 var(--sans);
    font-variant-numeric: tabular-nums;
}

.tstrip__unit { font-size: 12.5px; color: var(--ink-2); margin-left: 5px; }
.tstrip__gap { flex: 1; }

/* Шкала цели в полосе сводки: тянется на всю свободную ширину. Без flex-grow
   пустой div схлопнулся бы в ноль, и заливке нечего было бы заполнять. */
.tstrip__meter { flex: 1 1 120px; }

.tstrip__urgent {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-size: 13px;
}

.tstrip__urgent a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Узкий экран. Порядок отбрасывания колонок: сначала «когда» (нужна реже
   всего), потом номер, потом аватары. Срок не отбрасывается — он и есть то,
   ради чего в список смотрят с телефона. */

@media (max-width: 1024px) {
    /* `minmax(0, 1fr)`, а не `1fr`: голое `1fr` это `minmax(auto, 1fr)`, и
       колонка не сжимается уже своего содержимого. Широкий экран это правило
       имел с самого начала, а узкий — нет, и страница задачи с таблицами или
       блоками кода уезжала в горизонтальную прокрутку на телефоне. Заметно
       было только там, где в описании есть таблица: IR-219 и IR-19 не влезали,
       IR-294 без таблиц влезала.

       Обёртки `.table-scroll` и `pre { overflow-x: auto }` тут ни при чём и
       работали верно — им просто не давали шанса: контейнер вокруг них рос
       вместе с ними. */
    .tsheet { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 760px) {
    /* ключ · заголовок · дедлайн · метки · пара · действие.

       Ключ здесь остаётся, хотя раньше уходил первым. Он не украшение: за
       номером в список и ходят чаще всего, и копируется он щелчком. Место под
       него берём у значка важности — тот и так виден цветом строки.

       Бейдж подзадач (IR-53) своей колонки здесь больше не просит — с
       26.08.2026 он стоит внутри заголовка, а не рядом с ним. */
    .trow {
        /* Те же переменные, что на широком экране (правило `.trow` в
           начале файла) — «В строке видно» обязана работать одинаково на
           любой ширине, а не только там, где сетку не переопределяли под
           телефон. Важности тут нет вовсе — она не колонка грида на этом
           экране, см. `.trow > .asgmenu` ниже. */
        grid-template-columns: var(--col-key, calc(var(--task-key-w) + var(--task-key-gap)))
                               minmax(0, 1fr) var(--col-due, var(--task-due-w)) var(--col-labels, auto)
                               var(--col-pair, auto) var(--col-do, auto);
        gap: 8px;
    }
    /* Номера колонок здесь свои — на этой ширине их шесть, не семь (важность
       вне грида, см. `.trow > .asgmenu` ниже), и без переопределения
       остались бы от широкого экрана: пропавший заголовок задачи тем же
       багом, что и на широком (см. комментарий у `.board--hide-*`). */
    .trow__key { grid-column: 1; }
    .trow__main { grid-column: 2; }
    .trow__deadline { grid-column: 3; }
    .trow__labels { grid-column: 4; }
    .trow__pair { grid-column: 5; }
    .trow__do { grid-column: 6; }
    /* Значок важности больше не прячется на телефоне (живой отзыв владельца
       28.08.2026: «слева есть пустое место, туда поместится метка
       важности как раз», об этом самом зазоре — см. `.trow` выше, левое
       поле 28px рассчитано на значок и по-прежнему на месте, хотя колонка
       под него в гриде здесь не заведена).

       Вместо колонки — абсолютное позиционирование внутри левого поля:
       строка остаётся `position: relative`, и значок занимает готовый
       зазор, не будучи участником грида вовсе. Это и есть требование
       «остальные элементы не должны сдвигаться вправо» — займи он свою
       колонку в `grid-template-columns` заново, ключ и всё за ним сдвинулись
       бы на её ширину, как на широком экране. Обёртка `.asgmenu` (не сам
       `.prio` внутри неё) — с 28.08.2026 значок стал меню, см. `.board--kanban`
       выше.

       Центрирование по высоте — отступом, не `transform: translateY(-50%)`:
       у `transform` есть побочный эффект, который здесь и всплыл (живая
       проверка браузером агента 28.08.2026) — он превращает элемент со
       свойством в собственный containing block для потомков с `position:
       fixed`. Меню важности как раз `position: fixed` на этой ширине экрана
       (см. «Меню на телефоне — листом поверх страницы» ниже по файлу): вместо
       viewport'а целиком оно мерило «весь экран» по 14-пиксельному значку и
       схлопывалось в него же. Половина высоты строки (14px) вместо неё —
       обычный отступ, эффекта на потомков не создаёт. */
    .trow > .asgmenu {
        /* `grid-column: auto` возвращает базовое правило (нужно «В строке
           видно» на широком экране, см. `.board--hide-*` в начале файла).
           Живой баг 28.08.2026: с явным номером колонки containing block
           абсолютного потомка грида — не вся строка, а САМА эта колонка
           (здесь она чужая, `.trow__key` ниже занял «1» на этой ширине), и
           `left: 7px` считался уже от неё, а не от левого края строки —
           значок уехал на середину номера. */
        grid-column: auto;
        position: absolute;
        left: 7px;
        top: 50%;
        margin-top: -7px;
    }
    /* С 23.09.2026 в том же поле живёт и галочка выбора (групповые
       действия, Антон). Вдвоём они в 28px не помещаются: поле растёт на
       ширину значка с зазором (14 + 7), галочка остаётся на своих 7px,
       значок важности отходит на прежние 28. Заголовок уже на 21px —
       цена того, что выбирать можно и с телефона. */
    .trow { padding-left: 49px; }
    .trow > .asgmenu { left: 28px; }
    .fbar__caption { width: auto; }
    /* Пара сжимается до одного кружка: 62 пикселя на двух человек — роскошь,
       когда рядом нечем показать срок. Исполнитель важнее автора: его меняют,
       автора только читают. */
    .trow__from, .tflow { display: none; }
}

@media (max-width: 520px) {
    /* Телефон: **ключ · заголовок · исполнитель**, и всё. Состав задан
       владельцем 21.08.2026 после того, как он посмотрел список с телефона.

       Что ушло и почему. Дедлайн, метки и кнопка перехода — три колонки,
       которые в 520 пикселях отнимали у заголовка больше половины, а
       заголовок и есть то, по чему задачу узнают. Срок и метки видны в самой
       карточке, кнопка там же; с телефона список читают, а не разбирают.

       Что осталось. Ключ — за ним и приходят: он копируется касанием, и
       раньше на телефоне его не было вовсе, отчего «не копируется» и
       выглядело поломкой. Исполнитель — единственное, что отвечает на вопрос
       «моё или не моё», не открывая задачу. */
    /* Ширина ключа — той же переменной, что и на широком экране. Здесь она
       была вписана числом и пережила правку колонки 23.08.2026: на широком
       экране отступ ушёл, на телефоне остался. Третье место с тем же числом —
       ровно тот случай, ради которого переменная и заводилась.

       Бейдж подзадач (IR-53) больше не отдельный трек (см. правило `.trow`
       выше) — видимых здесь остаются ключ, заголовок и пара, метки, дедлайн
       и кнопка статуса спрятаны ниже. */
    .trow {
        /* Метки, срок и действие уже сняты явно, строкой ниже, — здесь их
           перепрятывать через переменную незачем. Ключ и пара — те же
           переменные, что и на более широких экранах (см. `.trow` в
           начале файла). */
        grid-template-columns: var(--col-key, calc(var(--task-key-w) + var(--task-key-gap)))
                               minmax(0, 1fr) var(--col-pair, auto);
    }
    /* Три трека, три номера — тот же довод, что у 760px чуть выше: без
       явного номера прятание ключа сдвинуло бы заголовок в его колонку. */
    .trow__key { grid-column: 1; }
    .trow__main { grid-column: 2; }
    .trow__pair { grid-column: 3; }

    .trow__labels,
    .trow__deadline,
    .trow__do { display: none; }

    /* Пара возвращается, но только правой половиной: автор скрыт правилом
       760 пикселей выше, и в колонке остаётся один кружок исполнителя. */
    .trow__pair { display: flex; }

    /* Меню шире колонки — прижимаем к правому краю листа, а не к метке. */
    .lblmenu__pop { width: min(232px, 74vw); }
}

/* Агенты.

   Карточка агента отвечает на два вопроса до всякого текста: какого он уровня
   и работает ли сейчас. Поэтому уровень и состояние стоят рядом с именем, а
   цвет уровня — мера полномочий, а не украшение. */

.agrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--gap);
    margin-bottom: var(--gap);
}

.acard {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 16px 18px;
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    text-decoration: none;
}

.acard:hover { border-color: var(--rule-strong); }

/* Выключенный агент приглушён, но читаем: это состояние, а не ошибка. */
.acard--off { background: var(--side-bg); }
.acard--off .acard__title { color: var(--ink-2); }

.acard__head {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.acard__title {
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.acard__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.acard__purpose {
    margin: 0;
    font-size: 13px;
    color: var(--ink-2);
    overflow-wrap: anywhere;
}

.acard__foot {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    font-size: 11.5px;
    color: var(--ink-3);
}

/* Уровень доступа. Три ступени — три тональности: чем выше, тем больше
   открыто, и это должно быть видно раньше, чем прочитан текст. */

.alevel {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid currentColor;
    font: 500 11px/1.5 var(--sans);
}

.alevel--chief { color: var(--bad); }
.alevel--lead { color: var(--warn); }
.alevel--line { color: var(--ink-2); }

.alevel-block {
    padding: 14px 16px;
    background: var(--side-bg);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.alevel-block__head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
    font-size: 13px;
}

/* «Что читает / чем пользуется / кому поручает / что спрашивает» — четыре
   вопроса, на которые человек должен ответить до того, как нажмёт «Включить». */

.arights {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px 20px;
}

.arights__row p {
    margin: 2px 0 0;
    font-size: 13px;
    color: var(--ink-2);
}

.atree {
    margin: 6px 0 0;
    padding-left: 14px;
    list-style: none;
    border-left: 1px solid var(--rule);
}

.atree li {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 3px 0;
    font-size: 13px;
}

.docpick__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
}

/* Входящие: зона приёма файла.

   Пунктирная рамка — единственное место в системе, где она уместна: это
   общепонятный знак «сюда можно бросить». Подсветка при перетаскивании
   меняет и рамку, и подложку: одного цвета рамки на светлом фоне мало,
   чтобы заметить попадание в цель. */

.drop {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.drop__zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 22px 18px;
    border: 1px dashed var(--rule-strong);
    border-radius: var(--radius);
    background: var(--sunk);
    text-align: center;
}

.drop__zone--over {
    border-color: var(--ozon);
    background: var(--accent-soft);
}

.drop__file { max-width: 100%; }

.drop__hint {
    margin: 0;
    font-size: 13px;
    color: var(--ink-2);
}

.drop__meta {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.drop__meta .field { flex: 1 1 240px; }

.drop__foot {
    display: flex;
    align-items: center;
    gap: 12px;
}

.drop__gap { flex: 1 1 auto; }

/* Галочка рассылки стоит между полями и кнопкой: её видно до нажатия
   «Прислать», а не после того, как сообщение уже ушло троим. */
.drop__tell { margin: 12px 0; font-size: 13px; color: var(--ink-2); }

.drop__meter {
    flex: 0 1 260px;
}

.drop__meter[hidden] { display: none; }

/* Синий, а не зелёный: полоса показывает ход, а не благополучный исход. */
.drop__meter .meter__fill { background: var(--accent); }

.drop__name {
    flex: 1 1 200px;
    font-size: 13px;
}

/* Только что принятый файл — чтобы в длинном списке не искать глазами. */
.trow--fresh { background: var(--accent-soft); }

/* Переключатель темы: значок показывает, куда переключит, а не что сейчас.
   На светлой теме луна, на тёмной солнце. Показывается только когда сработал
   скрипт — он же ставит data-theme. */

#theme-toggle[hidden] { display: none; }

.thico { display: none; }

#theme-toggle[data-theme="light"] .thico--to-dark,
#theme-toggle[data-theme="dark"] .thico--to-light { display: inline-flex; }

/* Кнопка отката к «плоскому» виду панели (IR-66) убрана 01.09.2026 вместе с
   самим видом — по просьбе владельца. Вид у панели теперь один, переключать
   нечего. Значки `.flico` ушли следом. */


/* Wiki
   ------------------------------------------------------------------
   Раздел читает markdown с диска, поэтому тело страницы приходит готовым
   HTML и оформляется здесь, а не классами в самом тексте.

   Композиция везде одна: содержимое слева, узкая колонка справа — она
   отвечает на «где я и куда дальше», а не повторяет содержимое. */

.wgrid { display: grid; gap: var(--gap); align-items: start; }
/* min-width: 0 — иначе широкая таблица внутри распирает колонку грида
   и страница уезжает в горизонтальную прокрутку целиком. */
.wgrid__main { min-width: 0; }
.wgrid__side { display: grid; gap: var(--gap); align-content: start; }

@media (min-width: 1000px) {
    .wgrid { grid-template-columns: minmax(0, 1fr) 288px; }
    /* На странице текста боковая колонка едет со скроллом: оглавление нужно
       ровно тогда, когда читатель уже в середине длинного текста. */
    .wgrid--page .wgrid__side { position: sticky; top: 18px; }
}

/* Поиск — главное действие раздела, поэтому он крупнее прочих полей */

.wsearch {
    display: flex;
    gap: 8px;
    margin-bottom: var(--gap);
}

.wsearch__field {
    flex: 1 1 auto;
    min-width: 0;
    padding: 13px 15px;
    font: 400 15px/1.2 var(--sans);
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius-ctl);
}

.wsearch__field::placeholder { color: var(--muted); }

.wsearch__field:focus-visible {
    outline: 2px solid var(--accent-ring);
    outline-offset: 1px;
    border-color: var(--accent);
}

.wsearch__go {
    padding: 0 20px;
    font: 500 14px/1 var(--sans);
    color: var(--ink);
    background: var(--surface-2);
    border: 1px solid var(--rule);
    border-radius: var(--radius-ctl);
    cursor: pointer;
}

.wsearch__go:hover { border-color: var(--rule-strong); }

/* Лента поступлений: дата — колонка, тело — колонка. Ровный ряд дат слева
   читается как хроника, ради этого табличные цифры и фиксированная ширина. */

.wfeed { list-style: none; margin: 0; padding: 0; }

.wfeed__item {
    display: grid;
    grid-template-columns: 78px minmax(0, 1fr);
    gap: 14px;
    padding: 11px 0;
    border-top: 1px solid var(--hair);
}

.wfeed__item:first-child { border-top: 0; padding-top: 2px; }
.wfeed__date { font-size: 12.5px; color: var(--muted); padding-top: 2px; }
.wfeed__body { display: grid; gap: 7px; min-width: 0; }
.wfeed__summary { color: var(--ink); }
.wfeed__item--skip .wfeed__summary { color: var(--ink-2); }
.wfeed__note { font-size: 12.5px; color: var(--muted); }

.wfeed__warn {
    font-size: 12.5px;
    color: var(--warn);
    border-left: 2px solid var(--warn);
    padding-left: 9px;
}

.wfeed__pages { display: flex; flex-wrap: wrap; gap: 6px; }

/* Ссылка-пилюля на страницу, куда лёг материал */

.wtag {
    display: inline-block;
    padding: 3px 10px;
    border: 1px solid var(--hair);
    border-radius: 999px;
    background: var(--surface-2);
    font-size: 12.5px;
    color: var(--ink-2);
    text-decoration: none;
}

.wtag:hover { border-color: var(--accent); color: var(--accent); }

/* Разделы плиткой: имя, счётчик, три свежие страницы */

.wcats {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
    gap: 12px;
}

.wcat {
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    padding: 13px 14px 12px;
    background: var(--surface);
}

.wcat--empty { background: none; }
.wcat--empty .wcat__count { color: var(--muted); }

.wcat__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.wcat__name { font: 500 14.5px/1.2 var(--sans); color: var(--ink); }
.wcat__count { font-size: 15px; color: var(--accent); }

.wcat__hint {
    margin: 5px 0 0;
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--muted);
}

.wcat__pages { list-style: none; margin: 10px 0 0; padding: 0; }
.wcat__pages li { padding: 3px 0; font-size: 13.5px; }
.wcat__more { display: inline-block; margin-top: 6px; font-size: 12.5px; color: var(--ink-2); }

/* Три шага — здесь нумерация означает порядок, а не украшает список */

.wsteps { list-style: none; counter-reset: step; margin: 0 0 14px; padding: 0; }

.wsteps li {
    counter-increment: step;
    display: grid;
    grid-template-columns: 22px minmax(0, 1fr);
    gap: 10px;
    padding: 8px 0;
    border-top: 1px solid var(--hair);
}

.wsteps li:first-child { border-top: 0; padding-top: 0; }

.wsteps li::before {
    content: counter(step);
    font: 600 12px/1.7 var(--mono);
    color: var(--accent);
}

/* Обе строки шага держим во второй колонке. Без этого пояснение занимало
   первую свободную ячейку — вторую строку колонки с номером, шириной 22px, —
   и текст сыпался по одному-два символа. Номер рисуется через ::before и
   тоже считается элементом сетки: детей у строки три, а колонки две. */
.wsteps__what,
.wsteps__how { grid-column: 2; }

.wsteps__what { display: block; font-size: 13.5px; color: var(--ink); }
.wsteps__how { display: block; font-size: 12.5px; line-height: 1.45; color: var(--muted); }

.wlink { font-size: 13px; }

.wstate { margin: 0 0 10px; font-size: 13.5px; color: var(--ink); }
.wstate--quiet { color: var(--ink-2); font-size: 12.5px; line-height: 1.5; }
.wstate:last-child { margin-bottom: 0; }
.wchips { display: flex; flex-wrap: wrap; gap: 7px; }
.wempty { margin: 0; color: var(--ink-2); }

/* Находка поиска */

.whit {
    display: grid;
    gap: 3px;
    padding: 11px 13px;
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    margin-bottom: 8px;
    text-decoration: none;
    color: inherit;
}

.whit:last-child { margin-bottom: 0; }
.whit:hover { border-color: var(--rule-strong); background: var(--surface-2); }
.whit__title { font-weight: 500; }
.whit__path { font: 400 12px/1.3 var(--mono); color: var(--muted); }
.whit__snippet { font-size: 13px; color: var(--ink-2); }

/* Хлебные крошки, оглавление, соседи */

.wcrumbs { font-size: 12.5px; color: var(--ink-2); margin-bottom: 10px; }
.wcrumbs__sep { color: var(--muted); margin: 0 6px; }

.wtoc, .wnear { list-style: none; margin: 0; padding: 0; }
.wtoc li, .wnear li { padding: 4px 0; font-size: 13.5px; line-height: 1.4; }
.wtoc__l3 { padding-left: 12px; font-size: 13px; }
.wtoc a, .wnear a { color: var(--ink-2); text-decoration: none; }
.wtoc a:hover, .wnear a:hover { color: var(--accent); }

/* Тело страницы: мера строки — главное, что отличает текст от простыни */

.wiki__body {
    max-width: 68ch;
    font-size: 15px;
    line-height: 1.68;
    color: var(--ink);
}

.wiki__body > :first-child { margin-top: 0; }

.wiki__body h2 {
    margin: 30px 0 10px;
    padding-top: 14px;
    border-top: 1px solid var(--hair);
    font: 650 17px/1.3 var(--sans);
    letter-spacing: -0.015em;
}

.wiki__body h3 {
    margin: 22px 0 7px;
    font: 600 14.5px/1.35 var(--sans);
    color: var(--ink);
}

.wiki__body h4 {
    margin: 18px 0 6px;
    font: 600 13px/1.4 var(--sans);
    color: var(--ink-2);
}

.wiki__body p { margin: 0 0 12px; }
.wiki__body ul, .wiki__body ol { margin: 0 0 14px; padding-left: 20px; }
.wiki__body li { margin: 5px 0; }
.wiki__body li::marker { color: var(--muted); }

.wiki__body blockquote {
    margin: 0 0 14px;
    padding: 8px 0 8px 15px;
    border-left: 2px solid var(--accent);
    color: var(--ink-2);
}

.wiki__body code {
    background: var(--surface-2);
    border-radius: 4px;
    padding: 1.5px 5px;
    font: 400 .86em/1.4 var(--mono);
}

.wiki__code {
    background: var(--surface-2);
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    padding: 12px 14px;
    overflow-x: auto;
    margin: 0 0 14px;
    font-size: 13px;
    line-height: 1.55;
}

.wiki__code code { background: none; padding: 0; }
.wiki__scroll { overflow-x: auto; margin: 0 0 14px; }

.wiki__table { border-collapse: collapse; width: 100%; font-size: 13.5px; }

.wiki__table th {
    font: 600 11px/1 var(--sans);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: left;
    padding: 0 12px 7px 0;
    border-bottom: 1px solid var(--rule);
}

.wiki__table td {
    padding: 8px 12px 8px 0;
    border-bottom: 1px solid var(--hair);
    vertical-align: top;
}

.wiki__table tr:last-child td { border-bottom: 0; }
.wiki__body hr { border: 0; border-top: 1px solid var(--hair); margin: 22px 0; }

/* «Источники» — подвал страницы: происхождение знания, а не часть текста */

.wiki__body h2#istochniki {
    margin-top: 34px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.wiki__body h2#istochniki ~ ul { font-size: 13px; color: var(--ink-2); }
.wiki__body h2#istochniki ~ p em { color: var(--muted); }

/* Переход между дашбордом и рабочей частью системы. Отбит чертой: это не
   ещё один раздел в ряду, а смена всего экрана, и читаться должен иначе. */
.side__cross {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--rule);
}

.side__cross--top {
    margin: 0 0 10px;
    padding: 0 0 10px;
    border-top: 0;
    border-bottom: 1px solid var(--rule);
}


/* Пояснение под блоком: мелко и приглушённо. Это справка на случай «забыл,
   что это такое», а не часть интерфейса, поэтому не должно спорить с ним. */
.panel__note {
    margin: 0;
    max-width: 78ch;
    font-size: 12px;
    line-height: 1.5;
    color: var(--ink-3);
}

/* Ряд галочек в карточке человека: области и боты. Галочка со словом рядом
   читается быстрее выпадающего списка, когда вариантов три-четыре. */
.pickrow { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 2px 0; }

.pick {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    cursor: pointer;
}

.pick input { margin: 0; }

/* Карточка человека: свод «что может» двумя колонками — слева открытое,
   справа закрытое. Две колонки, а не один список с галочками и крестиками:
   глаз сравнивает столбцы быстрее, чем значки в строках. */
.cando { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 32px; }

@media (max-width: 720px) {
    .cando { grid-template-columns: minmax(0, 1fr); }
}

.cando__col .eyebrow { display: block; margin: 14px 0 4px; }
.cando__col > .eyebrow:first-child { margin-top: 0; }


.plain { margin: 0; padding: 0; list-style: none; font-size: 13.5px; }
.plain li { margin: 2px 0; }

/* Список с галочками: перечислено всё, отметка отвечает «да» или «нет».
   Строки с прочерком приглушены — глаз сразу видит, чего у человека нет,
   не сверяя два списка. */
.marks { margin: 0; padding: 0; list-style: none; font-size: 13.5px; }
.marks li { display: flex; gap: 8px; margin: 3px 0; }

.mark {
    display: inline-block;
    width: 12px;
    flex: none;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.mark--off { color: var(--ink-3); }
.mark--off .mark { color: var(--rule-strong); }

/* Строка таймера как кнопка: расписание правится там же, где показано.

   Кнопке снимается вся её внешность — рамка, фон, выравнивание текста, —
   иначе ряд одинаковых строк превратился бы в ряд серых прямоугольников.
   Остаётся указатель и подсветка при наведении: без них строка выглядит
   мёртвой, и никто не догадается, что по ней можно щёлкнуть. */

.timer--act {
    width: 100%;
    font: inherit;
    color: inherit;
    text-align: left;
    background: none;
    border: none;
    border-radius: var(--radius-ctl);
    cursor: pointer;
    padding: 8px 6px;
    margin: 0 -6px;
}

.timer--act:hover { background: var(--surface-2); }
.timer--act:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.timer--act + .timer--act { border-top: 1px solid var(--rule); }

/* Подсказка под полем в окне расписания: формат календаря помнить наизусть
   никто не обязан, а ошибиться в нём легко и незаметно. */
.tmwin__hint {
    font-size: 12px;
    line-height: 1.4;
    color: var(--ink-2);
}

/* Вкладки раздела: Marketing Dashboard — первый раздел, у которого их больше одной.
   Отдельного вида не заводим, берём чипы: у них уже есть «включён» и они
   везде в системе означают одно и то же — выбор из ряда. */

.subtabs {
    display: flex;
    gap: 6px;
    margin-bottom: 18px;
}

/* Переработка дизайна. Таблица широкая и текстовая: обоснование — не число,
   а фраза, и оно должно читаться, а не обрезаться. */

/* Колонок тринадцать, из них две — связный текст. В ширину панели они влезают
   только сплющенными до переноса по слогам, поэтому таблице задан свой
   минимум, а тесно станет — .table-scroll прокрутит её вбок. Читать строку
   вправо привычнее, чем разбирать столбик из обрубков слов.

   Число — сумма минимумов колонок ниже плюс числовые: меньшее объявление
   ничего не давало, браузер всё равно раздвигал таблицу по ячейкам, а в CSS
   стояла цифра, из которой ничего не следовало. */
.rd-table { min-width: 2060px; }

/* Ячейки многострочные: обоснование в три строки против одной цифры остатка.
   Прижимаем всё к верху, иначе числа плавают по середине высокой строки. */
.rd-table td { vertical-align: top; }

.rd-col-art { min-width: 260px; }
.rd-col-st { min-width: 124px; }
.rd-col-kind { min-width: 152px; white-space: nowrap; }
.rd-col-why { min-width: 320px; }

.rd-name {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--muted);
}

.rd-no { color: var(--ink-3); }

.rd-why { max-width: 440px; }

/* Переход на карточку витрины. Знак вместо слова «карточка»: в таблице их
   шестьсот, и подпись у каждого превратила бы колонку вердикта в частокол.
   Тихий, пока на строку не навели, — вердикт важнее ссылки. */
.rd-open {
    margin-left: 6px;
    padding: 2px 4px;
    color: var(--ink-3);
    text-decoration: none;
    font-size: 13px;
}

tr:hover .rd-open { color: var(--ink-2); }
.rd-open:hover,
.rd-open:focus-visible { color: var(--accent); }

.rd-line {
    display: block;
    font-size: 12.5px;
    line-height: 1.45;
}

.rd-line + .rd-line { margin-top: 3px; }

/* Стоп-признак и пояснение — тише основного обоснования: они не про то, что
   менять, а про то, почему сначала стоит посмотреть в другую сторону. */
.rd-line--block { color: var(--warn); }
.rd-line--note { color: var(--muted); }

.rd-num { text-align: right; white-space: nowrap; }

/* Нужна и на Ozon, и на ВБ — самые дорогие строки. Подсветка слева полосой,
   а не фоном: фон тут уже занят наведением. */
.rd-row--both td:first-child { box-shadow: inset 2px 0 0 var(--bad); }

/* Статус из реестра — слово людей, а не вердикт машины: спокойный тон,
   без цвета состояния. Цвет здесь заняли вердикты, и спорить им незачем. */
.rd-status { white-space: nowrap; }

/* Пояснения этой вкладки читают целиком: это не подпись под таблицей, а
   правила отбора — по ним спорят с цифрами. Общие 78ch на кегле 12 рвали
   фразу на десяток обрубков в левой трети листа. */
.rd-note {
    max-width: 104ch;
    font-size: 13px;
    line-height: 1.55;
    color: var(--ink-2);
}

/* Отбор строк. Живёт внутри панели, поэтому отступ по бокам — тот же, что у
   её тела: строка фильтров должна стоять на одной вертикали с таблицей. */
.rd-filters {
    margin: 0;
    padding: 12px 18px 14px;
    border-top: 1px solid var(--rule);
}

/* Строка фильтров занимает всю ширину листа, а не левую его треть: таблица
   под ней широкая, и сбитый в угол ряд чипов читается как обрывок. */
.rd-filters .fbar__row { gap: 8px; }

/* Вторая ось — к правому краю: «Все / Ozon / ВБ / Обе» и «Обложка /
   Инфографика» это разные вопросы, и одним рядом чипов они сливались. */
.rd-filters__next { margin-left: auto; }

/* Поля ввода в системе тянутся на всю ширину — здесь они стоят в ряд чипов
   и должны быть по себе. */
.rd-filters__pick,
.rd-filters__find {
    flex: none;
    width: auto;
    padding: 5px 9px;
    font-size: 13px;
}

/* Поиск тянется по остатку строки: справа от него только счётчик, и пустое
   место между ними ничего не сообщало. */
.rd-filters__find {
    flex: 1 1 260px;
    width: auto;
    max-width: 520px;
}

/* Заголовок-кнопка: сортировка. Выглядит как заголовок и ведёт себя как
   кнопка — своей рамки и фона не заводит, чтобы шапка таблицы осталась
   шапкой, а не рядом кнопок. */
.rd-sort {
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.rd-sort:hover { color: var(--ink-2); }
.rd-sort--on { color: var(--ink); }

/* Направление — знаком после подписи, а не цветом: цвет уже занят
   состоянием «по этой колонке отсортировано». */
.rd-sort--on::after { content: " ↓"; }
.rd-sort--on.rd-sort--up::after { content: " ↑"; }

/* Колонка действия: кнопка «в работу» на вкладке и поле даты в черновиках. */
.rd-col-act { min-width: 132px; }

/* Ряд действий: формы стоят рядом, пока хватает ширины, и переносятся
   строкой, когда не хватает. Отступ задаёт контейнер, а не соседи через
   margin — на переносе margin-left оставлял кнопки слипшимися по вертикали,
   и они наезжали друг на друга. */
.rd-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
}

.rd-start {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

/* Подпись поля — часть ряда, а не строка над ним: она короткая, и
   собственная строка ради неё разрывала ряд надвое. */
.rd-day__label { flex: none; }

/* Селектор с тегом, а не голым классом: общее правило полей ввода задаёт
   width: 100% через input[type="text"], и голый класс ему проигрывает по
   специфичности — поле даты растягивалось на всю панель. */
input.rd-day {
    flex: none;
    width: 92px;
    padding: 5px 9px;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

/* Шапка панели переносит содержимое: справа в ней живут чипы («Скачать CSV»,
   «К списку»), и без переноса они наползали на заголовок на узком листе. */
.panel__head--wrap {
    flex-wrap: wrap;
    row-gap: 8px;
}


/* Артикул с названием — вход в карточку товара. Ссылка накрывает обе строки
   ячейки: попадать курсором в короткий артикул неудобно. Что это ссылка,
   видно по цвету и пунктиру под артикулом — без них триста строк подряд
   читались как простой текст, и внутрь никто не заходил. */
.rd-card {
    display: block;
    color: inherit;
    text-decoration: none;
}

.rd-card__id {
    color: var(--accent);
    border-bottom: 1px dotted var(--accent);
}

.rd-card:hover .rd-card__id { border-bottom-style: solid; }
.rd-card:hover .rd-name { color: var(--ink-2); }

/* Карточка кандидата: показатели парами «подпись — значение», сеткой в
   несколько колонок. Список определений, а не таблица: это не ряды
   однородных строк, а разные величины про один товар. */
.rd-facts {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 14px 24px;
}

.rd-fact { margin: 0; }

.rd-fact dt {
    font: 600 11px/1 var(--sans);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.rd-fact dd {
    margin: 5px 0 0;
    font-size: 14px;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.rd-card__table { min-width: 620px; }

/* Провал ниже порога отбора — тем же цветом, что и вердикт в таблице. */
.rd-weak { color: var(--bad); }

/* Комментарий: поле всегда открыто на правку, без кнопки «редактировать» —
   его дописывают по ходу работы, и лишний щелчок означает, что не допишут. */
.rd-note-form {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

.rd-note-field {
    width: 100%;
    min-height: 44px;
    font-size: 13px;
    line-height: 1.45;
}

.rd-note-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

/* ── Сворачивание длинного (IR-454) ──────────────────────────────────────
   Две вещи на странице задачи прячутся визуально: длинное описание и старые
   реплики. Общее правило одно — текст остаётся в разметке целиком. Ни то ни
   другое не удаляется и не подгружается: агент, читающий страницу, и поиск
   по ней видят всё. Прячется только показ. */

/* Переключатель описания — скрытый чекбокс с подписью. Не кнопка со скриптом:
   так разворачивание живёт и при выключенном JS, а инлайновый обработчик CSP
   всё равно не пропустит. Убран с глаз, но не `display: none`: так он
   остаётся доступен с клавиатуры. */
.fold__box {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Свёрнутый текст обрезан по высоте и растворяется в фоне страницы. Не
   `display: none` — видно начало, и по обрыву понятно, что дальше есть ещё.

   Класс `clip`, а не `fold`: `.fold` выше по файлу занят свёрнутыми блоками
   таймеров и рисует им `border-top`. Описание брало его себе и получало
   чужую полосу над текстом — в светлой теме едва заметную, в тёмной явную.
   Двум разным вещам одно имя давать нельзя, даже когда обе «сворачивают». */
.clip {
    position: relative;
    max-height: 16em;
    overflow: hidden;
}

.clip::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 5em;
    background: linear-gradient(to bottom, transparent, var(--page));
    pointer-events: none;
}

.fold__box:checked ~ .tdesc__body .clip { max-height: none; }
.fold__box:checked ~ .tdesc__body .clip::after { content: none; }

/* Подпись переключателя. Обе надписи лежат в разметке, показывается та, что
   отвечает состоянию: подменять текст скриптом ради двух слов незачем. */
.fold__more {
    display: inline-block;
    margin-top: 2px;
    color: var(--accent);
    font-size: 13px;
    cursor: pointer;
}

.fold__more:hover { text-decoration: underline; }

/* Кнопка вместо подписи (IR-36). Под длинным описанием серую ссылку не
   находили — текст выглядел просто оборванным. Цвет тот же акцентный, но
   теперь он заливка, а не буквы: подпись «Развернуть» обязана читаться как
   действие, потому что это единственный способ дочитать задачу.

   Подчёркивания при наведении здесь нет: у залитой кнопки оно выглядит
   ошибкой вёрстки. Взамен подложка чуть темнеет. */
.fold__more--cta {
    margin-top: 10px;
    padding: 5px 14px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--on-fill);
    font-weight: 500;
}

.fold__more--cta:hover {
    text-decoration: none;
    filter: brightness(.93);
}

.fold__shut { display: none; }
.fold__box:checked ~ .tdesc__body .fold__open { display: none; }
.fold__box:checked ~ .tdesc__body .fold__shut { display: inline; }

/* Клавиатура: обводка рисуется вокруг подписи, а не вокруг спрятанного поля. */
.fold__box:focus-visible ~ .tdesc__body .fold__more {
    outline: 2px solid var(--accent-ring);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Шапка обсуждения: «Обсуждение › ③». Счётчик считает реплики, а не строки
   ленты, — служебные записи запуска в него не идут. */
.feed__title { display: flex; align-items: center; gap: 8px; }

.feed__caret {
    width: 0;
    height: 0;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    border-left: 5px solid var(--muted);
}

.feed__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: 12px;
    font-weight: 500;
}

/* Кнопка «развернуть всё». Обе надписи снова лежат обе; какая видна, решает
   класс на ленте, который ставит `tasks.js`. Решётка техники — тем же
   компактным видом: соседи в одной строке шапки обязаны быть одного роста. */
.feed__all,
.feed__sys { padding: 4px 10px; font-size: 12px; }
.feed--open .fold__open { display: none; }
.feed--open .fold__shut { display: inline; }

/* Запись ленты — раскрывашка. Полоска с именем и датой служит и шапкой, и
   переключателем: щелчок по ней открывает и закрывает запись сам. */
.fitem > .feed__meta { cursor: pointer; list-style: none; }
.fitem > .feed__meta::-webkit-details-marker { display: none; }
.fitem > .feed__meta::marker { content: ""; }

/* Свёрнутая полоска чуть тусклее раскрытой: видно, где спрятан текст. */
.feed__item--said .fitem:not([open]) > .feed__meta { opacity: .82; }
.feed__item--said .fitem > .feed__meta:hover { opacity: 1; }

/* Служебная строка запуска — приклеена к реплике, тише её и не считается
   комментарием. */
.feed__tech {
    margin: 6px 16px 10px;
    color: var(--muted);
    font-size: 12px;
}


/* Меню на телефоне — листом поверх страницы, а не выпадашкой у кнопки.

   Правка владельца 24.08.2026: «метка, дедлайн и прочие выпадают за границу
   экрана». Так и есть — панель считает своё место от кнопки, а кнопки в
   карточке идут рядом и упираются в правый край; на узком экране половина
   меню оказывается за ним.

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

   Подложку не рисуем намеренно: тап мимо меню закрывает его и сейчас
   (`closeMenus` в tasks.js ловит клик вне `details`), а затемнение,
   нарисованное псевдоэлементом самого меню, считалось бы «внутри» — и тап по
   нему перестал бы закрывать.

   Специфичность выше обычной (`details.x[open] >`) не ради красоты: у
   отдельных меню есть свои правила с `left`/`right`, и без этого они
   перебивали бы позицию. */
@media (max-width: 600px) {
    details.asgmenu[open] > .asgmenu__pop,
    details.lblmenu[open] > .lblmenu__pop,
    details.fmenu[open] > .fmenu__pop,
    details.pmenu[open] > .pmenu__pop,
    details[open] > .cpick__pop {
        position: fixed;
        top: 72px;
        left: 12px;
        right: 12px;
        width: auto;
        min-width: 0;
        max-width: none;
        max-height: 62vh;
        overflow-y: auto;
        z-index: 80;
    }
}

/* Файл при реплике: строка под текстом. Удалённый остаётся отметкой — реплика
   «вот отчёт» без следа файла читается так, будто его не присылали. */
.feed__file {
    margin: 6px 18px 0;
    font-size: 12.5px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.feed__file--gone { font-style: italic; }

/* --- Лаборатория гипотез (вкладка Marketing Dashboard) --------------------
   Классов больше, чем хотелось бы, но политика запрещает инлайновые стили:
   `style=""` в этой системе просто не применится, и раскладка молча поедет. */
.lab-pulse .panel__body { padding-bottom: 14px; }
.lab-figs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 28px; }
.lab-fig { display: flex; flex-direction: column; gap: 2px; }
.lab-fig__num { font-size: 26px; font-weight: 650; line-height: 1.1; letter-spacing: -.02em; }
.lab-fig__cap { font-size: 12px; color: var(--muted); }
.lab-fig--warn .lab-fig__num { color: var(--bad); }
.lab-fig--quiet .lab-fig__num { font-size: 20px; color: var(--ink-2); }

.lab-tabs { margin-bottom: 18px; }

/* Реплика кота — не заголовок и не ошибка: своя строка спокойным тоном. */
.lab-cat {
    margin: 0 0 16px;
    padding: 10px 14px;
    border-radius: 9px;
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: 14px;
}

.lab-table { width: 100%; }
.lab-table th { white-space: nowrap; }
/* Гипотеза — длинный текст, и именно её читают: ей ширина, остальным по месту. */
.lab-td-hyp { min-width: 320px; }
.lab-td-when, .lab-td-status { white-space: nowrap; }
.lab-td-cat { color: var(--muted); font-size: 13px; min-width: 200px; }
.lab-link { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--rule-strong); }
.lab-link:hover { color: var(--accent); }
.lab-art { display: block; font-size: 12px; color: var(--muted); }
.lab-late { color: var(--bad); font-size: 12px; }

.lab-form { display: flex; flex-direction: column; gap: 4px; }
.lab-form .eyebrow { margin-top: 10px; }
.lab-form .eyebrow:first-child { margin-top: 0; }
.lab-row { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: flex-end; margin-top: 6px; }
.lab-cell { display: flex; flex-direction: column; gap: 3px; }
.lab-cell--wide { flex: 1 1 260px; }
.lab-cell .eyebrow { margin-top: 0; }
.lab-derived { font-size: 14px; color: var(--ink-2); padding: 5px 0; }

.lab-input, .lab-area {
    font: inherit;
    font-size: 14px;
    padding: 6px 10px;
    border-radius: 7px;
    border: 1px solid var(--hair);
    background: var(--surface);
    color: var(--ink);
    width: 100%;
}
.lab-area { resize: vertical; line-height: 1.45; }
/* С тегом, как у input.rd-day: базовое правило форм задаёт width: 100%
   селектором input[type="text"], и голый класс ему проигрывает. */
input.lab-input--short { width: 190px; }
input.lab-input--tiny { width: 80px; }

/* Форма записи внизу списка: поля в строку, чтобы мысль записывалась за один
   заход и не превращалась в анкету. */
.lab-new { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-end; }
.lab-new .lab-cell--wide .lab-input { width: 100%; }

.lab-actions { display: flex; gap: 10px; align-items: center; margin: 18px 0 0; }
.lab-kinds { margin-top: -6px; margin-bottom: 18px; }
.lab-td-kind { white-space: nowrap; color: var(--ink-2); font-size: 13px; }
.lab-sync { display: inline-flex; }

/* Вложенный пункт переносится по словам, а не обрезается многоточием.
   С 25.08.2026 у Marketing Dashboard длинные подписи — «Кандидаты на
   редизайн», «Дизайн-эксперименты», — и в одну строку панели они не встают.
   Обрезанное название читается хуже перенесённого: «Кандидаты на ред…» и
   «Дизайн-экспериме…» отличаются друг от друга только началом.

   Буллет прижимается к первой строке: по центру двухстрочного пункта он
   висел бы между строк и переставал читаться как метка списка. */
.side__sub .side__link { white-space: normal; align-items: flex-start; }
.side__sub .side__link .ico--bullet { margin-top: 3px; flex: none; }
/* Удаление гипотезы: тон опасного действия — тот же, что у кнопок системы. */
.lab-danger { align-items: baseline; flex-wrap: wrap; gap: 10px 14px; }
.lab-danger .panel__note { margin: 0; flex: 1 1 320px; }
button.chip--danger { border-color: var(--bad); color: var(--bad); }
button.chip--danger:hover { background: var(--bad); color: var(--surface); }
.lab-td-act { white-space: nowrap; text-align: right; }

/* ─────────────────────────────────────────────────────────────────────────
   Перекраска светлой темы (правка владельца 25.08.2026).

   Цвета названы человеком по Digital Color Meter, native values:
     панель      0.937 0.937 0.940 → #efeff0
     основное    0.977 0.977 0.980 → #f9f9fa
     наведение   0.941 0.937 0.941 → #f0eff0
     активный    0.882 0.882 0.886 → #e1e1e2

   Блок стоит В КОНЦЕ файла и целиком обёрнут в `prefers-color-scheme: light`
   ради двух вещей. Первая — тёмную тему не трогаем: она объявлена выше, и
   без обёртки этот блок перебил бы её. Вторая — обратимость: правка живёт
   одним куском, и откат сводится к удалению блока, а не к разбору, что
   именно было раньше.

   Дашборд не затронут — у него свои файлы (`details*.css`).

   Каждый селектор здесь держит `:not([data-theme="dark"])` не для красоты:
   без него блок ловит систему, а не переключатель в приложении. У людей с
   ОС на светлой теме (то есть у большинства) `prefers-color-scheme: light`
   истинно ВСЕГДА, что бы ни стояло на кнопке в подвале панели — жёсткие
   цвета ниже той же специфичности, что и `:root { --side-bg: var(--surface) }`
   выше, и как более поздние по файлу молча перебивали тёмную тему без
   единого предупреждения в консоли (живой случай 26.08.2026: кнопка
   переключала на тёмную, а панель и подсветка «Задач» оставались светлыми).
   `:root[data-theme="dark"]` двумя экранами выше того же файла не помогает:
   он переопределяет `--surface`, а не сами `--side-bg`/`--page`/`--paper` —
   те и так должны получить тёмный цвет через переменную, если бы этот блок
   не перехватывал их первым. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        --side-bg: #efeff0;      /* боковая панель */
        --page: #f9f9fa;         /* лист справа: фон под строками трекера */
        --paper: #f9f9fa;
        --side-hover: #f0eff0;   /* наведение в панели — тем же тоном, что строки */
    }

    /* Строка задачи под курсором и заголовки групп («В работе», «На ревью»).
       Раньше строка подсвечивалась цветом карточки — на новом фоне листа он
       почти совпал с ним, и подсветка перестала читаться. */
    :root:not([data-theme="dark"]) .trow:hover,
    :root:not([data-theme="dark"]) .trow--ctx,
    :root:not([data-theme="dark"]) .board--kanban .trow:hover,
    :root:not([data-theme="dark"]) .board--kanban .trow--ctx { background: #f0eff0; }
    :root:not([data-theme="dark"]) .tgroup__head { background: #f0eff0; }
    :root:not([data-theme="dark"]) .tgroup__head:hover { background: #e9e8ea; }
    /* Плашка отбора с выбранным значением — тем же серым, что полоса статуса
       двумя строками выше: обе несут один смысл («что-то выбрано»), и число
       то же самое, а не через `--surface-2` — та переменная в светлой теме
       держит соседний, чуть более сиреневый тон (#eae7f0). */
    :root:not([data-theme="dark"]) .fmenu__sum--on,
    :root:not([data-theme="dark"]) .rvchip--on { background: #f0eff0; }
    /* Выбранная для группового действия строка — тем же тоном, что под
       курсором: выбор держит подсветку и без мыши. */
    :root:not([data-theme="dark"]) .trow--picked { background: #f0eff0; }

    /* Текущий раздел в панели: заметно темнее наведения, иначе «вы тут» и
       «курсор здесь» выглядят одинаково. */
    :root:not([data-theme="dark"]) .side__link--on,
    :root:not([data-theme="dark"]) .side__item--on > .side__link { background: #e1e1e2; }

    /* Наведение на сам текущий пункт не должно гасить эту подсветку.
       У `:hover` специфичность выше, чем у `--on`, и без этой пары строка
       на секунду выглядела как обычная ненажатая (правка владельца
       26.08.2026: «Задачи» теряли подсветку под курсором). Тот же приём,
       что у `.side__item--on:hover` выше, только для подпунктов. */
    :root:not([data-theme="dark"]) .side__link--on:hover,
    :root:not([data-theme="dark"]) .side__item--on > .side__link:hover { background: #e1e1e2; }
}

/* Широкий экран: поля листа вдвое уже, контенту больше места (правка
   владельца 25.08.2026). При 1080 пикселях листа на мониторе 1920 по бокам
   оставалось по 420 пикселей пустоты — половину этого запаса отдаём тексту.
   Порог 1400 выбран так, чтобы на ноутбуках ничего не менялось: там поля и
   так узкие, и растягивать строку до края вредно для чтения. */
@media (min-width: 1400px) {
    .page { max-width: 1500px; }
}

/* Выбор файла в строке с «Опубликовать»: занимает остаток строки, но не
   распирает её — иначе правая пара кнопок уезжает за край на узком экране. */
.say__file {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 260px;
    font-size: 12px;
}

/* Карточка задачи: колонка в две трети листа (правка владельца 25.08.2026).
   На широком экране текст растягивался во всю ширину, и строка уходила за
   предел читаемости.

   Левый отступ сюда больше не приходит: его даёт `.tcard` один раз, той же
   переменной `--content-x`, что и вертикаль списка (см. ниже). Свой
   `margin-left`/`padding-left` здесь раньше дублировал этот же отступ —
   `.tcard` уже сдвигает всё своё содержимое, и вторая порция утаскивала
   заголовок карточки на 85px правее вертикали списка (правка владельца
   25.08.2026: «на широком экране проблема с вертикалями»).

   `.sheet-head` та же ширина, каким бы ни было число ниже (27.08.2026,
   живой отзыв): она стоит ДО `.tsheet--single` в разметке — сосед, а не
   родитель, — и без своего правила растягивалась во всю ширину `.tcard`,
   а не только на долю текстовой колонки. Заголовок иначе зрительно
   разъезжался бы с колонкой текста под ним, вместо того чтобы делить с
   ней один правый край и переноситься туда же, куда переносится тело.
   Селектор через `.tcard`, а не голый `.sheet-head`: этот класс общий на
   системе (агенты, ОКР, вики и т. д.), и без уточнения правка задела бы
   их все. */
@media (min-width: 1000px) {
    /* 666px — фиксированным числом, не процентом (правка владельца
       27.08.2026: «по моим метрикам сейчас примерно 584 пикселя ширина,
       давай 666»). Проценты (66%, 33%, 50% — вся история того же дня)
       давали разное число на разной ширине окна, и «половина» дважды
       разъезжалась между тем, что видел владелец у себя, и тем, что
       намерил я на своей ширине экрана — отсюда и метка в пикселях, а не
       в доле листа: она одна и та же везде, где страница вообще шире
       666px + `--content-x-icon` + правое поле `.page`. `.sheet-head` и
       `.tsheet__main` по-прежнему одной ширины, так что заголовок и
       текст переносятся по одному правому краю.

       На `.tsheet__main`, а не на самом `.tsheet--single` (было так же
       днём, живой отзыв «фильтры-свойства... не должны сокращать ширину
       полосы контента»): `.tsheet--single` — это контейнер сетки целиком,
       и на широком экране (см. следующее правило, `.props` в правой
       колонке) в нём же едет колонка свойств — общий `max-width: 666px`
       на контейнере ужимал бы 666px КОЛОНКИ ВМЕСТЕ, а не только текст,
       и сайдбар откусывал место у текста вместо того, чтобы висеть в
       свободном справа. `.tsheet__main` держит только текстовую колонку
       — ту же ширину, но без этой путаницы. */
    .tcard .sheet-head { max-width: 666px; }
    .tcard .tsheet__main { max-width: 666px; }
}

/* Свойства карточки — правой колонкой на широком экране (правка владельца
   27.08.2026: «на конечной странице задачи... есть место, чтобы перенести
   туда полосу свойств-фильтров... на широких они образуют блок справа от
   полосы контента, который прилипает к верхней границе окна, а контент
   продолжает скроллиться»).

   Порог 1400px — тот же, что у `.page { max-width: 1500px }` дальше по
   файлу: то же самое «широкий монитор», не новый одноразовый порог. Ниже
   этой ширины действует обычный откат `.tsheet--single`/`.tsheet` (см.
   выше и `@media (max-width: 1024px)` в середине файла) — там колонки
   нет вовсе, а `.props` просто первый блок в потоке, как в разметке.

   `order`, а не перестановка в разметке: `.props` в HTML стоит ДО
   `.tsheet__main` — это порядок для узкого экрана (свойства сразу под
   заголовком). На широком грид раскладывает элементы по счёту `order`, и
   меняя только его, можно отправить `.props` во вторую колонку, не трогая
   DOM и не задевая читающие его программы вроде экранного диктора для
   узкого экрана, где `order` не действует.

   `position: sticky; top: 28px` — то же число, каким `.page` отступает
   сверху в покое (см. комментарий у `.page`, «Верхнее поле — 28px») и
   каким `.tgroup__head` списка задач отступает от прилипшей шапки
   фильтров: один и тот же зазор от верха окна везде, где что-то к нему
   прилипает. `align-items: start` уже стоит на `.tsheet` — без него
   сетка растянула бы `.props` на всю высоту строки, и прилипать было бы
   нечему: сам блок был бы уже высотой с самый длинный текст рядом.

   Первая колонка — `minmax(0, 666px)`, а не `minmax(0, 1fr)` (тем же днём,
   живой отзыв: «должны быть прижаты к полосе контента, отступ определю
   позднее»). `1fr` забирал под колонку всё, что оставалось после 280px
   сайдбара, и `.tsheet__main` с его же собственным `max-width: 666px`
   вставал у левого края этой колонки, а сайдбар — у правого края
   ВСЕГО `.tsheet`, а не колонки текста: между ними получался разрыв в
   несколько сотен пикселей на широком мониторе. 666px здесь — то же
   число, каким ограничен `.tsheet__main` чуть выше, не выдуманное
   заново: колонка ровно по ширине того, что в ней лежит, и `.props`
   встаёт сразу следующей, через обычный `gap` того же `.tsheet`. Сам
   зазор ничем не подчёркнут нарочно — он и есть отступ «на потом». */
@media (min-width: 1400px) {
    .tcard .tsheet--single { grid-template-columns: minmax(0, 666px) 280px; }
    .tcard .tsheet__main { order: 1; }
    .tcard .props {
        order: 2;
        flex-direction: column;
        flex-wrap: nowrap;
        /* `flex-start`, не `stretch` (было — живой отзыв «обводка избыточно
           широкая»): `stretch` тянул каждую плашку на всю ширину колонки
           (280px), и рамка «Средний» была того же размера, что у «Слава
           Перевалов», хотя текста в разы меньше. Плашка по своей ширине,
           колонка просто не даёт ей вылезти за 280px (`.prop` и так несёт
           `max-width: 100%` — своё, не отсюда). */
        align-items: flex-start;
        margin: 0;
        position: sticky;
        top: 28px;
    }
    /* Здесь же времена встают столбиком под свойствами: колонка узкая, лентой
       им не разойтись. Дата уходит на свою строку, отступ выравнивает текст
       под подписи плашек. */
    .tcard .props__times { display: block; }
    .tcard .props__by { width: 100%; padding-left: 32px; }
    .tcard .props__by .num { display: inline; }
    .tcard .props__by--upd { margin-top: 10px; }
}

/* Шапка блока ресурсов: подпись слева, скрепка справа. Меню под скрепкой
   шире обычного — в нём две формы (25.08.2026). */
.res__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.res__add > .fmenu__pop { width: min(320px, 84vw); padding: 12px; }
.res__pop .res__link { margin-top: 12px; border-top: 1px solid var(--rule); padding-top: 12px; }

/* Ресурс-ссылка: та же строка, что у файла, но ведёт наружу. */
a.res__row { text-decoration: none; color: inherit; }
a.res__row:hover .res__name { text-decoration: underline; }

/* Правки блока ресурсов по замечаниям владельца 25.08.2026. */

/* Жирная линия под названием задачи убрана. Она разделяла шапку и содержимое
   на общих страницах, но в карточке под ней сразу идут плашки свойств, и
   черта читалась разделителем там, где ничего не разделено. Селектор через
   «Все задачи» — так правило достаётся только карточке, а списки и разделы
   остаются с прежней шапкой. */
.task-back + .sheet-head { border-bottom: none; }

/* Кнопка сворачивания и кольцо-счётчик — одним элементом ряда для
   `.res__head` (тот `display: flex; justify-content: space-between`): без
   общей обёртки в ряду было бы три прямых ребёнка вместо двух, и
   `space-between` раздвигал бы их поровну — кольцо съезжало к «+» через
   весь ряд, а не оставалось у слова «Подзадачи» (живая проверка в браузере
   агента 26.08.2026: 227px до кольца вместо пары пикселей). */
.res__head-lead { display: inline-flex; align-items: center; }
/* Правая группа шапки блока: кнопка «выполненные» и «+» рядом — у
   `.res__head` двое детей, как и было до кнопки. */
.res__head-tools { display: inline-flex; align-items: center; gap: 4px; }
/* Нажатое = выполненные скрыты: горит акцентом, как колокол подписок
   (Антон, 16.09.2026: «в нажатом состоянии ярче»); отжатое — как «+». */
.res__row[data-done="1"][hidden] { display: none; }

/* Подпись блока: не капс, чуть мельче, жирнее и темнее. Капслок читается как
   служебная метка раздела, а «Ресурсы» — обычный заголовок списка. */
.res__head .burn__head {
    margin: 0;
    font: 600 12.5px/1.2 var(--sans);
    letter-spacing: normal;
    text-transform: none;
    color: var(--ink-2);
}

/* Кольцо и «N/M» в шапке «Подзадачи» — та же пара, что в бейдже строки
   (`.subring`, выше), только без плашки-контура вокруг: заголовок и так на
   отдельной строке, обводка была бы лишней рамкой вокруг рамки.

   Снаружи `.subfold-toggle`, своим элементом ряда — не частью кнопки
   (владелец 26.08.2026: «кольцо прогресса... не должно входить в обводку,
   только подзаголовок Подзадачи»). Выравнивание по той же горизонтали не
   держится ни на чём особом: оба — прямые дети `.res__head`, а тот сам
   флекс-ряд с `align-items: center`.

   Отступ слева — 5px, половина того, что даёт правое поле самой кнопки
   (`.subfold-toggle`, 10px): владелец 26.08.2026 качнул это число трижды —
   сначала «в три раза дальше», затем «сильно вправо уезжало, сразу за
   подзаголовком» (отступ сняли совсем), затем «ещё на 1/2 от текущего
   отступа правее» (текущим на тот момент был как раз зазор от поля кнопки,
   10px — добавка равна его половине). Числа менялись не потому, что
   правило спорное, а потому что предыдущее значение проще всего описать
   на глаз, глядя на предыдущий результат.

   Шрифт «N/M» — тем же размером, что у названий в списке подзадач под
   заголовком (`.res__name`, 13px), а не унаследованным телом страницы
   (15px) — они не должны читаться разным шрифтом, хотя одно название сразу
   под другим. */
.subhead-count {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 5px;
}
.subhead-count .num { font-size: 13px; }

/* Заголовки «Подзадачи»/«Ресурсы» сворачивают блок (владелец 26.08.2026):
   треугольник слева, тот же приём (граница-в-треугольник), что у складки
   статусной группы в списке (`.tgroup__fold`). Кнопка, а не `<summary>` —
   в шапке уже есть своя интерактивность (меню «+»/скрепки правее), а
   `<details>` внутри `<summary>` — недопустимая вложенность, и клик по
   «+» заодно сворачивал бы список.

   Подсветка — только по наведению, не постоянным фоном, как у группы
   там: заголовков в панели несколько подряд, и постоянная плашка
   смотрелась бы тяжелее списка задач под ней. Форма — пилюлей, той же
   геометрии, что у плашек фильтра списка «Исполнитель»/«Дедлайн»/«Метка»
   (`.fmenu__sum`, `border-radius: 999px`), а не общим `--radius-ctl`
   (владелец 26.08.2026: «стиль обводки... сейчас плохой, должен быть
   светло-серым, по геометрии такой же, как... у фильтров»). Отступ слева —
   под сам треугольник, поэтому подпись стоит чуть правее прежнего
   (владелец 26.08.2026: «заголовки... сделать чуть правее»). */
.subfold-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px 3px 6px;
    margin-left: -6px;
    border: none;
    border-radius: 999px;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}
.subfold-toggle:hover { background: var(--surface-2); }

/* Чуть мельче складки статусной группы (та под заголовок покрупнее) —
   владелец 26.08.2026: «треугольнички... чуточку поменьше». */
.subfold-tri {
    flex: none;
    width: 0;
    height: 0;
    border-left: 3px solid transparent;
    border-right: 3px solid transparent;
    border-top: 4px solid var(--muted);
    transition: transform .12s ease;
}
.subfold-toggle[aria-expanded="false"] .subfold-tri { transform: rotate(-90deg); }

/* Скрыт — значит скрыт целиком, вместе с внутренними отступами: свёрнутая
   панель не должна оставлять пустой воздух там, где был список. Свойство
   дублирует правило браузера для `[hidden]` намеренно: без него будущая
   правка `.subfold-body { display: ... }` где-то ниже по файлу молча
   выключила бы сворачивание, и заметить это глазами трудно. */
.subfold-body[hidden] { display: none; }

/* Сворачивание — с плавностью (живой отзыв владельца 28.08.2026:
   «скрывается и разворачивается мгновенно, хорошо бы органической
   плавности»). Приём — сетка в одну строку-дробь: `1fr` даёт полную высоту
   содержимого, `0fr` сжимает её до нуля, и `grid-template-rows` умеет
   переходить между ними transition'ом (высота `auto` так не умеет).
   Единственный прямой потомок (`.res`, список подзадач или файлов, когда
   он есть) обязан уметь сжаться меньше своей естественной высоты —
   `overflow: hidden` на нём и даёт это (без него `auto`-минимум блока
   останавливает сжатие на высоте содержимого, и переход не виден).

   `.subfold-body--closing` вместо мгновенного `[hidden]`: `tasks.js`
   сначала запускает переход (класс), затем — по `transitionend` — уже
   ставит сам атрибут `hidden`, тем же приёмом, что сворачивание длинного
   описания «доезжает» до конца. Раскрытие — в обратном порядке: `hidden`
   снимается СРАЗУ, чтобы браузеру было что анимировать, а класс перехода
   убирается следующим кадром.

   `overflow: hidden` у `.res` — ТОЛЬКО пока идёт сам переход
   (`.subfold-body--animating`, ставит и снимает `tasks.js` вокруг обеих
   анимаций), не постоянно. Живой баг 28.08.2026: висел он всегда, и
   меню на три точки у строки ресурса, и меню исполнителя у строки
   подзадачи — оба всплывают вверх и обрезались тем же `overflow`, что
   держит саму строку в рамках при сжатии; в покое, когда высота никуда
   не едет, обрезать нечего, а меню открывать давно можно. */
/* Колонка — `minmax(0, 1fr)`, а не умолчательная `auto`. Автоминимум
   грид-элемента равен его же min-content, и длинная ссылка без пробелов
   (`.res__name`, `white-space: nowrap`) через него раздвигала колонку шире
   карточки: строка ресурса вырастала с 666 до 1011px и утаскивала за собой
   всю страницу в горизонтальную прокрутку. Многоточие при этом не
   срабатывало — обрезать было нечего, места хватало. `minmax(0, …)` снимает
   минимум, и лишнее снова режется (замер в браузере агента 28.08.2026). */
.subfold-body {
    display: grid;
    grid-template-rows: 1fr;
    grid-template-columns: minmax(0, 1fr);
    transition: grid-template-rows .18s ease;
}
.subfold-body--animating > .res { overflow: hidden; min-height: 0; }
.subfold-body--closing { grid-template-rows: 0fr; }

/* Скрепка: кружок виден всегда, а не проявляется под курсором. Тень та же,
   что у строк ресурсов под ним, — иначе кнопка выглядит из другого набора.
   При наведении белый фон сменяется светло-серым.

   Кружок — размером базового `.circle` (24px), как и было: владелец
   уточнил 26.08.2026, что «здоровым» была не сама кнопка, а именно значок
   «+» внутри неё — «я просил уменьшить только плюс, а кружки эти не
   трогать» (первая попытка тем же днём уменьшила и кружок тоже, откат
   здесь). Сам значок плюса уменьшен на треть, 14 → 9,3px, только у него —
   у скрепки свой размер (12,2px) ниже, его никто не просил трогать. */
.res__add > .circle {
    border-color: var(--hair);
    background: var(--card);
    box-shadow: var(--lift);
    color: var(--ink-2);
}

/* Общая вертикаль с точками и аватаром в строках под шапкой (просьба
   владельца 27.08.2026): у строки (.res__row) своё правое поле 5px и рамка
   1px, у кнопки «+» в шапке — их нет, и без этого отступа она сидела бы на
   6px правее содержимого строк.

   Само число (6px, было 11px) — из того же разбора: правое поле строки
   уменьшили, чтобы вертикаль отстояла от правой рамки строки ровно на
   столько же, на сколько сама она отстоит от верхней и нижней (см.
   `.res__row` выше). Отступ здесь должен меняться вместе с тем полем — это
   один и тот же зазор, просто без рамки и поля строки под кнопкой. */
.res__add { margin-right: 6px; }

.res__add .sico--plus { width: 9.3px; height: 9.3px; }

.res__add > .circle:hover {
    background: var(--sunk);
    box-shadow: var(--lift-hi);
    color: var(--ink);
}

/* Меню добавления без прокрутки: в нём две формы, и высота у него своя.
   Общий предел в 280 пикселей ставился под длинные списки фильтров. */
.res__add > .fmenu__pop {
    max-height: none;
    overflow: visible;
}

/* Блок ресурсов карточки: итог четырёх заходов правок 25.08.2026.

   Раньше эти значения лежали в файле четырьмя слоями — каждый следующий
   перекрывал предыдущий, и понять действующее число можно было только
   прочитав все. Здесь они сведены; история правок живёт в git.

   Что и почему:

   * шапка опущена внутренним отступом (12px), а не полем: вертикальные поля
     схлопываются с соседом сверху, и сдвиг зависел бы от того, что стоит над
     блоком. Зазор до первой строки — 6px, ровно как между строками списка;
   * ширина строки ничем не ограничена: файл обязан доходить до правого края
     колонки, как текст описания и реплик;
   * тени вдвое тише общей `--lift`: строк бывает десяток, и на светлом листе
     они начинали выглядеть выпуклыми плитками;
   * скрепка 12,2px при поле значка 15,5 — рисунок остаётся 11px, а низ дуги
     не срезается (см. `clip_icon` в `_taskbits.html`). */
.res__head {
    padding-top: 12px;
    margin-bottom: 6px;
}

.res__head .burn__head { font-size: 11.5px; }

/* Отступ снизу — 24px, вдвое больше и здесь стоял (32px) до правки
   владельца 26.08.2026: три отступа карточки (описание→подзадачи,
   подзадачи→ресурсы, ресурсы→обсуждение) должны быть одинаковыми, и
   держатся на этом же числе — см. `.subblock`/`.tdesc__files` выше. */
.res {
    max-width: none;
    margin-bottom: 24px;
}

.res__row,
.res__add > .circle {
    box-shadow: 0 1px 2px rgba(23, 20, 31, .03), 0 6px 16px rgba(23, 20, 31, .045);
}

.res__row:hover,
.res__add > .circle:hover {
    box-shadow: 0 2px 4px rgba(23, 20, 31, .04), 0 10px 24px rgba(23, 20, 31, .065);
}

.res__add .sico--clip { width: 12.2px; height: 12.2px; }

/* Правая часть строки: размер, дата, автор — тремя колонками фиксированной
   ширины. Сетка, а не поток: в потоке дату двигала длина имени автора, и
   «25 авг» стояло у каждой строки на своём месте. У ссылки размера нет, но
   слот под него есть — иначе её дата встала бы левее файловой.

   Имя автора прижато к правому краю и обрезается многоточием, а на узком
   экране прячется целиком: там важнее видеть имя файла, чем того, кто его
   принёс. */
.res__meta {
    display: grid;
    grid-template-columns: 4.5rem 3.5rem minmax(0, 8rem);
    align-items: center;
    gap: 10px;
    flex: none;
    font-size: 12px;
    color: var(--muted);
}

/* Строка подзадачи — только номер и аватар, третьей колонки (автор файла)
   у неё нет вовсе, а общая сетка ту колонку всё равно резервировала:
   аватар сидел на 183px левее правого края строки, а не вровень с ним
   (просьба владельца 27.08.2026: общая вертикаль с плюсом в шапке и
   точками у файла). Своя сетка ровно по числу колонок — без пустого
   хвоста. */
.res__meta--sub { grid-template-columns: auto; }
/* Номер подзадачи — между значком и названием (Антон, 16.09.2026), тем же
   цветом, что прежде в правой части строки. */
.res__subnum { flex: none; font-size: 12px; color: var(--muted); }

/* Строка «Вложений» и её заметка убраны 08.09.2026 вместе с самим блоком:
   файл задачи живёт в «Ресурсах», второго списка на карточке нет. Правила
   держались на `.res__meta--upload` и `.res__note`, которых больше нет ни в
   одном шаблоне. Разметку строки «Ресурсов» это не задевает — у неё своя
   сетка выше. */

.res__size,
.res__when { text-align: right; }

.res__who {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: right;
}

@media (max-width: 720px) {
    .res__meta { grid-template-columns: 4.5rem 3.5rem; }
    .res__who { display: none; }
}

/* Своей краски у поля обсуждения больше нет: с 31.08.2026 у него нет и
   самой панели — карточки лежат прямо на листе, и красить нечего. Замер
   #fbfafc (владелец, 25.08.2026) остался в истории на случай возврата. */

/* Левые вертикали трекера (правка владельца 25.08.2026, уточнено 27.08.2026).

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

       поле строки 28 + значок 16 + зазор 10 + номер + его воздух + зазор 10
       └──────────┬──────────┘
              --content-x-icon             --content-x (целиком)

   `--content-x-icon` — вертикаль значка важности («полоски», `.prio` в
   `.trow`): только поле строки, без всего, что после него. `--content-x` —
   вертикаль заголовка задачи: то же самое плюс значок, зазор, номер, его
   воздух и ещё зазор. Второе выражено через первое, чтобы 28px не жило
   двумя несвязанными литералами в одном файле.

   Колонка номера закреплена под три знака там же, где заданы все её ширины.

   Поле строки — 28, а не 12: с 26.08.2026 строка сама сдвинута вправо, под
   значок статуса в полосе группы над ней (см. `.trow`, комментарий у
   `padding`). Число здесь обязано следовать за тем же числом там, иначе
   вертикаль разойдётся ровно там, где её и выравнивали изначально.

   На узком экране отступ снимается — там 85 пикселей забирают у названия
   больше, чем стоит ровный край. */
:root {
    --content-x-icon: 28px;
    --content-x: calc(var(--content-x-icon) + 16px + 10px + 21px + var(--task-key-gap) + 10px);
}

@media (min-width: 900px) {
    /* Карточка задачи выравнивается по значку важности («полоски» в списке),
       а не по заголовку строки (правка владельца 27.08.2026 — раньше стояла
       `var(--content-x)`, вертикаль заголовка, теперь ближе к краю, туда, где
       начинаются полоски). `.fbar__row` в `tasks.html` по-прежнему считает от
       `--content-x` целиком: у фильтров своя вертикаль, заголовочная.

       Своего `max-width` здесь больше нет (был `66%` тем же днём, снят тем
       же днём) — «ограничь его максимальную ширину 66[%]» читалась как
       правило для всего `.tcard`, но колонка текста внутри уже мерена той
       же цифрой отдельным правилом (`.tcard .sheet-head`/`.tsheet--single`,
       ниже по файлу, «Карточка задачи: колонка в две трети листа») —
       матрёшка из двух `66%` подряд ужимала текст до 66%×66% ≈ 44% листа,
       и колонка стала «очень узкой» (живой отзыв тем же днём). Метки,
       автор, исполнитель и прочие плашки в шапке карточки той внутренней
       парой правил не задеты и по-прежнему тянутся во всю ширину — это и
       раньше было так, до всей этой правки, и не мешало. */
    .tcard { padding-left: var(--content-x-icon); }
}

/* Раздел «Здоровье» (IR-2, IR-19): строка состояния — имя, пояснение, итог.

   Красным помечается только беда, и это единственный цвет на странице: экран
   отвечает «всё ли в порядке», и если раскрашивать в нём всё, ответ придётся
   искать глазами. */
.hrow {
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.4fr) minmax(0, 1fr);
    align-items: baseline;
    gap: 12px;
    padding: 9px 14px;
}

.hrow__name { font-weight: 500; }

.hrow__note,
.hrow__state {
    font-size: 12.5px;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.hrow--bad .hrow__name { color: var(--bad); }
.hrow--bad .hrow__state { color: var(--bad); }

/* Второй уровень появился вместе с экраном проблем на «Сводке» (31.08.2026).
   Пауза, поставленная человеком, просроченная задача и неразобранное сырьё —
   это не то же самое, что упавший nginx. Крась их красным — и вернёшься к
   экрану, на котором ответ ищут глазами, ровно к тому, против чего писано
   правило выше. */
.hrow--warn .hrow__name { color: var(--warn); }
.hrow--warn .hrow__state { color: var(--warn); }

@media (max-width: 720px) {
    .hrow { grid-template-columns: 1fr; gap: 2px; }
}

/* Путь в шапке узкого экрана: «Все задачи › IR-51» (правка владельца
   26.08.2026).

   Оформление номера не меняется — оба шага наследуют шрифт `.handle__title`,
   поэтому кнопка и ссылка выглядят ровно как прежний номер. У кнопки шрифт
   приходится задавать явно: браузер не наследует его сам.

   Стрелка «←» перед «Все задачи» снята: шеврон-разделитель уже показывает,
   что это путь, а стрелка рядом с ним читается вторым указателем в другую
   сторону.

   `align-items: center`, а не `baseline` — на трёх разных элементах (ссылка,
   span-разделитель, кнопка с копированием) браузеры считают базовую линию
   не всегда одинаково: у `<button>` она чаще привязана к нижнему краю
   рамки, а не к линии текста внутри, и с `baseline` шеврон и номер стояли
   чуть выше «Все задачи» (владелец 26.08.2026: «точнее выровнять по одной
   горизонтали»). Центр по высоте от этой разницы не зависит — блоки у всех
   трёх одной высоты (строка в 12px), и `center` кладёт их вровень
   независимо от того, что каждый считает своей базовой линией. */
.crumbs {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.crumbs__step {
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

a.crumbs__step { color: var(--muted); }
a.crumbs__step:hover { color: var(--ink); }

.crumbs__sep {
    font: inherit;
    color: var(--muted);
    opacity: .7;
}

/* Строка «← Все задачи» с номером — только на широком экране. На телефоне
   её заменяет шапка с теми же путём и номером, и держать обе значило бы
   отдать строку под то, что уже видно наверху. Скрывается вместе с местом.
   До 31.08.2026 номер жил отдельно в `.sheet-head` — тогда прятались два
   места, теперь оно одно. */
@media (max-width: 900px) {
    .tcard > .task-back { display: none; }
}

/* «Лестница» — столбчатый график по часам/дням. Высота полоски — класс
   `ht-N` из scale.css: CSP не пускает inline style, а числа предсказать
   заранее нельзя.

   Число над столбиком — не украшение: до него график был читаем только
   наведением (title), а это ровно тот антипаттерн, из-за которого график
   назвали ненаглядным — значение обязано быть видно само, подсказка только
   дополняет. `.smokechart__value` зарезервирован по высоте всегда, даже
   пустой: без этого нулевые столбцы съезжали бы по высоте относительно
   подписанных. */
.smokechart {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    height: 160px;
    padding-top: 8px;
    border-bottom: 1px solid var(--rule);
    overflow-x: auto;
}
.smokechart__col {
    flex: 1 1 0;
    min-width: 16px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-end;
    height: 100%;
}
.smokechart__value {
    min-height: 15px;
    margin-bottom: 2px;
    text-align: center;
    white-space: nowrap;
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
}
.smokechart__bar {
    background: var(--accent);
    border-radius: 4px 4px 0 0;
    min-height: 2px;
}
.smokechart__bar--today { background: var(--warn); }
.smokechart__foot {
    margin-top: 4px;
    text-align: center;
    white-space: nowrap;
}


/* ── Дизайн-система (/design, 28.08.2026) ───────────────────────────────
   Лист-инвентарь: что нарисовано в интерфейсе и сколько его.

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

   Своих цветов и размеров здесь нет намеренно: страница про чужое оформление
   и не должна добавлять к нему собственное. Заливки образцов отдаёт
   /design/tokens.css, собираемый из тех же таблиц, за которыми лист наблюдает. */

.panel__head--sub {
    border-top: 1px solid var(--grid);
    padding-top: 14px;
}
.panel__head--sub h3 { margin: 0; font-size: 13px; font-weight: 600; }

/* Ряд: имя — образец — где применяется. Колонка имени фиксированной ширины,
   чтобы образцы начинались с одной вертикали и их можно было сравнивать
   глазом, не перескакивая. */
.dsrow {
    display: grid;
    grid-template-columns: 170px minmax(0, 1fr) minmax(0, 320px);
    gap: 18px;
    align-items: baseline;
    padding: 9px 16px;
    border-bottom: 1px solid var(--hair);
}
.dsrow:last-child { border-bottom: 0; }

/* Идентификатор моноширинным: его читают, только когда собираются назвать
   элемент в разговоре, — он служебный и не должен спорить с образцом. */
.dsrow__id {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--muted);
}
/* Образцы выравниваются по базовой линии, кроме рядов со значками: там
   середина, иначе значки повисают над строкой. */
.dsrow__demo { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.dsrow__demo--mid { align-items: center; }
/* Образец выпадающего меню в «Утверждённых элементах»: поповер тут стоит в
   потоке, а не всплывает над кнопкой — позиционирование снимается, стекло
   и содержимое остаются настоящими. */
.dsrow__demo > .fmenu__pop {
    position: static;
    display: block;
    width: auto;
    min-width: 180px;
}
.dsrow__where { font-size: 11.5px; color: var(--muted); }
/* Селекторы в графе «где» — тем же моноширинным, что и имя: это код, а не
   слова, и набранные шрифтом текста они читаются как название раздела. */
.dsrow__where code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10.5px;
    background: none;
    padding: 0;
}

/* Палитра — те же ряды, только первым идёт сам цвет. Плитка узкая и
   вытянутая: цвет сравнивают с соседним по вертикали, и высокая плитка
   разносит соседей дальше, чем нужно. */
.dsrow__chip {
    width: 46px;
    height: 18px;
    border-radius: 4px;
    /* Граница обязательна: без неё белый и почти белый образцы сливаются
       с листом и выглядят пустым местом. */
    border: 1px solid var(--hair);
}
.dsrow__val {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--muted);
}
/* Палитра: цвет — имя — значение — где. Четыре колонки вместо трёх. */
.dsrow--sw { grid-template-columns: 46px 150px 130px minmax(0, 1fr); }

/* Описание хука (/admin/hooks): докстринг файла как есть.
   <pre>, потому что переносы значимы — там абзацы и перечисления. Шрифт
   текстовый, а не моноширинный: это проза о том, зачем хук нужен, а не код.
   Перенос по словам обязателен — иначе длинная строка докстринга растянет
   лист вбок. */
.hookdoc {
    margin: 0;
    font: inherit;
    font-size: 12.5px;
    color: var(--ink-2);
    white-space: pre-wrap;
    word-break: break-word;
}

/* ── Свод строками: Догма и Ересь (08.09.2026) ───────────────────────────
   Правило — не задача: у него нет исполнителя, срока и важности, зато
   формулировку и «почему» надо ЧИТАТЬ целиком, а не узнавать по первым
   словам. Поэтому строка свода — свой блок `.rule`, а не `.trow` трекера:
   тот режет заголовок многоточием и держит семь колонок под поля, которых
   у правила нет. Только существующие токены и размеры (заморозка стилей,
   test_design): 14px/500 заголовку, 12px «почему», радиус --radius-ctl.

   Колонки: галочка пакета (24px; в Догме галочек нет и колонка — тот же
   отступ, что в эскизе Антона) · номер · текст · правая часть (метки и
   значки либо выбор слоя). Между строками воздух, а не рамки; наведение
   светлее фона — тем же ходом, что `.trow:hover`. */
.rule {
    display: grid;
    grid-template-columns: 24px var(--task-key-w) minmax(0, 1fr) auto;
    grid-template-areas: "pick key main side";
    align-items: start;
    gap: 4px 10px;
    padding: 8px 12px;
    border-radius: var(--radius-ctl);
}
.rule:hover { background: var(--surface); }
.rule__pick { grid-area: pick; justify-self: center; margin: 4px 0 0; }
.rule__key {
    grid-area: key;
    padding-top: 2px;
    font-size: 12px;
    color: var(--ink-3);
    text-decoration: none;
}
.rule__key:hover { color: var(--ink); text-decoration: underline; }
.rule__main { grid-area: main; min-width: 0; }
/* Заголовок переносится, а не обрезается: это и есть «документ». */
.rule__title {
    display: block;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.4;
    color: var(--ink);
    overflow-wrap: anywhere;
}
/* «Почему» — под формулировкой, приглушённо, с переносами автора
   (`.inline__text` даёт pre-line). Подпись рисует ::before: она переживает
   подмену текста ответом сервера и остаётся местом для щелчка, когда поле
   опустело. */
.rule__why {
    display: block;
    margin: 2px 0 0;
    font-size: 12px;
    line-height: 1.45;
    overflow-wrap: anywhere;
}
.rule__why::before { content: "почему: "; color: var(--ink-3); }
.rule__none { color: var(--ink-3); }
.rule__field .inline__form { margin-top: 4px; }
.rule__side {
    grid-area: side;
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 24px;
}
.rule__meta { display: flex; align-items: center; gap: 6px; }

/* Значки действий. Место занято всегда (пустой слот `--gap` держит столбец
   ровным), видимость — по наведению на строку или фокусу внутри неё, как у
   `.feed__act` реплик; невидимый значок клика не ловит. Слот 24px — в строке
   текста 30px кнопка `.iconbutton` спорила бы с заголовком. */
.rule__acts { display: flex; align-items: center; gap: 2px; }
.rule__act {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-ctl);
    background: none;
    color: var(--ink-2);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: opacity .16s ease-out;
}
.rule:hover .rule__act,
.rule:focus-within .rule__act {
    opacity: .85;
    pointer-events: auto;
    transition: opacity .16s ease-in;
}
.rule__act:hover,
.rule__act:focus-visible { opacity: 1; background: var(--surface-2); color: var(--ink); }
.rule__act:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 1px; }
.rule__act--gap { cursor: default; }
.rule__act--gap:hover { background: none; }

/* Без курсора наведения нет — значки видны всегда. */
@media (hover: none) {
    .rule__act { opacity: .85; pointer-events: auto; }
}

/* Узкий экран: правая часть уходит под текст, столбец значков остаётся
   ровным, галочка и номер — на месте. */
@media (max-width: 760px) {
    .rule {
        grid-template-columns: 24px var(--task-key-w) minmax(0, 1fr);
        grid-template-areas: "pick key main" ". . side";
        gap: 4px 8px;
    }
    .rule__side { flex-wrap: wrap; }
}
@media (max-width: 520px) {
    .rule { padding: 6px 8px; }
}

/* ====== Карта источников дашборда (Антон, 11.09.2026) ======

   Источник — отдельная карточка-строка, а не строка таблицы: их дюжина, у
   каждой свой статус, и в плотной сетке трекера они читались одним полотном.

   Техническое (канал, глубина источника, даты в наборах) убрано под значок «i»
   и живёт в подсказке: на карточке остаётся имя, лампочка и ответ на вопрос
   «когда обновлялось». Подсказка — та же стеклянная плашка, что у календаря
   активности (`.cal__cell[data-tip]`), теми же токенами семейства float.

   Стили заморожены (31.08.2026), поэтому здесь нет ни одного нового цвета,
   кегля, радиуса или тени — только уже существующие. */
.srcrow {
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    margin-bottom: 10px;
}

/* Заголовок — ОТДЕЛЬНОЙ СТРОКОЙ и первым (Антон, 11.09.2026): в две колонки
   метка состояния сдвигала имена источников, и они переставали читаться
   столбцом. Теперь сверху имя со значком «i», ниже — метка и текст. */
.srcrow__title {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    margin-bottom: 6px;
}

/* Метка и текст на одной линии. `align-items: baseline` увёл бы бейдж вверх
   при переносе текста на вторую строку — поэтому по верхнему краю. */
.srcrow__line {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    min-width: 0;
}

/* Лампочка-метка: зелёная — последнее обновление прошло, красная — нет.
   Точка в 6 пикселей, как у метки запуска в «Активности», на карточке
   потерялась (Антон, 11.09.2026: «сильно незаметная»), поэтому лампочка сидит
   внутри обычного бейджа состояния — того же, которым размечены агенты и
   гипотезы. Цвет берётся из `currentColor` бейджа: у метки и её рамки один
   источник цвета, разъехаться им негде. */
.srcrow__lamp {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
}

.srcrow__name {
    font-weight: 650;
    color: var(--ink);
    overflow-wrap: anywhere;
}

/* Значок «i». Держит подсказку, поэтому `position: relative` — плашка внизу
   растёт от него, а не от края экрана. */
.srcrow__i {
    position: relative;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid var(--hair);
    color: var(--ink-3);
    font: 600 11.5px/1.4 var(--sans);
    cursor: default;
}

.srcrow__i:hover { color: var(--ink-2); }

.srcrow__i[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    left: 0;
    top: calc(100% + 7px);
    z-index: 5;
    width: max-content;
    max-width: 340px;
    padding: 6px 10px;
    white-space: pre-line;
    text-align: left;
    font: 400 12px/1.35 var(--sans);
    color: var(--ink);
    background: var(--float-bg);
    border: 1px solid var(--float-line);
    border-radius: var(--radius);
    box-shadow: var(--float-shadow);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    pointer-events: none;
    visibility: hidden;
}

.srcrow__i[data-tip]:hover::after { visibility: visible; }

.srcrow__state {
    font-size: 12.5px;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.srcrow--bad .srcrow__state { color: var(--bad); }

@media (max-width: 720px) {
    .srcrow__line { flex-direction: column; gap: 6px; }
}

/* ---------- Ход прогона на «Дашборд v2» (11.09.2026) -----------------------
   Полосы — тот же `.meter__track` / `.meter__fill`, что у лимитов Claude:
   новых цветов, кеглей и радиусов нет (стили заморожены 31.08.2026), только
   тонкий вариант дорожки и два состояния заливки, которых у лимитов не было:
   «идёт» — акцентом, «ждёт» — без заливки. Ширина — классами pct-N из
   scale.css: CSP не пускает style="", а скрипт опроса переставляет класс. */
.meter__track--run { flex: 1 1 auto; }
.meter__track--thin { height: 6px; border-radius: 3px; }
.meter__track--thin .meter__fill { border-radius: 3px; min-width: 0; }
.meter__fill--run { background: var(--accent); }
.meter__fill--wait { background: transparent; }
.meter__fill--wait-run { background: var(--accent-ring); }
/* Прошлый прогон — заливка приглушена: та же дорожка, но не «сейчас». */
.meter__fill--past { background: var(--hair); }

.runbar {
    display: flex;
    align-items: center;
    gap: 12px;
}

.runbar__pct {
    flex: none;
    min-width: 3.2em;
    text-align: right;
    font-weight: 600;
    color: var(--ink);
}

/* Этапы ночи в ряд: имя, тонкая полоса, подпись. На узком экране — столбцом. */
.stages {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px 20px;
    margin-top: 14px;
}

.stage__name {
    display: block;
    margin-bottom: 5px;
    font-size: 12.5px;
    color: var(--ink-2);
}

.stage--run .stage__name { color: var(--accent); font-weight: 600; }
.stage--bad .stage__name { color: var(--bad); }

.stage__note,
.srcrow__prognote,
.hrow__prognote {
    display: block;
    margin-top: 4px;
    font-size: 11.5px;
    line-height: 1.4;
    color: var(--ink-3);
}

/* Полоса источника — под строкой состояния, во всю ширину карточки. */
.srcrow__prog { margin-top: 10px; }

/* Полоса сборщика — внутри колонки состояния. Дорожка `span`, а не `div`:
   она вложена в `span.hrow__state`, и блочный элемент внутри строчного —
   невалидная разметка. */
.hrow__prog { display: block; margin-top: 6px; }
.hrow__prog .meter__track { display: block; }
.hrow__prog .meter__fill { display: block; }

/* Заменённые наборы на карте источников (11.09.2026): серый бейдж и серый
   текст — история, а не состояние. Токены существующие. */
.badge--muted { color: var(--ink-3); }


/* Файлы, которые приносит человек: список с датой, когда положен. */

/* ---------- Карточка источника: свёрнутая по умолчанию (11.09.2026) ----------
   Заголовок — кнопка: щелчок разворачивает детализацию. Шеврон рисуется
   рамкой, без картинок; стрелка поворачивается при aria-expanded. Токены
   существующие. */
.srcrow__head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    margin: 0 0 6px;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.srcrow__chev {
    flex: none;
    width: 7px;
    height: 7px;
    margin-left: 2px;
    border-right: 1.5px solid var(--ink-3);
    border-bottom: 1.5px solid var(--ink-3);
    transform: rotate(-45deg);
    transition: transform .15s ease;
}
.srcrow__head[aria-expanded="true"] .srcrow__chev { transform: rotate(45deg); }
.srcrow__count { margin-left: auto; flex: none; font-size: 12px; color: var(--ink-3); }

.srcrow__details[hidden] { display: none; }
.srcrow__details {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--hair);
    display: grid;
    gap: 8px;
}

/* Строка детализации: слева метка «в базе», справа имя и текст. */
.detail { display: flex; align-items: flex-start; gap: 10px; }
.detail__body { min-width: 0; flex: 1 1 auto; display: block; }
.detail__name { display: block; color: var(--ink-2); font-weight: 500; }
.detail__text { display: block; font-size: 12.5px; line-height: 1.5; color: var(--ink-3); overflow-wrap: anywhere; }
.detail--bad .detail__name { color: var(--bad); }
.detail--muted .detail__name { color: var(--ink-3); font-weight: 400; }

/* Копится ли в нашей базе: три состояния тем же бейджем, что у агентов. */
.indb {
    flex: none;
    min-width: 62px;
    margin-top: 2px;
    padding: 2px 8px;
    border-radius: 20px;
    border: 1px solid currentColor;
    font: 600 11px/1.4 var(--sans);
    text-align: center;
}
.indb--yes { color: var(--ok); }
.indb--part { color: var(--warn); }
.indb--no { color: var(--ink-3); }
.indb--keep { color: var(--ink-3); border-style: dashed; }   /* остаётся у источника по решению */

.srcrow__count.num { font-variant-numeric: tabular-nums; }

/* Строки источников и таблиц базы — без своей рамки (Антон, 11.09.2026):
   они внутри одной панели, и вторая обводка на каждой строке дробила блок.
   Разделитель — линия панели, ширина — вся панель. */
.srcrow--flat {
    border: 0;
    border-radius: 0;
    background: transparent;
    margin: 0 -18px;
    padding: 12px 18px;
}
.srcrow--flat + .srcrow--flat { border-top: 1px solid var(--rule); }
.srcrow--flat:first-child { margin-top: -18px; padding-top: 14px; }
.srcrow--flat:last-child { margin-bottom: -18px; padding-bottom: 14px; }
