/* Экраны логистики: «План / факт / остаток» и лента вердиктов ОКК.
 *
 * Общее — в src/web/ds.css, он подключается ПЕРЕД этим файлом. Здесь только
 * то, чего нет на других экранах: свежесть прогонов, таблица пакетов,
 * ворота готовности, редактор пакета, разбор расхождений и карточки вердиктов.
 *
 * ЧТО ИЗМЕНИЛОСЬ И ПОЧЕМУ. Раньше эти экраны были тёмными (#0e1116, синий
 * акцент, скругления, системный шрифт) — то есть выглядели как отдельный
 * продукт рядом с дашбордом аналитики. Один человек за утро открывает оба,
 * и разный вид — это не вопрос вкуса: в тёмной теме красный значил «сломано»,
 * а синий — «ссылка», в светлой лаймовый значит «результат», а коралловый —
 * «потеря». Два словаря цвета в одном продукте означают, что не работает ни
 * один. Разметка при этом не переписывалась: классы, которые расставляет
 * JS, остались прежними до единого имени — менялись только правила для них.
 *
 * ЖЁЛТОГО В СИСТЕМЕ НЕТ. Здесь было три состояния — ok / warn / bad, зелёное,
 * жёлтое, красное. В дизайн-системе два цвета смысла: лайм (результат) и
 * коралл (потеря). Третьего не заводили намеренно, и «предупреждение»
 * отличается от «поломки» не цветом, а весом: тонкая полоса против жирной,
 * обычное начертание против полужирного. Так же устроена полоса устаревших
 * данных у аналитики (.stale-note) — приём не выдуман здесь, а взят оттуда.
 *
 * ЦВЕТНОГО ТЕКСТА НЕТ ТОЖЕ. Коралловый на бумажном фоне даёт контраст 2,8:1
 * при норме 4,5:1 — числом, набранным этим цветом, пользоваться нельзя.
 * Поэтому тревога в таблице показана ЗАЛИВКОЙ ячейки, а само число остаётся
 * чернильным и читаемым. Это ровно то, что делает аналитика в td.low-data. */

/* --- свежесть прогонов ---------------------------------------------------- */

.runs{display:grid}
.run{display:grid;grid-template-columns:150px minmax(0,1fr) auto;gap:12px;align-items:center;
     padding:11px 13px;border:1px solid var(--line);border-top:0;background:var(--surface)}
.run:first-child{border-top:1px solid var(--line)}
.run-kind{font-weight:900}
.run-detail{color:var(--muted);font-size:var(--fs-sub);font-variant-numeric:tabular-nums}
/* Несвежий и никогда не запускавшийся — полосой слева, как у .stale-note:
   тонкая значит «предупреждение», жирная — «этого не было вовсе». */
.run.stale{border-left:3px solid var(--orange);background:var(--loss)}
.run.never{border-left:5px solid var(--orange);background:var(--loss);font-weight:700}

/* Причина падения и признак «идёт сейчас» — во всю ширину карточки и под
   датами. Обе строки длинные: текст ошибки портала и pid с временем начала
   не помещаются в колонку рядом с плашкой, а сокращать их многоточием значит
   прятать ровно то, ради чего их и показывают. */
.run-error,.run-now{grid-column:1 / -1;font-size:var(--fs-sub);line-height:var(--lh-note)}
.run-error{padding:8px 10px;background:var(--loss);border-left:3px solid var(--orange);word-break:break-word}
.run-now{color:var(--muted)}

/* --- проблемы ------------------------------------------------------------- */

.problem{margin-top:16px}
.problem:first-child{margin-top:0}
.problem-title{margin-bottom:7px;font-size:var(--fs-micro);font-weight:900;letter-spacing:.04em;text-transform:uppercase}
.problem-title .n{font-variant-numeric:tabular-nums}
.problem ul{margin:0;padding-left:18px}
.problem li{margin-bottom:4px;color:var(--muted);font-size:var(--fs-note);line-height:var(--lh-note)}
.problem li .mono{color:var(--ink);font-family:var(--mono);font-size:var(--fs-sub)}
.problem-hint{margin-bottom:7px;color:var(--muted);font-size:var(--fs-sub);line-height:var(--lh-note)}

