/* Компоненты и экраны мини-аппа v2 (концепт A «Сегодня»). Токены — в tokens.css, здесь только
   то, что собирается из токенов. Правило файла: ни одного числа, которого нет в шкале токенов
   (4/8/12/16/24/32, радиусы 8/10/14, кегли 34/22/17/15/13/11). Каждый экран — своя секция ниже,
   экранные правила не пересекаются: экран правит только свой префикс класса. */


/* ---------- Каркас концепта A: «Сегодня» — задача-первый ----------
   Телефон: таббар снизу, экраны стеком. Планшет: рейка иконок слева. Десктоп: боковая
   панель с подписями + контент 760px; карточка машины на широком экране встаёт справа. */
#app{display:grid;grid-template-columns:1fr;min-height:100svh}
.side{display:none}
/* min-width:0 обязателен: .screen — грид-ячейка #app (grid-template-columns:1fr), а у ячеек грида
   дефолтный min-width:auto — ячейка не сжимается уже своего контента и «распирает» колонку вширь
   (classic grid blowout). Внутри карточки машины это ловит нескролящийся .seg.scroll (шесть вкладок
   в один ряд без переноса): его max-content шире 375px, и без min-width:0 это раздувало ВЕСЬ экран
   вбок вместо горизонтальной прокрутки самой полосы вкладок. */
.screen{display:none;padding:0 var(--pad-r) calc(96px + env(safe-area-inset-bottom,0px)) var(--pad-l);max-width:760px;margin:0 auto;width:100%;min-width:0}
.screen.on{display:block}
/* Базовая шапка: полоса во всю ширину в отрицательных полях экрана (повторяют поля экрана, чтобы в
   ландшафте с вырезом не оставалось полосы голого фона). Сейчас все шапки — .topbar.material, и
   правило ниже переводит их в плашку-карточку; голая .topbar оставлена как основа (высота, флекс). */
.topbar{position:sticky;top:0;z-index:5;display:flex;align-items:center;justify-content:space-between;height:52px;
  margin:0 calc(-1 * var(--pad-r)) 0 calc(-1 * var(--pad-l));padding:0 var(--pad-r) 0 var(--pad-l)}
.topbar .t-large{font-size:24px} /* оформление 2026-09-21: кегли на шаг ниже, см. раздел в конце файла */
/* Плашка заголовка — белая скруглённая карточка на ВСЕХ экранах и ширинах (владелец, 2026-09-02:
   «прямоугольный фон некрасиво смотрится»; до этого карточкой была только «Сегодня» на десктопе,
   у остальных — полоса с прямыми углами до кромки). Живёт внутри полей экрана, а не в отрицательных:
   свой отступ сверху (--sp-3) и внутренние поля --sp-4. Плотный фон --card без backdrop-filter —
   тут нечего размывать, и заодно нет известного артефакта Chromium «чёрная полоса» при сочетании
   backdrop-filter с фикс-слоем .bgfx (z-index:-1). Прилипает не к самой кромке, а на --sp-2 ниже,
   чтобы прилипшая плашка не упиралась в край окна. Тень — только после прокрутки (класс .scrolled
   на <html>, core.js initBgFx; сбрасывается при смене экрана); в покое плашка без тени.
   ВНИМАНИЕ: сумма top + высота задаёт, где заканчивается прилипшая шапка — от неё считается top у
   .tosec (липкие заголовки разделов ТО) ниже по файлу. */
/* ШАПКА — НЕ ПЛАШКА, А КРАЙ КОНТЕЙНЕРА (2026-09-21, владелец: «некрасиво прокручивается,
   наложение… продумай, как будет красиво»). Раньше это был остров-карточка, который липнет на 8px
   ниже кромки, а дыру над ним и вырезы скруглённых углов затыкала непрозрачная заглушка. Форма и
   была причиной: остров всегда даёт щель сверху и просветы в углах, а строки списка обрывались о
   непрозрачный край. Теперь в покое у шапки фона нет вовсе — заголовок лежит на фоне экрана; при
   прокрутке под ним включается сплошная полоса ВО ВСЮ ширину контейнера (щели и вырезов не
   существует), а ниже кромки — 16px растворения: строки тают, а не режутся. Тень и линия не нужны,
   границу рисует сам градиент. Геометрия (top:--sp-2, height:52px) не тронута: от неё считаются
   липкие заголовки разделов .tosec. */
.topbar.material{margin:var(--sp-3) 0 0;padding:0 var(--sp-4);top:var(--sp-2);background:transparent;
  -webkit-backdrop-filter:none;backdrop-filter:none;border:0;border-radius:0}
/* Полоса под шапкой и растворение под её кромкой. Оба слоя лежат ПОД собственным фоном шапки
   (z-index:-1) и раздвинуты на поля экрана, поэтому закрывают и боковые поля, и бывшие вырезы
   углов. Включаются вместе с классом .scrolled — в покое их нет. */
.topbar.material::before{content:"";position:absolute;left:calc(-1 * var(--pad-l));right:calc(-1 * var(--pad-r));
  top:calc(-1 * var(--sp-2));bottom:0;background:var(--bg);z-index:-1;border-radius:0;
  opacity:0;transition:opacity .18s linear}
.topbar.material::after{content:"";position:absolute;left:calc(-1 * var(--pad-l));right:calc(-1 * var(--pad-r));
  top:100%;height:var(--sp-4);background:linear-gradient(to bottom,var(--bg),var(--bg-0));z-index:-1;
  pointer-events:none;opacity:0;transition:opacity .18s linear}
:root.scrolled .topbar.material::before,:root.scrolled .topbar.material::after{opacity:1}
@media (prefers-reduced-motion:reduce){.topbar.material::before,.topbar.material::after{transition:none}}
/* Остров-таббар прижат к боковым кромкам: в ландшафте на телефонах без рейки (ширина < 700, iPhone SE
   и подобные) 12 px мало, если сбоку вырез — берём большее из двух. */
.tabbar{position:fixed;left:max(12px,var(--safe-l));right:max(12px,var(--safe-r));bottom:calc(10px + env(safe-area-inset-bottom,0px));z-index:6;display:flex;align-items:center;justify-content:space-around;height:64px;padding:0 8px;border-radius:22px;border:1px solid var(--sep);box-shadow:var(--shadow)}
.tab{text-decoration:none;display:flex;flex-direction:column;align-items:center;gap:3px;font-size:10px;font-weight:600;color:var(--label2);min-width:64px;padding:4px 0;border-radius:8px}
.tab svg.i{width:24px;height:24px}
.tab.on{color:var(--accent)}
/* 16 px до заголовка раздела вместо 24 (владелец, 2026-09-22). Шкала соблюдена: между группами
   16, от заголовка к его содержимому 6 — расстояние внутри группы по-прежнему меньше, чем между
   группами, а это и есть правило близости. */
.sec{margin:var(--sp-4) 0 6px;display:flex;align-items:baseline;justify-content:space-between}
.sec .t-cap{margin-left:var(--sp-4)}
.sec a{font-size:15px;color:var(--accent);text-decoration:none;font-weight:500}
/* Сводка-«ключ» на Сегодня: три числа, не шесть плиток */
.kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-2)}
.kpi{background:var(--card);border-radius:var(--r-m);padding:10px 12px;display:flex;flex-direction:column;gap:2px} /* плотнее — оформление 2026-09-21 */
.kpi b{font-size:24px;font-weight:700;letter-spacing:-.02em;line-height:1.1;text-shadow:var(--text-shadow)} /* кегль на шаг ниже — оформление 2026-09-21 */
.kpi b.warn{color:var(--warn-text)}.kpi b.bad{color:var(--bad-text)}
.kpi span{font-size:12px;color:var(--label2);font-weight:500}
/* Две строки в строке списка: подпись под значением, а не рядом */
/* Вертикальный воздух двухстрочной строки задаёт САМА строка, а не внутренняя колонка
   (владелец, 2026-09-22: «пройдись везде по межстрочным, сделай компактно»). Было два слагаемых:
   9 px сверху и снизу у .grow плюс отступ строки — на бою это давало 70–75 px под 39 px текста.
   Теперь одно: .row держит 6 px, а однострочные строки всё так же не ниже --row (44 px). */
