/* ═══════════════ ДОСКА ═══════════════
   Зона: канбан (.board, .col, .col-h, .donebar, .col-more), карточка на доске
   (.tcard и всё на ней), фильтры (.filters, .ffold, .fpanel), режим выбора,
   вкладки стадий на телефоне, списковый вид (.ltable, .lrow), таймлайн (.tl,
   .tl2-…, .otl-…), проблемы, экраны проекта / объекта / этапа.
   ПРАВИЛО РАЗРЕЗА: этот файл правит ОДИН агент за раз и только свою зону.
   Подключается ВТОРЫМ — после base, до card. */

/* ═════════════ МЕТКА «ВЕРНУЛИ НА ДОРАБОТКУ» ═════════════ */
.retmark{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:700;
  background:var(--st-review-bg);color:var(--st-review);border-radius:999px;padding:2px 9px;margin-bottom:6px}
.retmark svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
/* правило возврата живёт в конце файла — иначе его перебивают цвета статусов */

/* ═════════════ ОБЪЕКТ: список этапов ═════════════ */
.stagelist{display:flex;flex-direction:column;gap:10px}
.stage-row{display:flex;align-items:center;gap:14px;background:var(--surface);border-radius:14px;
  box-shadow:var(--sh-1);padding:14px 16px;cursor:pointer;transition:transform .16s var(--ease),box-shadow .2s}
.stage-row:hover{transform:translateX(2px);box-shadow:var(--sh-2)}
.stage-row:active{transform:scale(.995);box-shadow:var(--sh-press);transition-duration:0s}
.stage-row .nm{flex:1;min-width:0}
.stage-row .nm b{font-size:15px;font-weight:600;display:block}
.stage-row .nm span{font-size:12px;color:var(--ink-2);font-weight:500}
.stage-row .bar{width:150px;flex:none;display:flex;align-items:center;gap:8px}
.stage-row .bar .mbar{flex:1}
.barpct{font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;min-width:30px;text-align:right}
.mbar{height:7px;border-radius:6px;background:var(--surface-2);overflow:hidden;border:1px solid var(--line)}
.mbar i{display:block;height:100%;border-radius:6px;background:var(--accent);transition:width .5s var(--ease)}
.stage-row .cc{font-size:12.5px;color:var(--ink-2);font-weight:500;white-space:nowrap;font-variant-numeric:tabular-nums}
.sdot{width:9px;height:9px;border-radius:50%;flex:none}
.stage-row-empty{background:transparent;box-shadow:none;border:1.5px dashed var(--line);opacity:.8}
.stage-row-empty:hover{opacity:1;background:var(--surface-2)}
.stage-row-empty .sdot{border:1.5px dashed var(--ink-2)}
.stage-hint{display:block;font-size:11px;color:var(--ink-2);font-weight:500;margin-top:1px}

/* ─── ЭТАП РАСКРЫВАЕТСЯ НА МЕСТЕ (аккордеон), а не уводит на отдельный экран.
   Обёртка .stage-item — обычный div: вложить панель или вторую кнопку ВНУТРЬ
   .stage-row нельзя, это <button>, кнопка в кнопке невалидна. Ширину обёртка
   наследует от .stagelist, поэтому панель всегда ровно по ширине строки. */
.stage-item{display:flex;flex-direction:column;min-width:0}
.stage-item>.stage-row{width:100%}
.stage-row .chevs{flex:none;width:16px;height:16px;color:var(--ink-2);display:inline-flex;
  align-items:center;transition:transform .18s var(--ease)}
.stage-row .chevs svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2}
.stage-item.open>.stage-row .chevs{transform:rotate(180deg)}
.stage-item.open>.stage-row{border-radius:14px 14px 0 0}
.stage-item.open>.stage-row:hover:not(:active){transform:none}        /* иначе строка «съедет» с панели */
.stage-panel{background:var(--surface-2);border-radius:0 0 14px 14px;padding:6px 8px 8px;
  box-shadow:var(--sh-1);animation:stgIn .18s var(--ease)}
@keyframes stgIn{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
.stage-panel-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:8px 8px 10px}
.stage-panel-h .spmix{flex:1;min-width:150px;font-size:12.5px;color:var(--ink-2);font-weight:500;
  font-variant-numeric:tabular-nums}
.stage-panel-h .spmix b{color:var(--ink);font-weight:700}
.sp-fire{margin-left:8px;color:var(--st-fire);font-weight:700;white-space:nowrap;
  display:inline-flex;align-items:center;gap:4px}
.sp-fire svg{width:12px;height:12px}
.sp-acts{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.sp-btn{height:32px;padding:0 11px;font-size:12.5px;background:var(--surface)}
.sp-btn svg{width:14px;height:14px}
.sp-hint{font-size:12px;color:var(--ink-2);font-weight:500}
.stage-panel .ltable{box-shadow:var(--sh-1)}
.stage-panel .lrow{padding:11px 14px}
.stage-panel .lrow .lm.od{color:var(--st-fire);font-weight:700}
.sp-showall{margin-top:8px;background:var(--surface)}
/* ═════════════ ВРЕМЯ ЭТАПА (Волна 5, ТЗ 7.1) ═════════════
   Блок в раскрытой панели этапа: строка дат + строчные действия + плашка
   опоздания. Цвета строго по каналам закона стиля: даты — нейтраль,
   опоздание — терракота (--alarm-bg/--alarm), закрытость — нейтральный чип. */
.stg-time{display:flex;align-items:center;gap:4px 10px;flex-wrap:wrap;padding:8px 8px 0}
.stg-dates{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--ink);
  min-height:44px}
.stg-dates svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;flex:none;color:var(--ink-2)}
/* строчное действие (закон стиля §2): текст цветом --accent, без рамки и заливки;
   min-height держит тач-цель 44px, не раздувая строку визуально */
.stg-linkact{display:inline-flex;align-items:center;background:none;border:0;padding:0 8px;min-height:44px;
  color:var(--accent);font-size:12.5px;font-weight:600;cursor:pointer;border-radius:8px}
.stg-linkact:hover{background:var(--accent-soft)}
.stg-closebtn{margin-left:auto;height:36px;padding:0 12px;font-size:12.5px;background:var(--surface)}
.stg-closebtn svg{width:14px;height:14px}
.stg-closed{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--ink-2);
  min-height:44px}
.stg-closed svg{width:14px;height:14px;stroke:var(--st-done);fill:none;stroke-width:2.2;flex:none}
.stg-form{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;padding:2px 8px 6px}
.stg-form label{display:flex;flex-direction:column;gap:4px;font-size:10.5px;font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--ink-2)}
.stg-form input[type=date]{min-height:44px;border:1px solid var(--line);border-radius:10px;
  background:var(--card);color:var(--ink);font:inherit;font-size:13px;padding:0 10px;min-width:140px}
.stg-form input[type=date]:focus{outline:none;border-color:var(--accent)}
.stg-form .btn{height:44px}
.stg-late{display:flex;align-items:flex-start;gap:8px;background:var(--alarm-bg);color:var(--alarm);
  border-radius:10px;padding:9px 12px;margin:8px 8px 2px;font-size:12.5px;font-weight:600;line-height:1.4}
.stg-late svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.2;flex:none;margin-top:1px}
/* чип в строке этапа: «закрыт» — нейтраль, «опаздывает» — терракота. Инлайном
   внутри <b>, чтобы не плодить строку и не ломать сетку .stage-row */
.stg-chip{display:inline-block;vertical-align:2px;margin-left:8px;font-size:10px;font-weight:700;
  letter-spacing:.03em;border-radius:999px;padding:2px 8px;white-space:nowrap}
.stg-chip.closed{background:var(--st-new-bg,var(--surface-2));color:var(--ink-2)}
.stg-chip.late{background:var(--alarm-bg);color:var(--alarm)}
.stg-stashnote{font-size:11.5px;color:var(--ink-2);font-weight:500;padding:8px 10px 0}
/* запасной экран этапа: тот же блок, но на «бумаге», а не внутри панели */
.stg-timewrap{background:var(--surface);border-radius:var(--r-card);box-shadow:var(--sh-1);
  padding:4px 10px 8px;margin-bottom:14px}
/* бейдж «сделана часть» на карточке доски: сам .partline живёт в card.css,
   здесь только отступ строки-обёртки */
.tcard .tc-part{margin-top:7px}
.lrow .partline{flex:none}

/* диалог переименования этапа */
.sr-note{font-size:12.5px;color:var(--ink-2);margin:2px 0 0;line-height:1.45}
.sr-warn{margin-top:10px;padding:9px 11px;border-radius:10px;background:var(--surface-2);
  border-left:3px solid var(--st-fire);font-size:12.5px;line-height:1.45;color:var(--ink)}
.sr-err{border-left-color:var(--st-fire);background:var(--accent-soft)}
.sr-sum{padding:10px 12px;border-radius:12px;background:var(--surface-2)}
.sr-arrow{font-size:15px;font-weight:600}
.sr-arrow span{color:var(--ink-2);margin:0 6px}
.sr-scope{font-size:12px;color:var(--ink-2);margin-top:3px;font-weight:500}
.sr-big{margin-top:12px;font-size:14px;font-weight:600}
.sr-big .tnum{font-size:26px;font-weight:700;color:var(--accent);vertical-align:-2px;margin-right:4px}
@media(max-width:620px){
  .stage-panel .lrow{flex-wrap:wrap}
  .stage-panel .lrow .lt{flex:1 1 100%;white-space:normal}
  .stage-panel .lrow .lm{flex:1 1 100%;padding-left:20px;white-space:normal}
  .stage-panel-h .sp-acts{width:100%}
  .sp-btn{flex:1}
}

/* ═════════════ КАНБАН ═════════════ */
/* Правка владельца 9 августа: «не создавать новую строку — не плоди экранные
   сдвиги». Строка фильтров ВСЕГДА одна и всегда одной высоты — height (не
   min-height): не переносится (nowrap), сворачивание меняет только
   СОДЕРЖИМОЕ строки — доска под ней не прыгает ни на пиксель.
   Правка 16.08 (tasks/portfel-lyudi-vertikalno.md): горизонтальная прокрутка
   снята — тот же брак «ползунок глупо», что был в строке людей портфеля.
   Что не влезло по ширине, прячет и считает layoutFiltersRow() (board.vid.js)
   и показывает как «ещё N» (.fmore) — CSS сам это не умеет, ширина элементов
   зависит от выбранных значений и языка. */
.filters{display:flex;gap:var(--s2);flex-wrap:nowrap;overflow:hidden;
  margin-bottom:var(--s3);align-items:center;height:var(--h-row)}
.filters>*{flex:none}
.filters .fchip-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:150px}
@media(max-width:680px){
  /* узкий экран: у выпадашки и так виден выбранный пункт — статичная подпись
     перед ней («Объект:») только отъедает ширину, повторяя то, что уже видно.
     aria-label на самом <select> (sel() в board.vid.js) держит доступность. */
  .filters label.fchip .fchip-lbl{display:none}
}
.filters .fmore{color:var(--ink-3);font-weight:600}

/* Правка владельца 9 августа: фильтры ОТКРЫТЫ по умолчанию и живут в ОДНОЙ
   строке с самими выпадашками; отдельной строки-кнопки «Фильтры» больше нет,
   сворачивание — уголок-шеврон справа («аккуратно в углу, эргономично»).
   .ffold прижат вправо через margin-left:auto — это и есть «в углу». */