/* Падения, уже закрытые успехом. Список тот же, тревога — другая: эти строки
   в счётчик панели не идут, и тревожное число над ними означало бы ровно то,
   от чего блок избавляли, — «сломано» там, где чинить нечего. Отсюда
   приглушённый счётчик и отбивка сверху: глаз должен отделить их от списков
   выше раньше, чем прочтёт заголовок. */
.problem.healed{margin-top:22px;padding-top:14px;border-top:1px solid var(--line)}
.problem.healed .problem-title,.problem.healed .problem-title .n{color:var(--muted)}

.all-clear{padding:9px 12px;border-left:3px solid var(--green);background:var(--warm);font-size:var(--fs-note)}

/* --- таблицы -------------------------------------------------------------- */

/* Ширины по умолчанию не задаются: колонок здесь от четырёх до двенадцати,
   содержимое разной длины, и браузер раскладывает их лучше, чем угаданные
   проценты. Задать их может человек, потянув за край заголовка, — тогда
   включается .cols-fixed (см. ниже).
   Прокрутка — у обёртки .table-wrap из общей системы. */
.grid{width:100%}
.grid th{padding:0 10px;color:var(--muted);font-size:var(--fs-micro);font-weight:900;
         letter-spacing:.04em;text-align:left;text-transform:uppercase;line-height:1.25}
.grid td{text-align:left}
.grid td.num,.grid th.num{text-align:right;font-variant-numeric:tabular-nums}
.grid td.num{font-size:var(--fs-num);font-weight:900}
.grid td.act{text-align:right;white-space:nowrap}

/* --- столбцы: сортировка, отбор, перекладка, ширина ------------------------ */

/* Заголовок пакетов — не текст, а два элемента управления в ряд: кнопка
   сортировки во всю ширину и узкий значок отбора у края. Отступ снимается с
   ячейки и отдаётся кнопке: иначе нажатие у самого края заголовка попадало бы
   в пустое поле и не делало ничего. */
#packages thead th{padding:0}
.col-head{display:flex;align-items:stretch;height:46px}
/* .sort и .sort.active — из общей системы (ds.css): та же кнопка заголовка,
   что на экране конверсии. min-width:0 обязателен — без него длинное название
   распирает flex-элемент и выдавливает значок отбора за край ячейки. */
.col-head .sort{flex:1 1 auto;min-width:0;overflow:hidden;cursor:grab}
body.cols-dragging .col-head .sort{cursor:grabbing}
.col-filter{width:22px;flex:0 0 22px;border:0;border-left:1px solid var(--line);
            background:transparent;color:var(--muted);font-size:var(--fs-micro);line-height:1}
.col-filter:hover{background:var(--green);color:var(--ink)}
/* Включённый отбор виден на самом столбце, а не только строкой над таблицей:
   строка говорит «показаны не все», значок — какой именно столбец прячет. */
.col-filter.on{background:var(--green);color:var(--ink);font-weight:900}

/* Ручка ширины. Семь пикселей — в них попадают мышью, не целясь; уже — и
   вместо ручки берут соседнюю кнопку. Занимает место в ряду, а не лежит
   поверх ячейки: наложенная полоска перекрыла бы край значка отбора. */
.col-grip{flex:0 0 7px;cursor:col-resize}
.col-grip:hover{background:var(--green)}
body.cols-resizing{cursor:col-resize;user-select:none}

/* Столбец, который тащат, и тот, на чьё место его бросят. Полупрозрачность
   для первого и лаймовая метка для второго: оранжевый здесь запрещён — он
   означает потерю, а перекладка столбцов ничего не теряет. */
#packages th.col-dragging{opacity:.45}
#packages th.col-drop{box-shadow:inset 3px 0 0 var(--green)}
body.cols-dragging{user-select:none}

/* Ширины заданы руками. table-layout:fixed берёт их из первой строки, и
   содержимое перестаёт раздвигать ячейку — значит, его надо обрезать, иначе
   длинное название клиента ляжет поверх соседнего столбца. */
#packages.cols-fixed td{overflow:hidden;text-overflow:ellipsis}
/* Закреплённой первой колонке общая система задаёт 320px тремя свойствами
   разом. Заголовку их перебивает инлайновый стиль, ячейкам — эта строка:
   селектор с id весомее, чем .table-wrap.pinned td:first-child. */
#packages.cols-fixed td:first-child{width:auto;min-width:0;max-width:none}

/* Строка «показаны не все». Лайм, а не оранжевый: отбор — выбор человека,
   а не потеря. Кнопка справа — единственное, что требуется, чтобы её погасить. */