.row .grow:not(.t-sub):not(.unit){display:flex;flex-direction:column;gap:2px;padding:0}
.row{padding-top:6px;padding-bottom:6px}
.feed .row,.frow{padding-top:6px;padding-bottom:6px}
.kpi{min-width:0}
.kpi span{white-space:normal;line-height:1.2}
.vrow{display:grid;grid-template-columns:4px 76px 1fr auto;gap:10px;align-items:center;min-height:52px;padding:6px 14px 6px 10px;position:relative}
.vrow+.vrow::before{content:"";position:absolute;left:100px;right:0;top:0;height:1px;background:var(--sep)}
.vrow .st{width:4px;height:36px;border-radius:4px;background:var(--fill)}
.vrow .st.bad{background:var(--bad)}.vrow .st.warn{background:var(--warn)}.vrow .st.ok{background:transparent}
.vrow>div{min-width:0}
.vrow .name{font-size:15px;font-weight:600;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vrow .meta{font-size:11px;color:var(--label2);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.vrow .km{white-space:nowrap;font-size:13px;font-variant-numeric:tabular-nums;color:var(--label2);text-align:right;line-height:1.2}
.vrow .km b{display:block;color:var(--label);font-size:14px;font-weight:600}
.vrow.sel{background:var(--accent-soft)}
.grp{padding:14px 14px 4px;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--label2);display:flex;justify-content:space-between}
.dhead{display:flex;align-items:center;gap:12px;padding:10px 0 12px;flex-wrap:wrap}
.dhead h1{font-size:22px;font-weight:700;letter-spacing:-.015em}
.dhead .plate{font-size:13px;padding:6px 8px}
.dhead .sp{flex:1}
.stat{display:flex;gap:1px;background:var(--sep);border-radius:var(--r-m);overflow:hidden;margin-top:6px}
.stat>div{flex:1;background:var(--card);padding:8px 12px;min-width:0} /* плотнее — оформление 2026-09-21 */
.stat b{display:block;font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.01em;white-space:nowrap}
.stat span{font-size:11px;color:var(--label2);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Колесо подписи (vehicle.js::renderRot, владелец 2026-09-21: «только 2 строки не более, и вторая
   строка ротируется вертикально колесом раз в 3 сек»). Окно высотой ровно в одну строку, внутри —
   столбик фактов, который сдвигается на процент от своей высоты: так число фактов не влияет ни на
   вёрстку, ни на арифметику сдвига. Высота ячейки перестала зависеть от длины подписи вовсе. */
.tbl{background:var(--card);border-radius:var(--r-m);overflow:hidden;margin-top:8px}
.tr{display:grid;gap:10px;align-items:center;min-height:42px;padding:6px 12px;font-size:13px;position:relative}
.tr+.tr::before{content:"";position:absolute;left:12px;right:0;top:0;height:1px;background:var(--sep)}
.tr.th{min-height:30px;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--label2);background:var(--card2)}
.tr .n{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.tr .pill{white-space:nowrap}
.tr .sub{font-size:12px;color:var(--label2);display:block;margin-top:1px}
/* Пилюля состояния («норма», «не осматривали», «требует внимания») — колонка по содержимому, а
   не фиксированные 84px: у .tbl стоит overflow:hidden, и русское «не осматривали» (~104px)
   обрезалось краем карточки (владелец, скриншот 2026-09-21). minmax(0,…) у текстовых колонок
   обязателен — иначе они не дают ужаться и пилюля снова вылезет за край. */
.units .tr{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) 90px minmax(84px,max-content)}
.journal .tr{grid-template-columns:52px 1fr auto}
/* Вкладка «Детали» карточки (tabParts в vehicle.js) — раньше жила на .journal и на 375px ломалась:
   колонка auto под пилюлю «внесено вручную · TEST_SEED_WHEELS» забирала 234px, названию оставалось
   62px, строка вырастала до 177px. Здесь пилюля ограничена fit-content(40%) с многоточием (её auto-
   минимум = 0 благодаря overflow:hidden, иначе nowrap распёр бы колонку до полной ширины), а на
   телефоне (<700px) уходит под текст второй строкой — текст от края до края, как в ленте. Строка без
   даты (.nodate — installed_at и removed_at пустые) не резервирует 52px под пустую дату. */
.parts .tr{grid-template-columns:52px minmax(0,1fr) fit-content(40%)}
.parts .tr.nodate{grid-template-columns:minmax(0,1fr) fit-content(40%)}
.parts .tr .pill{display:block;max-width:100%;line-height:22px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:699px){
  .parts .tr{grid-template-columns:52px 1fr;row-gap:4px}
  .parts .tr.nodate{grid-template-columns:1fr}
  .parts .tr .pill{grid-column:2;justify-self:start}
  .parts .tr.nodate .pill{grid-column:1}
}
.actions{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.actions .btn{flex:1;min-width:120px}
.vrow .to{display:none;font-size:12px;color:var(--label2);text-align:right;white-space:nowrap;min-width:96px}
.vrow .to.bad{color:var(--bad-text);font-weight:600}.vrow .to.warn{color:var(--warn-text);font-weight:600}
.vrow{padding-left:6px;padding-right:12px}.vrow>div{min-width:0}
.vrow .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.group>.vrow+.vrow::before{left:96px}
.row .st{width:4px;height:36px;border-radius:4px;background:var(--fill);flex:none}
.row .st.bad{background:var(--bad)}.row .st.warn{background:var(--warn)}.row .st.ok{background:var(--ok)}
/* Третья колонка ужата с 84 до 56: «норма» теперь точка (см. head.css), а пилюля остаётся
   только у просроченных и скорых — им 56 px хватает, а названию узла освободилось 28. */
.units .tr{grid-template-columns:minmax(0,1.4fr) 76px minmax(56px,max-content);row-gap:6px}
.units .tr .bar{grid-column:1/-1;margin-top:-2px}
/* Компактная строка узла на Обзоре машины (unitRowCompact в vehicle.js) — та же тощина, что у
   строки машины (.vrow, min-height:52px; владелец, скриншот: «раздел узлов самый длинный на
   экране»). Полоса ресурса здесь лежит внутри первой колонки (третьей строкой текста), а не
   отдельной строкой грида — так и не нужен row-gap+margin-top под неё, который в .units .tr
   выше добавлял лишние ~10px на каждую строку. Полная строка вкладки «ТО» (unitRow) этот класс
   не получает и остаётся как была. */
.units.compact .tr:not(.th){min-height:52px;padding:6px 10px}
.units.compact .tr .bar{margin-top:3px;height:3px}
/* Список узлов больше не режется топ-5 с раскрытием по кнопке «Все N» — владелец, 2026-09-02:
   список остаётся полным и скроллится внутри себя. Пять с половиной строк видно сразу (высота
   посчитана из шапки .tr.th 30px + N×52px), остальное — прокруткой; полоса — общий тонкий
   бегунок ::-webkit-scrollbar ниже по файлу, здесь только рамка контейнера. */
.scroll-units{max-height:316px;overflow-y:auto;overflow-x:hidden}
/* Вкладка ТО (tabTo в vehicle.js): раздел → подгруппа → узел.
   .tosec — заголовок раздела, липнет под шапкой карточки (.topbar, 52px) при прокрутке: на 72
   строки регламента это единственный ориентир «в каком я разделе». Скролл-контейнер на телефоне —
   страница, на десктопе (≥1100px) — .card-col; шапка в обоих случаях sticky top:0 в том же
   контейнере, поэтому одно значение top верно везде. Подложка — .material (та же, что у шапки):
   полупрозрачная с размытием, а не сплошной --bg, потому что под страницей лежит фоновый слой с
   пятнами (.bgfx) и сплошной прямоугольник цвета фона на нём был бы виден. z-index ниже шапки (5).
   .tr.gh — шапка подгруппы внутри карточки: та же капитель, что у .tr.th, плюс сводная пилюля и
   шеврон; тап сворачивает участников (обёртка .gmem[hidden]). Специфичность .units.compact .tr.gh — та же,
   что у .units.compact .tr:not(.th) выше, поэтому это правило обязано стоять ПОСЛЕ него. */
.tosec{position:sticky;top:calc(52px + var(--sp-2));z-index:2;align-items:center;flex-wrap:wrap;column-gap:var(--sp-3);row-gap:var(--sp-2);padding:6px var(--sp-2);margin-left:calc(-1 * var(--sp-2));margin-right:calc(-1 * var(--sp-2));border-radius:var(--r-s)}
/* top = --sp-2 (отступ прилипшей плашки от кромки) + 52px (её высота): заголовок раздела встаёт
   ровно под нижний край плашки .topbar.material — см. правило плашки в начале файла. */
.tosec .t-sub{font-weight:600;flex:1;min-width:0}
/* Раздел ТО свёрнут по умолчанию (владелец, 2026-09-02): заголовок — кнопка с шевроном, как у
   подгрупп; тап раскрывает подгруппы сразу с узлами. Бар среднего % выработки ресурса по узлам
   раздела (sectionBar в vehicle.js — длина от a.pct, цвет от худшего узла; заменил долю-по-
   статусам, которая почти всегда была сплошь зелёной, см. комментарий в vehicle.js) — второй
   строкой во всю ширину заголовка: на 375px рядом с длинной подписью и пилюлей 96px бара уже не
   помещаются, а во всю ширину он читается как полоса статуса раздела и не ломает липкость. */
.tosec .chev{width:16px;height:16px;color:var(--label3);transform:rotate(90deg);transition:transform .2s ease;flex:none}
.tosec.closed .chev{transform:none}
.tosec .bar{flex-basis:100%;width:100%}
.tosec .bar>i{min-width:6px}
.units .tr.gh{grid-template-columns:1fr auto auto;gap:8px;background:var(--card2);font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--label2)}
.units.compact .tr.gh{min-height:34px;padding:4px 8px 4px 10px}
.units .tr.gh .pill{text-transform:none;letter-spacing:0}
.units .tr.gh .chev{width:16px;height:16px;color:var(--label3);transform:rotate(90deg);transition:transform .2s ease}
.units .tr.gh.closed .chev{transform:none}
@media (prefers-reduced-motion:reduce){.units .tr.gh .chev,.tosec .chev{transition:none}}
.stat{display:grid;grid-template-columns:1fr 1fr}
@media (min-width:700px){.stat{grid-template-columns:repeat(4,1fr)}}
.dhead .sp{display:none}
.dhead .t-title{margin-right:2px}
/* Консьерж на «Сегодня»: голос сотрудника, а не виджет чата */
.concierge{background:var(--card);border-radius:var(--r-m);padding:12px 14px;display:flex;flex-direction:column;gap:4px;margin-bottom:12px} /* gap плотнее — оформление 2026-09-21 */
.concierge .who{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--label2);font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.concierge .who i{width:8px;height:8px;border-radius:50%;background:var(--ok);display:inline-block}
.concierge p{font-size:15px;line-height:1.32;text-wrap:pretty} /* последнее слово не остаётся сиротой на новой строке */
/* Строки консьержа с действием (2026-09-24, today.js::concLine): первая — главное на сегодня,
   крупнее и жирнее; тезисы — строками списка с тонкой линией, как строки плашек на всём экране.
   Стрелка справа — только у строки, которая куда-то ведёт: нет действия — нет стрелки. */
.concierge .conc-head{display:flex;align-items:center;gap:8px;font-size:16px;font-weight:650;line-height:1.3;
  margin-top:4px;text-wrap:pretty}
.concierge .conc-pts{display:flex;flex-direction:column;margin:4px 0 6px}
.concierge .conc-pt{display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 0;font-size:14.5px;
  line-height:1.3;border-top:1px solid var(--sep)}
.concierge .conc-head .chev,.concierge .conc-pt .chev{margin-left:auto;width:16px;height:16px;flex:none;color:var(--label3)}
.concierge .press{cursor:pointer}
@media (any-pointer:coarse){.concierge .conc-pt{min-height:44px}}
.ask{display:flex;align-items:center;gap:8px;height:40px;background:var(--fill);border-radius:10px;padding:0 12px;color:var(--label2);font-size:15px}

/* Строка машины */
.plate{font:600 13px/1 ui-monospace,"SF Mono",Menlo,Consolas,monospace;letter-spacing:.06em;background:var(--fill2);border:1px solid var(--sep);border-radius:5px;padding:5px 6px;color:var(--label);flex:none}
.ring{width:30px;height:30px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:700;flex:none;
  background:conic-gradient(var(--c,var(--ok)) calc(var(--v)*1%),var(--fill) 0);position:relative}
.ring::after{content:attr(data-v);position:absolute;inset:3px;border-radius:50%;background:var(--card);display:grid;place-items:center;font-variant-numeric:tabular-nums}
/* Сегодня: одно действие в строке */
.todo .grow{display:flex;flex-direction:column;gap:2px;padding:0}
.todo .t-sub{font-weight:600}
/* Высота — как у эталонной .btn.primary («+ Данные», vehicle.js:344): 36px мышь / 44px тач
   (tokens.css:144,149), не отдельные 48px — владелец, 2026-09-02: «проверь, чтобы стандарт
   кнопок соблюдался по высоте». Ширина на широком экране ограничена той же половиной строки
   .actions, что и у эталона (~442px), а не растягивается на всю панель .card-col — иначе
   «Заменить деталь»/«Добавить документ» получаются вдвое шире «+ Данные» рядом. */
/* Ширина — по строке, а не 442px: на широком экране кнопка была заметно короче соседних блоков
   (владелец, скриншот 2026-09-21: «сделай кнопку по ширине строки, почему она короткая»).
   Ограничение осталось только там, где узкая колонка задана самим блоком — .gate-box (320px). */
.big{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;height:36px;border-radius:var(--r-m);background:var(--accent-fill);color:#fff;font:600 17px/1 var(--font);border:0;margin-top:var(--sp-3)} /* радиус через токен вместо хардкода 12px — оформление 2026-09-21, значение то же */
@media (pointer:coarse){.big{height:44px}}
/* Ещё: модули */
.mods{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--sp-2)}
.mod{background:var(--card);border-radius:var(--r-m);padding:12px;display:flex;flex-direction:column;gap:8px;min-height:84px} /* плотнее — оформление 2026-09-21 */
.mod .ic{width:34px;height:34px;border-radius:7px;display:grid;place-items:center;color:#fff;filter:saturate(.78)} /* радиус мельче, цвет приглушённее — оформление 2026-09-21 */
.mod .t-head{font-size:15px}
.mod.soon{opacity:.55}
.mod .t-foot{font-size:12px}
/* Карточка машины */
.vc-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:6px 0 14px}
.vc-head .t-large{font-size:24px} /* оформление 2026-09-21 */
.facts{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--sep);border-radius:var(--r-m);overflow:hidden}
.fact{background:var(--card);padding:8px 12px;display:flex;flex-direction:column;gap:2px} /* плотнее — оформление 2026-09-21 */
.fact b{font-size:17px;font-weight:600}
.fact span{font-size:12px;color:var(--label2)}
.unit{display:flex;flex-direction:column;gap:6px;padding:11px 0}
.unit .line{display:flex;justify-content:space-between;align-items:center;gap:8px}
.unit .line b{font-size:15px;font-weight:600}
.unit .t-foot{font-size:12px}
.feed .grow{padding:0;display:flex;flex-direction:column;gap:2px}
.feed time{font-size:12px;color:var(--label2);min-width:52px;font-variant-numeric:tabular-nums}
/* Лента по машине (feedSection в vehicle.js) — постоянный раздел на Обзоре, без кнопки
   показать/скрыть (владелец, 2026-09-02: список весь целиком, скроллится внутри себя, без
   пагинации и без «Все N»). max-height — примерно 5 строк ленты + начало шестой, чтобы сразу
   было видно, что список длиннее видимого; полоса — общий тонкий бегунок ::-webkit-scrollbar
   ниже по файлу. Ширина на десктопе с колонкой «Машины» ограничена в @media(min-width:1100px)
   ниже — тут задаём только то, что верно всегда. */
.feed-scroll{max-height:360px;overflow-y:auto;overflow-x:hidden}
/* Теги под событием ленты (feedTags в vehicle.js; владелец, 2026-09-02: «пометки в виде тегов,
   чтобы их несколько было»). Лежат третьей строкой внутри .grow, а не справа: справа три-четыре
   пилюли отжали бы заголовок к дате. Мельче обычной пилюли (18px против 22), inline-block вместо
   inline-flex — чтобы длинный источник («из таблицы клиента · 19.05.2026») резался многоточием,
   а не ломал ширину строки. */