.ffold{margin-left:auto;position:sticky;right:0;min-width:44px;min-height:44px;border-radius:999px;
  background:linear-gradient(90deg,transparent,var(--bg) 30%);
  display:inline-flex;align-items:center;justify-content:center;gap:6px;color:var(--ink-3);transition:.15s}
.ffold:hover{background:var(--surface);color:var(--ink-2)}
.ffold .fnum{min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--accent);
  color:var(--on-accent);display:inline-grid;place-items:center;font-size:11.5px;font-weight:600}
.fbtn .fnum{min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--accent);
  color:var(--on-accent);display:inline-grid;place-items:center;font-size:11.5px}
.fbtn svg.car{transition:transform .2s var(--ease)}
.fbtn.open svg.car{transform:rotate(180deg)}
.fpanel{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;background:var(--surface);
  border-radius:var(--r3);box-shadow:var(--sh-1);padding:var(--s2) var(--s3);margin:0 0 var(--s4)}
/* лист «ещё N» наполняется из JS тем, что пряталка убрала из строки; убрать
   было нечего — коробка стоит с hidden, и display:flex обязан ей уступить,
   иначе под строкой висит пустая плашка с полем и тенью */
.fpanel[hidden]{display:none}
.fpanel .fchip{background:var(--card)}
.fpanel .fchip.on{background:var(--accent)}
/* ⚠️ align-items:start — иначе grid растягивает ВСЕ колонки до высоты самой
   длинной: на фильтре «без хозяина» в «Надо» 70 карточек, и три пустые колонки
   становились по 11 527 px каждая (владелец: «растягиваются вниз»). */
.board{display:grid;gap:var(--s3);grid-template-columns:1fr;align-items:start}
/* 4 колонки цикла: Надо · В работе · На проверке · Подтверждено (спец. Б-2).
   На среднем экране — 2×2, чтобы колонка не сжималась до нечитаемой. */