.filter-note{display:flex;align-items:center;justify-content:space-between;gap:12px;
             margin:0 0 12px;padding:9px 12px;border-left:3px solid var(--green);
             background:var(--warm);font-size:var(--fs-note);line-height:var(--lh-note)}

/* Список значений отбора. Живёт в body (см. openColMenu), поэтому position
   здесь fixed, а координаты ставит скрипт. */
.col-menu{position:fixed;z-index:60;width:264px;border:1px solid var(--ink);background:var(--surface)}
.col-menu-head{padding:9px 11px;border-bottom:1px solid var(--ink);background:var(--warm);
               font-size:var(--fs-micro);font-weight:900;letter-spacing:.04em;text-transform:uppercase}
.col-menu-tools{display:flex;gap:7px;padding:9px 11px 0}
.col-menu-search{width:calc(100% - 22px);height:28px;margin:9px 11px 0;padding:0 8px;
                 border:1px solid var(--ink);background:var(--paper);font-size:var(--fs-note)}
/* Высота ограничена: у «Клиента» значений под шестьдесят, и список во весь
   экран прячет собственные кнопки «Применить» и «Отмена». */
.col-menu-list{max-height:240px;margin:9px 0;overflow:auto}
.col-menu-item{display:flex;align-items:center;gap:8px;padding:4px 11px;font-size:var(--fs-note)}
.col-menu-item:hover{background:var(--warm)}
.col-menu-name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.col-menu-n{color:var(--muted);font-size:var(--fs-micro);font-variant-numeric:tabular-nums}
.col-menu-foot{display:flex;gap:7px;padding:0 11px 11px}
/* Применить — действие по умолчанию, и оно же единственное, которое меняет
   таблицу. Лайм ставит его впереди «Отмены» без второго размера кнопки. */
.col-menu-foot .accent{background:var(--green)}

/* Отгрузка в регион, которого нет в условиях пакета: либо ошибка правил, либо
   ручная отправка мимо системы. Заливка, а не цветной текст, — число обязано
   остаться читаемым. */
.grid tr.unexpected td{background:var(--loss)}
.grid tr.unexpected td:first-child{box-shadow:inset 3px 0 0 var(--orange)}
/* Пакет, у которого занято больше, чем осталось. Заметная строка, а не
   спрятанное расхождение: раздача по нему остановлена, и человек должен
   узнать об этом с экрана, а не из вопроса «почему ничего не уходит». */
.grid tr.overcommitted td{background:var(--loss)}
.grid tr.overcommitted td:first-child{box-shadow:inset 5px 0 0 var(--orange)}

/* Учебные пакеты: видны, но не в рабочих итогах. Приглушены, а не спрятаны —
   спрятанный пакет означает передачи без владельца на экране расхождений,
   а приглушённый читается как «есть, но не считается». */
.grid tr.training td{background:var(--warm);color:var(--muted)}
.grid tr.sep td{border-top:2px solid var(--ink);padding-top:10px;
                color:var(--muted);font-size:var(--fs-micro);font-weight:900;
                letter-spacing:.04em;text-transform:uppercase}

/* Состояние пакета. Словом, а не только цветом: текст плашки приходит из
   данных («Активен», «Пауза», «Черновик», «Закрыт»), поэтому дополнительный
   значок здесь не нужен — небуквенный признак уже есть. */
.st{display:inline-block;padding:3px 8px;border:1px solid var(--ink);
    font-size:var(--fs-micro);font-weight:900;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}
.st.active{background:var(--green)}
.st.paused{background:var(--loss)}
.st.draft{border-style:dashed;background:transparent;color:var(--muted)}
.st.done{background:var(--warm);color:var(--muted)}

/* Ноль остатка — это «всё отгрузили», а не тревога: приглушаем.
   Остаток меньше чем на два дня — предупреждение: заливка без жира.
   Занято больше, чем есть, — поломка: заливка и жирное начертание. */
.grid td.rest-zero{color:var(--muted)}
.grid td.rest-low{background:var(--loss)}
.grid td.warn-text{background:var(--loss)}
.grid td.bad-text{background:var(--loss);font-weight:900;box-shadow:inset 0 -3px 0 var(--orange)}