.feed .tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:2px}
.feed .tags .pill{display:inline-block;height:18px;line-height:18px;padding:0 6px;font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* Строка ленты (feedItemRow в vehicle.js) — вертикальная раскладка вместо строки-грида с колонкой
   даты: на 294px третьей колонки (.screen#vehicles .card-col .feed-scroll ниже) горизонтальная
   раскладка «дата|текст|пилюля» оставляла тексту меньше 200px, и он переносился по одному слову.
   .frow не переиспользует .row — тому нужен align-items:center на фиксированную строку 44px,
   здесь высота строки плавающая (мета-строка + текст в несколько строк + теги), а полоска слева
   должна тянуться на всю эту высоту, а не на зафиксированные 36px, как у .row .st. */
.frow{display:flex;align-items:stretch;gap:var(--sp-3);padding:var(--sp-2) var(--sp-4);position:relative}
.frow+.frow::before{content:"";position:absolute;left:var(--sp-4);right:0;top:0;height:1px;background:var(--sep)}
.frow>.st{width:4px;border-radius:4px;background:var(--fill);flex:none}
.frow>.st.bad{background:var(--bad)}.frow>.st.warn{background:var(--warn)}
.frow>.st.ok{background:var(--ok)}.frow>.st.accent{background:var(--accent)}
.frow>.grow{flex:1;min-width:0;padding:2px 0;display:flex;flex-direction:column;gap:2px}
/* Мета-строка сверху: дата + кто/откуда («2 сен · Консьерж», «6 окт 2025 · из таблицы клиента»).
   11px — самая мелкая кегль шкалы файла, эта строка не смысловая, а служебная подпись. */
.frow .fmeta{font-size:11px;line-height:1.3;color:var(--label2);font-variant-numeric:tabular-nums}
.frow .t-sub{white-space:normal;overflow-wrap:anywhere}
.frow .tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:2px}
.frow .tags .pill{display:inline-block;height:18px;line-height:18px;padding:0 6px;font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}
/* Планшет: рейка */
@media (min-width:700px){
  /* Ландшафт айфона попадает СЮДА (852×393, 932×430) — и именно здесь живёт жалоба «островок
     перекрывает меню слева». Колонку рейки расширяем ровно на боковой вырез, а содержимое рейки
     сдвигаем тем же padding-left: полезные 76 px остаются целыми, но начинаются после острова.
     Прибавка, а не max(): 76 px здесь — ширина иконок с подписью, ужимать её выреза нельзя.
     На всём, где выреза нет (десктоп, планшет, портрет), var(--safe-l)=0 и раскладка прежняя. */
  #app{grid-template-columns:calc(76px + var(--safe-l)) 1fr;--pad-l:var(--sp-4)}
  .side{display:flex;flex-direction:column;align-items:center;gap:4px;padding:16px 8px;padding-left:calc(8px + var(--safe-l));border-right:1px solid var(--sep);position:sticky;top:0;height:100svh}
  .side .brand{width:36px;height:36px;border-radius:10px;background:var(--label);color:var(--bg);display:grid;place-items:center;font:800 15px/1 var(--font);margin-bottom:12px}
  .side .tab{min-width:56px;font-size:10px}
  .side .lbl{display:none}
  .tabbar{display:none}
  .screen{padding-bottom:var(--sp-8)}
}
/* Десктоп: панель с подписями, карточка машины справа от списка */
/* ВНИМАНИЕ, читающему следом: блоков @media(min-width:1100px) здесь ДВА. Действующая раскладка
   десктопа — второй (рейка 64px, «как в B»): он идёт ниже, специфичность та же, поэтому по каскаду
   выигрывает он и переопределяет из первого блока #app, .side, .side .brand, .side .tab.on, .side .lbl.
   Из первого блока реально работает только то, чего нет во втором: .side .tab{font-weight}, размер
   svg в табе, цвет svg активного таба и все правила .screen/.screen#vehicles/.card-col.
   Дубль .side{align-items:stretch;padding:20px 12px} отсюда убран — обе его строки затирались
   вторым блоком до последнего пикселя, он ничего не задавал и только вводил в заблуждение
   (значения теперь одни, в блоке ниже). */
@media (min-width:1100px){
  #app{grid-template-columns:232px 1fr}
  .side .brand{margin:0 8px 18px}
  .side .tab{flex-direction:row;gap:10px;font-size:15px;font-weight:500;padding:8px 10px;min-width:0;justify-content:flex-start}
  .side .tab svg.i{width:20px;height:20px}
  .side .tab.on{background:var(--fill2);color:var(--label)}
  .side .tab.on svg{color:var(--accent)}
  .side .lbl{display:block}
  .screen{max-width:none;padding:0 max(var(--sp-8),var(--safe-r)) var(--sp-8) var(--sp-8)}
  .screen>.inner{max-width:760px}
  .screen#vehicles.on{display:grid;grid-template-columns:400px 1fr;gap:var(--sp-8)}
  .screen#vehicles .inner{max-width:none}
  .screen#vehicles .card-col{display:block;padding-top:52px}
  .screen#vehicle.on{display:none}
}
.card-col{display:none}
/* Десктоп как в B: рейка иконок 64px, список-панель, карточка справа */
@media (min-width:1100px){
  /* Та же защита от бокового выреза, что и на 700px: широких устройств с вырезом сбоку сейчас нет,
     но раскладка обязана быть верной сама по себе, а не по совпадению — иначе следующий планшет
     с вырезом снова спрячет рейку. При отсутствии выреза значения ровно прежние. */
  #app{grid-template-columns:calc(64px + var(--safe-l)) 1fr}
  .side{align-items:center;padding:14px 0;padding-left:var(--safe-l)}
  .side .brand{margin:0 0 10px}
  .side .tab{flex-direction:column;gap:0;min-width:44px;width:44px;height:44px;padding:0;justify-content:center;border-radius:10px;font-size:0}
  .side .tab.on{background:var(--accent-soft);color:var(--accent)}
  .side .lbl{display:none}
  /* --vlist-w — фактическая ширина колонки «Машины» слева (420px здесь и есть та, что реально
     побеждает в каскаде — см. предупреждение про два блока @media(min-width:1100px) выше по
     файлу). Переменная существует ТОЛЬКО в этом брейкпоинте: у .feed-scroll ниже её значение по
     умолчанию — 100% (var(--vlist-w,100%)), то есть на телефоне/планшете, где этот блок не
     действует, ограничение ширины ленты само отключается, а не требует отдельного сброса. */
  .screen#vehicles.on{grid-template-columns:420px 1fr;gap:0;padding:0;--vlist-w:420px}
  .screen#vehicles>.inner{background:var(--card);border-right:1px solid var(--sep);height:100svh;overflow:auto;padding:0 12px 24px}
  .screen#vehicles .group{background:transparent;border-radius:0}
  .screen#vehicles .vrow .plate{background:var(--card2)}
  .screen#vehicles .card-col{padding:0 32px 32px;height:100svh;overflow:auto}
  /* Раньше шапка панели тянулась на её края отрицательными полями (margin:0 -12px); с плашкой-
     карточкой (см. .topbar.material в начале файла) это лишнее — плашка стоит в полях панели. */
  /* Владелец, 2026-09-02: лента НЕ должна быть такой же широкой, как колонка «Машины» —
     примерно 70% от неё, а не во всю ширину карточки (карточка сама куда шире списка). */
  .screen#vehicles .card-col .feed-scroll{max-width:calc(var(--vlist-w,100%) * .7)}
}
/* Лента — третья колонка экрана «Машины» на широком десктопе (владелец, 2026-09-02: «не блок в
   потоке, а колонка справа, тем же приёмом, каким слева стоит список машин»). Элемент .feed-col
   создаёт vehicle.js (feedColumn) рядом с .inner и .card-col — тем же соседом в той же сетке, что
   и список; без открытой карточки его нет, и auto-трек схлопывается в ноль. Порог 1400, а не 1100:
   при 1100 на карточку осталось бы 322px (64 рейка + 420 список + 294 лента) — уже телефона; между
   1100 и 1399 лента остаётся в потоке Обзора с ограничением ширины строкой выше. Ширина самой
   ленты — 70% колонки списка (как и просили), 48px сверх того — поля колонки. Колонка прокручивается
   сама, как .inner и .card-col (см. core.js initBgFx про внутренние панели), поэтому .feed-scroll
   внутри неё не ограничен по высоте. Ниже порога элемент, оставшийся от широкого окна, спрятан. */
.feed-col{display:none}
@media (min-width:1400px){
  .screen#vehicles.on{grid-template-columns:420px 1fr auto}
  .feed-col{display:block;width:calc(var(--vlist-w,420px) * .7 + 48px);box-sizing:border-box;padding:0 0 32px;height:100svh;overflow:auto;border-left:1px solid var(--sep)}
  .feed-col>.sec{margin:18px var(--sp-4) var(--sp-2)}
  .feed-col .feed-scroll{max-height:none;overflow:visible}
}
.vrow .plate{font-size:11px;padding:5px 6px;text-align:center;white-space:nowrap;letter-spacing:.04em}

/* ---------- Фоновый слой с параллаксом ----------
   Владелец, 2026-09-02: «оживи картинку — фон и минимальная динамика, чтобы листая было видно, что
   листается». Не пейзаж и не декор (за это прежний мини-апп назван «детским»): два полотна с одним
   мягким пятном каждое — цвет акцента и нейтральное затемнение (--fx1/--fx2 в tokens.css).
   Полотно — вертикально повторяющаяся плитка высотой --fxh (ставит core.js initBgFx по высоте
   окна), поэтому сдвиг по модулю плитки бесшовен и эффект не кончается на длинных списках.
   Высота полотна 100% + плитка: при сдвиге вверх на величину до одной плитки окно остаётся
   закрытым. z-index:-1 — под содержимым, но над фоном html (см. комментарий у html в tokens.css).
   Движение задаёт transform из core.js — композитор сдвигает слой без перерисовки, background-
   attachment:fixed в WebView Telegram на iOS так не умеет. */
/* СВЕТ ФОНА — НЕПОДВИЖНЫЙ, ПАРАЛЛАКС — ОДНИМ ПОЛОТНОМ (владелец, 2026-09-25: «всё долго грузится
   и сильно лагает»). Замер на телефонной модели (390 px, плотность 3): два «световых эффекта»
   2026-09-24 — дышащий верхний свет (18 с) и дрейфующее сияние (70 с) — были БЕСКОНЕЧНЫМИ
   анимациями, а бесконечная анимация, даже медленная, заставляет браузер перерисовывать весь экран
   60 раз в секунду, пока приложение просто открыто: «Сегодня» в покое — 179 кадров и 1150 мс работы
   отрисовки за 3 с, без них — 29 кадров и 21 мс. На телефоне это нагрев, батарея и рывки при
   прокрутке. Движение там было настолько медленным, что его не видели — видно было только свет,
   поэтому свет остался ровно там же, где стоял, а движение ушло.
   Параллакс при прокрутке (владелец, 2026-09-02: «чтобы листая было видно, что листается») оставлен,
   но одним полотном вместо двух: оба пятна нарисованы на одной плитке. Каждое полотно — лишний слой
   размером больше экрана, который браузер сводит в каждом кадре прокрутки; второй удваивал эту цену.
   Замер прокрутки 2 с + 1 с покоя: было 685 мс отрисовки, стало ~170–200 мс.
   Свет сверху (--fx3) и холодное сияние снизу (--fx4) нарисованы на самом корне .bgfx — он
   неподвижен и растрируется один раз. */
.bgfx{position:fixed;inset:0;z-index:-1;overflow:hidden;pointer-events:none;
  background:radial-gradient(62% 41% at 29% -4%,var(--fx3),transparent 72%),
    radial-gradient(47% 35% at 54% 66%,var(--fx4),transparent)}
.bgfx>i{position:absolute;left:0;right:0;top:0;height:calc(100% + var(--fxh,900px));display:block;
  background-repeat:repeat-y;background-size:100% var(--fxh,900px);will-change:transform;
  background-image:radial-gradient(70% 42% at 14% 34%,var(--fx1),transparent 70%),
    radial-gradient(56% 28% at 86% 53%,var(--fx2),transparent 70%)}
/* Для тех, кто отключил анимации: слой остаётся, но стоит на месте — !important страхует от
   transform, который core.js мог выставить до смены настройки. */
@media (prefers-reduced-motion:reduce){.bgfx>i{transform:none!important;will-change:auto}}

/* ---------- Полоса прокрутки ----------
   Владелец, 2026-09-02: нативная полоса в «Журнале» — «уродливая, чёрная» (Chromium на Windows
   под color-scheme:dark рисует тёмный трек со стрелками). Оставляем только бегунок: тонкий,
   скруглённый, тёмно-серый с прозрачностью в обеих темах (--thumb/--thumb2 в tokens.css); трек,
   угол и стрелки прозрачные. Ширина 10 px, из них видимые 4: прозрачная рамка 3 px с
   background-clip:content-box даёт бегунку воздух, не сужая зону захвата мышью.
   Только для мыши (hover + pointer:fine): на телефоне полоса накладная и исчезает сама, а
   ::-webkit-scrollbar на iOS, наоборот, делает её постоянно видимой. Chromium ≥121 при заданном
   scrollbar-color игнорирует ::-webkit-scrollbar-*, поэтому стандартные свойства — только там,
   где вебкитовских псевдоэлементов нет (Firefox): @supports not selector(). */
/* ПОЛОС ПРОКРУТКИ НЕ ВИДНО НИГДЕ (владелец, 2026-09-22: «полосы прокрутки в полях убери в ленте и
   везде где есть»). Прокрутка при этом работает: скрыт только бегунок. Прежнее правило рисовало
   тонкий бегунок на мыши — в ленте и списках узлов он резал край содержимого и читался как рамка.
   Край, за которым есть продолжение, показывает затухание (.fade-t/.fade-b, markVEdges), а не
   полоса — этот приём в проекте уже был и теперь остаётся единственным. */
*{scrollbar-width:none}
::-webkit-scrollbar{width:0;height:0;display:none}
@media (hover:hover) and (pointer:fine){
  ::-webkit-scrollbar{width:0;height:0;display:none}
  ::-webkit-scrollbar-track,::-webkit-scrollbar-corner{background:transparent}
  ::-webkit-scrollbar-button{width:0;height:0;display:none}
}
/* Намёк, что ниже есть ещё: на планшете и десктопе нет таббара с размытой подложкой, и карточки
   режутся кромкой окна. Тонкое затухание в цвет фона у нижней кромки — кромочный эффект, а не
   линия. В конце списка оно ложится на пустой нижний отступ экрана и ничего не прячет. На
   десктопном сплите «Машины» левая панель белая на всю высоту — затухание в серый дало бы на ней
   полосу, там его нет. */
@media (min-width:700px){
  #app::after{content:"";position:fixed;left:0;right:0;bottom:0;height:28px;pointer-events:none;z-index:4;
    background:linear-gradient(to top,var(--bg),transparent)}
}
@media (min-width:1100px){#app:has(.screen#vehicles.on)::after{display:none}}

/* ---------- Курсор на кликабельном ----------
   Владелец, 2026-09-02 («100 раз просил»): над табами-фильтрами .seg («Все/ТО/Ремонт…»,
   «7/14/30 дней») курсор был текстовый (I-beam) — сегмент строится в ui.seg из голых <span> без
   .press, а cursor:pointer и user-select:none в этой вёрстке даёт только .press. Сегмент чинится
   здесь явно, а ниже — общая страховка по роли и тегу, чтобы следующий кликабельный элемент без
   .press не повторил ту же болезнь: у <button> в Chromium курсор по умолчанию тоже стрелка. */
.seg,.seg>span,[role="button"],[role="tab"],button,a[href],.tab,.btn,.icon-btn{cursor:pointer;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}

/* ---------- Оболочка: загрузка, вход, тост ---------- */
#app.booting .screen,#app.booting .side,#app.booting .tabbar{visibility:hidden}
.gate[hidden]{display:none}
.gate{position:fixed;inset:0;display:grid;place-items:center;padding:var(--sp-6);background:var(--bg);z-index:20}
.gate-box{display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);text-align:center;max-width:320px}
.gate-box .brand{width:56px;height:56px;border-radius:14px;background:var(--label);color:var(--bg);display:grid;place-items:center;font:800 22px/1 var(--font)}
.gate-box .big{margin-top:var(--sp-2)}
.gate-box .big:disabled{opacity:.55}
.toast{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(88px + env(safe-area-inset-bottom));background:var(--label);color:var(--bg);font-size:13px;padding:8px 14px;border-radius:999px;z-index:30;box-shadow:var(--shadow);max-width:calc(100% - 32px)}
@media (min-width:700px){.toast{bottom:24px}}
/* Пустые и ошибочные состояния экранов: честно говорят, что данных нет, и остаются на месте */
.empty{padding:var(--sp-3) var(--sp-4);text-align:center;color:var(--label2);font-size:15px;line-height:1.4}
.empty b{display:block;color:var(--label);font-weight:600;margin-bottom:4px}
.skeleton{background:var(--fill2);border-radius:var(--r-m);height:44px;animation:sk 1.2s ease-in-out infinite}
@keyframes sk{50%{opacity:.5}}
@media (prefers-reduced-motion:reduce){.skeleton{animation:none}}
/* Настройки: значение справа, сегмент-переключатель в строке */
.row .seg{width:auto;min-width:180px}
.row .seg>span{padding:5px 10px}