@media(min-width:760px){.board{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1120px){.board{grid-template-columns:repeat(4,1fr)}}
.col{background:var(--surface-2);border-radius:var(--r3);padding:var(--s3);min-height:120px;transition:background .2s}
/* Р1 — второй рельс: поле колонки 12 + это поле 4 = 16 от края доски, ровно
   столько же, сколько у .lrow и .tl2-bar-top. Восемь левых краёв стали тремя. */
.col-h{display:flex;align-items:center;gap:var(--s2);padding:0 var(--s1) var(--s3);font-size:14px;font-weight:600}
.col-h .cdot{width:9px;height:9px;border-radius:50%}
.col-h .n{margin-left:auto;font-size:12px;color:var(--ink-2);font-variant-numeric:tabular-nums;font-weight:600}
/* разгрузка доски (ТЗ 5.2): «Подтверждено» по умолчанию свёрнуто в полосу-счётчик.
   Кнопка сворачивания: тач-цель 44×44 (закон стиля), отрицательные поля гасят
   лишнюю высоту, чтобы шапка колонки не распухала. */
.col-h .col-min-btn{width:44px;height:44px;margin:-13px -12px -13px -4px;border-radius:7px;display:grid;place-items:center;color:var(--ink-2);background:none;flex:none}
.col-h .col-min-btn:hover{background:var(--surface)}
.col-h .col-min-btn svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.col.done-min{padding:0;display:flex}
.donebar{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:12px 8px;
  border-radius:14px;background:var(--surface-2);color:var(--ink-2);font-size:12.5px;font-weight:600;
  white-space:nowrap;transition:background .2s,color .2s;cursor:pointer}
.donebar:hover{background:var(--accent-soft);color:var(--accent)}
.donebar .cdot{width:9px;height:9px;border-radius:50%;background:var(--st-done);flex:none}
@media(min-width:760px){
  .board.done-min{grid-template-columns:1fr 1fr}
  .col.done-min .donebar{padding:12px 8px}
}
@media(min-width:1120px){
  .board.done-min{grid-template-columns:1fr 1fr 1fr 48px}
  .col.done-min .donebar{writing-mode:vertical-rl;padding:16px 6px}
}

/* ── РЕЖИМ ВЫБОРА (сложить пачку в корзину, только Создатель) ── */
.tcard.picking{cursor:pointer;user-select:none}
.tcard.picking .th{display:flex;align-items:center;gap:10px}
.tcard.picking .pickbox{width:22px;height:22px;flex:none;border-radius:6px;border:2px solid var(--line);
  display:grid;place-items:center;font-size:14px;font-weight:800;color:transparent;transition:.14s var(--ease)}
.tcard.picked{box-shadow:var(--sh-2),0 0 0 2px var(--accent) inset}
.tcard.picked .pickbox{background:var(--accent);border-color:var(--accent);color:var(--on-accent,#fff)}

.tcard{background:var(--surface);border-radius:var(--r3);box-shadow:var(--sh-1);padding:var(--s3);
  margin-bottom:var(--s2);cursor:pointer;position:relative;transition:transform .16s var(--ease),box-shadow .2s;border-left:4px solid var(--st-new)}
.tcard:hover{box-shadow:var(--sh-2);transform:translateY(-1px)}
/* --s1 (4), а не --s2 (8): на бою 6 — середина шкалы, округляется к меньшему.
   Восемь карточек в колонке — восемь лишних четвёрок ни за что. */
.tcard .th{font-size:14px;font-weight:600;line-height:1.3;margin-bottom:var(--s1)}
/* ТЗ 5.4: строка места — третья ступень серого (--ink-3), тише имени и срока */
.tcard .tm{font-size:11.5px;color:var(--ink-3);font-weight:500;display:flex;flex-wrap:wrap;gap:3px 9px}
.tcard .tm .od{color:var(--st-fire);font-weight:600}
.tcard.st-new{border-left-color:var(--st-new)}
.tcard.st-in_progress{border-left-color:var(--st-prog)}
.tcard.st-on_review{border-left-color:var(--st-review)}
.tcard.st-done{border-left-color:var(--st-done)}
.tcard.fire{border-left-color:var(--st-fire)}
/* ═══ карточка на доске v3 (спец. Б-3): кто · когда · что нового ═══ */
.tcard .tc-row{display:flex;align-items:center;gap:var(--s2);margin-top:var(--s2);flex-wrap:wrap}
.tcard .tc-who{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;color:var(--ink-2);
  max-width:100%;min-width:0}
.tcard .tc-who .av{width:20px;height:20px;border-radius:50%;background:var(--accent-soft);color:var(--accent);
  font-size:9.5px;font-weight:700;display:grid;place-items:center;flex:none;letter-spacing:.02em}
.tcard .tc-who .av.noone{background:var(--surface-2);color:var(--ink-2);padding:0}
.tcard .tc-who .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tcard .tc-due{font-size:11.5px;font-weight:600;color:var(--ink-2);margin-left:auto;white-space:nowrap}
.tcard .tc-due.od{color:var(--st-fire);font-weight:700}
.tcard .tc-due.today{background:var(--alarm-bg);color:var(--alarm);border-radius:999px;padding:1px 8px}
/* ТЗ 5.5: строка жизни — последнее событие задачи («Kris взял вчера»). Обычная —
   --ink-2; stale (молчит >14 дней) — терракота --alarm-line + часики, чтобы
   залежавшееся было видно не открывая карточку. Одна строка, лишнее — в «…». */
.tcard .tc-life{display:flex;align-items:center;gap:var(--s1);margin-top:var(--s2);font-size:11px;font-weight:400;
  color:var(--ink-2);line-height:1.3;min-width:0}
.tcard .tc-life .lx{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tcard .tc-life svg{flex:none}
.tcard .tc-life.stale{color:var(--alarm);font-weight:600}
.tcard .tag{cursor:default}
.tcard .tag:hover{background:var(--surface-2);color:var(--ink-2)}
.tcard .tc-upd{font-size:10px;font-weight:700;background:var(--accent);color:var(--on-accent);border-radius:999px;padding:2px 7px;
  white-space:nowrap;letter-spacing:.01em}
.col-more{width:100%;margin-top:4px;padding:8px;border-radius:9px;background:none;color:var(--accent);
  font-size:12px;font-weight:600;min-height:44px}
.col-more:hover{background:var(--accent-soft)}

/* ═══ ДОСКА НА ТЕЛЕФОНЕ: вкладки стадий вместо четырёх колонок подряд (дефект №16) ═══
   Ниже 760px колонки ложились друг под друга и документ вырастал до 28 700 px:
   «На проверке» лежала на 27 100 — тридцать три свайпа. Показываем ОДНУ стадию
   за раз, порциями. Сетка вкладок 2×2, а не полоса: индонезийские подписи
   («Perlu diperiksa», «Dikonfirmasi») в одну строку не влезают, а горизонтальная
   прокрутка вкладок отбирает свайп у ленты карточек. */
.bt{display:none}
@media(max-width:759px){
  .bt{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2);margin:0 0 var(--s3)}
  .bt-b{min-height:var(--h-row);display:flex;align-items:center;gap:8px;padding:0 12px;border-radius:12px;
    background:var(--surface-2);color:var(--ink-2);font-size:13px;font-weight:600;text-align:left;
    box-shadow:var(--sh-1) inset;transition:background .18s var(--ease),color .18s var(--ease)}
  .bt-b .cdot{width:9px;height:9px;border-radius:50%;flex:none}
  .bt-b .lb{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .bt-b .n{font-variant-numeric:tabular-nums;font-weight:700;flex:none}
  /* активную вкладку обводим акцентом: на солнце разницы фонов greige не видно */
  .bt-b.on{background:var(--surface);color:var(--ink);box-shadow:var(--sh-2),0 0 0 2px var(--accent) inset}
  .bt-b.on .n{color:var(--accent)}
  .bt-b:active{transform:scale(.985)}
  .board.bt-one{grid-template-columns:1fr}
}

/* таймлайн */
.tl{background:var(--surface);border-radius:var(--r-card);box-shadow:var(--sh-2);padding:16px;overflow-x:auto}
.tl-row{display:grid;grid-template-columns:150px 1fr;gap:12px;align-items:center;padding:7px 0;border-top:1px solid var(--line)}
.tl-row:first-child{border-top:none}
.tl-row .lab{font-size:13px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tl-track{position:relative;height:22px;background:var(--surface-2);border-radius:6px;min-width:320px}
.tl-bar{position:absolute;top:3px;height:16px;border-radius:5px;background:var(--accent);opacity:.9}
.tl-bar.fire{background:var(--st-fire)}
.tl-bar.done{background:var(--st-done)}

/* ═════════════ ТАЙМЛАЙН v2 — единая координатная сетка (Gantt) ═════════════ */
/* Ключ: ось, сетка, «сегодня» и полосы задач считаются от ОДНОГО нуля.
   xOf(day)=LW+(day-min)*ppd — LW (ширина колонки подписей) зашит в координату,
   поэтому тик даты D и любой элемент задачи с датой D стоят на одном X. */
/* поле --s3 (12), а не --s4 (16): на бою здесь 14, ровно посередине шкалы, а
   по её же правилу середина округляется К МЕНЬШЕМУ. */
.tl2-wrap{background:var(--surface);border-radius:var(--r-card);box-shadow:var(--sh-2);padding:var(--s3) 0 var(--s2)}
/* ⚠️ flex-wrap ОСТАЁТСЯ. Ставить nowrap+overflow:hidden без работающего
   сворачивателя нельзя: у этой полосы нет шеврона, а значит нет и «ещё N» —
   на 375 переключатель группировки просто исчез бы без следа, а это обрезка,
   то есть стоп-кран §10. Высота ряда честные 44, отступы по шкале. */
.tl2-bar-top{display:flex;align-items:center;gap:var(--s2);padding:0 var(--s4) var(--s3);
  flex-wrap:wrap;row-gap:var(--s2);min-height:var(--h-row)}
/* ═══ СЧЁТЧИКОВ .tl2-sums ЗДЕСЬ БОЛЬШЕ НЕТ (31.08) ═══
   Это был ПЯТЫЙ ряд счётчиков на одном экране и те же самые три числа, что уже
   стоят в фильтрах и в шапках колонок. «21 просрочено» работало как «прокрутить
   к первой» — чип «Внимание N» в строке фильтров вместо этого ФИЛЬТРУЕТ доску,
   то есть делает строго больше, и делает это одинаково во всех трёх видах.
   Прижим переключателя группировки к правому краю снят вместе с ними: в
   переносящейся полосе margin-left:auto рвёт ряд, вместо него распорка .rast. */
.tl2-zoom{display:inline-flex;gap:4px}
/* ⚠️ 44, А НЕ 32. Кнопки масштаба и «к сегодня»/«весь проект» были 32×32 —
   мельче нормы §3, и сторож считал их браком все эти прогоны. Строку всё равно
   держит переключатель группировки высотой 50, поэтому честные 44 не стоили
   здесь ни пикселя высоты. */
.tl2-zoom button,.tl2-btn{height:44px;min-width:44px;border-radius:9px;background:var(--surface-2);box-shadow:var(--sh-1);
  color:var(--ink-2);font-weight:600;display:inline-flex;align-items:center;justify-content:center}
.tl2-zoom button{width:44px;font-size:17px}
.tl2-btn{padding:0 13px;font-size:12.5px;gap:5px}
.tl2-zoom button:hover,.tl2-btn:hover{background:var(--accent-soft);color:var(--accent)}
/* ⚠️ ВНУТРЕННЕЙ ВЕРТИКАЛЬНОЙ ПРОКРУТКИ ЗДЕСЬ БОЛЬШЕ НЕТ (решение владельца 31.08).
   Было `overflow:auto;max-height:min(66vh,640px)` — два вертикальных бегунка на
   экране, страницы и таймлайна, и человек листал задачи в окошке 640 px. Теперь
   тело растянуто на все строки и листается общей прокруткой страницы, а
   горизонталь осталась своей.
   overflow-y:hidden, а не visible: у элемента с overflow-x:auto вторая ось не
   может быть visible — CSS приводит её к auto, и вертикальный бегунок вернулся бы
   сам. Высота при этом равна содержимому, поэтому прокручивать по вертикали
   нечего и бегунка не возникает.
   Собственный горизонтальный бегунок скрыт: он оказался бы у самого низа полутора
   тысяч пикселей. Видимый бегунок один и он всегда под рукой — у липкой оси
   (.tl2-os). Жест, колесо и клавиши тела при этом работают как работали. */
.tl2-scroll{overflow-x:auto;overflow-y:hidden;position:relative;scrollbar-width:none}
.tl2-scroll::-webkit-scrollbar{display:none}
.tl2-canvas{position:relative;min-width:100%}

/* ── ось дат: ОТДЕЛЬНЫЙ горизонтальный скроллер, липкий К СТРАНИЦЕ ──
   Элемент с overflow-x:auto является скроллпортом сразу по обеим осям, и
   position:sticky внутри него считается ОТ НЕГО. Пока ось лежала внутри тела,
   она липла к телу; тело перестало прокручиваться вертикально — и ось уехала бы
   вместе со страницей. Поэтому ось вынесена в свой скроллер, а он сам липнет к
   странице с поправкой на живую шапку платформы (--hdr-h). Горизонталь обоих
   связывает tlOsSvyaz() (board.vid.js). */
.tl2-os{position:sticky;top:var(--hdr-h);z-index:11;overflow-x:auto;overflow-y:hidden;
  background:var(--surface);border-bottom:1px solid var(--line);scrollbar-width:thin}
.tl2-osin{position:relative;height:46px}
.tl2-head{position:relative;z-index:1;height:46px;background:var(--surface)}
.tl2-head-corner{position:sticky;left:0;top:0;z-index:12;width:var(--tl-lw);height:46px;background:var(--surface);
  box-shadow:1px 0 0 var(--line);display:flex;align-items:flex-end;padding:0 10px 7px;
  font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2)}
.tl2-mon{position:absolute;top:6px;font-size:11px;font-weight:700;color:var(--ink);white-space:nowrap;pointer-events:none}
.tl2-tick{position:absolute;top:24px;bottom:0;font-size:10.5px;color:var(--ink-2);font-weight:600;
  padding-left:4px;white-space:nowrap;border-left:1px solid var(--line)}
.tl2-tick.mon{color:var(--ink);font-weight:700}

/* ── тело: слой сетки + линия «сегодня» + строки ── */
.tl2-body{position:relative}
.tl2-gridlayer{position:absolute;top:0;left:0;right:0;bottom:0;z-index:0;pointer-events:none}
.tl2-grid{position:absolute;top:0;bottom:0;width:1px;background:var(--line);opacity:.32}
.tl2-grid.mon{opacity:.6}
.tl2-today{position:absolute;top:0;bottom:0;width:2px;background:var(--accent);z-index:5;pointer-events:none}
.tl2-today .lbl{position:absolute;top:3px;left:4px;font-size:10px;font-weight:700;color:var(--on-accent);
  background:var(--accent);padding:1px 6px;border-radius:6px;white-space:nowrap;box-shadow:var(--sh-1)}
.tl2-today.flip .lbl{left:auto;right:4px}

/* ── лейн-заголовки групп (sticky-top под осью + sticky-left) ── */
/* ⚠️ НАЖИМАЕМАЯ ЧАСТЬ — 44, А НЕ 28. Сам заголовок — переключатель «свернуть/раскрыть», то есть
   кликабельный элемент, и по §3 он обязан быть не мельче 44. Вторая причина
   прямая: кнопка «+» ровно 44×44 стоит В ЭТОЙ ЖЕ строке, и при полосе 28 она
   свисала по 8 px вверх и вниз на соседние строки.

   ⚠️ 49 = 44 + 5, И ЭТО ДВЕ РАЗНЫЕ ПОЛОСЫ, А НЕ «стало посвободнее».
   ВЕРХНИЕ 44 — всё, что нажимают: подпись группы и кнопка «+» (обе ровно 44 в
   высоту, §3). НИЖНИЕ 5 — полоса диапазона группы, и туда не заходит ни один
   ребёнок заголовка.
   Почему пришлось: кнопка «+» липнет на left:var(--tl-lw) и занимает 44×44 от
   самого верха, а полоса диапазона начинается на той же координате — первый день
   окна тоже стоит на x=LW. В покое фон кнопки прозрачный, и пересечение не видно;
   на наведении фон var(--accent-soft) с z-index:8 закрашивал первые 44 px полосы,
   на фокусе то же делала обводка. Красить одно поверх другого нельзя, и подвинуть
   начало полосы нельзя тоже — это дата, а не украшение: сдвинь её, и группа
   соврёт, когда началась. Развели по вертикали: коробки перестали пересекаться
   совсем, а не «пока кнопка не подсвечена».
   Цена честная: +5 px на КАЖДЫЙ заголовок группы. Шесть групп на 375 — 30 px
   против 432, которые забирал снесённый слот «＋ задача»; ради этих 432 слот и
   сносили, 30 их не возвращают. */
/* ⚠️ position:relative, А НЕ sticky, И ЭТО НЕ ОТКАЗ ОТ ЛИПКОСТИ. Заголовок
   чередуется со строками своей группы — вынуть его из тела нельзя, а тело
   (.tl2-scroll) прокручивается по горизонтали, то есть является чужим
   скроллпортом: родной sticky считал бы липкость ОТ НЕГО и заголовок уехал бы
   вместе со страницей. Сдвиг считает tlLipkRisovat() (board.vid.js) ровно по
   правилу sticky — от низа липкой оси и не дальше конца своей группы. */
.tl2-lane-h{position:relative;top:0;z-index:7;height:49px;background:var(--surface-2);
  box-shadow:inset 0 -1px 0 var(--line);cursor:pointer}
/* height:100% (все 49) — липкая подпись обязана быть непрозрачной НА ВСЮ высоту:
   при горизонтальной прокрутке полоса диапазона уезжает влево и прячется именно
   под ней. padding-bottom:5px держит текст в верхней полосе, вровень со значком
   кнопки, — фон при этом остаётся на всех 49. */
.tl2-lane-cap{position:sticky;left:0;z-index:2;width:var(--tl-lw);height:100%;display:flex;align-items:center;gap:7px;
  padding:0 10px 5px;background:var(--surface-2);box-shadow:1px 0 0 var(--line);
  font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2)}
.tl2-lane-cap .chev{flex:none;font-size:9px;transition:transform .18s var(--ease)}
.tl2-lane-h.collapsed .tl2-lane-cap .chev{transform:rotate(-90deg)}
.tl2-lane-cap .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ⚠️ СЧЁТЧИКА ЗАДАЧ В ЗАГОЛОВКЕ БОЛЬШЕ НЕТ. Владелец дословно: «число задач всё
   равно бесполезное». Его место в липкой колонке 128 px занял «плюс», а имя
   группы получило освободившиеся пиксели — «Общие работы» перестали обрубаться. */
/* ⚠️ ПОЛОСА ДИАПАЗОНА ГРУППЫ ЖИВЁТ В СВОЕЙ НИЖНЕЙ ПОЛОСЕ 5 px И БОЛЬШЕ НИГДЕ.
   Она начинается на xOf(gmin), а у групп, где есть задача левее окна, gmin зажат
   в край — то есть у самого x=LW, где стоит кнопка «+». Сначала полосу опустили
   к низу (bottom:5px) — этого хватило значку кнопки, но не её фону: кнопка 44×44
   красится от самого верха, и на наведении накрывала полосу целиком.
   Теперь высота заголовка 49 = 44 (всё нажимаемое) + 5 (эта полоса), и полоса
   стоит вплотную к низу. Ни один ребёнок заголовка в эти 5 px не заходит, поэтому
   полоса видна целиком в любом состоянии кнопки — в покое, на наведении, на
   фокусе — и на любой ширине. */
.tl2-lane-range{position:absolute;bottom:0;height:5px;border-radius:3px;
  background:var(--accent);opacity:.28;pointer-events:none}

/* ── строки задач ── */
/* ⚠️ 30 (--h-data), А НЕ 44. ОТМЕНА ПРАВКИ 31.08 — строку поднимали до 44 «ради
   §3», и это была ошибка чтения регламента: тач-норма 44 писана для ОРГАНОВ
   УПРАВЛЕНИЯ, а строка таймлайна — содержимое, сама задача. Цена ошибки
   померена: +14 px на строку, при одиннадцати видимых строках +154 px пустоты
   на том же содержимом. Владелец 01.09 дословно: «отступы между строками в
   таймлайне выросли, весь объём страницы стал очень большой, на боевой
   компактнее — компактность восприятия информации пропала».
   ⚠️ СТОРОЖ ВЁРСТКИ БУДЕТ КРАСНЕТЬ НА ЭТОЙ СТРОКЕ, И ЭТО ОЖИДАЕМО. Он считает
   любой [onclick] тач-целью и отличить данные от органа не умеет. Промах строки
   данных — названное исключение §3, а не стоп-кран §10 (стоп-кранов ровно три:
   наложения, обрезки, горизонтальная прокрутка). Поднимать строку обратно —
   брак. Открыть задачу можно и с 30: строка во всю ширину полотна. */
.tl2-row{position:relative;height:var(--h-data);cursor:pointer}
.tl2-row.alt{background:color-mix(in srgb,var(--ink) 4%,transparent)}
.tl2-row:hover{background:var(--accent-soft)}
.tl2-label{position:sticky;left:0;z-index:6;width:var(--tl-lw);height:100%;display:flex;align-items:center;
  padding:0 10px;font-size:12.5px;font-weight:500;overflow:visible;white-space:nowrap;
  background:var(--surface);box-shadow:1px 0 0 var(--line)}
/* ═══ НАЗВАНИЕ НЕ РЕЖЕТСЯ ПО КРАЮ КОЛОНКИ (03.09) ═══════════════════════════
   Владелец: «не хватает места под названия задачи… если сильно длинное — пусть
   заходит чутка на линию времени». Замер до правки, SERENITY, 119 строк:
   на 1440 колонка 180 px (13 % таймлайна), обрезано 66 строк, видно 0.86 знака
   из единицы; на 390 колонка 133 px, обрезано 115 строк, видно 0.65.
   Многоточия у названия больше нет ВОВСЕ: оно съедало ещё один знак и врало,
   будто дальше ничего нет. Вместо него — маска .tl2-nm: коробка названия растёт
   ЗА колонку, поверх пустой сетки, и кончается ровно за 6 px до левого края
   своей полосы (предел --tl-nmw считает rowFor, board.vid.js). Полосы и ромб
   поэтому не закрыты ни на пиксель — не слоем, а геометрией.
   ⚠️ --tl-sx — ПРОКРУТКА, И БЕЗ НЕЁ ХВОСТ ЛЁГ БЫ НА ПОЛОСУ. Колонка липкая,
   полосы едут: прокрутили вправо — полоса подъехала под хвост. Предел считается
   от нуля канвы, поэтому из него вычитается scrollLeft (кладёт _afterRender), и
   хвост втягивается ровно настолько, насколько уехала канва.
   Фон коробки — фон СВОЕЙ строки на 0.85: над колонкой это тот же цвет пиксель
   в пиксель (0.85·C+0.15·C=C), а над сеткой сквозь него читаются линии дней. */
.tl2-label .tl2-nm{display:inline-flex;align-items:center;flex:none;min-width:0;
  overflow:hidden;white-space:nowrap;padding:2px 0;border-radius:0 4px 4px 0;
  max-width:max(calc(var(--tl-lw) - 20px), calc(var(--tl-nmw,0px) - var(--tl-sx,0px)));
  background:color-mix(in srgb,var(--surface) 85%,transparent)}
/* дети маски не сжимаются: сжатый .nmx дал бы scrollWidth==clientWidth, и проба
   «сколько названия видно» (tests/ekran_boya.js) считала бы обрезку невидимой */
.tl2-label .tl2-nm>*{flex:none}
.tl2-label .nmx{min-width:0;white-space:nowrap}
.tl2-row.alt .tl2-label .tl2-nm{background:color-mix(in srgb,color-mix(in srgb,var(--ink) 4%,var(--surface)) 85%,transparent)}
.tl2-row:hover .tl2-label .tl2-nm{background:color-mix(in srgb,var(--accent-soft) 85%,transparent)}
/* строка-черновик режется по-прежнему: там в подписи поле ввода, а не название */
.tl2-row.draft .tl2-label{overflow:hidden}
.tl2-row.alt .tl2-label{background:color-mix(in srgb,var(--ink) 4%,var(--surface))}
.tl2-row:hover .tl2-label{background:var(--accent-soft)}
.tl2-bar{position:absolute;top:50%;transform:translateY(-50%);height:12px;border-radius:6px;
  background:var(--st-prog);opacity:.9;z-index:3;min-width:4px}
.tl2-bar.new{background:var(--st-new)}
.tl2-bar.done{background:var(--st-done)}
.tl2-bar.fire{background:var(--st-fire)}
.tl2-dot{position:absolute;top:50%;width:13px;height:13px;transform:translate(-50%,-50%) rotate(45deg);
  background:var(--st-prog);border-radius:3px;box-shadow:var(--sh-1);z-index:4}
.tl2-dot.new{background:var(--st-new)}
.tl2-dot.done{background:var(--st-done)}
.tl2-dot.fire{background:var(--st-fire)}
.tl2-dlabel{position:absolute;top:50%;transform:translateY(-50%);font-size:10px;font-weight:600;
  color:var(--ink-2);white-space:nowrap;pointer-events:none;z-index:3}
/* ⚠️ Просрочка стала ВИДНОЙ (решение владельца изменено: раньше её показывали только
   обводкой, «заливка = статус»). Теперь красным закрашивается ХВОСТ — отрезок от срока
   до сегодня (или до фактического завершения). Статусный цвет при этом остаётся на
   основной полосе: видно и что со статусом, и насколько просрочено. */
.tl2-late{position:absolute;top:50%;transform:translateY(-50%);height:12px;border-radius:0 6px 6px 0;
  background:repeating-linear-gradient(135deg,var(--st-fire) 0 5px,color-mix(in srgb,var(--st-fire) 62%,#fff) 5px 10px);
  opacity:.95;z-index:4;min-width:3px;pointer-events:none}
.tl2-dlabel.late{color:var(--st-fire);font-weight:700}
/* подпись даты у ПРАВОГО края окна разворачивается внутрь канвы (см. rowFor) */
.tl2-dlabel.flip{transform:translate(-100%,-50%)}
/* фактическое завершение: где работу реально приняли (флажок), в отличие от срока */
.tl2-fin{position:absolute;top:50%;width:2px;height:20px;transform:translate(-50%,-50%);
  background:var(--st-done);border-radius:1px;z-index:6;pointer-events:none}
.tl2-fin::after{content:"";position:absolute;left:2px;top:0;width:9px;height:7px;
  background:var(--st-done);border-radius:0 2px 2px 0}
.tl2-fin.late{background:var(--st-fire)}
.tl2-fin.late::after{background:var(--st-fire)}
/* строка целиком: просроченные читаются полосой слева, чтобы блок сверху был виден разом */
.tl2-row.overdue .tl2-label{box-shadow:inset 3px 0 0 var(--st-fire)}
/* ═══ КРАЕВОЙ ШЕВРОН: «ТАМ ЕСТЬ ЕЩЁ» ═══
   Окно шкалы считается по ЖИВЫМ задачам, поэтому закрытая работа с прошлогодним
   сроком или хвост просрочки могут уйти за край. Задачу с экрана при этом не
   убирают НИКОГДА (это было бы враньё): её полоса зажимается в край, а шеврон
   говорит, что настоящая дата дальше. Точная дата — в подписи строки и в title.
   Цвет — тот же --ink-2, что у подписи даты: новых цветов не заводим. */
.tl2-kray{position:absolute;top:50%;transform:translateY(-50%);z-index:5;
  display:flex;align-items:center;color:var(--ink-2);opacity:.75;pointer-events:none}
.tl2-kray svg{stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
/* правый шеврон стоит НА краю канвы и рисуется внутрь: без сдвига он вылезал бы
   за totalW и добавлял горизонтальную прокрутку из ничего (§10, стоп-кран) */
.tl2-kray.b{transform:translate(-100%,-50%)}
.tl2-nodate{padding:12px 16px 4px}
.tl2-nodate .nd-h{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2);margin-bottom:8px}
.tl2-nodate .nd-list{display:flex;flex-wrap:wrap;gap:7px}
.tl2-chip{font-size:12px;font-weight:500;padding:6px 11px;border-radius:9px;background:var(--surface-2);
  box-shadow:var(--sh-1);cursor:pointer;transition:transform .14s var(--ease),background .2s;border-left:3px solid var(--st-new)}
.tl2-chip:hover{transform:translateY(-1px);background:var(--accent-soft)}
.tl2-chip.fire{border-left-color:var(--st-fire)}
.tl2-chip.done{border-left-color:var(--st-done)}

/* список */
.ltable{background:var(--surface);border-radius:var(--r-card);box-shadow:var(--sh-2);overflow:hidden}
/* ⚠️ ОДНА ВЫСОТА СТРОКИ. Было 4 разных высоты на 1440 и 9 на 375 — строка
   росла от того, влезла ли в неё строка жизни. min-height ровняет их все.
   ⚠️ 44, А НЕ 56. Строка списка — ДАННЫЕ, а не орган: ровнять её надо по
   собственному содержимому, а не по запасу. На бою она 13+17+13 = 43 без всякого
   min-height; 56 добавляли по 13 px на строку ни за чем.
   Замер 01.09 на 1440 (43 живые строки): 40 строк ровно 45, три — 50, и эти три
   выше не от строки жизни, а от метки «Вернули на доработку» (.retmark 18.7 px
   внутри .lt). То есть разнобой, ради которого заводили min-height, снят: высота
   больше не зависит от того, влезла ли строка жизни. Прятать под общий потолок
   ЛИШНЮЮ МЕТКУ незачем — это содержимое, а не промах вёрстки, и стоит оно
   43×11 = 473 px пустоты на экране. */
.lrow{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4);min-height:44px;
  border-top:1px solid var(--line);cursor:pointer;transition:background .14s}
/* ═══ СТРОКА СТОЛБЦОВ СПИСКА ═══
   ⚠️ НЕ sticky, И ЭТО НЕ ЗАБЫВЧИВОСТЬ. .ltable живёт с overflow:hidden (ради
   скругления и ради открытой карточки .task-inline внутри), а overflow:hidden
   создаёт скроллпорт: sticky считался бы ОТ НЕГО, а он не прокручивается —
   липкости не вышло бы, вышла бы неподвижная строка, притворяющаяся липкой.
   На телефоне строки складываются в два ряда, столбцов там нет — и шапки нет.
   ВЫСОТА --h-data (30), А НЕ --h-row (44): подписи столбцов 11 px не нажимаются
   ничем — это разметка данных, полосой управления она не является. */
.lhead{display:flex;align-items:center;height:var(--h-data);padding:0 var(--s4);
  font-size:11px;font-weight:600;letter-spacing:.04em;color:var(--ink-3);
  background:var(--surface-2);box-shadow:inset 0 -1px 0 var(--line)}
@media(max-width:759px){.lhead{display:none}.lrow{min-height:72px}}
.lrow:first-child{border-top:none}
.lrow:hover{background:var(--surface-2)}
.lrow .ldot{width:8px;height:8px;border-radius:50%;flex:none}
.lrow .lt{flex:1;min-width:0;font-size:14px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* min-width:0 + многоточие — страховка от ЖЁСТКОГО РЕЗА на любой ширине: без неё
   у .lm min-width:auto, сжиматься нечему, и лишнее не пряталось многоточием, а
   уезжало за край под .ltable{overflow:hidden}. Пока мета влезает (1366 с живыми
   данными) — сжатия нет и вид не меняется ни на пиксель. */
.lrow .lm{font-size:12px;color:var(--ink-2);white-space:nowrap;font-weight:500;
  min-width:0;overflow:hidden;text-overflow:ellipsis}
/* ТЗ 5.5: строка жизни в конце строки списка — та же логика, что на карточке */
.lrow .tc-life{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:500;
  color:var(--ink-2);white-space:nowrap;flex:none}
.lrow .tc-life.stale{color:var(--alarm);font-weight:600}
/* ═══ НА ТЕЛЕФОНЕ СТРОКА СПИСКА СКЛАДЫВАЕТСЯ, А НЕ УЕЗЖАЕТ ЗА КРАЙ ═══
   Замер 375 до правки: «Слежу» (.mslezhu.on > .msl-list > .ltable) — коробка 343
   при содержимом 724, за краем 381 px; «Моя доска»/список (.mfwrap > .mfboardw >
   .ltable) — 343 при 481. Обёртка .mfboardw живёт с overflow-x:visible, так что
   правые колонки человеку недоступны вовсе. Причина одна на оба места: .lm,
   .partline и .tc-life — nowrap и flex:none, сжиматься нечему.
   ПОЧЕМУ СКЛАДЫВАНИЕ, А НЕ ГОРИЗОНТАЛЬНАЯ ПРОКРУТКА .ltable. В .ltable
   вкладывается ОТКРЫТАЯ КАРТОЧКА: .task-inline встаёт afterend строки
   (app-core.js), и прокручиваемая таблица утащила бы вбок всю карточку. Плюс
   .lt держит flex-basis 0 и схлопывается РАНЬШЕ, чем строка вылезет за край: на
   375 заголовок задачи оставался огрызком в 8 px — прокрутка это не лечит.
   Ничего не прячем: за край не уходит ни одна колонка, тач-цель строки растёт.
   Складывание — приём, уже принятый в проекте для такой же таблицы («Сделано за
   период», .rep-row) и для панели этапа (.stage-panel .lrow); порог 759 — тот
   же, что у .rep-row и .botnav. */
@media(max-width:759px){
  .lrow{flex-wrap:wrap;row-gap:3px}
  .lrow .lt{white-space:normal}
  .lrow .lm{flex:1 1 100%;white-space:normal;overflow:visible;padding-left:20px}
  .lrow .tc-life{flex:1 1 100%;padding-left:20px}
}

/* проблемы */
.issue{display:flex;align-items:flex-start;gap:12px;background:var(--surface);border-radius:14px;box-shadow:var(--sh-1);
  padding:14px 16px;margin-bottom:10px;cursor:pointer;transition:box-shadow .2s,transform .16s var(--ease)}
.issue:hover{box-shadow:var(--sh-2);transform:translateY(-1px)}
.issue .pchip{font-size:10px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:3px 8px;border-radius:6px;flex:none;margin-top:1px}
.pchip.high{background:var(--st-fire-bg);color:var(--st-fire)}
.pchip.medium{background:var(--st-new-bg);color:var(--ink-2)}
/* «ждёт решения» — решение лично за владельцем, прятать нельзя, поэтому акцентный,
   не приглушённый серый (пункт 3 доработки навигации) */
.pchip.waiting{background:var(--accent-soft);color:var(--accent);font-weight:700}
.issue.issue-waiting{box-shadow:0 0 0 1.5px var(--accent) inset, var(--sh-1)}
.issue .ib{flex:1;min-width:0}
.issue .ib b{font-size:14.5px;font-weight:600;display:block;margin-bottom:3px}
.issue .ib .im{font-size:12px;color:var(--ink-2);font-weight:500;display:flex;flex-wrap:wrap;gap:3px 10px}

/* ⚠️ ВКЛАДКИ СТОЯТ В СВОБОДНОЙ СЕРЕДИНЕ .screen-head, А НЕ СВОЕЙ СТРОКОЙ.
   width:100% + margin 16/6 отдавали им целую полосу в 66 px, у которой правая
   половина пустовала. На узком экране .screen-head переносит их вниз, и там
   ширина снова полная — три слова в ряд иначе не встают. */
.projtabs{margin:0;width:auto}
/* nowrap — иначе «Доска недели» ломается на две строки: у кнопок flex-basis:0,
   и без запрета переноса полоса садится на минимальную ширину слова, хотя места
   в шапке вдвое больше нужного (живой замер 31.08: 106 px на кнопку при 724
   свободных). Не влезет — .screen-head перенесёт всю середину вниз целиком. */
.projtabs button{flex:1;min-height:44px;font-size:13.5px;white-space:nowrap}
.projtabs .n{margin-left:6px;opacity:.72;font-weight:700}

/* ═════════════ ОБЪЕКТ: мини-таймлайн (шаг B импорта навигации v2) ═════════════ */
.obj-tl{background:var(--surface);border-radius:var(--r-card);box-shadow:var(--sh-2);padding:16px 18px;margin-bottom:18px}
.obj-tl-empty{display:flex;align-items:center;gap:10px;color:var(--ink-2);font-size:12.5px}
.obj-tl-empty svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;flex:none}
.obj-tl-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;
  font-size:11.5px;color:var(--ink-2);margin-bottom:12px}
.obj-tl-head b{color:var(--ink);font-weight:600}
.obj-tl.folded{padding:10px 18px}
.obj-tl-head.sub{margin-top:6px;font-size:11.5px;opacity:.8}
.otl-fold{display:inline-flex;align-items:center;gap:9px;background:none;padding:6px 8px 6px 4px;
  border-radius:9px;color:var(--ink-2);font-size:12.5px;min-height:40px;cursor:pointer}
.otl-fold:hover{background:var(--surface-2)}
.otl-fold .chev{display:inline-flex;transition:transform .2s var(--ease)}
.otl-fold .chev svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2.2}
.otl-cnt{font-weight:700;color:var(--ink)}
.otl-cnt i{font-style:normal}
.otl-cnt i.fire{color:var(--st-fire)}
.obj-tl-today{display:inline-flex;align-items:center;gap:5px}
.obj-tl-today svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;flex:none}
.obj-tl-today.out{color:var(--st-fire)}
/* гистограмма-счётчик удалена по решению владельца (полосы старт→срок вместо неё — .otl-*) */
.obj-tl-track{position:relative;height:8px;background:var(--surface-2);border-radius:99px;margin:2px 1px 0}
.obj-tl-mark{position:absolute;top:-5px;width:2px;height:18px;border-radius:2px;background:var(--accent)}
.obj-tl-mark.now{background:var(--st-fire)}
.obj-tl-mark.now i{position:absolute;top:-15px;left:50%;transform:translateX(-50%);font-style:normal;
  font-size:9.5px;font-weight:700;color:var(--st-fire);white-space:nowrap}
.obj-tl-scale{display:flex;justify-content:space-between;font-size:10.5px;color:var(--ink-2);margin-top:8px}

/* ═════════════ таймлайн: просрочка ТОЛЬКО обводкой — заливка занята статусом (правило владельца) ═════════════ */
.tl2-bar.od,.tl2-dot.od{box-shadow:0 0 0 2px var(--alarm-line)}
.tl2-today .lbl{font-size:10.5px;padding:2px 8px}

/* ═════════════ объект: план работ полосами старт→срок (вместо гистограммы-счётчика) ═════════════ */
.otl-chart{position:relative;--otl-lw:150px;margin-top:6px;padding-top:18px}
.otl-gridlayer{position:absolute;top:18px;bottom:22px;left:var(--otl-lw);right:0;pointer-events:none;z-index:0}
.otl-grid{position:absolute;top:0;bottom:0;width:1px;background:var(--line);opacity:.55}
.otl-today{position:absolute;top:0;bottom:0;width:2px;background:var(--accent);z-index:3}
.otl-today i{position:absolute;top:-15px;left:50%;transform:translateX(-50%);font-style:normal;font-size:9.5px;
  font-weight:700;color:var(--on-accent);background:var(--accent);padding:1px 7px;border-radius:999px;white-space:nowrap}
.otl-row{display:grid;grid-template-columns:var(--otl-lw) 1fr;align-items:center;height:26px;cursor:pointer;position:relative;z-index:1;border-radius:7px}
.otl-row:hover{background:var(--accent-soft)}
.otl-lbl{font-size:12px;padding-right:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
.otl-track{position:relative;height:100%}
.otl-bar{position:absolute;top:50%;transform:translateY(-50%);height:10px;border-radius:6px;opacity:.9}
.otl-end{position:absolute;top:50%;width:11px;height:11px;transform:translate(-50%,-50%) rotate(45deg);border-radius:3px}
.otl-bar.od,.otl-end.od{box-shadow:0 0 0 2px var(--alarm-line)}
.otl-scale{position:relative;height:20px;margin-left:var(--otl-lw);margin-top:6px}
.otl-tick{position:absolute;top:4px;transform:translateX(-50%);font-size:10px;color:var(--ink-2);white-space:nowrap}
.otl-tick.first{transform:none}
.otl-tick.last{transform:translateX(-100%)}
@media(max-width:560px){.otl-chart{--otl-lw:96px}.otl-lbl{font-size:11px}}


/* ─────────── ВОЗВРАТ НА ДОРАБОТКУ (владелец 10.08) ───────────
   «Пускай сбоку оранжевая пометка — сейчас она жёлтая вместе с надписью
   „вернули на доработку“. А если выбрал задачу, она обводится жёлтым».
   Значит: у возврата — только корешок слева терракотой, без обводки;
   жёлтая обводка отдана ВЫБРАННОЙ задаче (screens.css, tanchor-*).
   Двойной класс и место в конце файла — потому что ниже по каскаду шли
   .tcard.st-new/.st-in_progress/… и красили корешок в цвет статуса,
   затирая возврат (замер: был серый вместо терракоты). */
.tcard.returned.returned{border-left-color:var(--alarm);box-shadow:var(--sh-1)}


/* ═════════════ ВОЛНА P4 — «Разложить план» и контроль плана ═════════════ */
/* таймлайн: тонкая заливка подтверждённого прогресса и маркер просрочки.
   Каналы не смешиваются: заливка внутри полосы — прогресс (длина), маркер у
   срока — терракота срока; статусный цвет полосы не подменяется. */
.tl2-bar{overflow:hidden}
.tl2-podtv{position:absolute;left:0;bottom:0;height:4px;border-radius:0 3px 3px 0;
  background:var(--st-done);opacity:.95;pointer-events:none}
.tl2-od-mark{position:absolute;top:50%;width:7px;height:7px;border-radius:50%;
  transform:translate(-50%,-14px);background:var(--alarm);pointer-events:none}

/* ═══ КОНТРОЛЬ ПЛАНА: ОДИН ЧИП В ПОЛОСЕ, ПРОВЕРКИ — ЛИСТОМ ПОД НЕЙ ═══
   Было шесть чипов подряд (472 px на 1440, два ряда по 96 px на 375) прямо в
   полосе управления. Стало: чип «План · N» в слоте 4 полосы, а сами проверки
   раскрываются листом .fpanel под полосой — там им место и там они не давят на
   первую задачу. .board-strip больше нет: его работу делает .screen-head.upr.
   ⚠️ ЧИП РИСУЕТСЯ ВСЕГДА, пока ответа /api/plan/kontrol ещё нет — с прочерком.
   Иначе он приезжал бы через ~300 мс и менял высоту полосы под руками (§8). */
.plan-kontrol{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin:0}
.plan-kontrol .pk-l{font-size:12px;font-weight:600;letter-spacing:.03em;color:var(--ink-2)}
.plan-chip{display:inline-flex;align-items:center;gap:var(--s1);font-size:12px;font-weight:500;
  padding:0 var(--s3);min-height:var(--h-row);border-radius:var(--r-pill);border:0;
  white-space:nowrap;background:var(--surface-2);color:var(--ink-2)}
.plan-chip .pc-n{font-variant-numeric:tabular-nums;font-weight:600;color:var(--ink)}
.plan-chip .pc-i{display:inline-flex;flex:none}
.plan-chip .pc-i svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.plan-chip.zhdet{color:var(--ink-3)}
/* ⚠️ НА ТЕЛЕФОНЕ СЛОВО УХОДИТ, ЗНАЧОК И ЧИСЛО ОСТАЮТСЯ. Замер на 375: ряд 2
   отдаёт чипу 70 px, «План · 2» ровно 70, «Rencana · 2» — 87, и по-индонезийски
   ряд ломался на третью строку (первая карточка 421 → 477, брак §8 №2). Значок
   одинаков в обоих языках, чип выходит 55 и остаётся тач-целью (голое число
   давало 30.7 — мельче §3). Подпись жива в aria-label и title парой RU/ID (§5). */
@media(max-width:759px){.plan-chip .pc-w{display:none}}
.plan-chip.warn{background:var(--alarm-bg);color:var(--alarm)}
.plan-chip.soft{background:var(--st-prog-bg);color:var(--ink-2)}
.plan-chip.klik{cursor:pointer;min-height:var(--h-row);color:var(--ink)}
.plan-chip.klik:hover{background:var(--accent-soft)}

/* ── источник плана: «Вставить» / «Таблица» ──
   Два чипа вместо второй кнопки в полосе доски: работа одна — разложить план.
   Тач-цель 44 (§3), выбранный отмечен заливкой акцента, а не рамкой (§1). */
.plan-istrow{display:flex;gap:var(--s1);margin-bottom:12px;flex-wrap:wrap}
.plan-ist{min-height:44px;padding:0 16px;border:0;border-radius:var(--r-pill);
  background:var(--surface-2);color:var(--ink-2);font-size:13.5px;font-weight:600;
  cursor:pointer;white-space:nowrap}
.plan-ist:hover{background:var(--accent-soft)}
.plan-ist.est{background:var(--accent);color:var(--on-accent)}
.plan-ssylka{flex:1;min-width:200px;min-height:44px}
/* числа проверки: подпись сверху, число снизу — читается одним взглядом и в
   обоих языках одинаково широко (§5) */
.plan-chisla{display:flex;gap:var(--s2);flex-wrap:wrap;margin-bottom:12px}
.plan-plitka{background:var(--card);border-radius:var(--r2);padding:8px 14px;
  box-shadow:var(--sh-1);min-width:88px}
.plan-plitka .pp-l{font-size:11.5px;font-weight:600;color:var(--ink-2);
  letter-spacing:.02em;white-space:nowrap}
.plan-plitka .pp-n{font-size:20px;font-weight:700;color:var(--ink);line-height:1.2}
.plan-plitka.trevoga{box-shadow:var(--sh-1),inset 3px 0 0 var(--alarm-line)}
.plan-plitka.trevoga .pp-n{color:var(--alarm)}
/* блоки разбора: спорное, правки человека, сироты, мусор */
.plan-blok{margin-bottom:12px;display:flex;flex-direction:column;gap:8px}
.plan-blok .plan-zag{font-size:12px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-2)}
.plan-blok .plan-zag .n{font-variant-numeric:tabular-nums;color:var(--ink)}
.plan-vopros{flex:1;min-width:180px;font-size:13.5px;color:var(--ink)}
.plan-melko{font-size:12.5px;color:var(--ink-2);line-height:1.6;margin-bottom:6px}
.plan-pusto{font-size:13.5px;color:var(--ink-2);line-height:1.6}

/* лист ввода плана */
.plan-vvod{margin-bottom:14px}
.plan-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.plan-lbl{font-size:12px;font-weight:600;color:var(--ink-2)}
.plan-sel{min-height:44px;max-width:260px}
.plan-text{width:100%;min-height:200px;resize:vertical;border:1px solid var(--line);
  border-radius:var(--r2);background:var(--card);color:var(--ink);
  padding:12px;font-size:13.5px;line-height:1.5;tab-size:8;white-space:pre}
.plan-btnrow{display:flex;gap:10px;justify-content:flex-end;margin-top:10px;flex-wrap:wrap}

/* сопоставление колонок TSV */
.plan-map{margin-bottom:14px}
.plan-map-scroll{overflow-x:auto}
.plan-map-t{border-collapse:collapse;min-width:100%}
.plan-map-t th{padding:4px 6px}
.plan-map-t td{padding:6px 8px;font-size:12.5px;color:var(--ink-2);
  border-top:1px solid var(--line-2);white-space:nowrap;max-width:220px;
  overflow:hidden;text-overflow:ellipsis}

/* предпросмотр: дерево «этап → задачи → подзадачи» */
.plan-prev{margin-bottom:14px}
.plan-massrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.plan-massrow .plan-due{min-height:44px}
.plan-derevo{display:flex;flex-direction:column;gap:8px}
.plan-etap{font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2);margin-top:8px}
.plan-task{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  background:var(--card);border-radius:var(--r2);padding:8px 10px;box-shadow:var(--sh-1)}
.plan-task.dyra{box-shadow:var(--sh-1),inset 3px 0 0 var(--alarm-line)}
.plan-num{font-size:11.5px;font-weight:700;color:var(--ink-2);min-width:20px;text-align:right}
.plan-title{flex:1;min-width:150px;min-height:44px}
.plan-due{min-height:44px;max-width:170px}
.plan-x{display:inline-flex;align-items:center;justify-content:center;min-width:44px;
  min-height:44px;border:0;border-radius:var(--r1);background:transparent;
  color:var(--ink-2);cursor:pointer}