/* Колонка «Факт» — единственная, которой ширину задаём руками. Браузер
   раскладывает колонки по самому длинному слову, а здесь самое длинное слово
   — число из двух цифр; под ним же стоят три подстрочника длиной со строку.
   Без этой ширины «сверено 116 · наши 0» рвётся на обрывки, и строка пакета
   вырастает втрое.
   160px — не догадка, а замер по всем 168 подстрочникам боевого экрана:
   при 132px влезает 35% из них, при 150px — 87%, при 160px — 97%, а дальше
   каждые следующие 88 пикселей покупают один процент. Оставшиеся 3% — строка
   «не в счёт: назначение не выяснено N», которой нужно 224px; держать под неё
   лишние 88 пикселей в КАЖДОЙ строке дороже, чем дать ей перенестись. */
.grid td.fact{min-width:160px}
.fact-parts{display:block;margin-top:3px;color:var(--muted);font-size:var(--fs-micro);font-weight:400;line-height:1.35;white-space:normal}
/* Начало учёта стоит под фактом строкой, а не в подсказке: ноль «ещё не
   отгружали» и ноль «считаем с такого-то числа» — разные ответы, и различить
   их надо не наводя мышь. */
.fact-since{display:block;margin-top:2px;color:var(--muted);font-size:var(--fs-micro);font-weight:400;font-style:italic;white-space:normal}
/* Внутри ячейки-числа предупреждение уже показано её заливкой. Подстрочнику
   остаётся только начертание — иначе тревога дублируется дважды в одном
   месте и перестаёт выделять что-либо. */
.fact-parts.warn-text,.fact-since.warn-text{color:var(--ink);font-weight:700}

/* Готовность: плашка и под ней — какие именно ворота закрыты. Список в одну
   строку с плашкой раздувал колонку так, что таблица уезжала за край панели,
   а уехавшее вправо не читают вовсе. */
.grid td.ready{max-width:210px;white-space:normal}
.ready-gates{margin-top:4px;color:var(--muted);font-size:var(--fs-micro);line-height:1.35}

/* --- ворота готовности ---------------------------------------------------- */

/* Класс здесь `ready-row`, а не `gate`, хотя по смыслу это ворота. Имя `gate`
   занято формой входа: в общей системе это блок на весь экран. Строка ворот
   с тем же классом получала бы его раскладку и растягивалась поверх
   редактора — кнопки под ней оставались видимыми, но клики забирала она. */
.ready-row{display:grid;grid-template-columns:1.6em minmax(0,1fr);gap:9px;
           padding:10px 0;border-bottom:1px solid var(--line)}
.ready-row:last-child{border-bottom:0}
.ready-mark{width:1.6em;font-weight:900;text-align:center}
/* Закрытые ворота: крест на коралловой заливке. Открытые: галочка на лайме.
   Знак и заливка несут одно и то же — по отдельности любого из них хватает. */
.ready-mark{background:var(--loss);box-shadow:inset 0 0 0 1px var(--ink)}
.ready-row.ok .ready-mark{background:var(--green)}
.ready-title{font-size:var(--fs-body);font-weight:700}
.ready-row.ok .ready-title{font-weight:400}
.ready-why{margin-top:3px;max-width:80ch;color:var(--muted);font-size:var(--fs-sub);line-height:var(--lh-note)}

/* --- редактор пакета ------------------------------------------------------ */

.editor{width:min(940px,94vw);max-height:88vh;padding:0;overflow:auto;
        border:1px solid var(--ink);background:var(--surface);color:var(--ink)}
.editor::backdrop{background:rgba(23,33,29,.35)}

.editor-head{position:sticky;top:0;z-index:1;display:flex;justify-content:space-between;
             align-items:flex-start;gap:16px;padding:16px 18px;
             border-bottom:1px solid var(--ink);background:var(--surface)}
.editor-head h2{margin:0;font-size:20px;letter-spacing:-.04em}
.editor-head .hint{margin:6px 0 0}

/* Результат и ошибка — два разных места. Сообщение «сохранено», занявшее
   место ошибки, читается как ошибка, и наоборот. */
.ed-msg{margin:14px 18px 0;padding:11px 13px;border:1px solid var(--ink);font-size:var(--fs-note);line-height:var(--lh-note)}
.ed-msg.bad{border-left:5px solid var(--orange);background:var(--loss);font-weight:700}
.ed-msg.ok{border-left:5px solid var(--green);background:var(--warm)}