/* ---------- Нижние листы (sheets.js): ввод в духе iOS ----------
   Подложка затемняет и закрывается по нажатию; лист выезжает снизу с «ручкой», на широком экране
   стоит по центру. Вход 140 мс, выход 95 мс (было 220/160): владелец просил «моментально». Уходящее по-прежнему
   короче приходящего, кривые прежние — сокращена только длительность. */
.scrim{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:40;animation:v2fade .14s ease-out}
/* background:var(--card), не var(--bg) — владелец, 2026-09-02: окно консьержа «слишком чёрное».
   Лист — приподнятая поверхность над контентом, а не сам фон страницы; на тёмной теме --bg это
   чистый #000 (Telegram-хром/OLED), --card — уже поднятые сегодня #3A3A3D, та же логика, что и
   у плашек заголовков. Разницы с фоном достаточно даже без отдельной тени сверху узла. */
.sheet{position:absolute;left:0;right:0;bottom:0;max-height:88svh;display:flex;flex-direction:column;background:var(--card);border-radius:14px 14px 0 0;
  padding-bottom:env(safe-area-inset-bottom);animation:v2up .14s cubic-bezier(.22,1,.36,1);box-shadow:0 -8px 32px rgba(0,0,0,.18)}
.sheet::before{content:"";width:36px;height:5px;border-radius:3px;background:var(--label3);margin:8px auto 0;flex:none}
.scrim.closing{animation:v2fade .095s ease-in reverse forwards}
/* Уход той же кривой, что и вход, и чуть короче (160 против 180 мс): ease-in запрещён — он
   тянет начало движения ровно тогда, когда глаз за ним следит (аудит движения 2026-09-22). */
.scrim.closing .sheet{animation:v2down .16s cubic-bezier(.32,.72,0,1) forwards}
.sheet-h{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 8px 8px;flex:none}
/* Заголовок листа — по центру ЛИСТА, а не по центру места, оставшегося от крестика:
   иначе «Что сделать» видимо смещено влево на половину ширины кнопки закрытия. */
/* Заголовок вынут из потока (центрируется по листу), поэтому в потоке остаётся одна кнопка
   закрытия — и space-between прижимал её к ЛЕВОМУ краю (скриншот владельца 2026-09-22: крестик
   слева от «Быстрый ввод»). Прижимаем содержимое строки вправо явно. */
.sheet-h{position:relative;justify-content:flex-end}
.sheet-h h2{font-size:17px;font-weight:600;text-align:center;flex:1;
  position:absolute;left:0;right:0;margin:0 auto;pointer-events:none}
.sheet-b{padding:4px var(--sp-4) var(--sp-4);overflow-y:auto;-webkit-overflow-scrolling:touch}
.sheet.tall{height:88svh}
@media (min-width:700px){
  .sheet{left:50%;right:auto;bottom:50%;width:480px;max-height:86svh;transform:translate(-50%,50%);border-radius:14px;padding-bottom:0}
  .sheet::before{display:none}
  .scrim.closing .sheet{animation:v2fade .16s cubic-bezier(.32,.72,0,1) reverse forwards}
}
@keyframes v2fade{from{opacity:0}to{opacity:1}}
@keyframes v2up{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes v2down{from{transform:translateY(0)}to{transform:translateY(100%)}}
@media (prefers-reduced-motion:reduce){.scrim,.sheet,.scrim.closing,.scrim.closing .sheet{animation:none}}
/* Поля формы: подпись сверху, поле 44px, всё на токенах */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--sp-3)}
.field label{font-size:13px;color:var(--label2);font-weight:500}
.field input,.field select,.field textarea{font-family:inherit;font-size:17px;line-height:1.3;color:var(--label);background:var(--card);
  border:1px solid var(--sep);border-radius:var(--r-m);min-height:44px;padding:10px 12px;width:100%;-webkit-appearance:none;appearance:none}
.field select{background-image:linear-gradient(45deg,transparent 50%,var(--label2) 50%),linear-gradient(135deg,var(--label2) 50%,transparent 50%);
  background-position:calc(100% - 18px) 50%,calc(100% - 13px) 50%;background-size:5px 5px;background-repeat:no-repeat;padding-right:36px}