.plan-x:hover{background:var(--surface);color:var(--danger)}
/* сложность строки плана (карточка «Сложность задачи ставится по правилу из
   пяти ступеней»): узкая колонка со словом ступени + серый чип подсказки.
   Тач-цель 44 приходит от .plan-sel, ширину режем своим правилом, чтобы
   «этап целиком» не растягивал строку и не вводил горизонтальную прокрутку. */
.plan-slz{max-width:170px}
.plan-slz-sovet{color:var(--ink-3)}
.plan-slz-vsem{display:inline-flex;align-items:center;gap:6px;min-height:44px;cursor:pointer}
/* галочка мерится сторожем вёрстки САМА, а не вместе с подписью: 44 стоит на
   самом input, иначе он честно печатает «24×24, надо 44×44» (WCAG 2.2 2.5.8) */
.plan-slz-vsem input{width:44px;height:44px;flex:none;accent-color:var(--accent);cursor:pointer}
@media(max-width:560px){.plan-slz{max-width:100%}}
.plan-subs{flex-basis:100%;display:flex;flex-wrap:wrap;gap:6px;padding-left:28px}
.plan-sub{font-size:12px;color:var(--ink-2)}
@media(max-width:560px){.plan-sel{max-width:100%}.plan-due{max-width:100%}}