.ed-block{padding:16px 18px;border-bottom:1px solid var(--line)}
.ed-block:last-child{border-bottom:0}
.ed-block h3{margin:0 0 6px;font-size:var(--fs-head);letter-spacing:-.02em}
.ed-details{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.ed-details summary{cursor:pointer;font-size:var(--fs-ui);font-weight:900;letter-spacing:.03em;text-transform:uppercase}

/* --- история пакета ------------------------------------------------------- */

.events{margin:10px 0 0;padding:0;list-style:none}
.events li{display:grid;grid-template-columns:150px 130px 140px minmax(0,1fr);gap:10px;
           padding:8px 0;border-bottom:1px solid var(--line);font-size:var(--fs-sub);line-height:1.4}
.events li:last-child{border-bottom:0}
.ev-when{color:var(--muted);font-family:var(--mono);font-size:var(--fs-micro)}
.ev-kind{font-weight:900}
.ev-actor{color:var(--muted)}
.ev-detail{color:var(--muted);font-family:var(--mono);font-size:var(--fs-micro);overflow-wrap:anywhere}

/* --- разрешение расхождений по одной передаче ----------------------------- */
/* Строка решения — это форма, а не элемент списка: у неё свой выбор пакета,
   своя причина и свой ответ сервера. Ответ показывается ВСЕГДА, а не только
   при ошибке: решение, не сдвинувшее факт, — обычный и правильный исход,
   и молчание в этом случае читается как «не сработало». */
.resolve{margin-top:18px}
.resolve-row{margin-bottom:10px;padding:12px 14px;border:1px solid var(--line);background:var(--paper)}
.resolve-head{display:flex;flex-wrap:wrap;gap:10px;align-items:baseline}
.resolve-who{font-size:var(--fs-body);font-weight:900}
.resolve-when{color:var(--muted);font-size:var(--fs-sub);font-variant-numeric:tabular-nums}
.resolve-why{margin-top:5px;color:var(--muted);font-size:var(--fs-note);line-height:var(--lh-note)}
/* Четыре клетки: пакет, назначение, причина, кнопка. Назначение стоит вторым,
   а не последним, потому что читается как продолжение первого вопроса: «чей
   объём» и сразу «платный ли». Уехав за поле причины, оно превращается в
   настройку, которую пролистывают. */
.resolve-form{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr) minmax(0,1.8fr) auto;
              gap:8px;margin-top:10px;align-items:center}
.resolve-form select,.resolve-form input{min-width:0;height:38px;padding:0 10px;
              border:1px solid var(--ink);background:var(--surface);font-size:var(--fs-control)}
.resolve-out{grid-column:1 / -1;margin-top:8px;padding:8px 10px;font-size:var(--fs-note);line-height:var(--lh-note)}
.resolve-out.ok{border-left:3px solid var(--green);background:var(--warm)}
.resolve-out.warn{border-left:3px solid var(--orange);background:var(--loss)}
.resolve-out.bad{border-left:5px solid var(--orange);background:var(--loss);font-weight:700}
/* Строка, у которой варианты есть, но выбрать нельзя ни один. Формы тут нет
   намеренно: она бы предлагала нажать кнопку, по которой сервер откажет.
   Вместо неё — действие, которого не хватает, и оно должно быть заметным. */
.resolve-stuck{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:10px}
.resolve-stuck .hint{margin:0;font-size:var(--fs-sub)}
/* Заблокированный вариант в списке виден, но выбрать его нельзя. */
.resolve-form option:disabled{color:var(--muted)}

/* --- лента вердиктов ОКК (okk.html) ---------------------------------------
   Отдельный экран, а не вкладка табло: там отвечают на вопрос «сколько
   осталось отгрузить», тут — «где машина ошиблась». Читают её подряд, сверху
   вниз, с открытой рядом карточкой лида, поэтому строка разбора важнее
   плотности: лишние 2 пикселя между проверками дешевле пропущенной ошибки. */

.okk-filters{display:flex;flex-wrap:wrap;gap:0;margin-bottom:14px;border:1px solid var(--ink)}
.okk-filter{height:38px;padding:0 14px;border:0;border-right:1px solid var(--ink);
            background:transparent;font-size:var(--fs-ui);font-weight:900;
            letter-spacing:.03em;text-transform:uppercase}
.okk-filter:last-child{border-right:0}
.okk-filter:hover{background:var(--warm)}
/* Выбранный фильтр залит лаймом — так же, как активная вкладка и активный
   пункт меню во всей системе. «Показаны все» — не тревога, и коралловым его
   красить нельзя. */