.field textarea{resize:vertical;min-height:88px}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.field .t-foot{margin-top:-2px}
.sheet-b hr{border:0;border-top:1px solid var(--sep);margin:var(--sp-3) 0}
/* Чат консьержа внутри листа: своя реплика справа, консьерж — слева, без аватаров */
.chat-messages{display:flex;flex-direction:column;gap:8px;padding:4px 0 8px;flex:1;overflow-y:auto}
.chat-user,.chat-agent{max-width:84%;padding:9px 12px;border-radius:14px;font-size:15px;line-height:1.35;white-space:pre-wrap;word-break:break-word}
.chat-user{align-self:flex-end;background:var(--accent-fill);color:#fff;border-bottom-right-radius:4px}
/* --card2, не --card: .sheet сам теперь --card (см. правило .sheet выше) — пузырь и поле ввода
   обязаны быть на ступень светлее контейнера, который их держит, иначе сливаются с ним же. */
.chat-agent{align-self:flex-start;background:var(--card2);border-bottom-left-radius:4px}
.chat-messages time{font-size:11px;color:var(--label3);align-self:center}
.chat-typing{align-self:flex-start;color:var(--label2);font-size:13px;padding:6px 12px}
.chat-input{display:flex;gap:8px;align-items:flex-end;padding-top:8px;border-top:1px solid var(--sep);flex:none}
.chat-input textarea{flex:1;min-height:44px;max-height:120px;font:inherit;font-size:17px;padding:10px 12px;border:1px solid var(--sep);border-radius:22px;background:var(--card2);color:var(--label);resize:none}
/* Быстрые ответы консьержа («Пофамильно», «Просрочки ТО»...) — владелец, 2026-09-02: «слипаются
   друг с другом без отступа». У .chip не было ни одного правила вообще — только .press (нажатие)
   без формы и зазора. gap на контейнере + форма пилюли по образцу .pill (tokens.css), но кликабельная. */
.chips{display:flex;flex-wrap:wrap;gap:var(--sp-2);padding:2px 0 var(--sp-2)}
.chip{display:inline-flex;align-items:center;height:32px;padding:0 var(--sp-3);border-radius:999px;
  background:var(--fill);color:var(--label);font-size:13px;font-weight:500;white-space:nowrap;border:0}
/* Кнопки агента разного рода различимы без чтения: «Отменить» — контур с акцентным текстом (срочно,
   окно пять минут, но не главное действие, поэтому не заливка), «Открыть карточку» — акцентный
   текст, это переход, а не реплика. Цвет текста — --accent, фон остаётся от .chip. */
.chip-undo{background:transparent;box-shadow:inset 0 0 0 1px var(--sep);color:var(--accent)}
.chip-open{color:var(--accent)}
/* Пузырь вложения (скрепка в чате) — тот же .chat-user, просто иконка+имя файла в ряд вместо текста */
.chat-attach{display:flex;align-items:center;gap:8px}
.chat-attach .ellip{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Сегмент с прокруткой — для 5–6 вкладок карточки на 390px */
.seg.scroll{overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.seg.scroll::-webkit-scrollbar{display:none}
.seg.scroll>span{flex:none;padding-left:12px;padding-right:12px}
/* Затенение края, за которым у полосы есть продолжение (классы ставит core.js::markEdges по
   реальной прокрутке). 28 px — ровно столько, чтобы подпись у края читалась как «уходит дальше»,
   но оставалась узнаваемой. */
.seg.scroll.fade-r{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent)}
.seg.scroll.fade-l{-webkit-mask-image:linear-gradient(to left,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to left,#000 calc(100% - 28px),transparent)}
.seg.scroll.fade-l.fade-r{-webkit-mask-image:linear-gradient(to right,transparent,#000 28px,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to right,transparent,#000 28px,#000 calc(100% - 28px),transparent)}
/* Тот же приём для вертикальных списков, которые скроллятся внутри себя (.scroll-units — узлы в
   карточке машины, .feed-scroll — её лента): нижняя, а иногда верхняя строка резалась ровно
   посередине текста — выглядело как сломанная вёрстка, а не как «дальше есть ещё» (владелец,
   2026-09-21). Классы fade-t/fade-b ставит core.js::markVEdges тем же способом, что markEdges
   выше — по фактическому scrollTop, второй механизм не заводим. Маска, а не отдельный слой поверх
   строк, — клики по затенённым строкам проходят как обычно, pointer-events не нужен. */
.scroll-units.fade-t,.feed-scroll.fade-t{-webkit-mask-image:linear-gradient(to bottom,transparent,#000 28px);mask-image:linear-gradient(to bottom,transparent,#000 28px)}
.scroll-units.fade-b,.feed-scroll.fade-b{-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to bottom,#000 calc(100% - 28px),transparent)}
.scroll-units.fade-t.fade-b,.feed-scroll.fade-t.fade-b{-webkit-mask-image:linear-gradient(to bottom,transparent,#000 28px,#000 calc(100% - 28px),transparent);mask-image:linear-gradient(to bottom,transparent,#000 28px,#000 calc(100% - 28px),transparent)}
/* Раскрытая история узла под строкой таблицы */
.tr.expand{grid-template-columns:1fr;background:var(--card2);padding:8px 12px 10px}
.tr.expand .feed-line{display:flex;gap:10px;font-size:13px;padding:4px 0}
.tr.expand .feed-line time{color:var(--label2);min-width:48px;font-variant-numeric:tabular-nums}
.anomaly{display:flex;gap:8px;align-items:flex-start;font-size:13px;color:var(--warn-text);padding:6px 0}

/* ---------- Оценка машины: звёзды — главная цифра (владелец, 2026-09-01), сотенный балл под капотом.
   Пять контуров, заливка по значению с шагом 0,5 через ширину слоя. Тон — от сервера (list_tone). */
.stars{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;font-variant-numeric:tabular-nums}
.stars .row5{position:relative;display:inline-block;line-height:1;font-size:14px;letter-spacing:1px;color:var(--fill)}
.stars .row5>i{position:absolute;left:0;top:0;overflow:hidden;white-space:nowrap;font-style:normal;color:var(--c,var(--ok))}
.stars b{font-size:13px;font-weight:600;color:var(--label)}
.stars.lg .row5{font-size:26px;letter-spacing:2px}
.stars.lg b{font-size:28px;font-weight:700;letter-spacing:-.02em}
.rating{background:var(--card);border-radius:var(--r-m);padding:14px;display:flex;flex-direction:column;gap:10px}
.rating .head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.rating .head .lbl{font-size:15px;font-weight:600}
.rating .gain,.group>.gain{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:14px;
  padding:10px var(--sp-4);min-height:var(--row)}
.rating .gain{padding:6px 0;min-height:0;border-top:1px solid var(--sep)}
.group>.gain+.gain{border-top:1px solid var(--sep)}
.rating .gain b,.group>.gain b{color:var(--ok-text);font-variant-numeric:tabular-nums;white-space:nowrap}
.vrow .km .stars{justify-content:flex-end}
.vrow .km .stars .row5{font-size:11px;letter-spacing:.5px}
.vrow .km .stars b{font-size:12px}
.score{display:inline-flex;align-items:baseline;gap:2px;font-variant-numeric:tabular-nums;font-weight:700}
.score b{font-size:15px;color:var(--c,var(--label))}
.score.lg b{font-size:34px;letter-spacing:-.02em;line-height:1;text-shadow:var(--text-shadow)}
.score small{font-size:11px;color:var(--label2)}
/* Ведущая колонка оценки в строке журнала (journal.js rowLead): фиксированная ширина под три
   цифры, число прижато вправо — «87» и «100» в соседних строках стоят по одной оси, и даты за
   ними не гуляют. */
.row>.lead{flex:none;min-width:30px;display:inline-flex;justify-content:flex-end}
/* Журнал парка (ui.feedRow с stackLead): оценка и дата друг под другом в одной колонке 44px вместо
   двух рядом (30 + ~43px): текст получает ~40px ширины (владелец, 2026-09-02). Дата 11px переносится
   на вторую строку только годом («6 окт» / «2025»), когда год не текущий — V2.fmt.date его и так
   не пишет для текущего. Строка компактнее прежних 57px (padding у .grow 6 вместо 9), но не ниже
   48px — порог пальца. Заголовок переносится, а не режется, как и в прежней раскладке (правило у
   `.row > time ~ .grow` ниже по файлу). Разделитель между строками начинается там же, где текст. */
.row.stack>.lead-col{flex:none;width:44px;display:flex;flex-direction:column-reverse;align-items:flex-end;gap:1px;line-height:1.2}
/* В журнале дата первична (когда это было), оценка — вторична, поэтому дата крупнее и весомее.
   Владелец, 2026-09-11: оценка обязана быть цветной по состоянию машины (как везде в приложении) —
   раньше здесь цвет гасился до --label2 плоским серым, это отменено. Приглушаем только размер и
   вес (12px, без свечения тени), а цвет — родной из ui.score (var(--c), см. scoreColorVar). */
.row.stack>.lead-col>.lead{min-width:0;font-size:12px;font-weight:600}
.row.stack>.lead-col>.lead .score b{text-shadow:none}
.row.stack>.lead-col>time{font-size:13px;font-weight:600;color:var(--label);font-variant-numeric:tabular-nums;text-align:right;white-space:normal;line-height:1.2}
/* :not(.unit) — только ради специфичности (0,4,0), чтобы перебить .row .grow:not(.t-sub):not(.unit) с padding 9. */
.row.stack>.grow:not(.unit){padding:6px 0;min-height:48px;justify-content:center}
.row.stack+.row.stack::before{left:calc(var(--sp-4) + 44px + var(--sp-3))}
/* «Требует действия» на Сегодня (today.js actionRow): headline — «как срочно» цветом тона,
   works — что делать, одной строкой с многоточием; обе внутри подписи .t-foot строки. */
.row .t-foot>.act-head{display:block;font-size:13px;font-weight:600;color:var(--label)}
.row .t-foot>.act-head.bad{color:var(--bad-text)}.row .t-foot>.act-head.warn{color:var(--warn-text)}
.row .t-foot>.act-works{display:block;font-size:13px;color:var(--label2)}
.vrow .km .score{justify-content:flex-end}
.vrow .km .score b{color:var(--c,var(--label))}

/* ---------- Карточка машины: мелкие правила, запрошенные экраном vehicle.js ---------- */
.stat b.warn{color:var(--warn-text)}                       /* пробег «со слов» — жёлтым (Блок 3) */
.tr .n.bad{color:var(--bad-text);font-weight:600}          /* отрицательный остаток */
.units .tr.hist{grid-template-columns:1fr;background:var(--card2);padding:0 12px 8px}
.tr.hist .feed .row{padding-left:0}
.tbl.pass .tr{grid-template-columns:110px 1fr}
.mono{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;letter-spacing:.04em}
.pills{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
/* «Что всплывёт» (horizonsRow в vehicle.js): подпись и пилюли — соседи, прижаты влево (владелец,
   2026-09-02: «смести ближе друг к другу, зачем ты растянул»). Точечный класс, а не правка .row:
   растяжение по краям — нужное поведение остальных строк («Требует действия … Все N»). */
.row.tight{justify-content:flex-start}
.row.tight>.t-sub{flex:none}
.row.tight .pills{justify-content:flex-start;flex:0 1 auto}
.pill.press{cursor:pointer}
.pill.press.on{background:var(--accent-fill);color:#fff}
.rating .gain.press{cursor:pointer}
.rating .t-cap{margin-top:4px}

/* ---------- Вертикальный ритм экрана: ОДНО правило вместо margin'ов по каждому js/screens/* ----------
   Экраны кладут узлы в .inner плоским списком — ui.mount(container,[topbar, search, seg, list]).
   Собственный margin-top до сих пор был только у .sec, поэтому всё остальное (поиск, сегмент,
   сноска, группа, кнопка) слипалось в 0 px: на «Машинах» поиск лежал вплотную на фильтрах, на
   «Задачах» и «Аренде» сегмент — на списке. Проставлять отступ руками в каждом экране бесполезно:
   следующий новый экран опять про него забудет — поэтому ритм задаётся здесь, у самого потока.
   Поток — это .inner и безымянные div-обёртки внутри неё (results/list/expHead/kpiBox/head/
   actionsBox), в которые экраны перерисовывают содержимое, плюс .vtab (короб вкладок карточки).
   Внутрь .group/.tbl/.kpis/.facts/.seg/.actions правило не лезет: у них свои разделители
   и gap, лишний margin разорвал бы строку списка пополам.
   Шкала: между смысловыми группами 24 (даёт .sec сверху), внутри группы 12 (это правило),
   от заголовка к его содержимому 8 (margin-bottom у .sec) — расстояние внутри группы меньше,
   чем расстояние между группами. */
.inner > * + *:not(.sec),
.inner > div:not([class]) > * + *:not(.sec),
.inner > .today-card > * + *:not(.sec),
.inner > .vtab > * + *:not(.sec),
.inner > .vtab > div:not([class]) > * + *:not(.sec){margin-top:var(--sp-3)}
/* Заголовок секции сам держит 8 px до своего содержимого — универсальные 12 тут лишние. */
.inner > .sec + *,
.inner > div:not([class]) > .sec + *,
.inner > .today-card > .sec + *,
.inner > .vtab > .sec + *,
.inner > .vtab > div:not([class]) > .sec + *{margin-top:0}
/* Плашка .topbar.material сама держит отступ сверху (--sp-3, см. начало файла); универсальные 12
   с ней совпадают, отдельного сброса не нужно. */

/* Журнал: заголовок записи переносится, а не обрезается многоточием.
   Строка ленты — единственная, где .t-sub несёт СМЫСЛ («Замена передних тормозных колодок»),
   а не подпись: обрезать её многоточием значит спрятать то, ради чего строку открыли. Ширины на
   375 px не хватает (дата 52 + пилюля справа), поэтому разрешаем вторую строку. Селектор через
   `time` ловит ровно ui.feedRow — обычные ui.row в списках машин многоточие сохраняют. */
.row > time ~ .grow > .t-sub.ellip,
.row.stack > .grow > .t-sub.ellip{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}

/* Шапка листа: крестик стоял в 8 px от края, тело листа — в 16, кнопка «прилипала» к краю.
   12 px — шаг шкалы между ними: кнопка отходит от края, но остаётся ближе к нему, чем текст. */
.sheet-h{padding:6px var(--sp-3) var(--sp-2)}

/* ---------- «Сегодня» на широком экране: лишнее уходит ВПРАВО, а не под скролл ----------
   Владелец: на десктопе экран не должен листаться вниз. Один узкий столбец 760 px оставлял
   половину блоков за нижней кромкой, поэтому на ≥1100 px .inner превращается в CSS-колонки:
   высота ограничена окном, а то, что не поместилось, перетекает в следующую колонку справа.
   break-inside:avoid обязателен — без него колонка разорвёт карточку пополам (заголовок вверху
   одной колонки, его список — в начале следующей). Шапка идёт column-span:all: она общая для
   всех колонок, и sticky ей уже не нужен — вертикального скролла тут нет.
   Высота колонок НЕ ограничена (2026-09-02, второй заход): прежняя схема «max-height по окну +
   column-fill:auto» при любом окне, где шесть карточек не умещались в N видимых колонок, уносила
   лишние в невидимые колонки справа — замерено при 1100–1440 и высоте 800–900: обрезаны 3–4
   карточки из 6, inner.scrollWidth 1949 при ширине 962. Overflow-колонки возникают ТОЛЬКО при
   ограниченной высоте, поэтому без max-height и с column-fill:balance колонок ровно column-count,
   карточки целые (break-inside:avoid), а если в маленьком окне не влезло по высоте — страница
   листается на недостающие десятки пикселей, но ничего не пропадает. На 1440×900 и 1920×1080
   всё умещается без прокрутки, как и просил владелец. */
@media (min-width:1100px){
  .screen#today{padding-top:var(--sp-4)}
  .screen#today > .inner{max-width:none;width:100%;display:flex;flex-wrap:wrap;gap:var(--sp-6);align-items:flex-start}
  /* Зоны собирает today.js::layoutZones: слева работа, справа контекст. Ширины фиксированы
     долями, а не подгоняются под высоту содержимого.
     Пропорция 2:1 (было 58%/34%, т.е. 1.7:1) — правка по замеру 2026-09-22 (playwright,
     cars.holdempro.online, 1280/1920, обе темы): первый блок каждой колонки (.tc-kpi слева,
     .concierge справа) стартует от общей шапки на одном top, то есть визуально это пара окон
     в одном ряду — а «дробная» 1.7:1 нарушала правило «равные или кратные 1:2». Базис 0 вместо
     процентов — чтобы grow-фактор (2 и 1) держал ровно 2:1 при любой ширине окна, а не только
     около 1280/1920, где случайно измеряли; .taside min-width:300px остаётся нижним пределом. */
  .screen#today > .inner > .tmain{flex:2 1 0;min-width:0;display:flex;flex-direction:column;gap:var(--sp-6)}
  .screen#today > .inner > .taside{flex:1 1 0;min-width:300px;display:flex;flex-direction:column;gap:var(--sp-6)}
  .screen#today > .inner > .today-card{flex:1 1 320px}
  /* Шапка на широком экране — больше не прилипающая полупрозрачная подложка, а отдельная плашка
     заголовка: свой отступ сверху (.screen#today), скруглённая карточка со своей тенью, без
     backdrop-filter (тут нечего размывать — под ней уже не едет контент, sticky не нужен). Именно
     эта смена background/backdrop-filter на плотную заодно убирает и подозреваемый источник чёрной
     полосы над «Сегодня» — конфликт фикс-слоя .bgfx (z-index:-1) с backdrop-filter шапки, известный
     артефакт Chromium: у плашки задач теперь просто нет backdrop-filter, конфликтовать нечему.
     max-height получает добавочный -var(--sp-8): 16px нового padding-top экрана + 16px нового
     margin-bottom шапки (раньше был margin-top:0 без нижнего отступа) — то же самое пространство,
     что раньше не учитывалось, и без поправки карточка «Требует действия» снова обрежется снизу
     (этот баг уже был сегодня, второй раз наступать нельзя). */
  /* Шапка занимает всю ширину сетки (была column-span:all у многоколоночного потока). */
  .screen#today > .inner > .topbar{flex:1 1 100%;position:static;margin:0;box-shadow:var(--shadow);
    justify-content:flex-start;gap:var(--sp-3);background:var(--card);border-radius:var(--r-l)}
  /* Заглушка просвета (см. .topbar.material::before в начале файла) здесь не нужна и вредна:
     шапка тут НЕ липкая (position:static) и лежит в многоколоночном потоке — прилипшего просвета
     не бывает, закрашивать нечего, а лишняя полоса фона над плашкой в колонках только мешает. */
  .screen#today > .inner > .topbar.material::before,
  .screen#today > .inner > .topbar.material::after{content:none}
  .screen#today > .inner > .today-card{margin:0}
  /* Список «Требует действия» — самая высокая карточка (49 строк по «Все N»): ограничен высотой
     окна и скроллится внутри себя (тот же приём, что .scroll-units в карточке машины; заголовок и
     кнопка «Все N» остаются на месте), иначе одна карточка растянула бы колонки на несколько
     экранов. Вычитаемое: поля экрана (16+32), плашка с отступом (52+16), заголовок секции с полями
     (24+17+8), кнопка «Все N» с отступом (12+44), margin карточки (16) — итого ~240, округлено
     до 88px + --sp-8 + 52px + --sp-4 + 140px по тем же слагаемым, что у .inner раньше. */
  .screen#today > .inner .act-list{max-height:calc(100svh - 88px - var(--sp-8) - 52px - var(--sp-4) - 140px);overflow-y:auto}
}
/* Три колонки — только с 1260px. Замерено: .inner = окно − 128px, трём колонкам по 360 + два зазора
   по 24 нужно 1128px, то есть окно ≥ 1256; между 1100 и 1259 при трёх колонках две карточки из
   шести физически обрезались overflow-x:hidden. Ниже порога — две колонки шире 360. */
/* Колонки задаёт сама сетка (auto-fit + minmax): сколько карточек шириной ≥340px влезло, столько и
   колонок. Отдельный порог на «три колонки» больше не нужен — и ровно он раньше решал, куда
   попадёт карточка, из-за чего на широком экране колонки начинались с разных блоков и выглядели
   кривыми (владелец, 2026-09-21: «что за кривая вёрстка объектов»). В сетке каждая карточка —
   отдельная ячейка: блок не может разорваться между колонками, а верхние кромки стоят на одной
   линии (align-items:start). */

/* Название машины в списке переносится на вторую строку, а не режется многоточием.
   «VOLKSWAGEN Passat · 2018» не влезал в 174 px колонки (нужно 189) и обрывался на «Passat · …» —
   то есть год, по которому машину и различают, пропадал. Две строки помещаются в те же 52 px
   высоты строки (15 px × 1.2 × 2 + подпись), поэтому список не разъезжается; клип на двух строках
   оставлен предохранителем на случай неадекватно длинного названия из справочника. */
/* Правка 2026-09-22: год уехал в подпись строки (vehicles.js), и название снова умещается в
   одну строку. Двухстрочный режим убран — из-за него строка с длинным названием росла до
   66 px при 52 у остальных, и список выглядел рваным. */
.vrow .name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---------- Выбор файла документа: камера / галерея, вместо голого <input type=file> ----------
   Две равные плитки-кнопки с иконкой и подписью — на телефоне это ровно то же самое, что
   системный лист «Сфотографировать / Выбрать из галереи» у нативных приложений. */
/* [hidden] обычным ".doc-preview[hidden]" уже чинили раньше сегодня в другом месте — тот же
   баг специфичности: .doc-pick{display:grid} и UA-правило [hidden]{display:none} равны по весу,
   класс побеждает первым в исходнике. Найдено сегодня агентом при работе над брифом. */
.doc-pick{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.doc-pick[hidden]{display:none}
.doc-pick-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  height:76px;border-radius:var(--r-m);background:var(--fill2);border:1px dashed var(--sep);color:var(--accent)}
.doc-pick-btn span{font-size:12px;font-weight:600;color:var(--label);text-align:center;line-height:1.2}
/* [hidden] и класс с display имеют одинаковую специфичность — класс из авторской таблицы
   стилей побеждает атрибут UA-таблицы, поэтому display нужно явно вернуть на none сам. */
.doc-preview[hidden]{display:none}
.doc-preview{display:flex;align-items:center;gap:8px;margin-top:8px;padding:10px 12px;background:var(--card);border-radius:var(--r-s)}
.doc-preview .icon-btn{margin-left:auto;width:28px;height:28px}

/* Статусы ТЕКСТОМ и белые надписи на синем — визуальный аудит 2026-09-13 (жалоба владельца «не
   вижу надписей на кнопках»). Правила выше переведены с --ok/--warn/--bad на --*-text (см. токены:
   заливки остаются прежними, темнеет только текст) и с --accent на --accent-fill там, где поверх
   синего белая надпись (.big, пузырь чата, нажатая пилюля). Проверка после правки — скрипт контраста
   по всем экранам в обеих темах, порог 3:1 для жирных и крупных, 4.5:1 для обычного текста. */

/* Индикатор консьержа (Блок 3, «Кнопка ИИ Консьержа: живой индикатор и сводка»; 2026-09-15).
   Точка — «есть непросмотренное срочное» (V2.ui.conciergeSignal в ui.js, гасится в V2.chat.open).
   Блик по кнопке — тот самый «живой» признак из Блока 3: пробегает редко (раз в ~4 с) и только
   пока точка горит, чтобы не превращать спокойный экран в мигающий. :has() — прогрессивное
   улучшение: в WebView без него останется одна точка, смысл не теряется. Движение выключается
   при «уменьшить движение» — точка остаётся статичной. */
.ask-icon{position:relative;display:inline-flex}
.ask-dot{position:absolute;top:-2px;right:-2px;width:8px;height:8px;border-radius:50%;
  background:var(--accent-fill);box-shadow:0 0 0 2px var(--fill);
  animation:v2askpulse 1.6s ease-in-out 3}
@keyframes v2askpulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.35);opacity:.55}}
.concierge .ask:has(.ask-dot){position:relative;overflow:hidden}
.concierge .ask:has(.ask-dot)::after{content:"";position:absolute;inset:0;pointer-events:none;
  background-image:linear-gradient(100deg,transparent 35%,rgba(255,255,255,.28) 50%,transparent 65%);
  background-size:220% 100%;background-repeat:no-repeat;background-position:-60% 0;
  animation:v2askglint 4s ease-in-out 2}
/* Пульс точки — 3 раза, блик — 2 раза, и дальше покой (2026-09-25). Бесконечными они держали
   экран «Сегодня» в перерисовке 60 раз в секунду, пока приложение просто открыто: блик двигает
   позицию фона, а это перерисовка кнопки в каждом кадре (замер: ~95 пересчётов стиля за 3 с
   покоя). Точка остаётся гореть — «есть непросмотренное» видно и без движения.
   Блик двигаем ПОЗИЦИЕЙ ФОНА, а не сдвигом слоя: сдвинутый слой выходил за кнопку и увеличивал её
   scrollWidth — проверка вёрстки честно считала это «обрезанным текстом» на каждом экране. */
@keyframes v2askglint{0%{background-position:-60% 0}45%,100%{background-position:160% 0}}
@media (prefers-reduced-motion:reduce){.ask-dot{animation:none}.concierge .ask:has(.ask-dot)::after{animation:none;display:none}}

/* Экран «Сотрудники», вариант B «Люди» (screens/staff.js, 2026-09-15): кружок с инициалами вместо
   фото — фотографий сотрудников в базе нет, а пустой серый силуэт у каждой строки читался бы как
   «данные не загрузились». Цвет — --accent на --accent-soft, не белый на заливке: мелкий текст. */
.staff-av{width:38px;height:38px;border-radius:50%;background:var(--accent-soft);color:var(--accent);
  font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex:none}

/* ==================================================================================
   Оформление 2026-09-21 (выбор владельца: почерк B на палитре A + приметные кнопки к ИИ)
   Переносится из проверенного оверрайда variant_d.css (QA: audit.py, 0 надписей с плохим
   контрастом на 9 экранах × 2 темы). Плотность и кегли для уже существующих правил (.sec,
   .kpi, .mod, .concierge, .fact/.stat>div, .big, .vc-head/.topbar .t-large) правлены НА
   МЕСТЕ выше по файлу — см. пометки «оформление 2026-09-21» у каждой строки. Здесь — то,
   чего в app.css раньше не было вовсе.

   Правка требований того же дня (владелец, после первого переноса: «не вижу градиента фона
   как в пресетах, сделай, чтобы плашки выделялись контрастно») ДВАЖДЫ меняет черновик
   почерка B: (1) .bgfx НЕ выключается — почерк B глушил фоновые пятна командой
   `.bgfx{display:none}`, эта строка из переноса сознательно выброшена, вместо неё --fx1/--fx2
   в tokens.css подняты до видимого глазом блика (см. комментарий там же); (2) карточки не
   ограничиваются тонкой линией — линия (--sep) держится, но раз владелец снял запрет на тени
   у почерка B, к ней добавлена --shadow: так плашка читается отдельным слоем, а не просто
   обведённым прямоугольником на градиенте. Числом (формула WCAG, как в audit.py): карточка
   к фону 1.27:1 в светлой теме и 1.90:1 в тёмной — оба выше порога, который задал владелец
   (1.25 / 1.9). Точные цвета карточки/фона — см. tokens.css. */

/* Разделители строк — во всю ширину карточки (край в край): это и есть «линейный» почерк варианта.
   Переопределяет базовый inset (left:var(--sp-4)) из tokens.css. */
.row+.row::before,.vrow+.vrow::before,.tr+.tr::before,.frow+.frow::before,.group>.vrow+.vrow::before{left:0}

/* Кегли на шаг ниже (было 34/22 — переопределяет базовые из tokens.css, .t-large внутри topbar/vc-head
   уже занижен точечной правкой выше по файлу до 24px, здесь — общий случай без этих контекстов). */
.t-large{font-size:30px}
/* Капители разделов были 10 px — ниже порога читаемости из нашей же шкалы (мелкий кегль не
   ниже 12, иначе разрядка и верхний регистр складываются в «серый шум»). 11.5 + чуть больше
   разрядки: место занимают те же, читаются заметно легче (владелец, 2026-09-22: «посмотри, что
   можно улучшить для комфорта»). */
.t-cap,.grp{font-size:11.5px;letter-spacing:.07em}

/* Карточки читаются отдельным слоем от градиентного фона: тонкая линия (--sep) + мягкая тень
   (--shadow). До правки требований 2026-09-21 здесь стояли только границы без теней («почерк B» в
   исходном виде) — владелец снял запрет на тени этой же правкой, тень обязательна, граница
   остаётся. .fact/.stat>div НЕ включены: это внутренние ячейки .facts/.stat, их уже разделяет
   встроенный grid-gap на --sep (см. определения .facts/.stat выше) — вторая рамка на ячейке дала
   бы двойную линию. */
.group,.kpi,.mod,.concierge,.facts,.stat,.tbl{border:1px solid var(--sep);box-shadow:var(--shadow)}

/* Обведённые элементы управления вместо залитых: вторичная кнопка, круглая иконка, поиск, сегмент.
   var(--sep) вместо var(--edge) — токена --edge в проекте нет и не было ни в tokens.css, ни в
   app.css; в исходном оверрайде это неопределённая переменная (см. отчёт), --sep — ближайший по
   смыслу существующий токен «тонкая линия», тот же, что уже красит --shadow-соседние разделители
   строк выше. */
.btn{background:transparent;border:1px solid var(--sep)}
.btn.primary{background:var(--accent-fill);border-color:transparent;color:#fff}
.btn.plain{border-color:transparent}
.icon-btn{background:transparent;border:1px solid var(--sep)}
.search{background:var(--card);border:1px solid var(--sep)}
.seg{background:var(--fill2);border:1px solid var(--sep)}
/* Активный сегмент — просто контрастная кнопка (владелец, 2026-09-22: «не нравится в меню
   переключателей двойная граница элементов, должна быть просто контрастная кнопка»). Было кольцо
   box-shadow 0 0 0 1px --sep у активного ВНУТРИ рамки самого переключателя — две линии в двух
   пикселях друг от друга. Осталась только подложка и мягкая тень: активный читается заливкой и
   весом шрифта, как в iOS, а рамку держит одна — внешняя. */
.seg>span.on{background:var(--card);box-shadow:0 1px 3px rgba(0,0,0,.18);color:var(--label);opacity:1;font-weight:650}

/* --- Кнопки обращения к ИИ: делаем приметными (владелец, 2026-09-21) --- */
/* «Спросить консьержа» на «Сегодня»: была подложка --fill и текст --label2 — читалась как поле
   ввода, а не как кнопка. Теперь заливка акцентом и белая надпись (белое — только на
   --accent-fill, правило проекта), высота 44 px, то есть полноценная цель нажатия на телефоне. */
.concierge .ask{background:var(--accent-fill);color:#fff;height:44px;font-weight:600;
  border:1px solid transparent;border-radius:var(--r-m)}
.concierge .ask svg{stroke:#fff}
/* Точка непросмотренного срочного на залитой кнопке: белая с акцентным кольцом — на синем она
   заметнее, чем прежняя синяя, и не сливается с заливкой. */
.concierge .ask .ask-dot{background:#fff;box-shadow:0 0 0 2px var(--accent-fill)}
/* «Спросить ИИ» в карточке машины: была вторичной рядом с залитой «+ Данные». Меняем ролями —
   к ИИ обращаются чаще, чем вносят данные руками, и именно её владелец просил сделать приметнее. */
.actions .btn:nth-child(2){background:var(--accent-fill);border-color:transparent;color:#fff;font-weight:600}
.actions .btn:nth-child(2) svg{stroke:#fff}
.actions .btn.primary{background:transparent;border:1px solid var(--accent);color:var(--accent);font-weight:600}
/* Обе кнопки ряда одинаковой ширины: у обведённой граница видимая (--accent, не --sep), иначе
   рядом с залитой она читалась как текст без кнопки — ровно то, на что владелец жаловался про ИИ. */

/* Вертикальный ритм шапки карточки машины (владелец, 2026-09-21: «много объектов сливаются
   вертикально», «что это за расстояния между объектами»). Было: всё подряд через 12px и одна
   распорка на 22px — ни иерархии, ни шкалы. Стало три ступени вместо одной:
     12px — внутри одной смысловой группы (заголовок → цифры),
     16px — между соседними блоками шапки (цифры → кнопки → горизонты),
     24px — перед новым разделом (полоса вкладок), и 12px от вкладок до их содержимого.
   Все значения — из шкалы токенов --sp-*, новых чисел не заводим. */
.stat{margin-top:var(--sp-3)}
.actions{margin-top:var(--sp-4)}
.actions+.group,.actions+.tbl{margin-top:var(--sp-4)}
.seg.vseg{margin-top:var(--sp-6)}
.vtab{margin-top:var(--sp-3)}

/* Разделители строк внутри карточек (владелец, 2026-09-21: «много объектов сливаются вертикально»).
   Линии рисовались в полпикселя (transform:scaleY(.5)) — приём для ретины, но на тёплой палитре
   при --sep 22% в светлой теме это давало контраст к карточке 1.43:1, то есть строки визуально
   слипались в один блок. Половинную высоту сняли, а сам токен --sep сделали заметнее в tokens.css:
   линия остаётся тонкой (1px), но её видно. */

/* Скругления: «почему журнал квадратный, паспорт скруглённый» (владелец, 2026-09-21).
   Проверка показала три места, где плашка с собственным фоном оставалась с прямыми углами, пока
   все соседние блоки скруглены токеном --r-m/--r-l:
   1) колонка списка машин на широком экране — это панель во всю высоту с фоном карточки;
   2) подсветка выбранной строки списка — заливка во всю ширину строки, углы прямые;
   3) шапка таблицы (.tr.th) — её фон рисовался поверх скруглённых верхних углов .tbl.
   Внутренние строки таблиц/групп сознательно остаются прямыми: их углы даёт контейнер. */
@media (min-width:1100px){
  .screen#vehicles>.inner{border-radius:var(--r-l);border-right:0;box-shadow:var(--shadow);
    height:calc(100svh - var(--sp-6));margin:var(--sp-3) 0}
}
.vrow.sel{border-radius:var(--r-m)}
.tbl>.tr.th:first-child{border-radius:var(--r-m) var(--r-m) 0 0}
.tbl>.tr:last-child{border-radius:0 0 var(--r-m) var(--r-m)}

/* Шапка внутри колонки списка машин (широкий экран) — тот же приём «край контейнера», только
   полоса цветом панели и со скруглением её верхних углов, а растворение — в прозрачную панель.
   Один механизм на страницу и на колонку, а не две разные заплатки. */
@media (min-width:1100px){
  .screen#vehicles>.inner>.topbar.material{margin-top:0;padding-top:var(--sp-2)}
  .screen#vehicles>.inner>.topbar.material::before{left:-12px;right:-12px;top:calc(-1 * var(--sp-2));bottom:0;
    background:var(--card);border-radius:var(--r-l) var(--r-l) 0 0}
  .screen#vehicles>.inner>.topbar.material::after{left:-12px;right:-12px;
    background:linear-gradient(to bottom,var(--card),var(--card-0))}
  .screen#vehicles>.inner.scrolled>.topbar.material::before,
  .screen#vehicles>.inner.scrolled>.topbar.material::after,
  .screen#vehicles>.card-col.scrolled .topbar.material::before,
  .screen#vehicles>.card-col.scrolled .topbar.material::after{opacity:1}
}

/* Растворение над таббаром-островом: строка списка обрывалась о его край и продолжалась под ним
   (та же беда «наложения», что была у шапки). Градиент фиксирован над рейкой, кликам не мешает. */
.tabbar-fade{position:fixed;left:0;right:0;bottom:0;height:calc(64px + var(--sp-8) + env(safe-area-inset-bottom,0px));
  background:linear-gradient(to bottom,var(--bg-0),var(--bg) 62%);pointer-events:none;z-index:5}
@media (min-width:1100px){.tabbar-fade{display:none}}

/* Шапка карточки машины одной плашкой (vehicle.js::headCard, 2026-09-21). Внутри четыре яруса:
   индекс со шкалой, строка фактов, горизонты, действия. Разделителей между ярусами нет — они
   разведены только воздухом по шкале, иначе внутри одной плашки снова получилась бы «куча полей».
   Строка фактов (.stat.inline) не плашка-в-плашке: фон прозрачный, ячейки разделены тонкой
   линией, отступы меньше обычных. */
.vhead{background:var(--card);border-radius:var(--r-m);border:1px solid var(--sep);box-shadow:var(--shadow);
  padding:12px 14px;margin-top:var(--sp-3);display:flex;flex-direction:column;gap:10px}
/* Подпись оценки («Требует внимания») стоит на одном уровне с самими цифрами (владелец,
   2026-09-22). Базовая линия для этого не годится: у цифры кегль 28, у подписи 13, и по базовой
   линии подпись визуально проваливается вниз; выравниваем по центру строки. */
.vhead-top{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3)}
.vhead-top .lbl{align-self:center;white-space:nowrap}
.vhead-top .lbl{font-size:13px;font-weight:600;color:var(--label2)}
.vhead .bar{margin:0}
.vhead-pills{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap}
.vhead .actions{margin-top:0}
.stat.inline{background:transparent;border-radius:0;gap:0;margin-top:0;display:grid;grid-template-columns:1fr 1fr}
.stat.inline>div{background:transparent;padding:6px 10px 6px 0;border-right:1px solid var(--sep)}
.stat.inline>div:nth-child(2n){border-right:0;padding-left:10px}
/* Правило нижней линии у первых двух ячеек снято 2026-09-21: ряд стал одноуровневым (три
   ячейки на телефоне, четыре от 700px), и линия висела под ними без пары. */
.stat.inline b{font-size:15px}
@media (min-width:700px){
  .stat.inline{grid-template-columns:repeat(4,1fr)}
  .stat.inline>div{border-bottom:0;border-right:1px solid var(--sep);padding:2px 10px}
  .stat.inline>div:first-child{padding-left:0}
  .stat.inline>div:last-child{border-right:0}
}
.nowrap{display:inline-flex;align-items:center;gap:8px;white-space:nowrap}

/* Перекомпоновка карточки машины (2026-09-21, владелец: «неэффективно расходует вертикальное и
   горизонтальное пространство — упростить, скомпоновать»). Три правки читаются вместе:
   1) .vhead-top получает третьего соседа — пилюли статуса (statusChips в vehicle.js), уехавшие
      из строки названия, поэтому им нужен прижатый вправо ряд в том же ярусе, что индекс.
   2) .stat.inline теперь трёхколоночная на телефоне (третья ячейка — горизонты 7/14/30, кликабельна
      целиком, отсюда min-height:44 у role=button), а не двухрядная сетка 2×2 под четыре факта.
   3) .ov/.ov-blk — двухколоночная раскладка «Сейчас»/«Узлы» на Обзоре (см. tabOverview), которая
      на широком экране заменяет собой прежний один узкий столбец с пустотой по бокам. */
.vhead{gap:var(--sp-2)}
.vhead-top{align-items:center;flex-wrap:wrap}
.vhead-top .chips{display:flex;gap:6px;margin-left:auto;flex-wrap:wrap}
.stat.inline{grid-template-columns:repeat(3,1fr)}
.stat.inline>div{border-bottom:0;border-right:1px solid var(--sep);padding:4px 10px}
.stat.inline>div:first-child{padding-left:0}
.stat.inline>div:last-child{border-right:0}
.stat.inline>div[role=button]{cursor:pointer;min-height:44px}
.seg.vseg{margin-top:var(--sp-4)}
.group>.row .gain-b{color:var(--ok-text);font-variant-numeric:tabular-nums;margin-right:var(--sp-2)}
.vtab .row .t-foot{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* ШИРИНА КОЛОНКИ НЕ БОЛЬШЕ КОНТЕЙНЕРА. Замер на бою 2026-09-22 (390 px): .ov сам был 358 px, а
   его единственная колонка — 411 px, и блоки «Сейчас»/«Узлы» вылезали за правый край экрана на
   53 px. Причина не в содержимом, а в правиле грида: колонка `1fr` не уже своего минимального
   содержимого, пока у ребёнка min-width:auto. minmax(0,1fr) + min-width:0 разрешают колонке
   сжиматься, и длинная строка внутри честно режется многоточием, а не растягивает экран. */
.ov{display:grid;grid-template-columns:minmax(0,1fr);gap:0}
.ov>.ov-blk{min-width:0}
@media (min-width:1280px){
  .vtab>.ov{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-6);align-items:start}
  .vtab>.ov>.ov-blk{min-width:0}
  .vtab>.ov>.ov-blk>.sec{margin-top:0}
}
@media (min-width:1400px){
  .screen#vehicles.on{grid-template-columns:360px minmax(0,1fr) 320px;--vlist-w:360px}
  .feed-col{width:320px}
  /* Трёхколоночная шапка отсюда снята 2026-09-21: на 1400–1599 колонке карточки остаётся ~630px,
     середина схлопывалась в 100px и число пробега вставало вертикально. Полоса включается с
     1600px (правило ниже), до неё шапка ярусная — та же, что на телефоне. */
}

/* Пальцевые цели: маленькие кнопки в строках («Сделано», «Ввести») оставались 36px — норма для
   мыши, но не для пальца (визуальный замер 2026-09-21: 8 целей ниже 44 на телефоне). */
@media (pointer:coarse){.btn{height:44px}.row[role=button]{min-height:44px}}
/* Подпись в узкой ячейке ряда фактов: на телефоне колонка ~105px, и «источник неизвестен» при
   11px обрезался многоточием. 10px — нижняя граница шкалы для служебной подписи (ui-density),
   ниже не опускаемся. */
.stat.inline span,.stat.inline .rot i{font-size:10px}

/* Строка проблем над шкалой индекса (vehicle.js::problemLine, 2026-09-21). Полоска тона слева —
   та же, что у строк «Сейчас», чтобы цвет означал одно и то же во всей карточке. Высота ровно в
   одну строку: когда проблем больше двух, они уезжают колесом (.rot), а не переносятся. */
.problems{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--label);min-height:20px}
.problems .st{width:3px;height:14px;border-radius:4px;flex:none}
.problems.bad .st{background:var(--bad)}
.problems.warn .st{background:var(--warn)}
/* Бейдж «+N» в строке «Сейчас»: зелёный текст на карточке давал 3.97:1 в светлой теме (замер
   2026-09-21) — берём тот же зелёный, что у статусных надписей, но на полтона темнее. */
:root:not([data-theme="dark"]) .group>.row .gain-b,:root:not([data-theme="dark"]) .group>.gain b{color:#176B31}

/* КОЛЕСО ФАКТОВ — общий механизм (vehicle.js::renderRot). Окно ровно в одну строку, внутри
   столбик, который сдвигается на процент своей высоты: число фактов не влияет ни на вёрстку, ни на
   арифметику сдвига. Правило общее, а не «для ячейки показателей»: колесо стоит и в подписи
   пробега, и в строке проблем, и в разделе шин — разные высоты строк там задаются переменной
   --rot-h, а механика одна. */
.rot{position:relative;display:block;height:var(--rot-h,16px);overflow:hidden}
.rot .rot-track{display:block;transition:transform .42s cubic-bezier(.22,1,.36,1)}
.rot .rot-track>i{display:block;height:var(--rot-h,16px);line-height:var(--rot-h,16px);font-style:normal;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (prefers-reduced-motion:reduce){.rot .rot-track{transition:none}}
.stat.inline .rot{--rot-h:14px}
.problems .rot{--rot-h:18px;flex:1;min-width:0}

/* Короткая строка узла: полоса тона, название, остаток. Одна строка, высота как у строки списка
   машин (44px пальцевая цель), без подписи и пилюли — см. комментарий в vehicle.js::unitRowCompact. */
.units .tr.short{grid-template-columns:3px minmax(0,1fr) auto;gap:10px;min-height:44px;align-items:center}
.units .tr.short .st{width:3px;height:18px;border-radius:4px}
.units .tr.short .ellip{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Две колонки «Обзора» — только когда колонке карточки реально хватает ширины. На 1280 ей
   доставалось ~300px на столбец, и строки узлов ломались на три строки (замер 2026-09-21). */
/* Две колонки «Обзора» вернулись на 1280 (2026-09-21): строки узлов стали однострочными, им
   хватает ~300px, а в одну колонку под карточкой на 1440 оставалась пустая нижняя треть экрана. */
@media (min-width:1280px){.vtab>.ov{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}

/* Вертикальный ритм ВНУТРИ вкладок карточки машины (владелец, 2026-09-21: «в деньгах проверь
   вертикальные отступы, я просил везде их проверить»). Замер на «Деньгах» показал разнобой: на
   телефоне 6/12/24, на широком экране в тех же местах 6/0/16/24 — потому что расстояние задавали
   собственные поля блоков (.concierge, .facts, .tbl), а они у разных блоков разные. Теперь
   расстояние держит ОДИН источник — поток вкладки, а собственные вертикальные поля блоков внутри
   неё гасятся. Три ступени те же, что в шапке карточки: 6 — заголовок к своему содержимому,
   12 — между блоками одной группы, 24 — перед новым разделом. */
.vtab>*+*{margin-top:var(--sp-3)}
.vtab>.sec+*{margin-top:6px}
.vtab>*+.sec{margin-top:var(--sp-6)}
:where(.vtab .concierge,.vtab .facts,.vtab .tbl,.vtab .group,.vtab .stat,.vtab .actions){margin-top:0;margin-bottom:0}
.vtab>.ov>.ov-blk>*+*{margin-top:var(--sp-3)}
.vtab>.ov>.ov-blk>.sec+*{margin-top:6px}

/* Ось заголовков и строк (владелец, 2026-09-21: «проверь оси заголовков и таблиц, чтобы всё было
   симметрично… чтобы ощущалось как iOS»). В iOS заголовок раздела стоит на той же вертикали, что
   и текст строк под ним, а не у самого края экрана. У нас строки имеют собственный отступ 16px, а
   заголовок стоял у края — ось ломалась на каждом разделе. Выравниваем заголовок и его ссылку по
   тексту строк; у таблиц шапка и данные уже на одной сетке. */
.sec{padding-left:var(--sp-4);padding-right:var(--sp-4)}
.tbl>.tr.th{padding-left:var(--sp-4)}
.tbl>.tr:not(.th){padding-left:var(--sp-4)}
.tr+.tr::before{left:var(--sp-4)}
/* Колонка списка машин на широком экране: между шапкой и поиском был зазор 4px — единственное
   место вне шкалы (замер 2026-09-21). */
/* Шапка колонки липкая и приподнята на 8px, поэтому её нижняя кромка съедает часть отступа
   следующего блока: живого зазора оставалось 4px из 12 (замер 2026-09-21). Добавляем эти 8px. */
@media (min-width:1100px){.screen#vehicles>.inner>.topbar.material+*{margin-top:calc(var(--sp-3) + var(--sp-2))}}
/* Узкие телефоны (≤360): крупное число в ячейке фактов не влезало и обрезалось. */
@media (max-width:360px){.stat.inline b{font-size:14px;letter-spacing:-.01em}}

/* Строка пробега в шапке карточки (vehicle.js::odoLine). Число, подпись-колесо и кнопка ввода в
   один ряд: на телефоне подпись ужимается, число и кнопка не двигаются. */
.odo{display:flex;align-items:center;gap:var(--sp-3);min-width:0}
.odo b{font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.odo .odo-cap{flex:1;min-width:0;font-size:11px;color:var(--label2)}
.odo .odo-cap .rot{--rot-h:14px}
.odo .btn{flex:none}
/* Правила полосы на 1600 сняты: шапка состоит из двух блоков (.vhead-l/.vhead-r). */

/* Узкие телефоны (≤360): пилюля статуса и длинная подпись нормы обрезались краем. Пилюля ужимается
   шрифтом, подпись переносится вместо обрезки (замер 2026-09-21 на 320px). */
@media (max-width:360px){
  .pill{font-size:10px;padding:2px 6px}
  /* Подпись под числом в плитке («просрочено», «в сервисе») на 320px не влезала в 11px: колонка
     65px против нужных 69 (замер 2026-09-21). 10px — нижняя граница шкалы для служебной подписи. */
  .kpi span{font-size:10px}
  .units .tr .sub{white-space:normal}
  .row .t-foot{-webkit-line-clamp:3}
}

/* Строка резины в шапке карточки (vehicle.js::tireLine): тот же ряд, что у пробега — название
   ужимается, кнопка не двигается. */
.odo.tire .tire-name{flex:1;min-width:0;font-size:13px;color:var(--label)}
.odo.tire{gap:var(--sp-2)}

/* Заголовок плашки консьержа: имя, под ним дата и размер парка. Обе строки — часть плашки, а не
   висящая над ней подпись (владелец, 2026-09-22). */
.concierge .who{margin-bottom:0}
.concierge .conc-date{margin:-2px 0 4px}
/* Верхние кромки зон на широком экране совпадают: обе зоны — колонки флекса от одной линии. */
@media (min-width:1100px){.screen#today>.inner>.tmain,.screen#today>.inner>.taside{margin-top:0}}

/* Два блока шапки карточки: слева состояние, справа действия (vehicle.js::headCard, 2026-09-22).
   До 700px — друг под другом, дальше — рядом; правый не уже 260px, иначе кнопки переносятся. */
.vhead{gap:var(--sp-3)}
.vhead-l,.vhead-r{display:flex;flex-direction:column;gap:10px;min-width:0}
.vhead-r{justify-content:space-between}
@media (min-width:700px){
  /* Блоки тянутся на всю высоту шапки, а плашки правого блока прижаты к НИЗУ (владелец,
     2026-09-22: «давай по низу центрировать плашки»). Замер до правки на 1440: левый блок
     заканчивался на 185, правый — на 156, то есть под кнопками висели 29 px пустоты, и низ
     карточки выглядел рваным. При align-items:start растянуть блок нельзя — нужен stretch. */
  .vhead{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(260px,1fr);column-gap:var(--sp-6);align-items:stretch}
  .vhead-r{justify-content:flex-end}
  .vhead .actions{margin-top:0}
  .vhead .actions .btn{white-space:nowrap}
}

/* Верхние кромки колонок «Обзора» совпадают (владелец, 2026-09-22: «оси окон, чтобы совпадали по
   верхним границам»). Заголовок раздела со ссылкой справа («Все 65») был выше обычного из-за
   выравнивания по базовой линии — обе колонки начинались на разной высоте. Фиксируем высоту
   строки заголовка и выравниваем по центру. */
.sec{align-items:center;min-height:22px}
.vtab>.ov>.ov-blk>.sec:first-child,.vtab>.ov>.ov-blk>*:first-child{margin-top:0}
.vtab>.ov{align-items:start}

/* «Сегодня»: интервал между карточками задаёт ТОЛЬКО зазор зоны (владелец, 2026-09-22: «на вебе в
   Сегодня очень большие вертикальные интервалы, должны быть по какому-то стандарту небольшими»).
   Замер на бою 1440/1920: между карточками 24 px зазора ПЛЮС 24 px собственного margin-top у
   заголовка .sec, с которого карточка начинается, — суммарно 48, вдвое больше шкалы. Это та же
   ошибка «двух слагаемых», из-за которой на лендинге 56 превращались в 112: гасим слагаемое,
   принадлежащее содержимому, и оставляем то, что принадлежит раскладке. Внутри карточки .sec
   по-прежнему держит свои 24 — там он и правда отделяет одну смысловую группу от другой. */
.screen#today .today-card > .sec:first-child{margin-top:0}

/* ПЛОТНОСТЬ «СЕГОДНЯ» (владелец, 2026-09-22: «межстрочные интервалы на главной всё ещё очень
   большие»). Замер на бою 1440 показал, из чего складывалась воздушность: строка действия 75 px
   при 58 px текста, пустое состояние «Ничего здесь» — 94 px при двух строках, заголовок раздела
   держал 24 px сверху, зазор между карточками 24. Правки ниже — только в пределах #today, чтобы
   не менять ритм экранов, которые владелец не трогал. Шкалу не нарушаем: 24 → 16, 12 → 8. */
.screen#today > .inner{gap:var(--sp-4)}
.screen#today > .inner > .tmain,.screen#today > .inner > .taside{gap:var(--sp-4)}
.screen#today .today-card > .sec:first-child{margin-top:0}
/* Подписи строки действия: интерлиньяж 1.25 вместо 1.35 — это подписи, а не абзац, им хватает
   меньшего (шкала ui-density: подпись 1.3, абзац 1.55). Шесть строк экономят по 3 px каждая. */
/* Срочность и работа теперь в ОДНОЙ строке (today.js::actionRow): .act-line держит их в ряд и
   режет многоточием целиком, а не каждую половину отдельно — иначе «Tires and wheels» обрезалось
   бы при живой пустоте справа. */
.screen#today .row .t-foot > .act-line{display:block;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.screen#today .act-head{font-size:13px;font-weight:600;color:var(--label)}
.screen#today .act-head.bad{color:var(--bad-text)}.screen#today .act-head.warn{color:var(--warn-text)}
.screen#today .act-dot,.screen#today .act-works{font-size:13px;color:var(--label2)}
/* Воздух строки задавался ДВАЖДЫ: 7 px у самой строки и ещё 9 px сверху и снизу у внутренней
   колонки .grow (замер на бою: строка 70 px при 39 px текста). Оставляем одно слагаемое — то, что
   принадлежит строке; строка становится 53 px. Это то же правило «зазор задаёт кто-то один». */
.screen#today .act-list .row{min-height:0}
/* Пустое состояние внутри карточки — не самостоятельный экран: 24 px сверху и снизу там были
   размером с целую строку данных. */
.screen#today .kpi{padding:8px 12px}

/* ОДИН РАЗМЕР У ОДИНАКОВЫХ ЭЛЕМЕНТОВ (владелец, 2026-09-22: «размеры кнопок, полей почему разные,
   всё должно совпадать по границам»). Замер показал три расхождения: пилюли 18 и 22 px на одном
   экране, поле поиска 24 px внутри своей рамки 36 px, и колонка шире контейнера (выше).
   Пилюля — не текст, а элемент управления: у неё одна высота везде, иначе строки «дышат»
   по-разному в соседних блоках. */
.pill{min-height:22px}
/* Поле поиска занимает всю высоту своей рамки: раньше кликабельной была только середина, а
   визуальная граница была больше настоящей зоны ввода. */
.search input{height:100%;align-self:stretch}