/* ═══════ ЗАВЕДЕНИЕ ЗАДАЧИ ПРЯМО В ТАЙМЛАЙНЕ ═══════ */
/* заголовок группы — flex-строка: подпись липнет к левому краю прокрутки,
   кнопка «+» липнет сразу за ней и при горизонтальной прокрутке не уезжает */
.tl2-lane-h{display:flex;align-items:center}
.tl2-lane-cap{flex:none}

/* ═══ «ПЛЮС» ВМЕСТО ЧИСЛА ЗАДАЧ ═══
   Владелец дословно: «пускай + не стоит по умолчанию доп строкой, пускай будет
   органично и красиво вместо числа задач — число задач всё равно бесполезное».
   Кнопка НАСТОЯЩИЕ 44×44 (не увеличенная псевдоэлементом хит-зона: сторож меряет
   сам элемент), рисуется ВСЕГДА, а не по наведению — на телефоне наведения не
   существует вовсе. Фон прозрачный, значок мелкий: в покое кнопка не шумит.
   Строки-слота под группой больше нет — он съедал 44 px под КАЖДОЙ группой и
   оставлял на 375 меньше двух строк задач под всё дело. */
/* ⚠️ align-self:flex-start — КНОПКА СТОИТ В ВЕРХНЕЙ ПОЛОСЕ 44 И НЕ СПУСКАЕТСЯ.
   Заголовок теперь 49 (44 нажимаемых + 5 под полосу диапазона). Оставь кнопку по
   центру — её коробка стала бы y∈[2.5,46.5] и снова заехала бы на полосу, а фон
   наведения закрасил бы её первые 44 px. Сама кнопка честные 44×44: тач-цель не
   тронута; вырос только заголовок под ней. */