.okk-filter.active{background:var(--green)}
.okk-filter-n{margin-left:6px;font-variant-numeric:tabular-nums}
.okk-filter:not(.active) .okk-filter-n{color:var(--muted)}

.okk-card{margin-bottom:12px;padding:13px 15px;border:1px solid var(--line);background:var(--surface)}
/* Вердикт — на левом ребре карточки. При прокрутке длинной ленты взгляд
   держится одной вертикали, и полоса отвечает на «что тут» раньше, чем глаз
   доберётся до слова. Толщина различает «вернуть» и «не решила»: жирная —
   поломка, тонкая — вопрос. */
.okk-card.v-pass{border-left:4px solid var(--green)}
.okk-card.v-return{border-left:5px solid var(--orange);background:var(--loss)}
.okk-card.v-unknown{border-left:3px solid var(--orange)}

.okk-head{display:flex;flex-wrap:wrap;align-items:center;gap:10px}
.okk-lead{font-family:var(--mono);font-size:var(--fs-num);font-weight:700}
/* Ссылка на карточку лида. Цвета для ссылок в системе нет — синий здесь не
   значит ничего, а лайм занят результатом. Отличие несёт подчёркивание, оно
   же единственное, что переживёт печать в чёрно-белом. */
a.okk-lead{color:inherit;text-decoration:underline;text-underline-offset:3px}
.okk-meta{margin-left:auto;color:var(--muted);font-size:var(--fs-micro);line-height:1.4;text-align:right}

.okk-reason{margin:10px 0 0;font-size:var(--fs-body);line-height:var(--lh-note)}
.okk-reason.muted{color:var(--muted);font-size:var(--fs-note)}
.okk-code{color:var(--muted);font-family:var(--mono);font-size:var(--fs-micro)}

.okk-checks{display:grid;gap:8px;margin-top:12px}
.okk-check{display:grid;grid-template-columns:132px minmax(0,1fr);gap:11px;align-items:start}
/* Значок не переносится по словам: «расходится» в две строки сдвигает всю
   строку разбора и ломает колонку, по которой глаз идёт сверху вниз. */
.okk-check .pill{white-space:nowrap}
/* Строка-справка ничего не утверждает — она приглушена, чтобы не спорить
   за внимание с настоящими проверками. */
.okk-info{opacity:.72}
.okk-info .pill{border-style:dashed;background:transparent;color:var(--muted)}
.okk-info .pill:before{content:none}
/* .okk-check-body правила не имеет и не должна: это вторая ячейка сетки,
   и всё, что ей нужно — уметь сжиматься, а это уже дано треком minmax(0,1fr)
   выше. Правило вида `min-width:0` здесь ничего бы не изменило, зато следующий
   читатель искал бы в нём смысл. */
.okk-check-name{font-size:var(--fs-body);font-weight:700}
.okk-check-detail{margin-top:2px;color:var(--muted);font-size:var(--fs-note);line-height:var(--lh-note);word-break:break-word}

/* Цитаты из расшифровки. Моноширинный таймкод — чтобы столбик не прыгал:
   по нему ищут это место в записи, а прыгающий столбик заставляет читать
   каждую строку заново. */
.okk-quotes{display:grid;gap:5px;margin-top:8px;padding-left:11px;border-left:2px solid var(--line)}
.okk-quote{display:flex;flex-wrap:wrap;gap:9px;align-items:baseline;font-size:var(--fs-note);line-height:var(--lh-note)}
.okk-time{color:var(--muted);font-family:var(--mono);font-size:var(--fs-micro)}
/* Сторону говорящего мы не знаем точно: запись моно, голоса не разделены.
   Подпись приглушена намеренно — она догадка, а не факт. */
.okk-who{color:var(--muted);font-size:var(--fs-micro);font-style:italic}
.okk-said{word-break:break-word}

.okk-more{display:flex;align-items:center;gap:14px;margin-top:6px}
.okk-more .muted{font-size:var(--fs-sub)}

@media(max-width:900px){
  .run{grid-template-columns:minmax(0,1fr) auto}
  .run-detail{grid-column:1 / -1}
  .resolve-form{grid-template-columns:1fr}
  .events li{grid-template-columns:1fr;gap:2px}
  .okk-check{grid-template-columns:1fr;gap:4px}
  .okk-meta{margin-left:0;width:100%;text-align:left}
}