.tl2-lane-add{position:sticky;left:var(--tl-lw);z-index:8;flex:none;align-self:flex-start;
  width:44px;height:44px;border:0;border-radius:var(--r2);
  background:none;color:var(--ink-2);cursor:pointer;display:inline-flex;
  align-items:center;justify-content:center;transition:background .16s var(--ease),color .16s var(--ease)}
.tl2-lane-add svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round}
.tl2-lane-add:hover{background:var(--accent-soft);color:var(--accent)}
.tl2-lane-add:focus-visible{outline:2px solid var(--accent);outline-offset:-6px}

/* ═══ ЗАВЕДЕНИЕ ЗАДАЧИ СТРОКОЙ ВНУТРИ ТАБЛИЦЫ ═══
   Здесь стояла коробка-черновик над прокруткой и кнопка «+» в заголовке группы.
   Владелец дословно: «пускай добавляется прям строка внутрь таблицы, прям
   абсолютно так же выглядящая как табличная строка». Коробка была формой, форму
   он и забраковал; кнопка по hover на телефоне не существует вовсе.

   ⚠️ ПРИЗНАК «ЕЩЁ НЕ СОХРАНЕНО» РОВНО ОДИН — заливка var(--accent-ghost), и она
   покрывает ВСЮ строку, включая липкую подпись и липкий слот исполнителя: иначе
   при горизонтальной прокрутке признак разорвался бы на куски. Ни рамок, ни
   пунктира, ни прозрачности, ни подписи. Хексов в блоке нет ни одного. */

/* ⚠️ СЛОТА «＋ ЗАДАЧА» ПОД ГРУППОЙ БОЛЬШЕ НЕТ. Он был отдельной строкой 44 px под
   КАЖДОЙ группой: шесть групп забирали 432 px из 490 доступных на 375×812, и под
   сами задачи оставалось 58 px — меньше двух строк. Заведение вернулось в
   заголовок кнопкой .tl2-lane-add, строка-черновик появляется по нажатию. */

/* строка-черновик: та же строка таблицы, только выше — внутри неё живут поле
   ввода и цели тяги, а псевдоэлементные хит-зоны в проекте запрещены (§3).
   Отличие в высоте исчезает в момент записи. */
/* --tln-pole — ЖИВАЯ ШИРИНА ПОДПИСИ ЧЕРНОВИКА. Одно число на подпись: и в
   спокойном виде, и с кареткой внутри. Подсказка про жест по нему больше НЕ
   стоит — она уехала к правой кромке (см. .tln-hint ниже). */
/* ⚠️ СТРОКА-ЧЕРНОВИК — СВОЙ КОНТЕКСТ НАЛОЖЕНИЯ, ОТМЕТКА 6. Своего слоя у строки
   не было (.tl2-row{position:relative} без z-index), поэтому её дети — подпись 8,
   подпись с кареткой 9, кружок исполнителя 8 — сравнивались НАПРЯМУЮ с липким
   заголовком группы (.tl2-lane-h z-index:7) и проезжали ПОВЕРХ него при
   вертикальной прокрутке внутри .tl2-scroll. Весь остальной таймлайн держит
   потолок 6 ровно затем, чтобы строки уезжали ПОД заголовок.
   Теперь строка — ОДИН слой на отметке 6, а её внутренняя лестница считается
   ВНУТРИ него и наружу не вылезает ничем. isolation:isolate — страховка: контекст
   останется, даже если кто-то снимет z-index. Лестница снизу вверх:
     1 дорожка · 2 полоса и ромб · 3 ручки и нить · 4 кнопка даты ·
     5 кружок исполнителя и подсказка · 6 подпись.
   Ни одно число внутри не превышает 6 — даже потеряв контекст, строка останется
   под заголовком группы (7) и под осью дат (10). */
.tl2-row.draft{--tln-pole:var(--tl-lw);height:44px;display:flex;align-items:center;cursor:default;
  position:relative;z-index:6;isolation:isolate;
  background:var(--accent-ghost);animation:pop .16s var(--ease)}
.tl2-row.draft:hover{background:var(--accent-ghost)}
/* ⚠️ ЛИПКИЕ СТОЛБЦЫ — ВЫШЕ РУЧЕК ЖЕСТА (см. .tln-grip ниже). Ручка 44×44 ездит
   по всей канве: увёл начало влево — ручка a ложилась на поле имени и вернуться
   в поле было нельзя (tlnTyanu первым делом делает blur), дотянул полосу вправо —
   ручка b накрывала кружок исполнителя и съедала нажатие. Порядок в DOM тут не
   помогает: у ручек своя система координат. */
.tl2-row.draft .tl2-label{flex:none;padding:0;background:var(--accent-ghost);z-index:6}
/* каретка в поле — подпись расширяется, как ячейка таблицы; строка при этом не
   двигается ни на пиксель: тот же верх, тот же левый край, тот же padding.
   ⚠️ 78vw ЗАБИРАЛО ВЕСЬ ЭКРАН: на 375 подпись становилась 292 px и накрывала
   дорожку целиком, а подсказку про жест правило ниже ещё и прятало — про «задай
   точку и тяни» человеку было НЕОТКУДА узнать. Теперь подпись отдаёт правую
   треть строки: 195 px под имя (25 знаков) и место подсказке.
   ⚠️ ТРЕТИЙ ПРЕДЕЛ — ЧТОБЫ ПОДПИСЬ НЕ ВСТРЕТИЛАСЬ С ПРАВОЙ ПАНЕЛЬЮ. Подпись липнет
   слева, подсказка с кружком — справа; разъехаться им негде, и на очень узком
   экране они бы наложились. Условие «не встретиться»: подпись ≤ ширина дорожки
   − кружок 44 − подсказка. Дорожка = 100vw − 32 (поля .wrap), под подсказку с
   запасом 84 → предел 100vw − 160. На 375 он равен 215 и не связывает ничего
   (52vw = 195), связывает только уже 333 px. */
.tl2-row.draft.pishu{--tln-pole:min(320px,52vw,calc(100vw - 160px))}
.tl2-row.draft.pishu .tl2-label{width:var(--tln-pole);max-width:calc(100% - 44px);z-index:6}
/* ⚠️ ОБЯЗАТЕЛЬНО: без этого полоса и ромб съедают pointerdown, предназначенный дорожке */
.tl2-row.draft .tl2-bar,.tl2-row.draft .tl2-dot{pointer-events:none;z-index:2}
.tln-name{width:100%;height:44px;padding:0 10px;border:0;outline:none;background:none;color:var(--ink);
  font-family:inherit;font-size:12.5px;font-weight:500}
.tln-name::placeholder{color:var(--ink-3)}
/* ⚠️ ПОДСКАЗКА ПРО ЖЕСТ — ПРАВАЯ ЛИПКАЯ ПАНЕЛЬ, А НЕ ТЕКСТ НАД ДОРОЖКОЙ.
   Стояла сразу за подписью, прозрачным текстом 10 px. Дата (.tln-date, координата
   канвы dueX) и ромб едут С ПРОКРУТКОЙ и вставали ровно под неё: при обычном
   открытии таймлайна (сегодня на 40 % ширины) обе надписи 10 px ложились друг на
   друга — ровно в том состоянии, в котором подсказку и читают. Развести их по
   горизонтали нечем: дата может оказаться в ЛЮБОЙ точке дорожки, значит любая
   точка дорожки рано или поздно окажется под подсказкой.
   Поэтому подсказка встала В ОДИН РЯД С КРУЖКОМ ИСПОЛНИТЕЛЯ — липкой панелью у
   правой кромки видимой области, с непрозрачным фоном и волоском слева. Тот же
   приём, что у липкой подписи слева: дата и ромб не пересекаются с подсказкой, а
   ПРОЕЗЖАЮТ ПОД НЕЙ, как полосы проезжают под подписью.
   Совсем убрать подсказку нельзя: это единственное место, где человек узнаёт про
   жест. Прячется она только вместе с ненадобностью — в тот миг, когда срок
   впервые тронут (tlnRisovat снимает узел по nv.tronut). */
.tln-hint{position:sticky;right:44px;z-index:5;flex:none;height:100%;
  display:flex;align-items:center;padding:0 8px;
  background:var(--accent-ghost);box-shadow:-1px 0 0 var(--line);
  font-size:10px;font-weight:600;color:var(--ink-3);white-space:nowrap;pointer-events:none}
/* пока подсказка на месте — волосок у панели один, её собственный */
.tln-hint + .tln-kto{box-shadow:none}
.tln-spacer{flex:1 1 auto;min-width:0}
/* исполнитель липнет к ПРАВОМУ КРАЮ ВИДИМОЙ ОБЛАСТИ: к дате он отношения не имеет
   и уезжать вместе с полосой не должен. Фон непрозрачный и тень слева — зеркало
   приёма липкой подписи, иначе полосы проезжали бы сквозь него. */
.tln-kto{position:sticky;right:0;z-index:5;flex:none;width:44px;height:44px;border:0;padding:0;cursor:pointer;
  display:grid;place-items:center;background:var(--accent-ghost);box-shadow:-1px 0 0 var(--line)}
.tln-kto svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
/* дорожка жеста. touch-action:pan-y — вертикаль (таймлайн и страница) остаётся
   браузеру, себе забираем только горизонталь; none отнял бы и вертикаль без
   единой выгоды. Цена честная: с этой полосы 44 px нельзя начать горизонтальный
   свайп — и только пока черновик открыт. */
.tln-track{position:absolute;left:var(--tl-lw);right:0;top:0;bottom:0;z-index:1;touch-action:pan-y}
.tln-guide{position:absolute;top:0;bottom:0;width:1px;z-index:3;pointer-events:none;
  background:var(--ink-2);opacity:.45}
/* ручки — НАСТОЯЩИЕ 44×44, прозрачные, центрированы на концах полосы.
   ⚠️ z-index НИЖЕ липких столбцов (.tl2-label 6 / .tln-kto 5) и ниже кнопки даты
   (4): ручка ездит по всей канве и заезжала на них, а tlnTyanu делает
   preventDefault и берёт капчер — нажатие по кружку исполнителя и по полю имени
   просто пропадало. Ручка при этом остаётся полной 44×44 везде, где под ней нет
   липкого столбца, то есть на всей длине дорожки. */
.tln-grip{position:absolute;top:50%;width:44px;height:44px;transform:translate(-50%,-50%);
  border:0;padding:0;background:none;cursor:ew-resize;z-index:3;touch-action:pan-y}
.tln-grip:focus-visible{outline:2px solid var(--accent);outline-offset:-8px;border-radius:var(--r2)}
/* ⚠️ ПОКА ДИАПАЗОНА НЕТ — РУЧКА ОДНА, И ЭТО СРОК. При рождении строки d0===d1, и
   обе ручки 44×44 стояли В ОДНОЙ ТОЧКЕ: b накрывала a целиком, нажатием бралась
   только b, до a добирались лишь с клавиатуры, а тач-цель на двоих оставалась
   одна и та же. Вторая ручка появляется ровно тогда, когда протяжкой создан
   диапазон: класс diapazon ставит и разметка (draftRowFor), и живая перерисовка
   (tlnRisovat) — во время тяги render() не зовут.
   display:none, а не прозрачность: сторож считает только нарисованные коробки, и
   спрятанная кнопка не должна ловить ни палец, ни Tab. */
.tl2-row.draft:not(.diapazon) .tln-grip.a{display:none}
/* дата — та же подпись, что у сохранённых строк (.tl2-dlabel), и она же кнопка
   календаря. Второго элемента с датой на строке нет: это была бы вторая правда об
   одном и том же. min-width держит тач-цель.
   ⚠️ СТОИТ НА dueX+22, А НЕ НА dueX+5. Ручка срока — 44×44 с центром в dueX, то
   есть занимает dueX−22…dueX+22; кнопка даты с dueX+5 съедала у неё правые 17 px
   и цель под палец падала до 27. Теперь кнопка начинается ровно там, где ручка
   кончается: 44 ручке и 44 кнопке, впритык и без нахлёста. Двигали кнопку, а не
   ручку — ручка обязана стоять на самом сроке. */
.tln-date{position:absolute;top:50%;transform:translateY(-50%);height:44px;min-width:44px;padding:0 4px;z-index:4;
  border:0;background:none;cursor:pointer;display:flex;align-items:center;white-space:nowrap;
  font-family:inherit;font-size:10px;font-weight:600;color:var(--ink-2)}
.tl2-row.draft.tyanu .tln-date{color:var(--ink)}
/* ═══ ПОПАПЫ ЧЕРНОВИКА — ВНЕ #tl2Scroll ═══
   Внутри липкой подписи панель получила бы ширину 108 px и была бы срезана
   overflow:hidden; внутри скроллера — обрезана по обеим осям и уехала бы вместе с
   содержимым. Календарь на 35 клеток там не живёт. */
.tln-pop{position:fixed;z-index:var(--z-pmenu);width:min(320px,92vw);display:none}
.tln-pop.on{display:block}
.tln-pop>details>summary{display:none}
/* ⚠️ «БЕЗ СРОКА» У ЧЕРНОВИКА ДЕЛАЛА ОБРАТНОЕ ПОДПИСИ: строка СТОИТ на шкале, без
   даты ей негде стоять — кнопка возвращала срок на сегодня и извинялась тостом.
   Кнопки, которая не может сработать, не бывает (§7): в этом контексте её просто
   нет. kalSetkaHtml общий, поэтому убираем стилем — у мини-формы «＋ Задача»
   срок пустым быть может, там кнопка честная и остаётся. */
.tln-pop .kal-pod .vchip.soft{display:none}
/* рождение настоящей строки: заливка черновика уходит за полсекунды — видно, ЧТО
   именно родилось среди чужих строк */
@keyframes tlBorn{from{background:var(--accent-ghost)}to{background:transparent}}
.tl2-row.rodilas{animation:tlBorn .5s var(--ease)}
/* оба ВХОДА снимаются, тяга — нет: это прямое управление, а не украшение
   (та же формулировка, что у спиннера в base.css) */
@media(prefers-reduced-motion:reduce){.tl2-row.draft,.tl2-row.rodilas{animation:none}}

/* ═══ § СРОК: ПЕРВОЕ ОБЕЩАНИЕ ═══════════════════════════════════════════════
   «переносов: 2» рядом со сроком на карточке доски — и только у той карточки,
   которой срок двигали. Подпись, а не значок: смысл не передаётся одним цветом
   (WCAG 2.2 SC 1.4.1), а терракота на доске уже занята просрочкой. Своей
   заливки и своего цвета нет — вторичный --ink-2, как у прочих подписей
   карточки; строка данных, не орган — не кликается. */
.tcard .tc-per{font-size:11px;font-weight:600;color:var(--ink-2);white-space:nowrap;margin-left:6px}

/* ═════════════ § ПЛАН ИЗ ТАБЛИЦЫ — предпросмотр строк и сводка «куда легло» ═════════════
   Карточка «Перенести план из таблицы владельца…», 03.09.2026. Владелец:
   «нужно удобно это внести, пускай вручную, но нужно удобно, чтобы парни не
   страдали».

   ⚠️ ШИРОКАЯ ТАБЛИЦА КАТИТСЯ ВНУТРИ СВОЕЙ КОРОБКИ, А НЕ ТЕЛОМ СТРАНИЦЫ.
   Восемь колонок на 390px не помещаются никак; горизонтальная прокрутка body —
   отказ сторожа вёрстки (§10 стиля), поэтому overflow-x висит на .plan-wp-scroll,
   а min-width строки задан внутри неё. Так же сделаны .plan-map-scroll выше.

   ⚠️ ОРГАНЫ 44px. Поля правятся ПАЛЬЦЕМ на телефоне (WCAG 2.2 SC 2.5.8), и
   строка данных остаётся плотной только за счёт того, что сама строка выше
   поля не становится: высоту держит поле.

   ⚠️ СНЯТАЯ СТРОКА ГАСНЕТ, НО НЕ ПРОПАДАЕТ. Исчезнувшая строка читается как
   «удалил», и человек не находит, что он снял; гашение обратимо той же
   галочкой. Цветом смысл не передаётся: рядом стоит снятая галочка. */
.plan-podist{margin:0 0 10px}
.plan-wp{margin-top:10px}
.plan-wp-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--line-2);border-radius:10px;background:var(--surface)}
.plan-wp-r{display:flex;align-items:center;gap:6px;min-width:900px;padding:4px 8px;
  border-bottom:1px solid var(--line-2)}
.plan-wp-r:last-child{border-bottom:0}
.plan-wp-h{position:sticky;top:0;z-index:2;background:var(--surface-2);
  font-size:11px;font-weight:600;color:var(--ink-2);padding:8px}
.plan-wp-r.dyra{background:color-mix(in srgb,var(--alarm-line) 10%,transparent)}
.plan-wp-r.snyata{opacity:.45}
.plan-wp-r>span{flex:0 0 auto;min-width:0}
.plan-wp-r .wc-v{flex:0 0 44px;display:flex;justify-content:center}
.plan-wp-r .wc-a{flex:0 0 150px;display:flex;flex-direction:column;font-size:11px;
  color:var(--ink-2);line-height:1.25;overflow:hidden}
.plan-wp-r .wc-a i{font-style:normal;font-size:10.5px;opacity:.8;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plan-wp-r .wc-t{flex:1 1 240px;min-width:200px}
.plan-wp-r .wc-e{flex:0 0 120px}
.plan-wp-r .wc-d{flex:0 0 140px}
.plan-wp-r .wc-k{flex:0 0 150px}
.plan-wp-r .wc-s{flex:0 0 120px;font-size:11px;color:var(--ink-2);
  display:flex;flex-direction:column;line-height:1.25}
/* ⚠️ 44px МЕРЯЕТСЯ У САМОГО ОРГАНА, А НЕ У ЕГО ОБЁРТКИ. Сторож вёрстки берёт
   прямоугольник элемента, по которому попадают пальцем, — то есть input, — и
   44-пиксельная <label> вокруг двадцатипиксельной галочки давала ему пятнадцать
   мелких целей на пятнадцати строках. Растим САМ input до 44×44, а видимый
   квадрат рисуем на 20px псевдоэлементом: палец получает норму WCAG 2.2
   SC 2.5.8, глаз — прежнюю плотную строку. */
.plan-wp-gal{display:flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;cursor:pointer}
.plan-wp-gal input{appearance:none;-webkit-appearance:none;position:relative;
  width:44px;height:44px;margin:0;box-sizing:border-box;background:transparent;
  border:0;cursor:pointer;flex:0 0 44px}
.plan-wp-gal input::before{content:"";position:absolute;left:12px;top:12px;
  width:20px;height:20px;box-sizing:border-box;border:2px solid var(--line);
  border-radius:5px;background:var(--surface)}
.plan-wp-gal input:checked::before{background:var(--accent);border-color:var(--accent)}
.plan-wp-gal input:checked::after{content:"";position:absolute;left:19.5px;top:15px;
  width:5px;height:11px;border:solid var(--on-accent);border-width:0 2px 2px 0;
  transform:rotate(45deg)}
.plan-wp-gal input:focus-visible{outline:2px solid var(--accent);outline-offset:-8px}
.plan-wp-gal input:disabled{cursor:default;opacity:.5}
.plan-wp-in{width:100%;min-height:44px;font-size:13px;padding:6px 8px}
.plan-wp-in.pusto{border-color:var(--alarm-line)}
.plan-wp-in.spor{border-color:var(--alarm-line);
  background:color-mix(in srgb,var(--alarm-line) 8%,var(--surface))}
/* ⚠️ ТЕКСТ — --alarm, А НЕ --alarm-line. Второй заведён для ОБВОДКИ (base.css:68)
   и на тексте даёт 3.4 при норме AA 4.5 — сторож вёрстки поймал семь строк
   «не приедет» подряд. Смысл цветом не передаётся: рядом стоит слово. */
.plan-wp-prich{font-style:normal;font-size:10.5px;color:var(--alarm);font-weight:600}
.plan-kuda{margin-top:10px}

/* Полосы прогона на таймлайне: обводка акцентом, без своей заливки — заливка
   занята статусом (правило владельца, см. блок «просрочка ТОЛЬКО обводкой»).
   Метка временная, её снимает App.planTabNaTaymline через 10 секунд. */
.tl2-bar.novaya,.tl2-dot.novaya{box-shadow:0 0 0 2px var(--accent)}

/* ═══ § ПОЛОСА ТАЙМЛАЙНА ПРАВИТСЯ НАЖАТИЕМ ═════════════════════════════════
   Карточка «Дата начала видна на карточке, ставится в плане и на таймлайне».

   ⚠️ ЗОНА НАЖАТИЯ — СВОЙ УЗЕЛ, А НЕ ПСЕВДОЭЛЕМЕНТ ПОЛОСЫ. Первая правка растила
   область через .tl2-bar::after — и её срезал `.tl2-bar{overflow:hidden}` выше
   (он нужен заливке .tl2-podtv, чтобы та резалась по скруглению полосы).
   Настоящая зона оставалась 12 px у полосы и 21 у ромба, сторож писал
   «476×12 при норме 44». Теперь .tl2-hit — ребёнок .tl2-row, у строки overflow
   нет, резать нечем. Высота во всю строку, ширина не меньше 24: WCAG 2.2
   SC 2.5.8 «Target Size (Minimum)» = 24×24 — этот порог взят.
   44 по §3 стиля здесь недостижимо: строка таймлайна ростом --h-data = 30 px,
   и зона в 44 вылезла бы за строку и воровала бы нажатия у соседней задачи —
   это хуже мелкой цели. 44 достигается только другой высотой строки, а это
   отдельное решение и не эта карточка; в «Сдаче» число названо прямо.
   Ни фона, ни рамки: сторож считает наложения по ПОКРАШЕННОМУ, и прозрачная
   зона чужих замеров не портит. */
.tl2-bar,.tl2-dot,.tl2-hit{cursor:pointer}
.tl2-hit{position:absolute;top:0;bottom:0;background:none;border:0;padding:0;z-index:4}
.tl2-hit:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.tl2-row.draft .tl2-hit{display:none}

/* Попап правки полосы: сосед попапа черновика, живёт вне прокрутки (иначе его
   срезало бы overflow) и позиционируется замером — App.tlBarPop. */
.tl-barpop{position:fixed;z-index:60}
.tl-barpop[hidden]{display:none}
.tl-barpop .tl-barpop-in{background:var(--surface);border-radius:12px;
  box-shadow:0 10px 30px rgba(0,0,0,.18);padding:12px;
  width:min(280px,92vw);display:flex;flex-direction:column;gap:6px}
.tl-barpop .tl-barpop-t{font-size:12.5px;font-weight:700;color:var(--ink);
  line-height:1.3;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;margin-bottom:2px}
.tl-barpop .tl-barpop-l{font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ink-2);font-weight:700}
.tl-barpop input[type=date]{width:100%;box-sizing:border-box;height:44px;
  min-height:44px;font-size:13px}
.tl-barpop .tl-barpop-b{display:flex;gap:8px;margin-top:4px}
.tl-barpop .tl-barpop-b>*{flex:1 1 0;min-width:0}
/* Пометка «приедет, но без исполнителя» (стоп 3 судьи): работа не отменена, и
   цвет у неё не тревожный, а вторичный — тревога занята словом «не приедет». */
.plan-wp-pom{font-style:normal;font-size:10.5px;color:var(--ink-2);font-weight:600}
/* Строка ждёт ответа на «переименовали?» (третий круг возврата): она не «создам»
   и не «не приедет» — она приедет, как только ответят. Своя мягкая заливка и
   слово рядом; цветом смысл не передаётся (WCAG 2.2 SC 1.4.1). */
.plan-wp-r.zhdet{background:color-mix(in srgb,var(--accent) 7%,transparent)}
.plan-wp-vopros{font-style:normal;font-size:10.5px;color:var(--accent);font-weight:600}

/* ⚠️ ДЛИННЫЙ ЧИП ОБЯЗАН ПЕРЕНОСИТЬСЯ (третий круг возврата, поймано сторожем).
   `.plan-chip` — inline-flex в одну строку: пока в нём стояли «Не приедет 3» и
   «Создам 8», это не мешало. Строка «Ждут ответа 1 — ответьте „это она?“…»
   вытянула его на 592 px при экране 390, и горизонтально поехало ВСЁ ТЕЛО
   страницы (сторож: hscroll true, документ 627 px). Прятать текст в подсказку
   нельзя — на телефоне её не видит никто, этим уже обжигались на кнопке
   «Залить». Значит переносим по словам: смысл остаётся на экране целиком, а
   ширину держит родитель. Правило только для длинных чипов плана — общий
   `.plan-chip` не трогаю, его короткие числа выравнены в строку нарочно. */
.plan-melko .plan-chip{white-space:normal;max-width:100%;text-align:left}
