/* ═══════════════════════════════════════════════════════════════════
   ЛИСТ — визуальный язык STRU_magic. Мир «Рабочий чертёж».

   ПРАВИЛА ПРИМЕНЕНИЯ — DESIGN.md в корне. Здесь только значения
   и правила их применения; довод, почему величина такая, живёт там.

   СКОУП — КЛАСС `.sh` НА <body>, А НЕ АТРИБУТ. Замер: имена классов
   двух языков пересекаются в 28 местах (.tl-bar, .pop, .seg, .tag, .col,
   .val, .nm, .n…). Атрибут на <html> не спасает: он истинен на всей
   странице, и правило `.pop` нового языка переверстало бы `.pop`
   старого на непереведённом экране. Класс на корне разметки — тот же
   приём, которым скоупирован унаследованный язык (`.tn`), и он держит
   потому, что старая разметка `.sh` не несёт по построению.

   `data-design` на <html> остаётся, но это СОСТОЯНИЕ выбора, а не
   скоуп: по нему решается, какой шаблон рендерить и грузить ли этот
   файл. Тема (`data-theme`) — независимая величина.

   ПОДКЛЮЧАЕТСЯ ПОСЛЕДНИМ. При равной специфичности с `.tn`-правилами
   унаследованного языка решает порядок подключения.

   ЛЕСТНИЦА ОТСТУПОВ СОВПАДАЕТ С УНАСЛЕДОВАННОЙ ДО ЧИСЛА (4/8/12/16/
   20/24/32), и это совпадение, а не заимствование: атом 4 при шаге 8
   выводится из плотности, а не из чужого файла. Языки независимы —
   прежний уходит, и связывать их значением нельзя.
   ═══════════════════════════════════════════════════════════════════ */

/* ═══ МИР «ЧЕРТЁЖ», РЕДАКЦИЯ 3 ═══════════════════════════════════
   СВЕТЛАЯ — карандашный чертёж: строго ахроматична. Ни капли тона
   в поверхностях; цвет появляется ТОЛЬКО там, где он что-то значит
   (четыре состояния, метка положения). Так лист и правда читается
   в чёрно-белой копии, а не только называется так.
   ТЁМНАЯ — синька: светлее прежней и честно синяя, потому что
   цианотипия и есть синяя. Две темы — два способа напечатать
   один чертёж, а не палитра и её перекрас.
   ЧЁРНЫЙ БЛОК ОДИН НА ЭКРАН. Их было три в одном углу — плашка
   листа, текущий пункт и активный сегмент, — и они читались
   случайным узором. Инверсию заслужил текущий пункт: он отвечает
   «где я». Плашка листа стала текстом, сегмент — тихим. */
.sh{
  /* ШИРИНА УГЛА И ШИРИНА СПИСКА — ОДНА ВЕЛИЧИНА, А НЕ ДВЕ РАВНЫЕ.
     Разойдясь на пиксель, они дают тот самый «перекрёсток» из линий,
     где правый край плашки не совпадает с границей панели. */
  --rail-w:208px;
  --hh:48px;          /* высота шапки таблицы и полотна плана */
  /* ВЫСОТА ВЕРХНЕЙ ГРАНИЦЫ ОКНА. Читают ОБА: контрастный угол и штамп.
     Они стоят в разных ячейках грида и сами по себе не выровняются —
     край выходит рваным, и это видно ровно там, где его рисуют.
     69 = поле 9 сверху, самая высокая клетка 51 (подпись 12, имя 24,
     почта 13), поле 9 снизу. Клетка «Исполнитель» трёхстрочная
     по решению владельца, и величина считается по ней.

     ПОЛЕ ОДНО И ТО ЖЕ С ОБЕИХ СТОРОН, и до этого оно таким не было:
     сверху 9, снизу 2. Считал я его по верху и по высоте клетки,
     а низ получался остатком — то есть не назначался вовсе. Владелец
     увидел это как «отступ мелковат» на FAQ, где клетка исполнителя
     стоит крайней и прижата к нижней линии. Одна величина на две
     стороны — правило языка, и оно относится к самому языку тоже. */
  --stamp-h:69px;
  --ground:#EBEBEB;  --field:#F4F4F4;  --sheet:#FFFFFF;
  --hair:#E7E7E7;    --line:#D6D6D6;   --heavy:#141414;  --ink:#141414;
  --ann:#5C5C5C;     --ann2:#6E6E6E;
  --ok:#146B45;  --old:#7E5400;  --none:#6A6A6A;  --bad:#A82316;  --mark:#1A4FA0;
  /* ПОДЛОЖКИ ЧЕТЫРЁХ СОСТОЯНИЙ. Их четыре, потому что состояний четыре:
     набор из двух — не набор, а два случая. */
  --ok-bg:#E9F2ED;  --old-bg:#F6F1E3;  --none-bg:#EFEFEF;  --bad-bg:#FBECEA;
  --col-w:272px;    /* ширина колонки доски; продукт вправе переопределить */
  --bar:#3D3D3D;  --bar-done:#B4B4B4;  --bar-sect:#1E1E1E;  --wknd:#F4F4F4;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px; --s8:32px;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,monospace;
  --tab:tabular-nums lining-nums;

  /* --- РАДИУСЫ. Скругление означает «этим управляют»; данные и полотно
     не скругляются. Четыре ступени, и все малые. */
  --r-hair:2px;   /* пункт списка, активный сегмент */
  --r-ctl:3px;    /* контролы, узлы, всплывающее */
  --r-zone:4px;   /* зона группировки */
  --r-pill:999px; /* счётчик-пилюля: форма, а не ступень скругления */

  /* --- ТЕНИ. Тень означает всплытие. Исключение одно и названо: у
     активного сегмента она показывает выбор и потому в восемь раз слабее. */
  --sh-pop:0 6px 20px rgba(10,20,32,.16);
  --sh-seg:0 1px 2px rgba(0,0,0,.10);
  --sh-drawer:0 0 40px rgba(6,20,34,.45);
  --scrim:rgba(10,20,32,.45);  /* затемнение под накладной папкой */
  /* Заливка наведения НА ИНВЕРСИИ. На тёмном угле подложка обязана быть
     светлее его, на светлом (тёмная тема инвертирует угол) — темнее.
     Одно имя, два значения — как и у всей палитры. */
  --wash:rgba(255,255,255,.12);

  /* --- ПОЛОТНО ПЛАНА --- */
  --rh:28px;      /* строка полотна и реестра */
  --cw:24px;      /* столбец дня */
  --link-w:1px;   /* вес связи: тоньше знака раздела */
  --link-head:5px;
  --link-dim:.7;  /* пол приглушения: .65 даёт 2.65 при пороге 3.0 */
  --ms-gap:4px;   /* торец связи не касается ромба */
  /* ВЕЛИЧИНЫ ПЕРИОДА ЗАДАЁТ ПРОДУКТ, А НЕ ЯЗЫК. Они считаются из дат
     и меняются на каждом проекте; умолчания здесь — чтобы витрина
     собиралась, а не чтобы кто-то на них положился. */
  --tl-days:60;      /* столбцов в периоде */
  --tl-wknd-a:72px;  /* левый край субботы в первой неделе */
  --tl-wknd-b:120px; /* правый край воскресенья */
  --tl-today:0px;    /* координата осевой линии «сегодня» */

  /* ── МОСТ ТОКЕНОВ (UI-79) ─────────────────────────────────────────
     Продукт держит свой слой имён поверх прежнего языка — `--md-*`
     и `--brand` в `app.css`. На них опираются страничные `<style>`
     девяти служебных экранов и несколько правил без скоупа
     (`.hint`, `.btn-provider`, `.muted`). Пока эти имена показывают
     на материаловскую палитру, служебный лист под новым оформлением
     говорит на ТРЕТЬЕМ языке — ни на прежнем, ни на новом.

     Переписывать девять страничных блоков поимённо значило бы делать
     работу, которая выбросится вместе с прежним оформлением. Мост
     дешевле и честнее: НАШИ имена следуют за НАШИМ языком, и вся
     страничная вёрстка переезжает разом.

     Мост накрывает только собственный слой продукта. Шкалы прежнего
     языка (`--n0…--n9`, `--st-*`) не трогаем: они его, и подменять
     чужие величины под собой — это уже не мост, а подлог. */
  --brand:var(--ink);                 --brand-ink:var(--sheet);
  --md-primary:var(--ink);            --md-on-primary:var(--sheet);
  --md-primary-soft:var(--field);     --md-primary-soft-h:var(--hair);
  --md-surface:var(--sheet);          --md-on-surface:var(--ink);
  --md-outline:var(--ann2);           --md-outline-variant:var(--hair);
  --bg:var(--ground);
  --md-ok:var(--ok);                  --md-ok-soft:var(--ok-bg);
  --md-warn:var(--old);               --md-warn-soft:var(--old-bg);
  --md-error:var(--bad);              --md-error-soft:var(--bad-bg);

  color-scheme:light;
}
[data-theme="dark"] .sh{
  color-scheme:dark;
  --wash:rgba(0,0,0,.12);
  --sh-pop:0 8px 28px rgba(0,0,0,.6);
  --sh-seg:0 1px 2px rgba(0,0,0,.4);

  --ground:#0E2338;  --field:#173352;  --sheet:#1C3D61;
  --hair:#2A5178;    --line:#3C6B98;   --heavy:#E6F0FA;  --ink:#E9F2FB;
  --ann:#AEC8DF;     --ann2:#95B5D2;
  --ok:#63CE99;  --old:#E6BA6A;  --none:#8FAFC9;  --bad:#FF9382;  --mark:#FFFFFF;
  --ok-bg:#122C21;  --old-bg:#2C2415;  --none-bg:#173352;  --bad-bg:#3A1B17;
  --bar:#9FC4E4;  --bar-done:#4A749C;  --bar-sect:#D3E4F4;  --wknd:#15304C;
  color-scheme:dark;
}
.sh *{box-sizing:border-box}
/* Высота корня — условие каркаса; на <html> её ставит продукт,
   язык за пределы своей разметки не выходит. */
body.sh{margin:0;height:100%;overflow:hidden}
body.sh{background:var(--ground);color:var(--ink);display:flex;flex-direction:column;
  font:14px/1.4 var(--sans);-webkit-font-smoothing:antialiased}
.sh .num, .sh .val, .sh .d, .sh dd{font-variant-numeric:var(--tab)}
.sh :focus-visible{outline:2px solid var(--mark);outline-offset:1px}
/* ТА ЖЕ РАМКА, НО СЕЛЕКТОРОМ ТОЙ ЖЕ ТЯЖЕСТИ — иначе её не видно никогда.
   Строка выше весит 0-2-0, а в языке TN лежит
   `.tn input:focus-visible, … .tn button:focus-visible, …` — 0-2-1 из-за
   имени тега. Оба класса стоят на одном `<body>`, и на КАЖДОЙ нажатой
   кнопке «Листа» рисовалась синяя рамка TN (`--accent` #2f5bd0 в светлой,
   #7d9dff в тёмной), а не своя `--mark`.
   Нашлось аудитом состояний 06.09.2026 на карточке интеграции (`DATA-3`) —
   первом замеряемом состоянии, где что-то вообще получает фокус. То есть
   дефект был во всём языке с самого перехода, а виден стал там, где
   появилось состояние с фокусом: чего не мерили, того и не было.
   Повторяем форму селектора один в один; `sheet.css` грузится после
   `design.css`, поэтому при равной тяжести побеждает своя рамка. */
.sh input:focus-visible, .sh select:focus-visible, .sh textarea:focus-visible,
.sh button:focus-visible, .sh a:focus-visible,
.sh .btn-primary:focus-visible, .sh .btn-tinted:focus-visible,
.sh .btn-quiet:focus-visible{outline:2px solid var(--mark);outline-offset:1px}

/* ── ШТАМП. Плашка листа — текст, а не блок. Клетки не сжимаются
      ниже своего содержимого; имя листа обрезается многоточием. ── */
.sh .stamp{flex:none;display:flex;align-items:stretch;gap:var(--s5);padding:0 var(--s4) 0 0;
  background:var(--sheet);border-bottom:1px solid var(--line)}
/* КОНТРАСТНЫЙ УГОЛ. Правый край совпадает с границей панели по
   построению: обе стороны читают --rail-w. Кнопка свёртки живёт
   здесь же — сворачивает то, над чем стоит. */
.sh .corner{flex:none;width:var(--rail-w);background:var(--heavy);color:var(--sheet);
  display:flex;align-items:center;gap:var(--s1);padding:7px var(--s3) 7px var(--s1)}
.sh .corner .id{min-width:0;display:flex;flex-direction:column;gap:1px}
.sh .collapse{flex:none;width:32px;height:32px;display:inline-flex;align-items:center;
  justify-content:center;background:none;border:none;cursor:pointer;border-radius:var(--r-hair);
  color:var(--sheet);opacity:.72}
.sh .collapse:hover{opacity:1;background:var(--wash)}
.sh .collapse svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;transition:transform 160ms}
.sh body:has(.collapse[aria-expanded="false"]) .collapse svg{transform:scaleX(-1)}
/* Свёрнутый список — не пустая полоса, а указатель по номерам листа. */
.sh body:has(.collapse[aria-expanded="false"]){--rail-w:52px}
.sh body:has(.collapse[aria-expanded="false"]) .corner .id{display:none}
.sh body:has(.collapse[aria-expanded="false"]) .rail{padding-left:0;padding-right:0}
.sh body:has(.collapse[aria-expanded="false"]) .rail h4{display:none}
.sh body:has(.collapse[aria-expanded="false"]) .idx{justify-content:center;padding:0}
.sh body:has(.collapse[aria-expanded="false"]) .idx .lbl{display:none}
.sh .stamp .cell{flex:none;display:flex;flex-direction:column;gap:1px;white-space:nowrap}
.sh .stamp .k{font-size:10px;letter-spacing:.05em;color:var(--ann2);text-transform:uppercase}
.sh .stamp .v{font-size:14px;font-weight:500;letter-spacing:-.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sh .corner .k{color:var(--sheet);opacity:.6}
.sh .corner .v{font-size:14px;font-weight:600}
.sh .stamp .grow{flex:1;min-width:var(--s2)}

.sh .wrap{display:grid;grid-template-columns:var(--rail-w) minmax(0,1fr);flex:1;min-height:0}

/* ── ИНДЕКС. Единственная инверсия на экране. ───────────────────── */
.sh .rail{background:var(--sheet);border-right:1px solid var(--line);
  display:flex;flex-direction:column;overflow:auto;min-height:0;padding:var(--s3) var(--s2)}
.sh .rail h4{margin:var(--s4) 0 var(--s1) var(--s2);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ann2);font-weight:500}
.sh .rail h4:first-child{margin-top:0}
.sh .idx{display:flex;align-items:center;gap:var(--s2);padding:0 var(--s2);min-height:30px;
  font:14px var(--sans);color:var(--ann);background:none;border:none;cursor:pointer;
  width:100%;text-align:left;border-radius:var(--r-hair)}
.sh .idx .no{font-family:var(--mono);font-size:10px;color:var(--ann2);font-variant-numeric:var(--tab)}
.sh .idx:hover{background:var(--field);color:var(--ink)}
.sh .idx[aria-current="page"]{background:var(--heavy);color:var(--sheet);font-weight:500}
.sh .idx[aria-current="page"] .no{color:var(--sheet);opacity:.55}

/* ── ПОЛЕ И ПОЛОСЫ УПРАВЛЕНИЯ ───────────────────────────────────── */
.sh .main{background:var(--ground);min-height:0;display:flex;flex-direction:column;overflow:hidden}
.sh .tools{flex:none;display:flex;align-items:center;gap:var(--s2);padding:var(--s3) var(--s4);
  background:var(--sheet);border-bottom:1px solid var(--line);position:relative;z-index:20}
/* Полоса отбора — вторая строка, сворачивается. */
.sh .filters{flex:none;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  padding:0 var(--s4) var(--s3);background:var(--sheet);border-bottom:1px solid var(--line);
  position:relative;z-index:19}
.sh body:has(.f-toggle[aria-expanded="false"]) .filters{display:none}

.sh .search{flex:1;min-width:120px;max-width:420px;font:13px var(--sans);padding:0 var(--s3);
  min-height:30px;border:1px solid var(--line);border-radius:var(--r-ctl);background:var(--sheet);color:var(--ink)}
.sh .search::placeholder{color:var(--ann2)}

/* Значок отбора без слова: воронка плюс счётчик выбранного. */
.sh .f-toggle{flex:none;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 var(--s2);
  border:1px solid var(--line);border-radius:var(--r-ctl);background:var(--sheet);color:var(--ink);cursor:pointer}
.sh .f-toggle svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round}
.sh .f-toggle .cnt{font-size:12px;font-weight:600;font-variant-numeric:var(--tab);
  min-width:17px;height:17px;padding:0 4px;border-radius:var(--r-pill);background:var(--heavy);
  color:var(--sheet);display:inline-flex;align-items:center;justify-content:center}
.sh .f-toggle[aria-expanded="true"]{background:var(--field);border-color:var(--ann2)}

/* Сегментный переключатель: дорожка + приподнятый активный, без чёрного. */
.sh .seg{display:inline-flex;flex:none;background:var(--field);border:1px solid var(--line);
  border-radius:var(--r-ctl);padding:2px;gap:2px}
.sh .seg button{font:13px var(--sans);padding:0 var(--s3);min-height:26px;border:none;border-radius:var(--r-hair);
  background:none;color:var(--ann);cursor:pointer}
.sh .seg button[aria-pressed="true"]{background:var(--sheet);color:var(--ink);font-weight:600;
  box-shadow:var(--sh-seg)}
[data-theme="dark"] .sh .seg button[aria-pressed="true"]{box-shadow:var(--sh-seg)}

.sh .ctl{font:13px var(--sans);padding:0 var(--s3);min-height:30px;border:1px solid var(--line);
  border-radius:var(--r-ctl);background:var(--sheet);color:var(--ink);cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;flex:none}
.sh .ctl:hover{border-color:var(--ann2)}
.sh .ctl .cnt{color:var(--ann2);font-variant-numeric:var(--tab)}
.sh .ctl .chev{color:var(--ann2);font-size:10px}
.sh .ctl[aria-expanded="true"]{border-color:var(--heavy);background:var(--field)}
.sh .ctl--go{background:var(--heavy);color:var(--sheet);border-color:var(--heavy);font-weight:500}
.sh .grow{flex:1}

/* ── ВСПЛЫВШЕЕ ──────────────────────────────────────────────────── */
.sh .pop{position:absolute;background:var(--sheet);border:1px solid var(--line);border-radius:var(--r-ctl);
  box-shadow:var(--sh-pop);min-width:268px;padding:var(--s2) 0;z-index:30}
[data-theme="dark"] .sh .pop{box-shadow:var(--sh-pop)}
.sh .pop h5{margin:0;padding:var(--s2) var(--s4) var(--s1);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ann2);font-weight:500}
.sh .opt{display:flex;align-items:flex-start;gap:var(--s3);padding:6px var(--s4);cursor:pointer;min-height:34px}
.sh .opt:hover{background:var(--field)}
.sh .opt input{appearance:none;-webkit-appearance:none;margin:2px 0 0;flex:0 0 15px;width:15px;height:15px;
  border:1px solid var(--ann2);border-radius:var(--r-hair);background:var(--sheet);display:grid;place-content:center;cursor:pointer}
.sh .opt input:checked{background:var(--mark);border-color:var(--mark)}
.sh .opt input:checked::after{content:"";width:8px;height:8px;background:var(--sheet);
  clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%)}
[data-theme="dark"] .sh .opt input:checked::after{background:var(--sheet)}
.sh .opt .tx{flex:1;min-width:0}
.sh .opt .tt{display:flex;align-items:baseline;gap:var(--s2);font-size:13px}
.sh .opt .tt .n{margin-left:auto;color:var(--ann2);font-variant-numeric:var(--tab);font-size:12px}
.sh .opt .df{font-size:11px;line-height:1.35;color:var(--ann2);margin-top:1px}
.sh .pop .foot{display:flex;gap:var(--s2);padding:var(--s2) var(--s4) 0;margin-top:var(--s1);
  border-top:1px solid var(--hair)}

/* ── КОЛОНКИ И УЗЛЫ ─────────────────────────────────────────────── */
.sh .cols{flex:1;min-height:0;display:flex;gap:var(--s3);padding:var(--s3) var(--s4);overflow-x:auto}
.sh .col{flex:0 0 272px;display:flex;flex-direction:column;min-height:0;background:var(--field);border-radius:var(--r-zone)}
.sh .col > .hd{display:flex;align-items:baseline;gap:var(--s2);padding:var(--s3) var(--s3) var(--s2);
  font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--ann);font-weight:600}
.sh .col > .hd .n{margin-left:auto;color:var(--ann2);font-variant-numeric:var(--tab);font-weight:400}
.sh .col > .bd{padding:0 var(--s2) var(--s2);display:flex;flex-direction:column;gap:var(--s2);
  overflow:auto;min-height:0}
.sh .node{background:var(--sheet);border:1px solid var(--hair);border-radius:var(--r-ctl);padding:var(--s3);
  display:block;text-decoration:none;color:inherit;cursor:pointer;position:relative}
.sh .node:hover{border-color:var(--line)}
.sh .node .tag{display:flex;align-items:baseline;gap:var(--s2);font-size:11px;color:var(--ann2);margin-bottom:5px}
.sh .node .tag .code{font-family:var(--mono);letter-spacing:-.02em;color:var(--ann)}
.sh .node .nm{font-size:14px;font-weight:600;line-height:1.3;letter-spacing:-.008em}
.sh .node .val{font-size:23px;font-weight:600;letter-spacing:-.022em;line-height:1.15;margin:7px 0 9px}
.sh .node .val .u{font-size:13px;font-weight:400;color:var(--ann2);margin-left:3px}
/* Значение словом, а не числом: берёт ступень имени, а не величины. */
.sh .node .val--none{color:var(--ann2);font-weight:400;font-size:14px}
.sh .node .val--bad{color:var(--bad);font-size:14px}
.sh .node dl{display:grid;grid-template-columns:auto 1fr;gap:3px var(--s3);margin:0;font-size:12px}
.sh .node dt{color:var(--ann2);white-space:nowrap}
/* ЗНАЧЕНИЕ — ЧЕРНИЛОМ. Подпись и значение стояли двумя оттенками
   серого; доведённые до нормы 4.5, они сошлись бы в один. Различать
   их должен УРОВЕНЬ, а не оттенок: данные получают чернило, подпись —
   третичный тон. */
.sh .node dd{margin:0;text-align:right;color:var(--ink)}
.sh .node dd.e{color:var(--ann2);font-style:italic}
.sh .node .st{display:flex;align-items:baseline;gap:var(--s2);margin-top:10px;font-size:11px}
.sh .node .st .w{font-weight:600;letter-spacing:.02em}
.sh .node .st .d{margin-left:auto;color:var(--ann2);font-variant-numeric:var(--tab)}
.sh .st--ok .w{color:var(--ok)} .sh .st--old .w{color:var(--old)}
.sh .st--none .w{color:var(--none)} .sh .st--bad .w{color:var(--bad)}
.sh .node.is-rev{box-shadow:inset 3px 0 0 var(--mark)}

/* ── УЗКИЙ ЛИСТ ─────────────────────────────────────────────────── */
@media (max-width:860px){
  /* Сужение — тот же переключатель, что и кнопка: список уезжает
     в накладную папку. Отдельного бургера нет, иначе одно состояние
     жило бы в двух местах. */
  .sh .wrap{grid-template-columns:minmax(0,1fr)}
  .sh .stamp{gap:var(--s4);padding-right:var(--s3)}
  /* Распорка делила свободное место с углом пополам — на 390px углу
     доставалось 52px и имя листа сжималось в «Д..». На узком листе
     распорки нет: место принадлежит имени. */
  .sh .stamp .grow{display:none}
  .sh .corner{width:auto;flex:1 1 auto;min-width:132px}
  .sh body:has(.collapse[aria-expanded="false"]) .corner{width:auto}
  .sh body:has(.collapse[aria-expanded="false"]) .corner .id{display:flex}
  .sh .stamp .drop{display:none}
  .sh .rail{position:fixed;top:0;bottom:0;left:0;width:min(272px,84vw);z-index:50;
    transform:translateX(-100%);visibility:hidden;box-shadow:var(--sh-drawer);
    transition:transform 220ms cubic-bezier(.23,1,.32,1),visibility 220ms}
  .sh body:has(.collapse[aria-expanded="true"]) .rail{transform:translateX(0);visibility:visible}
  .sh body:has(.collapse[aria-expanded="true"]) .scrim{display:block}
  .sh body:has(.collapse[aria-expanded="false"]) .rail h4, .sh body:has(.collapse[aria-expanded="false"]) .idx .lbl{display:revert}
  .sh body:has(.collapse[aria-expanded="false"]) .idx{justify-content:flex-start;padding:0 var(--s2)}
  .sh .tools, .sh .filters{overflow-x:auto;padding-left:var(--s3);padding-right:var(--s3)}
  .sh .filters{flex-wrap:nowrap}
  .sh .seg button, .sh .ctl, .sh .search, .sh .f-toggle{min-height:40px}
  .sh .collapse{width:44px;height:44px}
  .sh .idx{min-height:44px}
  .sh .cols{gap:var(--s2);padding:var(--s2) var(--s3);scroll-snap-type:x mandatory}
  .sh .col{flex:0 0 min(304px,88vw);scroll-snap-align:start}
  .sh .node .val{font-size:26px}
}
/* Ниже 480 в шапке остаётся только съём данных: имя листа важнее
   счётчика показанного, а он и так стоит на счётчиках отбора. */
@media (max-width:480px){
  .sh .stamp .drop2{display:none}
  .sh .corner{min-width:0}
}
.sh .scrim{display:none;position:fixed;inset:0;z-index:40;border:none;background:var(--scrim)}

/* ТЕМА «СИСТЕМНАЯ» — ТРЕТЬЕ ЗНАЧЕНИЕ, А НЕ ОТСУТСТВИЕ ВЫБОРА.
   В превью её не было вовсе, и это дыра: `data-theme` продукта принимает
   light | dark | system, умолчание — system. Тёмные значения обязаны
   доставаться и тем, кто ничего не выбирал. */
@media (prefers-color-scheme: dark){
[data-theme="system"] .sh{
  --ground:#0E2338;  --field:#173352;  --sheet:#1C3D61;
  --hair:#2A5178;    --line:#3C6B98;   --heavy:#E6F0FA;  --ink:#E9F2FB;
  --ann:#AEC8DF;     --ann2:#95B5D2;
  --ok:#63CE99;  --old:#E6BA6A;  --none:#8FAFC9;  --bad:#FF9382;  --mark:#FFFFFF;
  --ok-bg:#122C21;  --old-bg:#2C2415;  --none-bg:#173352;  --bad-bg:#3A1B17;
  --bar:#9FC4E4;  --bar-done:#4A749C;  --bar-sect:#D3E4F4;  --wknd:#15304C;
  color-scheme:dark;
}
  [data-theme="system"] .sh{color-scheme:dark}
}


/* ═══ РЕЕСТР ════════════════════════════════════════════════════════
   СВЕДЁН ИЗ ДВУХ КОПИЙ. Отчёт и план держали каждый свою таблицу —
   разные имена классов, одинаковые правила, разошедшиеся поля
   у числовой колонки. Компонент один, вариант `--plan` добавляет
   фиксированную раскладку и высоту строки полотна. */
.sh .reg{width:100%;border-collapse:collapse;font-size:13px}
.sh .reg th{text-align:left;font-size:11px;font-weight:500;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ann2);padding:0 var(--s3) 6px 0;white-space:nowrap}
.sh .reg td{padding:7px var(--s3) 7px 0;border-top:1px solid var(--hair);
  vertical-align:baseline}
.sh .reg th.n,.sh .reg td.n{text-align:right;font-variant-numeric:var(--tab)}
/* Последняя колонка снимает правое поле; остальные держат, иначе
   значение слипается с соседней ячейкой — «24 %Победа». */
.sh .reg th:last-child,.sh .reg td:last-child{padding-right:0}
.sh .reg tbody tr:hover td{background:var(--field)}
.sh .reg td .quiet{color:var(--ann2)}

.sh .reg--plan{table-layout:fixed}
.sh .reg--plan th{height:var(--hh);vertical-align:bottom;padding:0 var(--s2) 6px;
  border-bottom:1px solid var(--line)}
.sh .reg--plan td{height:var(--rh);padding:0 var(--s2);border-top:none;
  border-bottom:1px solid var(--hair);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;font-variant-numeric:var(--tab);vertical-align:middle}
.sh .reg--plan td.nm{font-variant-numeric:normal}
.sh .reg--plan td.ind{padding-left:var(--s5)}
/* Раздел не может быть мельче своих задач: различают вес и регистр. */
.sh .reg--plan tr.is-sect td{font-weight:600;font-size:13px;
  background:var(--field)}
/* Разрядка и капитель — ТОЛЬКО на имени раздела. На датах и числах они
   раздувают ячейку: при table-layout:fixed «01.07» уезжало в «01....». */
.sh .reg--plan tr.is-sect td.nm{letter-spacing:.04em;text-transform:uppercase}
.sh .tl-pb{display:inline-block;width:38px;height:3px;background:var(--hair);
  margin-right:var(--s2);vertical-align:middle}
.sh .tl-pb i{display:block;height:3px;background:var(--bar)}

/* ═══ СПРАВКА ПО ЗНАЧКУ ═════════════════════════════════════════════
   Обоснование всплывает по ⓘ у заголовка, а не раскрывается под
   величиной: место под цифрой принадлежит цифре. */
.sh .how{display:inline;position:relative}
/* ЗНАК НАБРАН ТЕМ ЖЕ ШРИФТОМ, ЧТО И ВСЁ ОСТАЛЬНОЕ. Первая редакция
   ставила курсивный полужирный Georgia — «издательский» ⓘ, который
   в интерфейсе читается как заглавная I, да ещё наклонная. Владелец:
   «обычно regular и i». Он прав, и довод сильнее привычки: знак,
   набранный ЧУЖОЙ гарнитурой, приходится уравнивать с соседним текстом
   вручную при каждой смене шрифта — тот же довод, по которому язык
   отказался от рисованных знаков в пользу набранных.

   ЦЕЛЬ РАСТЁТ, А КРУЖОК НЕТ — как у флажка. Шестнадцать пикселей —
   это размер ЗНАКА рядом с подписью в 11px; 24-пиксельный кружок
   спорил бы с ней по весу. Площадь даёт невидимое поле вокруг. */
/* ОДИН ПИКСЕЛЬ ПОЛЯ СНИЗУ — ЭТО ОПТИЧЕСКОЕ ЦЕНТРИРОВАНИЕ, А НЕ ПОДГОНКА.
   Геометрически знак стоял ровно: краска отклонялась на 0,06 влево
   и 0,125 вверх от центра кружка. Но глаз читает не габарит, а МАССУ,
   а у строчной «i» масса в стержне — точка над ним почти ничего не весит.
   Замер центра тяжести краски при настоящем разрешении: +0,63 вниз.
   Каждый пиксель поля снизу поднимает её на полпикселя; при одном
   остаётся +0,134 — это и есть ноль в пределах растра. Владелец увидел
   перекос раньше, чем его нашла линейка: первые две мерки (метрика
   шрифта и габарит краски) сказали «ровно» и обе смотрели не туда.

   ВЫРАВНИВАНИЕ ПО СЕРЕДИНЕ СТРОЧНОЙ, А НЕ НА ТРИ ПИКСЕЛЯ ВНИЗ (26.08.2026).
   Прежние −3px были выверены под один случай — заголовок в 24px. Знак с тех
   пор встал и в подписях 13–14px, и в заголовках разделов: там те же три
   пикселя опускали его заметно ниже строки, что владелец и увидел на профиле.
   `vertical-align: middle` — это не «примерно посередине»: CSS определяет его
   как совмещение середины элемента с линией «базовая + половина роста
   строчной». То есть ровно то отношение, которое глаз и читает как «на одной
   строке», и оно держится при любом кегле само, без второй мерки на каждый
   новый случай. Замер до правки: знак стоял на 6,9 пикселя ниже середины
   строки в заголовке 24px. */
.sh .how > summary{list-style:none;cursor:pointer;display:inline-flex;
  align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;
  border:1px solid var(--ann2);color:var(--ann2);
  font:500 11px/1 var(--sans);font-style:normal;padding-bottom:1px;
  vertical-align:middle;margin-left:6px;position:relative}
.sh .how > summary::before{content:"";position:absolute;inset:-4px}
.sh .how > summary::-webkit-details-marker{display:none}
.sh .how > summary:hover{border-color:var(--ink);color:var(--ink)}
.sh .how[open] > summary{background:var(--ink);border-color:var(--ink);color:var(--sheet)}
.sh .how .body{position:absolute;top:24px;left:-8px;width:min(320px,calc(100vw - 24px));z-index:30;
  background:var(--sheet);border:1px solid var(--line);border-radius:var(--r-ctl);
  box-shadow:var(--sh-pop);padding:var(--s3) var(--s4) var(--s4);
  font:400 12px/1.55 var(--sans);color:var(--ann);letter-spacing:0;text-transform:none}
.sh .how .body b{color:var(--ink);font-weight:600}
.sh .how .body ul{margin:var(--s2) 0 0;padding-left:var(--s4)}

/* ═══ ПЛАШКА «В РАЗРАБОТКЕ» ═════════════════════════════════════════
   Штатное состояние раздела, а не ошибка. */
.sh .wip{font-size:10px;letter-spacing:.06em;text-transform:uppercase;font-weight:500;
  color:var(--ann2);background:var(--none-bg);padding:3px 7px;border-radius:2px}

/* ═══ ЛИСТ-ДОКУМЕНТ ═════════════════════════════════════════════════ */
.sh .doc{flex:1;min-height:0;overflow:auto;background:var(--ground);
  padding:var(--s6) var(--s8) var(--s8)}
.sh .doc > .paper{max-width:880px;background:var(--sheet);border-radius:var(--r-zone);
  padding:var(--s8) var(--s8) var(--s6)}
.sh .doc h1{font-size:22px;font-weight:600;letter-spacing:-.02em;margin:0 0 var(--s1)}
.sh .doc h2{font-size:14px;font-weight:600;letter-spacing:-.01em;
  margin:var(--s8) 0 var(--s3);display:flex;align-items:center;gap:var(--s3)}
.sh .doc .lead{font-size:13px;color:var(--ann);margin:0 0 var(--s8)}
.sh .kpis{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s6) var(--s8)}
/* ПЛИТКА, А НЕ СТРОКА (27.08.2026). Подпись стояла инлайном слева от
   числа; длинная («ВСЕГО ТЕНДЕРОВ») переносилась и роняла число со
   строки — значения ряда плясали по вертикали (найдено владельцем).
   Подпись встала НАД числом, как в клетке штампа (.cell: .k над .fact),
   держит одну строку с многоточием — у чисел общая линия при любой
   длине слов. Подстрока .of живёт под числом и на сетку не влияет. */
.sh .kpi{display:flex;flex-direction:column;min-width:0}
.sh .kpi .k{position:relative;font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ann2);font-weight:500;margin-bottom:6px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sh .kpi .v{font-size:38px;font-weight:600;letter-spacing:-.03em;line-height:1;
  font-variant-numeric:var(--tab)}
.sh .kpi .v .u{font-size:13px;font-weight:400;color:var(--ann2);margin-left:3px}
.sh .kpi .of{font-size:12px;color:var(--ann);margin-top:6px}
/* Ряд на четыре величины — показатели пункта отчёта (винрейт: процент,
   выиграно, всего, окно). На узком складывается в 2×2, не в столбик:
   четыре числа — это один взгляд, а не четыре экрана. */
.sh .kpis.kpis--4{grid-template-columns:repeat(4,minmax(0,1fr))}
.sh .kpis + .kpis{margin-top:var(--s6)}

/* ── РАЗДЕЛ «КТО ПРОДЮСЕР» (28.08.2026) ─────────────────────────────── */
/* Ячейка «число + полоска»: доля от максимума КОЛОНКИ, ширина — данные
   (инлайновая ширина полосы — тот же приём, что у полос VPN). */
.sh .cell-barwrap{display:flex;align-items:center;gap:8px;min-width:110px}
.sh .cell-barwrap .v{min-width:1.4em;font-variant-numeric:var(--tab)}
/* ДОРОЖКА ПОЛОСКИ — СВОИМ ТОКЕНОМ, А НЕ ЧУЖИМ. Здесь стоял `--n3` — токен
   НЕЙТРАЛЬНОГО РЯДА ЯЗЫКА TN, которого в палитре «Листа» нет вовсе. Значение
   не резолвилось, дорожка выходила прозрачной, и сквозь неё читался фон,
   который рисует `.tn` — оба класса стоят на одном `<body>`. Аудит состояний
   на `/reports/1` писал это обеими темами: `.cell-bar bg #e7e7e4` и `#2a2a30`.
   Та же грабля, что уже разобрана выше про `--fill`: язык обязан объявлять
   своё сам, иначе он молча живёт на чужом. */
.sh .cell-bar{flex:1;height:6px;border-radius:999px;background:var(--hair);overflow:hidden}
.sh .cell-bar > span{display:block;height:100%;background:var(--ann2);border-radius:999px}
/* Свёрнутые оценки NPS в ячейке: треугольник разворачивает столбик
   «оценка + комментарий». Явный маркер — системный у details погашен. */
.sh .cell-exp > summary{list-style:none;cursor:pointer;white-space:nowrap}
.sh .cell-exp > summary::-webkit-details-marker{display:none}
.sh .cell-exp > summary::before{content:"\25B8";display:inline-block;margin-right:4px;
  color:var(--ann2);transition:transform .12s}
.sh .cell-exp[open] > summary::before{transform:rotate(90deg)}
/* `background` ОБЪЯВЛЕН ЯВНО, хотя строке подложка не нужна. Правило
   `.tn .row` (0-2-0) даёт `background: var(--n0)` и рисует полосу
   ВНУТРИ ячейки таблицы; здешнее правило тяжелее (0-3-0), но перебивает
   только то, что называет, а фона оно не называло. Аудит читал это как
   чужой цвет `.row bg #17171a` в тёмной теме. Строка раскрытия — часть
   ячейки, своей подложки у неё нет: так и записано. */
.sh .cell-exp .row{display:flex;gap:6px;align-items:baseline;
  padding:2px 0 2px 14px;white-space:normal;min-width:220px;background:transparent}
.sh .cell-exp .row b{flex:none}
/* Выбор продюсера + его доска в теле отчёта. Доска living по содержимому
   (без --fill: это документ, а не экран), вбок прокручивается сама. */
.sh .rp-boards{margin:var(--s4) 0}
.sh .rp-boards .rp-boards-pick{max-width:320px}
.sh .rp-boards .board{margin-top:var(--s3);overflow-x:auto;padding-bottom:var(--s2)}
/* Колонка доски в ДОКУМЕНТЕ ограничена по высоте и прокручивается внутри:
   у активного продюсера в «Сдан» десятки карточек — без потолка доска
   съедала документ и глотала прокрутку страницы (замер 30.08: колонка
   на 35–50 карточек). Заголовок колонки остаётся на месте сам — липкость
   у .board-col .head уже в языке. */
.sh .rp-boards .board-col{max-height:440px}
.sh .rp-boards .board-col > .body{overflow-y:auto}
/* В модале ряду тесно при любой ширине окна — 2×2 всегда: медиазапрос
   меряет окно, а не контейнер, и в узком модале на широком экране
   четыре плитки давились в строку. */
.sh .modal .kpis.kpis--4{grid-template-columns:repeat(2,minmax(0,1fr))}
@media (max-width:860px){
  .sh .kpis.kpis--4{grid-template-columns:repeat(2,minmax(0,1fr))}
}
/* Строка-подпись под показателями: «КЛИЕНТ ВТБ [значок]». Не клетка
   kv-таблицы — аннотация пункта: по чему отбирали. */
.sh .kv-line{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;
  margin:var(--s2) 0 var(--s4)}
.sh .kv-line .rp-k{font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ann2);font-weight:500}
.sh .kv-line .v{font-size:14px;font-weight:600;color:var(--ink)}
.sh .kv-line .pj-lnk img{width:16px;height:16px;vertical-align:-3px}
@media (max-width:860px){
  .sh .doc{padding:var(--s3)}
  .sh .doc > .paper{padding:var(--s5) var(--s4)}
  .sh .kpis{grid-template-columns:minmax(0,1fr);gap:var(--s5)}
  .sh .kpi .v{font-size:34px}
  .sh .reg{display:block;overflow-x:auto;white-space:nowrap}
}

/* ═══ ПОЛОТНО ПЛАНА ═════════════════════════════════════════════════
   Словарь выведен из чертежа: размерная линия — задача, размерная
   скобка — раздел, поле допуска — оценка, контур без тела — план,
   осевая линия — сегодня, ромб — веха. Достоверность кодируется
   весом штриха и наличием тела, а не краской: лист остаётся верным
   в чёрно-белой копии. Число столбцов задаёт продукт через --tl-days. */
.sh .tl-track-wrap{position:relative;width:calc(var(--cw) * var(--tl-days))}
.sh .tl-dhs{display:grid;grid-template-columns:repeat(var(--tl-days),var(--cw));grid-template-rows:auto 1fr;
  height:var(--hh);position:sticky;top:0;z-index:5;
  background:var(--sheet);border-bottom:1px solid var(--line)}
.sh .tl-mh{font-size:11px;font-weight:600;color:var(--ann);padding:3px var(--s2) 1px;
  border-left:1px solid var(--line);grid-row:1}
.sh .tl-dh{grid-row:2;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  font-size:10px;line-height:1.25;color:var(--ann2);padding-bottom:5px}
.sh .tl-dh b{font-weight:500;color:var(--ann);font-variant-numeric:var(--tab)}
.sh .tl-dh.we{background:var(--wknd)}
.sh .tl-dh.today b{background:var(--mark);color:var(--sheet);border-radius:var(--r-hair);padding:0 3px}

/* Три веса штриха, как на листе: 0.13 сетка, 0.25 размер, 0.5 общий размер. */
.sh .tl-track{display:grid;grid-template-columns:repeat(var(--tl-days),var(--cw));grid-auto-rows:var(--rh);
  position:relative;
  background-image:
    repeating-linear-gradient(to right,transparent 0 var(--tl-wknd-a),var(--wknd) var(--tl-wknd-a) var(--tl-wknd-b),
      transparent var(--tl-wknd-b) calc(var(--cw) * 7)),
    repeating-linear-gradient(to right,var(--hair) 0 1px,transparent 1px var(--cw)),
    repeating-linear-gradient(to bottom,transparent 0 calc(var(--rh) - 1px),
      var(--hair) calc(var(--rh) - 1px) var(--rh))}

/* ОСЕВАЯ ЛИНИЯ: штрихпунктир, а не сплошная — на чертеже ось рисуют так. */
.sh .tl-today-line{position:absolute;top:0;bottom:0;left:var(--tl-today);width:0;z-index:4;
  border-left:1px dashed var(--mark)}
.sh .tl-today-line::before{content:"";position:absolute;top:0;bottom:0;left:-1px;width:1px;
  background:repeating-linear-gradient(to bottom,var(--mark) 0 9px,transparent 9px 13px,
    var(--mark) 13px 15px,transparent 15px 19px)}

/* ЗАДАЧА — размерная линия: тело с засечками-торцами на обоих концах. */
.sh .tl-bar{align-self:center;height:10px;position:relative;z-index:2;
  background:var(--bar-done);
  border-left:1px solid var(--bar);border-right:1px solid var(--bar)}
.sh .tl-bar i{position:absolute;inset:0 auto 0 0;background:var(--bar)}
.sh .tl-bar::before,.tl-bar::after{content:"";position:absolute;top:-3px;bottom:-3px;width:1px;background:var(--bar)}
.sh .tl-bar::before{left:-1px} .tl-bar::after{right:-1px}
.sh .bar--late{background:color-mix(in srgb,var(--bad) 26%,transparent);
  border-color:var(--bad)}
.sh .bar--late i{background:var(--bad)}
.sh .bar--late::before,.bar--late::after{background:var(--bad)}
/* ПЛАН — контур без тела: начерчено, но не построено. */
.sh .bar--plan{background:none;border:1px dashed var(--bar)}
.sh .bar--plan::before,.bar--plan::after{display:none}
/* ОЦЕНКА — номинал плюс ПОЛЕ ДОПУСКА за концом: «от и до», не одно число. */
.sh .bar--est{background:none;border:1px solid var(--bar)}
.sh .bar--est .tl-tol{position:absolute;top:50%;left:100%;height:1px;background:var(--bar);
  opacity:.55;transform:translateY(-.5px)}
.sh .bar--est .tl-tol::after{content:"";position:absolute;right:0;top:-4px;bottom:-4px;width:1px;
  background:var(--bar);opacity:.55}

/* РАЗДЕЛ — размерная скобка НАД группой: общий размер, засечки вниз. */
.sh .tl-dim{align-self:start;margin-top:9px;height:9px;position:relative;z-index:2;
  border-top:2px solid var(--bar-sect)}
.sh .tl-dim::before,.tl-dim::after{content:"";position:absolute;top:0;width:2px;height:8px;
  background:var(--bar-sect)}
.sh .tl-dim::before{left:0} .tl-dim::after{right:0}
.sh .tl-dim span{position:absolute;left:50%;top:-9px;transform:translateX(-50%);
  background:var(--sheet);padding:0 5px;font-size:10px;font-weight:600;color:var(--bar-sect);
  font-variant-numeric:var(--tab)}
/* ВЕХА — РОМБ. Событие без протяжённости: у него нет начала и конца,
   только момент, и ромб — единственная фигура на листе, у которой
   нет длины ни по одной оси. */
.sh .tl-ms{align-self:center;justify-self:center;width:11px;height:11px;z-index:2;
  background:var(--bar-sect);transform:rotate(45deg)}
/* СВЯЗЬ — ГРУППА, А НЕ ЭЛЕМЕНТ (разбор 0.55, D-128; уточнено 0.74, D-191).
   Цвет объявлен ОДИН РАЗ, на группе, через `color`; линия берёт его
   обводкой, торец — заливкой. Разойтись нечему по построению, а не
   по договорённости: маркер с `fill:context-stroke` отказывает молча
   и оставляет серый наконечник на покрашенной линии.
   Приглушение имеет ПОЛ: 0.7 держит 3.0 к фону в обеих темах, 0.65 уже нет. */
.sh .tl-lk{position:absolute;top:0;left:0;pointer-events:none;z-index:3}
.sh .tl-lk .link{color:var(--ann2);stroke:currentColor;stroke-width:var(--link-w);fill:none;
  stroke-linejoin:round;opacity:var(--link-dim)}
.sh .tl-lk .link *{stroke:inherit;fill:none}
.sh .tl-lk .link .link-head{fill:currentColor;stroke:none}
.sh .tl-lk .link--active{color:var(--mark);opacity:1}

.sh .tl-legend{flex:none;display:flex;align-items:center;gap:var(--s5);padding:7px var(--s4);
  background:var(--sheet);border-top:1px solid var(--line);font-size:11px;color:var(--ann2);
  overflow-x:auto;white-space:nowrap}
.sh .tl-legend span{display:inline-flex;align-items:center;gap:6px}
.sh .tl-sw{width:22px;height:9px;flex:none;background:var(--bar)}
.sh .sw--est{background:none;border:1px solid var(--bar)}
.sh .sw--plan{background:none;border:1px dashed var(--bar)}
.sh .sw--sect{height:0;border-top:2px solid var(--bar-sect)}
.sh .sw--late{background:color-mix(in srgb,var(--bad) 40%,transparent);
  border-left:1px solid var(--bad);border-right:1px solid var(--bad)}
.sh .sw--ms{width:0;height:0;background:none;border-left:6px solid transparent;
  border-right:6px solid transparent;border-top:10px solid var(--bar-sect)}

/* ═══ РАСКОЛ ════════════════════════════════════════════════════════
   Неподвижная панель слева, прокручиваемое полотно справа. Обе панели
   держат ОДНУ высоту строки (--rh) и ОДНУ высоту шапки (--hh): иначе
   строки таблицы и полосы полотна разъезжаются, и разъезжаются молча. */
.sh .split{flex:1;min-height:0;display:grid;
  grid-template-columns:428px minmax(0,1fr);background:var(--sheet)}
.sh .split > .pane{min-width:0}
.sh .split > .pane--l{border-right:1px solid var(--line);overflow:hidden}
.sh .split > .pane--r{overflow:auto}
.sh .doc .quiet{font-size:12px;line-height:1.55;color:var(--ann2);margin:0}
@media (max-width:860px){
  .sh .split{grid-template-columns:minmax(0,1fr)}
  .sh .split > .pane--l{border-right:none;border-bottom:1px solid var(--line);
    max-height:44%;overflow:auto}
  .sh .reg--plan th:nth-child(2),.sh .reg--plan td:nth-child(2){display:none}
}

/* ТРЕТИЙ УРОВЕНЬ И ОПАСНОЕ ДЕЙСТВИЕ. Пробел нашёлся на странице миграции:
   у прежнего языка уровня было три плюс опасное, у нового — два. В продукте
   есть «Удалить проект», и рисовать его обычной кнопкой значит потерять
   единственный признак необратимости.
   Опасность — МОДИФИКАТОР, а не отдельная кнопка: форма та же, меняется
   чернило. Четвёртого уровня нет по той же причине, по какой нет третьего
   уровня всплытия. */
.sh .ctl--quiet{background:none;border-color:transparent;color:var(--ann)}
.sh .ctl--quiet:hover{background:var(--field);border-color:transparent;color:var(--ink)}
.sh .ctl--danger{color:var(--bad);border-color:var(--line)}
.sh .ctl--danger:hover{border-color:var(--bad);background:var(--bad-bg)}
.sh .ctl--go.ctl--danger{background:var(--bad);border-color:var(--bad);color:var(--sheet)}

/* ═══════════════════════════════════════════════════════════════════
   СЛОВАРЬ ПРОДУКТА, СКАЗАННЫЙ НА ЭТОМ ЯЗЫКЕ

   Разметку продукта рисует Preact — 700 строк в projects.html плюс
   общий ui.js. Переименовать в них тридцать классов значит трогать
   поведение ради вида, а поведение здесь дороже: доска, отбор,
   перетаскивание, очередь обновления. Поэтому язык говорит на именах,
   которые продукт уже произносит.

   ЭТО НЕ ВТОРАЯ КОПИЯ КОМПОНЕНТОВ. Значения те же токены, что выше;
   здесь только сопоставление имён. Копии соответствовало бы своё
   оформление, а его тут ноль.

   ЦЕНА И СРОК. Имена прежнего языка переживут его самого — это долг,
   и он назван. Гасится переименованием в Preact, когда переход
   закончится и прежний язык уйдёт; до тех пор экран переводится
   ОДНИМ классом `.sh` на <body>, без правки разметки.
   ═══════════════════════════════════════════════════════════════════ */

/* --- каркас --- */
/* КЛАСС РАМЫ СТОИТ НА ТОМ ЖЕ ТЕГЕ, ЧТО И СКОУП, И `.sh .app` НЕ
   СОВПАДАЕТ НИ С ЧЕМ. `<body class="tn app … sh">` — `app` не потомок
   `sh`, а тот же элемент. Правило было мёртвым с первого дня: ширину
   рамы всё это время задавал ПРЕЖНИЙ язык (`--side-w: 208px`), а наша
   `--rail-w` не значила ничего. Заметно стало только когда угол
   в свёрнутом состоянии получил `width:var(--rail-w)` явно и разошёлся
   с рамой на четыре пикселя — владелец увидел, что «пиксели пляшут».
   Прежний язык пишет это верно: `:is(.tn .app, .tn.app)`. Берём так же.

   `--rail-w` приравнена к 208 намеренно. Объявленные 212 не действовали
   ни дня, и весь экран согласован и принят на 208; ставить теперь 212
   значило бы сдвинуть всё на четыре пикселя без просьбы.

   ДВЕ ЗАПИСИ ВЫСОТЫ ПОДРЯД — НЕ ОПЕЧАТКА, взяты у прежнего языка
   (D-127): браузер, не знающий `dvh`, отбрасывает второе объявление
   целиком, и высота осталась бы `auto` — каркас сложился бы по
   содержимому, отказ полный и бесшумный. Пока правило было мёртвым,
   нас прикрывала эта самая защита у соседа. */
:is(.sh .app,.sh.app){display:grid;grid-template-columns:var(--rail-w) minmax(0,1fr);
  grid-template-rows:auto minmax(0,1fr);
  height:var(--app-h,100vh);
  height:var(--app-h,100dvh);background:var(--ground)}
:is(.sh .app--nonav,.sh.app--nonav){grid-template-columns:minmax(0,1fr)}
/* UI-140 (05.09.2026): БЕЗ ПАНЕЛИ СОДЕРЖИМОЕ ЛЕЗЛО ПОД ПОЛОСУ С МАРКОЙ.
   Правило выше убирает колонку рельса, но `.app-main` ниже прибит
   к `grid-column:2;grid-row:1/-1`: с панелью полоса живёт в колонке
   рельса и содержимому не мешает, а в одноколоночной сетке они
   оказываются в одной ячейке. Замер на бою: полоса y 0–69, карточка
   y 20 — наложение в 49 пикселей.
   Приём не новый: ровно это уже сделано для узкого экрана ниже
   (`.sh .app-main{grid-column:1;grid-row:2}` в медиазапросе). Там
   одноколоночную сетку получают сужением окна, здесь — отсутствием
   панели; случай второй, а правило то же, и его просто не завели.
   Задевало это не только экраны ошибок (`UI-51`, из-за них и нашлось),
   а ВХОД — единственную страницу, которую видит каждый и всегда. */
:is(.sh .app--nonav,.sh.app--nonav) .app-main{grid-column:1;grid-row:2}
.sh .app-top{grid-column:1;grid-row:1;display:flex;align-items:center;gap:var(--s2);
  background:var(--ink);color:var(--sheet);border:none;
  height:var(--stamp-h);min-height:0;padding:0 var(--s3)}
/* ПРОЩАЙ, ШЛЯПА. Аватар-колпак стоял в углу, пока фирменного знака
   там не было — под маркой шёл домен набором. Теперь вензель студии
   настоящий, и колпак стал третьим знаком подряд в одном углу.
   Решение владельца 21.08.2026. Снимается полем, а не правкой
   разметки: на прежнем оформлении колпак остаётся. */
.sh .app-top .brand-logo{display:none}
/* Поле принадлежит УГЛУ, а не марке внутри него. Марка несла
   16/20/12 от прежнего языка — со снятой шляпой это стало видно:
   блок уезжал вправо и вниз от середины. */
.sh .app-top .brand{color:var(--sheet);text-decoration:none;display:flex;
  align-items:center;gap:var(--s2);padding:0}
.sh .brand-text{font-size:14px;font-weight:600}
.sh .brand-text small{display:block;font-size:10px;letter-spacing:.05em;opacity:.6;text-transform:uppercase}
/* ГАБАРИТ БУРГЕРА. Было 30×30 при знаке в 13px — меньше всего, что
   человек встречал в этой роли. 40 на точном указателе, 44 на грубом
   (ниже, в блоке грубого указателя). Знак 18px: ☰ набирается шрифтом,
   и его величина — кегль, а не толщина линии. */
/* КНОПКА СТОИТ НА ОДНОМ МЕСТЕ, И ЭТО НЕ ВКУСОВЩИНА. Она была последней
   в углу, а угол менял ширину — при сворачивании кнопка улетала к
   правому краю окна. Кнопка, которая переезжает от собственного
   нажатия, заставляет искать её заново каждый раз. `order` вместо
   правки разметки: разметка общая с прежним оформлением. */
.sh .app-top .nav-toggle{order:-1;margin:0}
.sh .app-top .nav-toggle{width:40px;height:40px;min-height:40px;padding:0;
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  font-size:18px;line-height:1;border:none;background:none;border-radius:var(--r-ctl)}

/* ЗНАК БРЕНДА — ОБВЕДЁННЫЙ, А НЕ НАБРАННЫЙ. Под маркой стоял домен
   набором; владелец попросил сам знак. Растр 640×156 обведён по
   полутонам (`tools/trace_png.py`) — шестнадцать петель: двенадцать
   букв, полоса и три просвета в R, O, A.

   Ставится МАСКОЙ, а не картинкой: в углу цвет знака — `currentColor`,
   а угол в тёмной теме переворачивается (подложка светлеет, чернило
   темнеет). Картинка так не умеет, ей цвет пришлось бы вшить, и в
   одной из тем знак пропал бы на своём же фоне.

   Текст домена остаётся в разметке и уезжает за край, а не гасится
   `display:none`: скрытое так не читается вслух вовсе. */
/* ЗНАК ТЕКУЧИЙ, А НЕ НАЗНАЧЕННЫЙ. Первый раз я поставил 118 пикселей
   «на глаз по картинке» и знак вылез за угол: тексту там 82 — угол 208
   минус поля 24, бургер 40, значок 26 и два зазора. Ширина берётся
   от места, `contain` держит пропорцию, и знак не обрежется, что бы
   ни поставили рядом. Третий раз за день величина назначалась вместо
   того, чтобы быть снятой. */
.sh .app-top .brand,.sh .brand-text{flex:1 1 auto;min-width:0}
/* Приглушённость взята у соседа по углу, а не выдумана: бургер здесь
   уже стоит на .72, и второй величины для той же роли — «второстепенное
   в углу» — заводить незачем. Знак компании идёт вторым голосом после
   имени продукта: лист принадлежит magic, magic принадлежит студии. */
.sh .brand-text small{display:block;width:100%;height:14px;margin-top:4px;opacity:.72;
  font-size:0;line-height:0;letter-spacing:0}
/* ЗНАК НЕСЁТ ПСЕВДОЭЛЕМЕНТ, А ТЕКСТ ОСТАЁТСЯ ТЕКСТОМ. Сначала знак
   и подпись сидели на одном элементе: подложка `currentColor`, текст
   уведён за край. Аудит поймал контраст 1.00 — цвет текста в точности
   равен цвету подложки, потому что подложка И ЕСТЬ краска знака.
   Замер прав: такая пара в разметке действительно есть, и «на самом
   деле не видно» — не довод, а оправдание. Краска ушла на `::before`,
   текст остался нулевым кеглем: коробки у него нет, а вслух он
   по-прежнему читается. */
.sh .brand-text small::before{content:"";display:block;width:100%;height:14px;
  background:currentColor;
  -webkit-mask:url(/static/stereotactic.svg) no-repeat left center/contain;
          mask:url(/static/stereotactic.svg) no-repeat left center/contain}

.sh .app-side{grid-column:1;grid-row:2;background:var(--sheet);border-right:1px solid var(--line);
  display:flex;flex-direction:column;overflow:auto;min-height:0;padding:var(--s3) var(--s2)}
.sh .app-main{grid-column:2;grid-row:1/-1;display:flex;flex-direction:column;
  min-width:0;min-height:0;overflow:hidden;background:var(--ground)}
.sh .app-body{flex:1;min-height:0;overflow:auto}
.sh .app-bar{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;flex:none;
  padding:var(--s3) var(--s4);background:var(--sheet);border-bottom:1px solid var(--line);
  font-size:13px;color:var(--ann)}
.sh .app-bar .fact b{color:var(--ink);font-weight:600;font-variant-numeric:var(--tab)}
.sh .section{padding:var(--s4);border:none}
.sh .section--plain{padding:var(--s5)}
.sh .surface,.sh .card{background:var(--sheet);border:1px solid var(--hair);
  border-radius:var(--r-ctl);padding:var(--s5);box-shadow:none}

/* --- навигация --- */
.sh .nav-group{margin-bottom:var(--s4)}
.sh .nav-group .t-label,.sh h4.t-label{font-size:10px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ann2);font-weight:500;margin:0 0 var(--s1) var(--s2);display:block}
.sh .nav-link{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);
  padding:0 var(--s2);min-height:30px;font-size:14px;color:var(--ann);
  text-decoration:none;border-radius:var(--r-hair)}
.sh .nav-link:hover{background:var(--field);color:var(--ink)}
.sh .nav-link[aria-current]{background:var(--ink);color:var(--sheet);font-weight:500}
.sh .nav-foot{margin-top:auto;padding:var(--s3) var(--s2) 0;font-size:11px;color:var(--ann2)}

/* --- кнопки и поля --- */
.sh button,.sh .btn-primary,.sh .btn-tinted,.sh .btn-quiet,.sh .icon-btn{
  font:13px var(--sans);min-height:30px;padding:0 var(--s3);color:var(--ink);
  background:var(--sheet);border:1px solid var(--line);border-radius:var(--r-ctl);
  cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;
  justify-content:center;gap:6px;box-sizing:border-box}
.sh button:hover{border-color:var(--ann2)}
.sh .btn-primary{background:var(--ink);color:var(--sheet);border-color:var(--ink);font-weight:500}
.sh .btn-primary:hover{opacity:.9;border-color:var(--ink)}
.sh .btn-tinted{background:var(--field);border-color:transparent}
.sh .btn-tinted:hover{background:var(--hair);border-color:transparent}
/* ТИХАЯ КНОПКА — ВСЁ-ТАКИ КНОПКА. Была без подложки и без контура,
   и до наведения читалась надписью: владелец на бою — «пока не наведёшь
   курсор, выглядит как надпись». «Тихая» означает второстепенная,
   а не невидимая; контур без тела — как раз то, чем чертёж показывает
   границу, не заливая её. Отличие от обычной кнопки остаётся: у той
   подложка листа, у этой её нет. */
/* ЗНАК ПЕРЕСЪЁМА НАРИСОВАН, А НЕ НАБРАН (UI-124). Здесь стоял символ
   ↻ (U+21BB). Ни в PP Neue Montreal, ни в Inter его нет, поэтому его
   рисовал системный запасной шрифт: на тёмной теме он приходил другим,
   и это ровно тот случай, что уже записан на странице сравнения про
   глиф ▦ — значок должен быть файлом, а не символом.

   Рисунок ещё и вращается ровно вокруг своей середины: клетка «Данные
   на» поворачивает знак при наведении и крутит во время съёма.

   Маска, а не картинка: цвет берётся из `currentColor`, поэтому знак
   сам следует за состоянием — приглушён в покое, темнеет при наведении,
   гаснет у выключенной кнопки. */
.sh{--знак-пересъём:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1.5 -2.6 27 27' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='23 4 23 10 17 10'/%3E%3Cpath d='M20.49 15a9 9 0 1 1-2.12-9.36L23 10'/%3E%3C/svg%3E")}
/* ГАЛОЧКА ОТВЕТА (UI-127). Значок почты на полторы секунды становится
   галочкой — «адрес в буфере». Рисунок, а не символ ✓: причина та же,
   что у знака пересъёма. Цвет — тот же зелёный, которым язык говорит
   «в порядке», и другого значения у него в продукте нет. */
.sh .ok-ico{width:1em;height:1em;fill:none;stroke:var(--ok);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.sh .icon-btn.is-copied{border-color:var(--ok)}
.sh .icon-btn.is-failed{border-color:var(--bad)}

.sh .sync-ico{display:inline-block;width:1em;height:1em;background:currentColor;
  -webkit-mask:var(--знак-пересъём) center/contain no-repeat;
  mask:var(--знак-пересъём) center/contain no-repeat}

.sh .btn-quiet{background:none;border-color:var(--line);color:var(--ann)}
.sh .btn-quiet:hover{background:var(--field);color:var(--ink);border-color:var(--ann2)}
.sh .btn-quiet:disabled{border-color:var(--hair);color:var(--ann2);cursor:not-allowed}

/* ПОВТОРЯЮЩЕЕСЯ ДЕЙСТВИЕ ПИШЕТСЯ ЗНАКОМ, А НЕ СЛОВОМ. Двенадцать строк
   таблицы — двенадцать раз «синхронизировать»: слово перестаёт читаться
   и превращается в текстуру, а колонка под него шире самого длинного
   значения в таблице. Знак ↻ тот же, что у клетки «Данные на»: одно
   действие — один знак.

   СЛОВО ОСТАЁТСЯ В РАЗМЕТКЕ и уезжает нулевым кеглем, а не `display:none`:
   скрытое так не читается вслух, и кнопка стала бы для скринридера
   пустой. Прежнее оформление слова не теряет — правило под `.sh`. */
/* ЗАЗОР МЕЖДУ ЗНАКОМ И ПУСТОТОЙ (UI-124). Владелец: «в интеграциях
   „повторить“ не центрирована». Развёртка кнопки по пикселям: знак
   стоял на 3 пикселя ЛЕВЕЕ середины — и до замены глифа, и после неё,
   то есть дело было не в знаке.

   Слово «синхронизировать» никуда не делось: оно уехало НУЛЕВЫМ КЕГЛЕМ,
   чтобы кнопка осталась названной для скринридера. Но нулевой кегль
   не отменяет существования: для флексбокса это по-прежнему элемент,
   пусть и шириной ноль, а у кнопки `gap:6px`. Флексбокс честно
   отцентровал ПАРУ «знак + зазор + ничто» — и знак ушёл влево ровно
   на половину зазора.

   Общее правило: `gap` считает невидимое наравне с видимым. Там, где
   элемент спрятан кеглем, а не удалён, зазор надо снимать руками. */
.sh .sync-btn.btn-quiet{font-size:0;width:32px;min-width:32px;padding:0;
  gap:0;justify-content:center}
.sh .sync-btn.btn-quiet::before{content:"";width:15px;height:15px;
  background:currentColor;-webkit-mask:var(--знак-пересъём) center/contain no-repeat;
  mask:var(--знак-пересъём) center/contain no-repeat}
.sh .sync-btn.btn-quiet:disabled::before{opacity:.5}
.sh .btn-tinted.is-danger,.sh .btn-quiet.is-danger{color:var(--bad);background:var(--bad-bg)}
/* ═══ ЗНАЧКОВЫЕ КНОПКИ: РАМКА И РАЗМЕР (UI-121) ══════════════════════
   Владелец: «часть в рамке, часть маленькие и без рамок». Замер
   подтвердил: 24, 26, 30 и 40 пикселей, и одна кнопка с рамкой из пяти.
   Язык был почти последователен — не хватало ЗАПИСАННОГО ПРАВИЛА,
   поэтому размеры и расползлись по месту применения.

   РАМКА ОЗНАЧАЕТ «ЗДЕСЬ НАЧИНАЕТСЯ УПРАВЛЕНИЕ», как и скругление.
   Она нужна знаку, который стоит на поверхности САМ ПО СЕБЕ, среди
   содержимого, и должен быть найден: воронка отбора в полосе рядом
   с полями и переключателями. Без рамки она читалась бы как значок-
   украшение, а не как кнопка.
   Знаку ВНУТРИ уже обрамлённого предмета рамка не нужна: шапка модала,
   угол карточки, поле ввода — предмет её уже поставил, и вторая рамка
   внутри первой это рамка в рамке.

   РАЗМЕР БЕРЁТСЯ ОТ СОСЕДЕЙ, А НЕ ОТ ЗНАЧКА. Формулировка «в полосе»
   первой редакции не выдержала замера: шапка модала полосой отбора
   не является, а знаки в ней те же 30px — и правильно.
    · 30px — там, где рядом стоят или могут встать контролы стандартной
      высоты: полоса отбора, штамп, шапка модала. Правило языка «все
      контролы полосы одной высоты» относится и к знакам.
    · 24px — там, где вокруг СОДЕРЖИМОЕ: угол карточки, ячейка, поле
      ввода. Знак не должен спорить с текстом, и 24 — ровно нижняя
      граница цели (WCAG 2.5.8).
    · 40px — знак без соседей: бургер на марке. Мерить его нечем,
      кроме удобства руки.
   На пальце всё вырастает до 44px — правило крупных целей ниже.

   Промежуточных величин нет: 26px, встретившийся в замере, — это
   не решение, а остаток от высоты строки. */
.sh .icon-btn{width:30px;padding:0;background:none;border-color:transparent;color:var(--ann2)}
/* Знак СРЕДИ СОДЕРЖИМОГО — мельче. Правило написано по МЕСТУ, а не
   по кнопке: одна и та же кнопка в шапке и в карточке должна быть
   разной, и решает это место, а не тот, кто её ставит.
   `height`, а не только `min-height`: без него высоту добирала строка,
   и знак выходил 24×26 — величина, которой никто не назначал. */
.sh .board-card .icon-btn,.sh .card-corner .icon-btn,
:is(.sh .pair>.v,.sh td) .контакты>.icon-btn,.sh .ms--один>.ms-x{
  width:24px;height:24px;min-height:24px}
.sh .pj-updbtn{width:24px;height:24px;min-height:24px}
.sh .icon-btn:hover{background:var(--field);color:var(--ink);border-color:transparent}
.sh .icon-btn>svg,.sh .icon-btn>img{width:16px;height:16px;display:block}
.sh input,.sh select,.sh textarea{font:13px var(--sans);min-height:30px;padding:0 var(--s3);
  color:var(--ink);background:var(--sheet);border:1px solid var(--line);border-radius:var(--r-ctl)}
.sh textarea{padding:var(--s2) var(--s3);line-height:1.5}
.sh input::placeholder,.sh textarea::placeholder{color:var(--ann2)}
.sh .field{display:flex;flex-direction:column;gap:var(--s1);margin-bottom:var(--s4)}
/* UI-153: поле выбора проекта Notion в заявке IHB обёрнуто в `.ac` (под ним список подсказок) —
   столбец растягивает обёртку, а не поле, и оно стояло узким среди полей во всю ширину. */
.sh .field>.ac>input{width:100%}
.sh .field>.label,.sh .t-label{font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ann2);font-weight:500}
.sh .check{display:inline-flex;align-items:center;gap:var(--s2);min-height:30px;cursor:pointer}
.sh .check input{appearance:none;-webkit-appearance:none;margin:0;flex:0 0 15px;width:15px;height:15px;
  min-height:0;padding:0;border:1px solid var(--ann2);border-radius:var(--r-hair);
  background:var(--sheet);display:grid;place-content:center}
.sh .check input:checked{background:var(--mark);border-color:var(--mark)}
.sh .check input:checked::after{content:"";width:8px;height:8px;background:var(--sheet);
  clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%)}
.sh .filter,.sh .chip{font:13px var(--sans);min-height:30px;padding:0 var(--s3);
  border-radius:var(--r-ctl);border:1px solid var(--line);background:var(--sheet);color:var(--ink)}
.sh .filter[aria-pressed="true"],.sh .chip[aria-pressed="true"]{
  background:var(--ink);color:var(--sheet);border-color:var(--ink)}

/* --- полосы --- */
.sh .toolbar{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;flex:none;
  padding:var(--s3) var(--s4);background:var(--sheet);border-bottom:1px solid var(--line)}
.sh .pager{display:flex;align-items:center;gap:var(--s2);flex:none;padding:var(--s3) var(--s4);
  background:var(--sheet);border-top:1px solid var(--line);font-size:13px;color:var(--ann)}

/* --- доска --- */
.sh .board{display:flex;gap:var(--s3);padding:var(--s3) var(--s4);overflow-x:auto;min-height:0}
.sh .board-col{flex:0 0 var(--col-w,272px);display:flex;flex-direction:column;min-height:0;
  background:var(--field);border:none;border-radius:var(--r-zone)}
.sh .board-col>.head{display:flex;align-items:baseline;gap:var(--s2);padding:var(--s3) var(--s3) var(--s2);
  font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--ann);font-weight:600}
.sh .board-col>.head .count,.sh .board-col>.head .n{margin-left:auto;color:var(--ann2);
  font-weight:400;font-variant-numeric:var(--tab)}
.sh .board-col>.body{padding:0 var(--s2) var(--s2);display:flex;flex-direction:column;
  gap:var(--s2);overflow:auto;min-height:0}
.sh .board-col.is-over{outline:2px dashed var(--mark);outline-offset:-2px}
.sh .board-card{position:relative;display:block;background:var(--sheet);
  border:1px solid var(--hair);border-radius:var(--r-ctl);padding:var(--s3);
  color:inherit;text-decoration:none;cursor:pointer;box-shadow:none}
.sh .board-card:hover{border-color:var(--line);box-shadow:none}
.sh .board-card.is-static{cursor:default}
.sh .board-card.is-dragging{opacity:.5}
/* ═══ КАРТОЧКА — КЛЕТКА ШТАМПА, А НЕ МЕШОК (UI-74) ═══════════════════
   Замер до правки: 159…229 пикселей, размах 70 при одинаковом наборе
   полей. Вторая строка названия +18, третья +36, перенос юрлица +17,
   нет состояния −21, нет значков и номера −34.

   Плавающая высота стоит дороже, чем кажется. Колонка перестаёт быть
   сеткой: глаз не может считать карточки по вертикали, соседние
   колонки расходятся по строкам, и «третья сверху» в одной колонке
   не на одной линии с «третьей сверху» в другой. Хуже: высота
   становится ЗНАКОМ, которого никто не назначал, — карточка выше
   выглядит важнее, хотя означает лишь длинное имя.

   Штамп на чертеже так не работает. У него клетки на месте всегда,
   заполнены они или нет; пустая клетка — это тоже сведение.

   ЧЕТЫРЕ КЛЕТКИ ЗАКРЕПЛЕНЫ:
     состояние — одна строка, место держится и когда состояния нет;
     название  — ДВЕ строки всегда, длиннее обрезается многоточием;
     сведения  — четыре строки по одной, значение не переносится;
     подвал    — одна строка, место держится и без значков и номера.

   ЦЕНА НАЗВАНА: карточка становится 211px вместо нынешних 159…229,
   то есть на 18 выше самой частой (одностроковое имя со всеми полями).
   В колонку помещается на одну карточку меньше на каждые двенадцать.
   Обрезка имени — плата за то, что колонка стала сеткой; полное имя
   в одном щелчке, в карточке проекта. */
.sh .board-card .cat{display:flex;align-items:center;gap:6px;font-size:11px;
  color:var(--ann2);margin-bottom:5px;min-height:16px}
.sh .board-card .cat .sw{width:6px;height:6px;border-radius:50%}
/* Состояния нет — его строка всё равно есть. Не пустой элемент
   в разметке, а поле здесь: разметка общая с прежним оформлением,
   и заводить в ней пустышку значило бы менять чужой экран. */
.sh .board-card:not(:has(.cat)){padding-top:calc(var(--s3) + 21px)}
.sh .board-card .nm{font-size:14px;font-weight:600;line-height:1.3;letter-spacing:-.008em;
  margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;min-height:2.6em}
/* НАЗВАНИЕ УСТУПАЕТ МЕСТО УГЛОВОЙ КНОПКЕ (UI-130). Кнопка обновления
   стоит `position:absolute` в углу — то есть из потока вышла, и место
   под себя не просит. Название занимает всю ширину карточки и на второй
   строке уезжало ПОД кнопку: замер — 266 px² наложения на каждой
   карточке доски.
   Видно это стало только когда в фикстуре появились разные имена:
   пока все шесть проектов звались одинаково и коротко, наложение было
   тем же, а глазу незаметным. Дефект был всегда — не было случая,
   на котором он проявляется.
   Поле только у карточек С кнопкой: у остальных название имеет право
   на всю ширину. */
.sh .board-card:has(.card-corner) .nm{padding-right:22px}
/* ЗНАЧЕНИЕ НЕ РЕЖЕТСЯ — ОНО ПЕРЕНОСИТСЯ (UI-98). Обрезка была платой
   за ровную высоту, и плата оказалась не та: юрлица различаются
   ХВОСТОМ — «ООО Стереотактик», «…Про», «…Фильм», — и многоточие
   делало три разные компании одной. Владелец увидел это на бою.

   Замер: значению 128 пикселей, самому длинному юрлицу нужно 155.
   В одну строку не влезает никак — подпись «Направление» занимает 79
   из 219. Значит перенос, а высоту держит ЗАРЕЗЕРВИРОВАННАЯ пятая
   строка: четыре сведения плюс одно про запас. Карточка выросла
   на строку — это цена того, что значение читается целиком.

   Зазор сокращён с 12 до 8: четыре пикселя тоже работают на значение. */
.sh .board-card .facts{display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:3px var(--s2);margin:var(--s2) 0 0;font-size:12px;
  /* ПЯТЬ СТРОК НА ЧЕТЫРЕ СВЕДЕНИЯ: четыре и одна про запас. Перенос
     длинного значения занимает её, и высота карточки не меняется.
     Считается от кегля, а не вписана числом: 5 строк по 1.4em плюс
     три зазора по 3. Замер: короткие 76, с длинным юрлицом 93. */
  min-height:calc(5 * 1.4em + 9px);align-content:start}
.sh .board-card .facts dt{color:var(--ann2);white-space:nowrap}
/* Значение в одну строку. `min-width:0` обязателен: в гриде колонка
   по умолчанию не сжимается меньше содержимого, и многоточие
   не срабатывает вовсе — обрезка молча не работает. */
.sh .board-card .facts dd{margin:0;text-align:right;color:var(--ink);
  font-variant-numeric:var(--tab);min-width:0;overflow-wrap:anywhere}
.sh .board-card .facts dd.is-empty{color:var(--ann2);font-style:italic}
.sh .board-card .lead{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s3);margin:7px 0 9px}
.sh .board-card .lead .k{font-size:13px;color:var(--ann2)}
.sh .board-card .lead .v{font-size:23px;font-weight:600;letter-spacing:-.022em;
  font-variant-numeric:var(--tab)}
.sh .board-card .foot{display:flex;align-items:center;gap:var(--s2);margin-top:10px;
  padding-top:0;border-top:none;font-size:11px;color:var(--ann2);min-height:34px}
/* 34 — высота ряда со значками (значок 30 плюс его поле). Ставил
   26 «на глаз по кнопке» и получил остаточный размах в 8px:
   величину надо было СНЯТЬ, а не назначить. */
.sh .card-corner{position:absolute;top:var(--s1);right:var(--s1)}

/* Ссылка в ячейке реестра: 24 на точном указателе (WCAG 2.5.8 AA),
   44 на грубом — выше, в блоке грубого указателя. */
.sh table.grid a{display:inline-flex;align-items:center;min-height:24px}
.sh .link{display:inline-flex;align-items:center;min-height:24px}

/* ЛИПКАЯ КОЛОНКА РЕЕСТРА (UI-78). Прежний язык красит её своей
   палитрой (`--n0`/`--n1`), и путь у него длиннее нашего словаря:
   `.tn table.grid th.col-stick`. Отвечаем тем же путём — иначе под
   новым оформлением у шапки липкой колонки оставалась чужая подложка
   #FAFAF9. Поймано замером чужих цветов на «Контрактах».

   Подложка обязана быть НЕПРОЗРАЧНОЙ: под липкой колонкой едут
   соседние, и полупрозрачная показала бы их насквозь. */
.sh table.grid th.col-stick,.sh table.grid td.col-stick{background:var(--sheet)}
.sh table.grid thead th.col-stick{background:var(--field)}
.sh table.grid td.col-stick::after{background:var(--hair)}
/* CTR-19: у шапки черта — тенью: `::after` шапки занят стрелкой сортировки (см. design.css). */
.sh table.grid th.col-stick{box-shadow:inset -1px 0 var(--hair)}

/* ═══ СЛОВАРЬ СЛУЖЕБНЫХ ЛИСТОВ (UI-79) ══════════════════════════════
   Девять служебных экранов говорят почти теми же именами, что уже
   переведены. Замер по всем сразу: чаще прочего просят `table.tight`
   (пять экранов), `.hint` (три) и подписи пользователя (три).

   ЧАСТЬ ИЗ НИХ ЖИВЁТ В `app.css` БЕЗ СКОУПА, и это хуже пробела:
   `.hint` и `.btn-provider` объявлены без всякого языка и красятся
   материаловскими токенами — то есть на новом листе они не «не
   переведены», а активно говорят на третьем языке. Отвечаем именем
   в имя. */

/* ТАБЛИЦЫ В FAQ (UI-125). Они не реестр, а часть текста: «Раньше →
   Теперь», «Команда → Что делает». Сортировать их нечего, а вот
   говорить они обязаны тем же голосом. До этого их рисовал страничный
   `<style>` материаловскими токенами — 14 пикселей, начертание 700,
   рамка вокруг каждой клетки: третий язык на том же листе. */
.sh .faq table{border-collapse:collapse;width:100%;margin:var(--s3) 0}
.sh .faq th,.sh .faq td{border:none;border-bottom:1px solid var(--hair);
  padding:5px var(--s4) 5px 0;font-size:13px;text-align:left;vertical-align:top;
  color:var(--ink)}
.sh .faq th:last-child,.sh .faq td:last-child{padding-right:0}
.sh .faq tr:last-child td{border-bottom:none}

/* Тесная таблица — тот же реестр, только по содержимому и без рамки.
   Держим её на тех же величинах, что `.grid`: строка 28, подпись
   колонки 11 прописными, волос под строкой. */
.sh table.tight{border-collapse:collapse;width:auto}
.sh table.tight th,.sh table.tight td{padding:5px var(--s4) 5px 0;font-size:13px;
  border-bottom:1px solid var(--hair);white-space:nowrap;color:var(--ink)}
/* Шапка `.tight` описана вместе с шапкой `.grid` (UI-125) — здесь
   остаётся только то, что у тесной таблицы своё. */
.sh table.tight tr:last-child td{border-bottom:none}
/* ПОЛЕ У РАМКИ. Прежний язык ставит первой колонке нулевое левое поле —
   так она встаёт вровень с текстом над таблицей, и это верно, когда
   таблица стоит на листе открыто. Внутри рамки то же правило прижимает
   значение вплотную к линии: замер — ячейка на 250, рамка на 249.
   Крайним колонкам даём поле у самого края, внутренний ритм не трогаем. */
.sh table.tight th:first-child,.sh table.tight td:first-child{padding-left:var(--s3)}
.sh table.tight th:last-child,.sh table.tight td:last-child{padding-right:var(--s3)}

/* КНОПКИ В КЛЕТКЕ ДЕЙСТВИЙ СТОЯТ НА ОДНОМ УРОВНЕ (UI-134). Владелец:
   «кнопки не по уровню теперь». Замер: «повторить» и «удалить» стоят
   в одной клетке и расходятся по вертикали на 2,5 пикселя, а середина
   ни одной из них не совпадает с серединой поля частоты в соседней
   клетке.

   Причина в том, ЧЕМ они выровнены. Каждая кнопка обёрнута своей формой
   `display:inline`, и две формы рядом — это две строчные коробки,
   которые браузер ставит НА ОБЩУЮ БАЗОВУЮ ЛИНИЮ. Базовая линия зависит
   от шрифта содержимого, а содержимое у кнопок разное: у «повторить»
   слово уехало нулевым кеглем (UI-124), у «удалить» стоит обычный
   крестик. Одинаковые снаружи коробки сели на разной высоте.

   Правило: в клетке действий выравнивать по СЕРЕДИНЕ КОРОБКИ, а не по
   базовой линии текста, которого там нет. */
.sh .col-act{white-space:nowrap}
.sh .col-act > form{display:inline-flex;vertical-align:middle;margin:0}
.sh .col-act > form + form{margin-left:var(--s2)}

/* Подсказка тише подписи, а не громче — довод прежнего языка сохранён
   (приёмка 2026-08-06), меняется только палитра. */
.sh .hint{color:var(--ann2);font-weight:400;font-size:12px;margin-top:var(--s2)}
/* PROJ-22: счётчик предела длины под полем — вид «Листа»; смысл и слова в design.css и static/limit.js. */
.sh .limit-count{display:block;align-self:flex-end;text-align:right;font-size:12px;color:var(--ann2);font-variant-numeric:var(--tab);margin-top:var(--s1)}
.sh .limit-count[data-state="warn"]{color:var(--old)}
.sh .limit-count[data-state="over"]{color:var(--bad);font-weight:600}
/* КАРТОЧКА ПОЛОСЫ ПОД КУРСОРОМ «Загрузки» — `КарточкаПолосы` из ui.js (EMP-13; общая с EMP-33:
   лист «Сотрудники» и вкладка профиля). Вместо системной подсказки: та приходит с задержкой,
   рисуется шрифтом ОС и умеет одну строку. Имя для чтения с экрана — на самой полосе (`aria-label`). */
.sh .em-tip{position:fixed;z-index:60;pointer-events:none;max-width:22rem;
  background:var(--sheet);color:var(--ink);border:1px solid var(--line);box-shadow:var(--sh-pop);
  padding:var(--s3) var(--s3) var(--s2)}
.sh .em-tip h4{margin:0 0 var(--s1);font-size:13px;font-weight:600;line-height:1.3}
.sh .em-tip dl{margin:0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:2px var(--s2);font-size:12px}
.sh .em-tip dt{color:var(--ann2);white-space:nowrap}
.sh .em-tip dd{margin:0}
.sh .em-tip .st{margin:var(--s2) 0 0;font-size:11px;color:var(--ann2)}
.sh textarea[aria-invalid="true"],.sh input[aria-invalid="true"]{border-color:var(--bad)}

/* ИМЯ ЧЕЛОВЕКА И ПРОЕКТА — ЗНАЧЕНИЕ, АДРЕС И ЛОГИН — ПОМЕТКА.
   В разметке это голые крючки без стилей; язык даёт им смысл, а не
   украшение: имя читают, адрес сверяют. Логин моноширинный по той же
   причине, что коды в реестре, — его сличают посимвольно. */
.sh .u-name,.sh .pj-name{color:var(--ink)}
.sh .u-mail{color:var(--ann2);font-size:12px}
.sh .prov-login{color:var(--ann2);font-size:12px;font-family:var(--mono)}

/* ═══ ЦВЕТА КАТЕГОРИЙ (UI-120) ══════════════════════════════════════
   КАТЕГОРИЯ — НЕ СОСТОЯНИЕ, И ЭТО ГЛАВНОЕ РАЗЛИЧИЕ. Состояний четыре,
   они названы языком и означают «что случилось»: готово, просрочено,
   нет данных, отказ. Категорий сколько заведёт продукт, и они означают
   «что это»: съёмка, монтаж, графика. Смешивать их палитры нельзя —
   иначе «звук» начнёт читаться как предупреждение.
   Отсюда правило: тона категорий подобраны ПОДАЛЬШЕ от тонов состояний
   и никогда не заливают строку или карточку — только знак: галочку
   флажка, рамку невыбранной категории, букву плашки.

   ШЕСТЬ, А НЕ БОЛЬШЕ. Седьмой цвет уже не опознаётся без подписи,
   и список из семи читается быстрее, чем вспоминается палитра. Нужен
   седьмой разряд — значит разрядов слишком много для цвета, и различать
   их надо словом.

   Пока не используются: заведены по решению владельца 24.08.2026 —
   «цветные флажки нужны, они сейчас не используются, но будут». Витрина
   показывает их, чтобы, когда понадобятся, их не завели заново рядом. */
.sh{--кат-1:#1A4FA0; --кат-2:#0F6E77; --кат-3:#6A3FA0;
    --кат-4:#8A4B10; --кат-5:#A02D6B; --кат-6:#44566B}
.sh .tag--cat-1{color:var(--кат-1)}
.sh .tag--cat-2{color:var(--кат-2)}
.sh .tag--cat-3{color:var(--кат-3)}
.sh .tag--cat-4{color:var(--кат-4)}
.sh .tag--cat-5{color:var(--кат-5)}
.sh .tag--cat-6{color:var(--кат-6)}
/* КАТЕГОРИЯ ВИДНА И НЕ ОТМЕЧЕННОЙ — рамкой. Список отборов читают
   до того, как в нём что-то выбрали; если цвет появляется только
   с галочкой, до первого выбора список одноцветен и не помогает. */
.sh .check--кат-1 input{border-color:var(--кат-1)}
.sh .check--кат-2 input{border-color:var(--кат-2)}
.sh .check--кат-3 input{border-color:var(--кат-3)}
.sh .check--кат-4 input{border-color:var(--кат-4)}
.sh .check--кат-5 input{border-color:var(--кат-5)}
.sh .check--кат-6 input{border-color:var(--кат-6)}
.sh .check--кат-1 input:checked{background:var(--кат-1);border-color:var(--кат-1)}
.sh .check--кат-2 input:checked{background:var(--кат-2);border-color:var(--кат-2)}
.sh .check--кат-3 input:checked{background:var(--кат-3);border-color:var(--кат-3)}
.sh .check--кат-4 input:checked{background:var(--кат-4);border-color:var(--кат-4)}
.sh .check--кат-5 input:checked{background:var(--кат-5);border-color:var(--кат-5)}
.sh .check--кат-6 input:checked{background:var(--кат-6);border-color:var(--кат-6)}

/* Вход. Кнопка провайдера — единственный контрол на своём листе,
   и потому она первичная: чернило на листе, а не подложка. */
.sh .btn-provider{display:flex;width:100%;margin:var(--s2) 0;padding:var(--s3) var(--s4);
  border-radius:var(--r-ctl);border:1px solid var(--line);background:var(--sheet);
  color:var(--ink);font:500 14px var(--sans);text-decoration:none;cursor:pointer;
  align-items:center;justify-content:center;gap:var(--s2);min-height:44px}
.sh .btn-provider:hover{border-color:var(--heavy);background:var(--field)}
.sh .btn-provider.disabled{opacity:.5;cursor:not-allowed}
.sh .btn-provider.disabled:hover{border-color:var(--line);background:var(--sheet)}

/* ЧЕТЫРЕ ОСТАТКА ПОСЛЕ МОСТА — прямые обращения к шкале прежнего
   языка (`--st-ok`, `--n1`, `--n9`). Мост их не берёт намеренно:
   это ЕГО величины, и подменять их под собой значило бы не строить
   мост, а подделывать чужой словарь. Отвечаем именем в имя. */
.sh .ok{color:var(--ok)}
.sh .error{color:var(--bad)}
.sh .check{color:var(--ink)}
.sh .faq th,.sh .faq code{background:var(--field)}
.sh .faq th,.sh .faq td,.sh .faq code,.sh .faq figure img,
.sh .faq figure video{border-color:var(--hair)}

/* ПОДПИСЬ КАРТОЧКИ ОТЧЁТА (UI-84). Прежний язык красит её своей шкалой
   напрямую (`--n6`/`--n7`), путь у него длиннее словаря — отвечаем тем же.

   Вес значения снижен с 600 до 500 по тому же доводу, по которому
   на карточке проекта завели `.facts`: жирное значение рядом с бледной
   подписью кричит громче названия карточки, а название здесь — главное.
   Строение разметки не трогаем: «Агентство: Х» одной строкой — решение
   этого экрана, и менять его надо вместе с решением про саму карточку. */
.sh .board-card .meta{color:var(--ann2);border-color:var(--hair)}
.sh .board-card .meta b{color:var(--ink);font-weight:500}

/* ═══ ДОКУМЕНТ (UI-87) ═══════════════════════════════════════════════
   Аналитический отчёт — не лист, а документ: нумерованные разделы любой
   глубины, таблицы внутри них, источники внизу. Общего с листом у него
   рама и штамп; всё остальное своё.

   ШТАМП ДОКУМЕНТА В ДВА РЯДА. Решение владельца 22.08.2026 из двух
   черновиков. У настоящего углового штампа клеток много и они в
   несколько рядов — это и есть его устройство; отдельной шапки
   у документа после этого нет вовсе.

   Замер: 1440 и 1180 — 150 пикселей, все семь клеток второго ряда
   на месте. При 1024 ряд переносится; поведение на узком объявлено
   ниже отдельно, как у всех листов. */
/* ПУТЬ ДЛИННЕЕ БАЗОВОГО, А НЕ ПОЗЖЕ ЕГО. Базовая полоса штампа
   объявлена ниже по файлу тем же весом и перебивала `height:auto` —
   второй ряд рисовался и тут же обрезался `overflow:hidden`. Порядок
   правил — плохая опора: он меняется от любой вставки. Вес — хорошая. */
.sh .app-bar.app-bar--doc{flex-wrap:wrap;height:auto;align-items:flex-start;overflow:visible}
/* РЯД СВЕДЕНИЙ УКЛАДЫВАЕТСЯ САМ, БЕЗ ТОЧКИ ПЕРЕЛОМА. Первая редакция
   держала его строкой и объявляла перестроение при 860 — а клетка
   переставала влезать уже при 1100, и между этими числами она молча
   уходила за край. Точка перелома, назначенная на глаз, всегда мимо:
   ширину ряда решает не окно, а сумма клеток. `auto-fit` решает это
   сам на любой ширине, и назначать нечего. */
.sh .app-bar.app-bar--doc .ряд2{flex:1 0 100%;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  align-items:flex-start;gap:var(--s2) var(--s5);
  margin-top:11px;padding-top:11px;border-top:1px solid var(--hair)}
/* Ряд клеток, стоящий В ПОЛОСЕ ОДИН (плашка заявки отчёта): линия-отбивка
   отделяла его от первого ряда штампа, которого здесь нет. */
.sh .app-bar.app-bar--doc > .ряд2:first-child{margin-top:0;padding-top:0;border-top:0}
.sh .app-bar.app-bar--doc .cell{min-width:0}
.sh .app-bar.app-bar--doc .cell .fact{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Выбор версии — КЛЕТКА, а не кнопка: «какая версия открыта» это
   сведение о документе, а не действие над ним. Поле выбора раздето
   до текста, чтобы не спорить весом с соседними клетками. */
.sh .выбор-версии{border:none;background:none;color:var(--ink);font:500 14px var(--sans);
  min-height:0;padding:0 18px 0 0;margin:0;cursor:pointer;max-width:100%}
/* Счёт вместо перечня. Клетка держит одну строку, чего бы в ней ни было. */
.sh .cell .fact--список{display:inline-flex;align-items:center;gap:2px;overflow:visible}
/* ⓘ У СЧЁТА ОБЯЗАН РАСКРЫВАТЬСЯ. Правило выше (0,3,0) проигрывало
   `.app-bar--doc .cell .fact{overflow:hidden}` (0,5,0), и поповер
   с именами обрезался клеткой — «по клику ничего не появляется»
   (найдено владельцем 27.08.2026). Тот же класс мёртвого модификатора,
   что .col-num: специфичность, а не порядок. Шесть классов перебивают
   и узкую медиа-версию правила. */
.sh .app-bar.app-bar--doc .cell .fact.fact--список{overflow:visible}
/* Поповер наследует nowrap и right-align клетки — в 320px коробке это
   одна бесконечная строка. Возвращаем прозе прозу. */
.sh .app-bar .cell .how .body{white-space:normal;text-align:left;font-weight:400}
/* СПРАВКА У СЧЁТА ЯКОРИТСЯ ВПРАВО. Клетки команд стоят в правой части
   плашки, а поповер 320px рос от знака вправо — и выходил за лист:
   на 10px при 1440 и на 280 при 390 (замер аудита 28.08.2026, все
   состояния листа). Правый якорь растит его внутрь листа; на телефоне
   знак стоит у правого края строки — туда же. */
.sh .fact--список .how .body{left:auto;right:-8px}

/* Бриф — не клетка штампа: это проза, а у прозы нет ширины колонки. */
.sh .doc-бриф{margin:0;padding:var(--s3) var(--s4);background:var(--field);
  border-bottom:1px solid var(--hair)}
.sh .doc-бриф .k{display:block;font-size:11px;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ann2);margin-bottom:3px}
/* БРИФ — КОРОБКА НА ТРИ СТРОКИ, А НЕ ПОЛОТНО (решение владельца 09.09.2026).
   Бриф в заявку попадает письмом целиком: на боевом отчёте это тридцать строк,
   которые занимали пол-экрана и отодвигали вниз то, ради чего лист открывают, —
   сам отчёт. Три строки называют, о чём заявка; остальное прокручивается.

   ВЫСОТА ЗАДАНА, А НЕ ОГРАНИЧЕНА СВЕРХУ, и это ради ручки. `resize` даёт
   браузерную ручку в правом нижнем углу, но перетаскивание ставит элементу
   `height` — а `max-height` его тут же обрежет, и ручка окажется мёртвой.
   Поэтому высота по умолчанию — `height`, а `max-height` держит потолок,
   заведомо больший: тянуть можно до 60% высоты окна.

   Печать берёт бриф ЦЕЛИКОМ: на бумаге прокрутки нет, и обрезанный бриф
   в PDF был бы потерей данных, а не экономией места. */
.sh .doc-бриф .v{font-size:13px;line-height:1.55;color:var(--ink);white-space:pre-wrap;
  height:4.65em;min-height:1.55em;max-height:60vh;overflow:auto;resize:vertical}
@media print{.sh .doc-бриф .v{height:auto;max-height:none;overflow:visible;resize:none}}

/* ШТАМП ДОКУМЕНТА НА УЗКОМ ЛИСТЕ: БОЛЬШЕ РЯДОВ, МЕНЬШЕ КОЛОНОК.
   Так устроен и настоящий чертёж — узкий формат не выбрасывает клетки
   из штампа, он перестраивает их сетку.

   Замер до правки: на 600 и 390 клетки второго ряда выходили за полосу
   и молча обрезались `overflow`, а трёх из тринадцати не было видно
   вовсе. Это ровно тот класс дефекта, что UI-56: не «обрезано»,
   а «не о чем догадаться».

   ВЕРСИЯ ОСТАЁТСЯ, и это исключение из общего узкого правила. Оно
   прячет третью клетку штампа как справочную — у листов там «Версия»
   продукта, сведение. Здесь на том же месте ВЫБОР версии отчёта:
   не сведение, а единственный способ открыть предыдущую сборку.
   Убрать управление, потому что оно оказалось третьим по счёту, —
   это отказ по недоразумению.

   Исполнитель уходит в подвал папки, как на всех листах: он про того,
   кто смотрит, а не про то, на что смотрят. */
@media (max-width:860px){
  .sh .app-bar.app-bar--doc{height:auto;padding:var(--s2) var(--s3)}
  .sh .app-bar.app-bar--doc .cell:nth-child(3){display:flex}
  .sh .app-bar.app-bar--doc .ряд2{grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
    gap:5px var(--s3);margin-top:var(--s2);padding-top:var(--s2)}
  /* Первый ряд на узком тоже сетка: пять клеток строкой не встают,
     а перенос флексом даёт рваные ряды разной высоты. */
  .sh .app-bar.app-bar--doc{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
    gap:5px var(--s3);align-items:start}
  .sh .app-bar.app-bar--doc .ряд2{grid-column:1/-1}
  .sh .app-bar.app-bar--doc .grow{display:none}
  /* КЛЕТКА НА УЗКОМ — СТРОКА, А НЕ СТОЛБИК. Подпись над значением стоит
     36 пикселей; двенадцать таких клеток дают 315 — треть телефонного
     экрана до первой строки содержания. Строка «подпись слева, значение
     справа» стоит 20, и это те же двенадцать клеток, ни одна не спрятана.
     На узком формате настоящий штамп перестраивается так же. */
  .sh .app-bar.app-bar--doc .cell{flex-direction:row;align-items:baseline;
    justify-content:space-between;gap:var(--s2);min-height:0}
  .sh .app-bar.app-bar--doc .cell .k{font-size:10px;flex:none}
  .sh .app-bar.app-bar--doc .cell .fact,
  .sh .app-bar.app-bar--doc .cell h1{font-size:12px;min-height:0;
    overflow:hidden;text-overflow:ellipsis;text-align:right}
  .sh .app-bar.app-bar--doc .cell .sub{display:none}
}

/* ЛИПКАЯ СТОПКА ЛЮБОЙ ГЛУБИНЫ.
   Высота каждого уровня — величина языка, значит и смещение под ним
   тоже величина, а не результат замера скриптом: уровень добавляется
   ОДНОЙ строкой. Прежний язык считал так же, но останавливался на трёх.

   ДВА РАЗНЫХ ИМЕНИ — «где липнуть мне» и «где липнуть детям» — нарочно.
   Одно имя дало бы ссылку свойства на само себя, а это в CSS цикл:
   объявление становится недействительным целиком и молча. Поэтому
   смещение своего заголовка записано в самом правиле уровня, а `--stack`
   раздел выставляет ДЕТЯМ — и шапка таблицы липнет ровно под тем
   уровнем, внутри которого стоит, а не под самым глубоким возможным. */
/* ЛЕСТНИЦА ОБЪЯВЛЕНА НА САМОМ СКОУПЕ, А НЕ НА КОНТЕЙНЕРЕ `.doc`.
   Первая редакция вешала её на `.doc` — контейнер, который был
   в моём ЧЕРНОВИКЕ и которого в продукте нет ни на странице отчёта,
   ни в его модале. Величины не объявлялись нигде, `top: calc(var(--h1)
   + …)` становился недействительным целиком, и уровни ниже первого
   не липли вовсе, а первый липнул по литеральному нулю — заголовки
   наезжали друг на друга. Отказ бесшумный: невычислимый `calc`
   не пишет в консоль ничего.

   Владелец нашёл это и в модале, и на развёрнутой странице. Урок тот же,
   что с `.sh .app`: правило, написанное под разметку черновика, живёт
   ровно до встречи с настоящей. */
.sh{--h1:40px;--h2:34px;--h3:30px;--h4:28px;--h5:26px;--h6:26px}
/* Рамка вложенного раздела — из нашей шкалы. Всплыла только когда
   в заглушке появились вложенные уровни: на одном уровне рамке
   не с чем было соседствовать, и чужой цвет никто не видел. */
.sh .doc-sec{border-color:var(--hair)}
/* ГЛУБОКИЙ ЗАГОЛОВОК УХОДИТ ПОД МЕЛКИЙ, А НЕ ПОВЕРХ НЕГО. У всех
   уровней был один `z-index`, и порядок наложения решала разметка:
   при отлипании «1.2.1 Клиент» наезжал на «1 Идти ли в тендер», потому
   что стоит в разметке позже. Читается как поломка — глубокий уровень
   не может закрывать свой же родительский.

   Лестница наложения обратна лестнице смещений: чем мельче уровень,
   тем он выше. Шапка таблицы ниже всех — она содержимое раздела. */
.sh .doc-h{position:sticky;z-index:3;margin:0;background:var(--sheet);
  border-bottom:1px solid var(--hair);display:flex;align-items:center;
  gap:var(--s2);padding:0 var(--s4);letter-spacing:-.008em;color:var(--ink)}
.sh .doc-h .num{color:var(--ann2);font-weight:500;font-variant-numeric:var(--tab);flex:none}
/* ЗАГОЛОВОК НЕ РЕЖЕТ СЕБЯ ЦЕЛИКОМ — РЕЖЕТ ТОЛЬКО СВОЙ ТЕКСТ.
   Прежний язык ставит `overflow:hidden` на всю шапку раздела, чтобы
   длинное название не ломало её фиксированную высоту. Довод верный,
   исполнение слишком широкое: вместе с текстом обрезается и окно
   справки, которое из шапки всплывает, — знак нажимается, а читать
   нечего. Замер раскладки поймал 127 обрезанных пикселей.
   Обрезка переехала на сам текст, окну выход открыт. */
.sh .doc-h{overflow:visible}
/* ПОЛОСА РАЗДЕЛОВ ПЕРЕНОСИТСЯ, А НЕ УЕЗЖАЕТ ЗА КРАЙ. Она `inline-flex`
   и меряется по содержимому: пять названий дают 764 пикселя, и на
   телефоне 390 из них уходило за раму и молча обрезалось. Замер
   раскладки поймал ровно 390.

   Перенос, а не горизонтальная прокрутка: полоса прокрутки у нас
   появляется под курсором, а на телефоне курсора нет — уехавший
   раздел стал бы неизвестен. Три ряда кнопок некрасивы, зато все
   пять разделов видны. */
.sh .toolbar .seg{max-width:100%;flex-wrap:wrap}
/* Тот же случай в шапке модала (панель отчёта, 27.08.2026): у .seg
   flex:none — сам он не ужимается, и шесть подписей разделов рвали
   окно вправо. Найдено владельцем на бою. */
.sh .modal .line .seg{max-width:100%;flex-wrap:wrap}
.sh .doc-h .т{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* ═══ ЗАГОЛОВОК ЧИТАЕТСЯ КАК НАЧАЛО РАЗДЕЛА (UI-118) ═════════════════
   Владелец: «не видно начало новых разделов, заголовки не читаются как
   начала разделов». Причина в устройстве, а не в оформлении.

   ЛИПКОСТЬ ПОТРЕБОВАЛА ФИКСИРОВАННОЙ ВЫСОТЫ — иначе не сложить лестницу
   смещений, — а фиксированная высота отняла ВОЗДУХ НАД ЗАГОЛОВКОМ.
   Именно он и сообщает «здесь начался раздел»: над заголовком места
   должно быть больше, чем под ним. У нас его не было вовсе, сверху
   и снизу поровну — по нулю.

   ВТОРОЕ: ступени шкалы кончились на третьем уровне. 16 / 14 / 13 / 12 /
   12 / 12 при одном начертании — это две различимые ступени на шесть
   уровней. «1.2.1 Клиент» и «1.2.1.1 Строка» выглядели одинаково.

   ЧТО ПРАВИТСЯ ЗДЕСЬ, А ЧТО НЕТ. Отчёт владельца устраивает, и его вид
   не трогаем: первые три уровня остались как были. Правится ровно то,
   что сломано и в отчёте тоже:
    · воздух вернулся, но не в заголовок, а в РАЗДЕЛ (правило ниже):
      высота заголовка обязана остаться постоянной, иначе рассыплется
      лестница; отступ сверху её не трогает;
    · пятый и шестой уровни отличаются от четвёртого не кеглем, а
      НАБОРОМ — прописными с разрядкой. Там, где кегль кончился (12/12/12
      при одном начертании — две различимые ступени на шесть уровней),
      различие берут из другой оси.

   Витрина заголовками языка не пользуется вовсе: её разделы — ровня
   друг другу, липкая лестница им не нужна, а ориентир даёт оглавление.
   Механику отчёта нельзя переносить туда, где нет его задачи. */
.sh .doc-h[data-level="1"]{z-index:16;top:0;height:var(--h1);font-size:16px;font-weight:600;
  background:var(--field);border-bottom-color:var(--line)}
.sh .doc-h[data-level="2"]{z-index:15;top:var(--h1);height:var(--h2);font-size:14px;font-weight:600}
.sh .doc-h[data-level="3"]{z-index:14;top:calc(var(--h1) + var(--h2));height:var(--h3);
  font-size:13px;font-weight:600;color:var(--ann)}
.sh .doc-h[data-level="4"]{z-index:13;top:calc(var(--h1) + var(--h2) + var(--h3));height:var(--h4);
  font-size:13px;font-weight:600;color:var(--ann)}
.sh .doc-h[data-level="5"]{z-index:12;top:calc(var(--h1) + var(--h2) + var(--h3) + var(--h4));
  height:var(--h5);font-size:11px;font-weight:600;color:var(--ann);
  text-transform:uppercase;letter-spacing:.06em}
.sh .doc-h[data-level="6"]{z-index:11;top:calc(var(--h1) + var(--h2) + var(--h3) + var(--h4) + var(--h5));
  height:var(--h6);font-size:11px;font-weight:500;color:var(--ann2);
  text-transform:uppercase;letter-spacing:.06em}

/* ВОЗДУХ ПЕРЕД РАЗДЕЛОМ, А НЕ ВНУТРИ ЗАГОЛОВКА. Заголовок обязан
   держать постоянную высоту — на ней стоит лестница липкости. Отступ
   сверху её не трогает: он принадлежит разделу.
   Чем крупнее раздел, тем больше воздуха перед ним; первый раздел
   в документе воздуха не получает — над ним нет ничего, что нужно
   было бы отделить. */
.sh .doc-sec[data-level="1"]{margin-top:var(--s8)}
.sh .doc-sec[data-level="2"]{margin-top:var(--s6)}
.sh .doc-sec[data-level="3"]{margin-top:var(--s5)}
.sh .doc-sec[data-level="4"],.sh .doc-sec[data-level="5"],
.sh .doc-sec[data-level="6"]{margin-top:var(--s4)}
.sh .doc-sec:first-child{margin-top:0}
.sh .doc-sec > .doc-sec:first-of-type{margin-top:var(--s3)}
/* СОСТОЯНИЕ НА КРАЮ ПЛАШКИ-СВЁРТКИ — тот же приём, что у карточек доски
   (UI-100, `data-состояние`): тень внутрь, НЕ псевдоэлемент и НЕ border —
   border участвует в боксе и сдвинул бы содержимое помеченной плашки
   относительно соседних. Первая редакция красила край ВСЕЙ секции
   (.doc-sec) — снята приёмкой владельца 03.09.2026: полоса на секции
   рвалась об отступы содержимого и цепляла kv; корешок нужен у самого
   блока «Касдев · …» с кнопкой разворота. У .acc есть рамка и радиус —
   тень скругляется ими сама, как у карточки. Значение атрибута —
   состояние языка; правило только под «bad»: мёртвых правил не держим. */
.sh .acc[data-edge="bad"]{box-shadow:inset 3px 0 0 var(--bad)}
.sh .doc-sec[data-level="1"]{--stack:var(--h1)}
.sh .doc-sec[data-level="2"]{--stack:calc(var(--h1) + var(--h2))}
.sh .doc-sec[data-level="3"]{--stack:calc(var(--h1) + var(--h2) + var(--h3))}
.sh .doc-sec[data-level="4"]{--stack:calc(var(--h1) + var(--h2) + var(--h3) + var(--h4))}
.sh .doc-sec[data-level="5"]{--stack:calc(var(--h1) + var(--h2) + var(--h3) + var(--h4) + var(--h5))}
.sh .doc-sec[data-level="6"]{--stack:calc(var(--h1) + var(--h2) + var(--h3) + var(--h4) + var(--h5) + var(--h6))}
/* Шапка таблицы липнет под тем уровнем, ВНУТРИ которого стоит. Селектор
   не требует контейнера `.doc` — его в продукте нет; требование `.doc`
   и было причиной того, что шапка липла по нулю и уезжала под заголовок. */
/* ШАПКА ТАБЛИЦЫ ЛИПНЕТ К ТАБЛИЦЕ, А НЕ К ЛЕСТНИЦЕ ЗАГОЛОВКОВ (UI-122).
   Было `top: var(--stack)` — шапка вставала под стопкой заголовков
   раздела. Замысел понятен: и заголовки, и шапка остаются видны.
   На деле лестница вытолкнула шапку ВНУТРЬ таблицы.

   Механика: `sticky` не даёт элементу подняться выше `top` от верха
   прокрутки. Пока таблица стоит ниже 104px — всё в порядке. Но стоит
   ей оказаться выше (а это происходит при каждой прокрутке и сразу
   при раскрытии свёрнутого блока), шапка съезжает на 104px ВНИЗ
   и накрывает первые строки. Замер на отчёте: шапка на 84px от верха
   таблицы, наезжает на первую строку. Владелец: «в аналитических
   отчётах заголовки таблиц сломаны».

   Лестница — механизм ЗАГОЛОВКОВ: они вложены друг в друга, и стопка
   выражает вложенность. Таблица заголовком не является, она содержимое
   раздела, и её шапка принадлежит ей самой. Отсюда `top:0`: шапка
   липнет к своей прокручиваемой обёртке — там, где обёртка
   прокручивается, — и не липнет там, где прокручивать нечего.
   Ничего не выталкивается и не накрывается. */
/* ТАБЛИЦА В ДОКУМЕНТЕ ТЕЧЁТ, А НЕ ПРОКРУЧИВАЕТСЯ В СЕБЕ. Обёртка
   реестра несёт `max-height:70vh; overflow:auto` — верно для реестра,
   который занимает лист целиком, и неверно для таблицы внутри отчёта:
   там она делает из таблицы отдельное окошко, и прокрутка страницы
   «залипает» в нём.
   Второе следствие важнее первого: прокручиваемая обёртка становится
   ОПОРОЙ ЛИПКОСТИ. Пока она есть, шапка меряет свой `top` от неё,
   а не от страницы, — и лестница заголовков раздела для шапки
   недостижима. Именно это и ломало её раньше на 104px.
   Снимаем обёртке и потолок, и прокрутку: в документе таблица —
   часть текста, а не окно в нём. */
.sh .doc-sec .grid-wrap{max-height:none;overflow:visible}
/* Теперь опора липкости — сама страница, и шапка встаёт ПОД стопку
   заголовков раздела, внутри которого стоит. */
.sh .doc-sec table.grid thead th,.sh .doc-sec table.tight thead th{
  position:sticky;top:var(--stack,0px);z-index:10;background:var(--field)}

/* НА УЗКОМ ЭКРАНЕ ПРОКРУТКА ВОЗВРАЩАЕТСЯ, И ЭТО ОСОЗНАННЫЙ РАЗМЕН.
   Замер раскладки поймал цену правки выше: на 390px таблица без обёртки
   молча обрезается справа на 34px — а молчаливая обрезка хуже неудобной
   прокрутки, потому что о ней нельзя догадаться.
   Вместе с прокруткой возвращается и её липкая опора, поэтому шапка
   здесь меряет `top` от обёртки, а не от лестницы: на телефоне лестница
   и так съедает половину экрана, и подводить под неё ещё и шапку
   значило бы оставить под данные полосу в два пальца. */
@media (max-width: 860px){
  .sh .doc-sec .grid-wrap{overflow:auto}
  .sh .doc-sec table.grid thead th,.sh .doc-sec table.tight thead th{top:0}
}

/* ТАБЛИЦЫ ОТЧЁТА ШИРЕ ЛИСТА — ПРОКРУЧИВАЮТСЯ ВБОК И НА ШИРОКОМ
   (решение владельца 27.08.2026: «Выигранные» на восемь колонок
   обрезались справа без единого признака продолжения). Тот же размен,
   что в медиа-блоке выше: обёртка снова скроллпорт, шапка таблицы
   меряет top от неё и лестница заголовков её не подбирает.
   «Молчаливая обрезка хуже неудобной прокрутки» — правило уже
   заплачено на узком экране, здесь оно то же самое. Скоуп — только
   разделы отчёта (.rp-sec-body лист, .rp-sec модал): остальным
   документам таблицы по-прежнему текут. */
.sh .rp-sec-body .grid-wrap,.sh .rp-sec .grid-wrap{overflow:auto;
  /* Тени-подсказки прокрутки (приём Комарова, как у .tn .grid-wrap в
     app.css): край, за которым есть ещё колонки, отбрасывает тень;
     крышки цветом листа едут с содержимым и гасят её у домотанного
     края. Без них обрез на «Периоде» читался как конец таблицы —
     UI-56 в точности. Тень из чернил, не rgba: в тёмной теме чёрное
     на тёмном невидимо. */
  background-image:
    linear-gradient(to right, var(--sheet), transparent),
    linear-gradient(to left,  var(--sheet), transparent),
    linear-gradient(to right, color-mix(in srgb, var(--ink) 16%, transparent), transparent),
    linear-gradient(to left,  color-mix(in srgb, var(--ink) 16%, transparent), transparent);
  background-position:left center,right center,left center,right center;
  background-repeat:no-repeat;
  background-size:28px 100%,28px 100%,12px 100%,12px 100%;
  background-attachment:local,local,scroll,scroll}
.sh .rp-sec-body table.grid thead th,.sh .rp-sec table.grid thead th{top:0}

/* ТЕЛО РАЗДЕЛА НЕ КРАСИТ СЕБЯ. Оно красилось землёй (#EBEBEB) внутри
   белого листа — серая коробка в белой коробке, — и вторичный текст
   на ней давал контраст 4.28 при норме 4.5. Величина `--ann2` считана
   против БУМАГИ; положив её на землю, мы поменяли условие задачи,
   а не цвет текста. Лист и есть бумага, второй подложки ему не нужно:
   разделы отделяет липкий заголовок и волос под ним, а не заливка. */
.sh .rp-sec-body{background:transparent;border-radius:0;padding:var(--s3) var(--s4)}

/* Оговорка: подпись текстом, подробность за знаком. */
.sh .doc-справка{margin:var(--s3) 0;font-size:12px;color:var(--ann2)}
/* Содержимое оговорки бывает длиннее одного абзаца — окно не растёт
   бесконечно, а прокручивается внутри себя. */
.sh .how .body{max-height:min(40vh,320px);overflow:auto}
.sh .how .body p{margin:0 0 var(--s2)}
.sh .how .body p:last-child{margin-bottom:0}

/* ШКАЛА РАСХОДА (UI-93). Всплыла в замере только сейчас: блока VPN
   не было в заглушке, и его цвета не проверялись ничем.

   ЗАЛИВКА — КОЛИЧЕСТВО, А НЕ СОСТОЯНИЕ, и потому она чернильная,
   а не цветная. Довод не наш, он записан прежним языком в самом
   шаблоне профиля: «это КОЛИЧЕСТВО, а не состояние». Прежний язык
   при этом красил её акцентом — тем самым коричневым, который
   на снимке читается как тревога при 83 % из 100.

   Пороги остаются цветными, и вот они как раз состояние: 75 % —
   «скоро кончится», 90 % — «кончается». Там цвет говорит. */
/* ФОРМА ШКАЛЫ ЖИВЁТ В ЯЗЫКЕ, А НЕ В СТРАНИЧНОМ СТИЛЕ. Она была
   объявлена в `<style>` профиля — и на карточке пользователя, где
   те же данные, шкалы не было вовсе: разметка есть, правил нет.
   Доля от лимита — величина, и у величины есть длина; показывать её
   одними числами значит заставлять сравнивать в уме. */
.sh .vpn-bar{height:12px;margin:var(--s1) 0;background:var(--field);
  border-radius:var(--r-pill);overflow:hidden}
.sh .vpn-bar > span{display:block;height:100%;border-radius:var(--r-pill)}
.sh .vpn-bar > span{background:var(--bar)}
.sh .vpn-bar.hi > span{background:var(--old)}
.sh .vpn-bar.crit > span{background:var(--bad)}
/* Код набирается на поле листа, а не на чужой подложке. Ключ
   подключения — тот же код, но блоком: его выделяют и копируют
   целиком, поэтому у него своё поле и перенос по любому символу. */
.sh code{background:var(--field);border-radius:var(--r-hair);padding:0 4px}
.sh .vpn-key code{background:var(--field);border:1px solid var(--hair);
  padding:var(--s2) var(--s3);border-radius:var(--r-ctl);display:block;
  word-break:break-all;font-size:12px}

/* ═══ ПОЛЕ ДАТЫ ПО СЕГМЕНТАМ И КАЛЕНДАРЬ (UI-97) ═════════════════════
   Всплыли, когда в аудит завели состояние «форма нового отчёта»:
   до этого модал не открывался ни разу, и семнадцать находок в нём
   не видел никто. Форма — самое частое место ошибки в любом продукте
   и самое редкое место замера.

   Устройство поля берём у прежнего языка целиком — оно верное и
   выстрадано (UI-32): нативный `<input type=date>` показывает формат
   и подписи, которых продукт не выбирает, а «06.07» и «07.06» — разные
   дни. Меняем только палитру и величины на наши. */
/* ЗНАЧОК КАЛЕНДАРЯ ОПИСЫВАЕТ ЯЗЫК, А НЕ СОСЕД (UI-123). Правило жило
   только в `app.css` под `.tn`, и всюду, где рядом нет прежнего
   оформления, значок выходил ЧЁРНЫМ КВАДРАТОМ: у SVG без правил заливка
   по умолчанию, а рисунок здесь сделан штрихом. Владелец увидел это
   на странице сравнения — там `app.css` не подключён.
   Третий пробел того же рода за сутки (после `--fill` и `.modal--full`):
   язык обязан описывать СВОЁ, иначе он держится на соседе и падает,
   как только сосед отходит.
   Размер от ШРИФТА, а не в пикселях: знак стоит внутри поля, в одной
   строке с цифрами, и обязан меняться вместе с их кеглем. */
.sh .cal-ico{width:1.05em;height:1.05em;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.sh .date-seg{display:inline-flex;align-items:baseline;border:1px solid var(--line);
  border-radius:var(--r-ctl);background:var(--sheet);padding:0 var(--s2);
  min-height:30px;font-variant-numeric:var(--tab)}
.sh .date-seg:focus-within{outline:2px solid var(--mark);outline-offset:1px;
  border-color:var(--mark)}
.sh .date-seg > input{font:13px var(--sans);font-variant-numeric:inherit;border:none;
  background:none;padding:0;text-align:center;min-height:0;color:var(--ink)}
.sh .date-seg > input:focus-visible{outline:none;background:var(--field);
  border-radius:var(--r-hair)}
.sh .date-seg > span[aria-hidden]{color:var(--ann2);padding:0 1px}
.sh .date-seg > .cal-btn{margin-left:var(--s1);min-height:24px;min-width:24px;
  border:none;background:none;color:var(--ann2);padding:0;
  display:inline-flex;align-items:center;justify-content:center}
.sh .date-seg > .cal-btn:hover{color:var(--ink)}
/* СЕГМЕНТ — САМОСТОЯТЕЛЬНАЯ ЦЕЛЬ, и норма на него распространяется:
   щелчок по «мм» ставит курсор в месяц, а не в день, значит это
   отдельное место нажатия, а не часть одного. Замер: 16×16 при норме 24.

   Высоту даём даром — сегмент растягивается на всё поле, вид при этом
   не меняется вовсе: у него нет ни подложки, ни рамки. Ширину
   доводим до нормы явно; дата от этого становится просторнее,
   и это цена попадания пальцем, а не украшение. */
.sh .date-seg{padding-top:0;padding-bottom:0;align-items:stretch}
.sh .date-seg > input{display:flex;align-items:center;justify-content:center;
  min-height:24px;min-width:24px}
.sh .date-seg > .y{min-width:40px}
.sh .date-seg > span[aria-hidden]{display:flex;align-items:center}

.sh .cal{background:var(--sheet);border:1px solid var(--line);
  border-radius:var(--r-zone);box-shadow:var(--sh-pop)}
.sh .cal-dow{font-size:11px;color:var(--ann2)}
.sh .cal-day{background:none;border:1px solid transparent;
  border-radius:var(--r-ctl);color:var(--ink);cursor:pointer}
.sh .cal-day:hover{background:var(--field)}
.sh .cal-day.is-off,.sh .cal-day.is-holiday{background:var(--wknd);color:var(--ann)}
.sh .cal-day.is-today{border-color:var(--mark);color:var(--mark);font-weight:600}
.sh .cal-day.is-sel{background:var(--ink);color:var(--sheet);border-color:var(--ink)}

/* НАДПИСЬ «ЗАГРУЗКА…» СТОИТ НА ЗЕМЛЕ, А НЕ НА БУМАГЕ (UI-97).
   Величина `--ann2` считана против бумаги и на земле даёт 4.28 при
   норме 4.5. Текст живёт секунду, но секунда — это тоже время, когда
   на него смотрят; к тому же на медленной сети он живёт дольше.
   Берём ступень темнее: на земле она даёт 5.6. */
.sh .mount-fill > .muted{color:var(--ann)}

/* ═══ СКЕЛЕТ ЗАГРУЗКИ (UI-102) ═══════════════════════════════════════
   Каркас листа известен ДО данных: рама, штамп, полоса отбора и число
   колонок не изменятся от того, что приедет. Показывать вместо всего
   этого одну крутилку — значит прятать то, что уже готово, и заодно
   терять место: страница дважды перекладывается, сначала пустая,
   потом полная.

   Чертёж и здесь подсказывает: сначала проводят линии, потом наносят
   размеры. Скелет — это лист с проведёнными линиями и ещё не
   вписанными значениями.

   САМИ ПОЛОСЫ ОПИСАНЫ В app.css, а не тут: разметка ожидания одна
   на оба оформления, а прежнее `sheet.css` не грузит — опиши полосы
   только здесь, и большинство, которое пока на прежнем, увидело бы
   вместо скелета пустые карточки. Язык уточняет своё: тон берёт
   токеном (на ступень темнее ОБЕИХ подложек — белого листа и серой
   подложки колонки, иначе полоса в заголовке колонки исчезает) и
   скругление. */
.sh{--sk-фон:var(--hair)}
.sh .sk{border-radius:var(--r-hair)}
.sh .sk--загл{margin-bottom:var(--s2)}

/* Скелет карточки повторяет её устройство: название, сведения, подвал.
   Не «серый прямоугольник вместо карточки», а та же карточка без
   вписанных значений — иначе при появлении данных лист перекладывается,
   и глаз теряет место. Замер: 210px против 207px у настоящей. */
/* Полосы имени — в высоту его строки (14px × 1.3), а не в свою:
   заготовка обязана занять ровно ту же высоту, что займёт имя. */
.sh .board-card.is-sk .nm .sk--загл{height:calc(1.3em - 4px);margin-bottom:4px}
.sh .board-card.is-sk .facts .sk{width:100%}

/* --- реестр --- */
.sh .grid-wrap{overflow:auto;background:var(--sheet);border:1px solid var(--hair);
  border-radius:var(--r-ctl);max-height:var(--grid-max,70vh)}

/* ═══ РЕЕСТР ВО ВЕСЬ ЛИСТ (UI-103) ═══════════════════════════════════
   ЯЗЫК ОБЯЗАН ОБЪЯВЛЯТЬ СВОИ МОДИФИКАТОРЫ САМ. `--fill` были описаны
   только в прежнем оформлении (`.tn .grid-wrap--fill`, `.tn .board--fill`),
   и новый язык молча ими пользовался: оба класса стоят на одном `<body>`.
   Работало это ровно до столкновения. `.sh .grid-wrap` выше и
   `.tn .grid-wrap--fill` — обе весом 0-2-0; при равной специфичности
   решает порядок подключения, а `sheet.css` грузится ПОСЛЕ `design.css`.
   Потолок 70vh побеждал отмену потолка.
   Замер владельца на проде: окно 800px, реестр 560px = ровно 70vh,
   под ним пустая полоса в треть экрана, и она не менялась с размером окна.
   Доске повезло: её потолок никто не переобъявлял, и отказ спал.

   Отсюда правило, а не заплатка: высота реестра и доски в языке «Лист»
   объявлена ЗДЕСЬ. Когда прежнее оформление уйдёт, цепочка не порвётся —
   сейчас порвалась бы, и снова бесшумно. */
.sh .grid-wrap--fill{max-height:none;min-height:0;flex:1 1 auto}
.sh .board--fill{align-items:stretch;flex:1 1 auto;min-height:0}
/* ═══ ШАПКА ТАБЛИЦЫ ОДНА НА ВЕСЬ ПРОДУКТ (UI-125) ═══════════════════
   Владелец: «проверь чтобы ВСЕ ТАБЛИЦЫ были с такими заголовками и
   с функцией сортировки — сейчас „одинаковость“ не соблюдена внутри
   продукта».

   Замер по тринадцати листам подтвердил три разных шапки:
     · `.grid` — белая, как лист, снизу линия;
     · `.tight` (девять служебных таблиц) — прозрачная, начертание 600;
     · таблицы в FAQ — вовсе без языка: 14 пикселей, начертание 700.
   Хуже того, ВНУТРИ одной таблицы шапка расходилась сама с собой:
   у липкой первой колонки фон был `--field`, у остальных клеток —
   `--sheet`, и на контрактах шапка читалась половиной серой,
   половиной белой. Шов был виден и не значил ничего.

   ПОДЛОЖКА, А НЕ ЛИСТ. Шапка — не данные, а имена колонок; она
   принадлежит раме, а не содержимому. Тон `--field` тот же, что
   у поля ввода: «здесь не значение, здесь устройство».

   СОРТИРУЕМАЯ КОЛОНКА ГОВОРИТ ОБ ЭТОМ САМА. Раньше на проектах
   сортировка работала, но выглядела как обычный текст: курсор
   не менялся, а наведение красило подпись материаловским токеном
   `--md-primary` — то есть на новом листе она говорила третьим языком.
   Теперь состояние ведёт `aria-sort`: одно и то же свойство и для
   скринридера, и для глаза.

   МЕСТО ПОД СТРЕЛКУ ЗАНЯТО ВСЕГДА. Стрелка, появляющаяся при нажатии,
   толкала бы соседние подписи — таблица дёргалась бы при каждой
   сортировке. Треугольник нарисован границами, а не набран: символы
   ▲ и ▼ пришли бы из запасного шрифта, как ↻ до UI-124. */
.sh table.grid th,.sh table.tight th,.sh .faq table th{
  background:var(--field);color:var(--ann2);font-weight:500;font-size:11px;
  letter-spacing:.05em;text-transform:uppercase;text-align:left;
  border-bottom:1px solid var(--line)}
.sh th[aria-sort]{cursor:pointer;user-select:none;-webkit-user-select:none}
.sh th[aria-sort]:hover{color:var(--ink)}
.sh th[aria-sort]:focus-visible{outline:2px solid var(--mark);outline-offset:-2px}
.sh th[aria-sort]::after{content:"";display:inline-block;width:0;height:0;
  margin-left:.45em;vertical-align:.05em;border:4px solid transparent;opacity:0}
.sh th[aria-sort="ascending"]::after{border-bottom-color:currentColor;
  margin-bottom:4px;opacity:1}
.sh th[aria-sort="descending"]::after{border-top-color:currentColor;
  margin-top:4px;opacity:1}
.sh th[aria-sort="ascending"],.sh th[aria-sort="descending"]{color:var(--ink)}
/* Число сортируется по правому краю — стрелка встаёт слева от подписи,
   иначе она уезжает от колонки цифр, к которой относится. */
.sh th.num[aria-sort]::after{margin-left:0;margin-right:.45em;float:left}

.sh table.grid{border-collapse:separate;border-spacing:0;width:max-content;font-size:13px}
.sh table.grid th{position:sticky;top:0;z-index:2;background:var(--field);color:var(--ann2);
  font-weight:500;font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  text-align:left;padding:var(--s2) var(--s3);white-space:nowrap;border-bottom:1px solid var(--line)}
.sh table.grid td{padding:0 var(--s3);height:var(--rh);white-space:nowrap;
  border-bottom:1px solid var(--hair);vertical-align:middle}
.sh table.grid th.num,.sh table.grid td.num{text-align:right;font-variant-numeric:var(--tab)}
.sh table.grid tbody tr:hover td{background:var(--field)}
.sh table.grid td.col-stick,.sh table.grid th.col-stick{position:sticky;left:var(--stick-left,0);
  z-index:2;background:var(--sheet)}
.sh table.grid thead th.col-stick{z-index:3}

/* --- всплывающее --- */
.sh .modal-backdrop{position:fixed;inset:0;background:var(--scrim);display:flex;
  align-items:flex-start;justify-content:center;padding:6vh var(--s4);z-index:50;overflow-y:auto}
.sh .modal{background:var(--sheet);border:1px solid var(--line);border-radius:var(--r-ctl);
  box-shadow:var(--sh-pop);width:100%;max-width:720px;max-height:calc(100dvh - 12vh);
  display:flex;flex-direction:column;min-height:0}
/* РАЗВЁРНУТОЕ ОКНО — ТОТ ЖЕ ОТКАЗ, ЧТО БЫЛ У РЕЕСТРА (UI-105).
   `.modal--full` объявлен только в прежнем оформлении, и новый язык
   молча им пользовался: оба класса стоят на одном `<body>`. Но
   `.tn .modal--full{max-width:none}` и `.sh .modal{max-width:720px}`
   весят одинаково (0-2-0), а `sheet.css` грузится ПОЗЖЕ — ширина
   побеждала отмену ширины. Кнопка при этом честно переключалась:
   подпись менялась на «Свернуть окно», значок на ⤡, `aria-pressed`
   на true. Отказ был не в кнопке, а в том, что её состояние ничего
   не меняло, — и владелец справедливо прочитал это как «не отрабатывает».

   Второй случай подряд из одного корня: язык обязан объявлять СВОИ
   модификаторы сам. Третьего не будет — правило записано здесь. */
.sh .modal--full{max-width:none;width:calc(100vw - var(--s4) * 2);
  height:calc(100dvh - 12vh)}

.sh .modal>.head{display:flex;align-items:baseline;gap:var(--s2);flex:none;
  padding:var(--s3) var(--s4);border-bottom:1px solid var(--hair);font-size:14px;font-weight:600}
.sh .modal>.body{flex:1;min-height:0;overflow:auto;padding:0 var(--s4) var(--s4)}
.sh .modal>.body>:first-child{margin-top:var(--s4)}
.sh .modal>.foot{display:flex;gap:var(--s2);justify-content:flex-end;align-items:center;
  flex:none;padding:var(--s3) var(--s4);border-top:1px solid var(--hair)}
.sh .menu,.sh .ac-list,.sh .ms-pop{background:var(--sheet);border:1px solid var(--line);
  border-radius:var(--r-ctl);box-shadow:var(--sh-pop);padding:var(--s2) 0}
/* ВСПЛЫВАЮЩЕЕ В ОКНЕ — НА ТОЙ ЖЕ БУМАГЕ, ЧТО НА ЛИСТЕ (DIR-17, 18.09.2026).
   Третий случай из корня UI-105 (выше, у `.modal--full`): язык TN назначает
   всплывающему внутри окна свой уровень глубины — `.tn .modal .ms-pop` и соседи
   красят фон в `--surface-over` (#272730) и весят 0-3-0 против 0-2-0 у `.sh .ms-pop`.
   Список в окне правки карточки специалиста открывался серым, а те же списки
   отбора на листе — синькой; владелец заметил по снимкам. У «Листа» глубину
   всплывающего несут рамка и тень, а не другой тон: бумага одна. Равный вес,
   и `sheet.css` грузится позже — поэтому правило побеждает. */
.sh .modal .menu,.sh .modal .ac-list,.sh .modal .ms-pop,.sh .modal .cal{background:var(--sheet)}
.sh .menu-item{display:flex;align-items:center;gap:var(--s2);padding:6px var(--s4);
  min-height:34px;font-size:13px;color:var(--ink);cursor:pointer;background:none;
  border:none;border-radius:0;width:100%;text-align:left;justify-content:flex-start}
/* ВЫБРАННЫЙ ПУНКТ — ЧЕРНИЛОМ, А НЕ СИНИМ TN (UI-146). В языке TN лежит
   `.tn .menu-item[aria-selected="true"]{background:var(--accent-soft);color:var(--accent)}`
   той же тяжести 0-3-0. Фон мы перебивали, а цвет — нет, и выбранное значение
   списка на «Листе» было синим `#2f5bd0` (в тёмной `#7d9dff`), мимо палитры;
   рамка без стиля берёт тот же `currentColor`. Нашлось первым состоянием
   аудита, открывшим список выбора одного значения («год раскрыт» в очереди
   архивирования): тот же урок, что с рамкой фокуса выше — не мерили, не видели.
   Для списка автокомплита у TN своё правило тяжелее (0-4-0) — повторяем его форму. */
.sh .menu-item:hover,.sh .menu-item[aria-selected="true"]{background:var(--field);color:var(--ink)}
.sh .ac-list .menu-item[aria-selected="true"]{background:var(--field)}
/* ФЛАЖОК ВНУТРИ ПУНКТА НЕ НЕСЁТ СВОЕЙ ВЫСОТЫ (UI-107). `.check` объявлен
   с `min-height:30px` — и правильно: флажок, стоящий в форме сам по себе,
   обязан быть целью в 30 пикселей. Но в пункте меню цель — ВСЯ СТРОКА,
   и собственная высота флажка складывалась с высотой пункта.
   Замер: пункт 55px вместо 34, флажок прижат к верху, подпись на 6px
   ниже его центра — ровно то, что владелец увидел как «проблемы
   с paddings». Список из пяти значений занимал 275px там, где хватает 170.

   Правило общее и по МЕСТУ, а не по списку: любой флажок внутри строки
   меню — часть строки, а не отдельный элемент управления. */
.sh .menu-item > .check{min-height:0;align-self:center}
.sh .ac-hint{font-size:11px;color:var(--ann2);padding:var(--s1) var(--s4)}

/* ═══ СВЁРНУТЫЙ БЛОК (UI-118) ════════════════════════════════════════
   `.acc` описан только в прежнем оформлении (app.css), и в отчёте
   свёрнутые блоки — «Выигрыш», «Проигрыш», «Текущие тендеры» — до сих
   пор рисовал ОН: чужие токены, чужой радиус, синий треугольник мимо
   палитры. Внутри «Листа» это читалось как чужая вставка, а тело блока
   без своего отступа — как поломка отступов (замечено владельцем).

   Знак свёртки — тот же, что у отбора (`«`/`»` в `.flt`): язык уже
   выбрал набранный знак вместо рисованного, и заводить второй способ
   показывать «раскрыто/свёрнуто» незачем. */
.sh .acc{border:1px solid var(--hair);border-radius:var(--r-ctl);
  margin:var(--s2) 0;background:var(--sheet)}
.sh .acc > summary{cursor:pointer;padding:var(--s2) var(--s4);font-size:13px;
  font-weight:600;color:var(--ink);list-style:none;display:flex;align-items:center;
  gap:var(--s2);min-height:34px;border-radius:var(--r-ctl)}
.sh .acc > summary::-webkit-details-marker{display:none}
.sh .acc > summary::before{content:"›";color:var(--ann2);font-weight:400;
  transition:transform .15s;flex:none;line-height:1}
.sh .acc[open] > summary::before{transform:rotate(90deg)}
.sh .acc > summary:hover{background:var(--field)}
.sh .acc[open] > summary{border-bottom:1px solid var(--hair);
  border-radius:var(--r-ctl) var(--r-ctl) 0 0}
.sh .acc-body{padding:var(--s3) var(--s4) var(--s4);border-top:none}

/* ═══ ШКАЛА РАСХОДА (`.q-bar`) ═══════════════════════════════════════
   Расход из лимита — величина, которую показывают и админский раздел
   «Доступ в интернет», и профиль сотрудника. В прежнем оформлении она
   описана в app.css; здесь — та же роль словами «Листа».

   Чернила измерения у языка уже есть: `--bar` рисует все полосы срока
   в лестнице, `--bar-done` — их подложку. Заводить для расхода третий
   цвет незачем: это тот же жанр — «сколько от целого».

   Пороги 75/90 % отданы `--old`/`--bad`. Натяжка та же, что в прежнем
   оформлении, и называется вслух: эти цвета язык объявляет про доверие
   к числу, а здесь они значат «близко к лимиту». Шкалы серьёзности,
   отдельной от состояний данных, у языка нет. */
.sh .q-bar{height:8px;border-radius:0;background:var(--bar-done);
  overflow:hidden;margin:var(--s1) 0}
.sh .q-bar > span{display:block;height:100%;background:var(--bar);border-radius:0}
.sh .q-bar.hi > span{background:var(--old)}
.sh .q-bar.crit > span{background:var(--bad)}
/* СТРОКА ПОИСКА В СПИСКЕ. Отбита от значений линией, а не воздухом:
   воздух в списке уже означает «пункт», и лишний зазор читался бы как
   пустая строка. Ширина по краям всплывающего окна с теми же полями,
   что у пунктов, — поиск стоит в их колонке, а не над ней. */
.sh .ms-search{display:block;width:calc(100% - var(--s4) * 2);
  margin:0 var(--s4) var(--s2);padding:0 var(--s2);min-height:28px;
  font:13px var(--sans);color:var(--ink);background:var(--sheet);
  border:1px solid var(--line);border-radius:var(--r-ctl)}
.sh .ms-search:focus{outline:none;border-color:var(--ann2)}
.sh .menu:has(> .ms-search){padding-top:var(--s2)}

/* ВЫБОР ОДНОГО ЗНАЧЕНИЯ (UI-112). В форме поле занимает всю ширину —
   в отличие от отбора, где кнопка меряется своей подписью: там она
   стоит в ряду себе подобных, здесь — в колонке полей, и колонка
   задаёт ширину всем одинаково.

   МЕСТО ПОД КРЕСТИК ОТВЕДЕНО ВСЕГДА, даже когда его нет. Иначе выбор
   значения сдвигал бы само значение влево, а сброс — вправо: поле
   дёргалось бы на каждое действие. */
.sh .ms--один{display:block;position:relative}
.sh .ms--один>.ms-btn{width:100%;justify-content:flex-start;padding-right:52px}
.sh .ms--один>.ms-btn>.val{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sh .ms--один>.ms-btn>.arrow{position:absolute;right:var(--s3);pointer-events:none}
/* UI-161: по вертикали — растяжкой top/bottom и margin:auto, а не
   translateY(-50%). На теле оба класса оформления, и `.tn button:active`
   (scale(.97), design.css) весомее этого правила: на нажатии он заменял
   сдвиг, и крестик падал на полвысоты за край поля. Рамка фокуса — внутрь:
   поле сжимается по тексту, до значения всего 3px, и внешняя рамка
   (2+1px) ложилась на буквы. */
.sh .ms--один>.ms-x{position:absolute;top:0;bottom:0;margin:auto 0;right:26px;
  width:24px;min-height:24px;padding:0;display:inline-flex;align-items:center;
  justify-content:center;font-size:15px;line-height:1;color:var(--ann2);
  background:none;border:none;border-radius:var(--r-hair);cursor:pointer}
.sh .ms--один>.ms-x:hover{color:var(--ink);background:var(--field)}
.sh .ms--один>.ms-x:focus-visible{outline-offset:-2px}
/* Список шириной с поле. `max-width:340px` прежнего оформления
   пережил бы `left/right`: ширину задаёт не растяжение, а потолок. */
.sh .ms--один>.ms-pop{left:0;right:0;max-width:none}
/* Хост поднятого нативного списка занимает место списка, который он
   заменил: в полосе действий админки поля стоят в ряд, в профиле —
   колонкой, и ширину в обоих случаях задаёт место, а не виджет. */
.sh .ms-хост{display:block;min-width:0}
.sh .admin-actions .ms-хост,.sh .toolbar .ms-хост{display:inline-block;min-width:12rem}

/* ВЫНЕСЕННЫЙ СПИСОК СТОИТ НАД ВСЕМ (UI-113). Координаты ему считает
   и ставит скрипт — здесь только слой: список всплывает поверх модала
   (60) и всего, что модал накрыл. Высоту ограничивает не это правило,
   а расчёт: сколько места осталось до края окна, столько и дано. */
/* ВЫСОТУ СЧИТАЕТ СКРИПТ, ПРОКРУТКУ ОБЯЗАН ДАТЬ ЯЗЫК (найдено владельцем
   02.09.2026). `useПлавающийСписок` ставит `.ms-pop` предельную высоту по
   остатку места до края окна — но `overflow` у неё не было, и содержимое
   просто вылезало за предел: отбор с двумя сотнями значений («Клиенты» на
   /directors) рисовался во всю длину и не прокручивался вовсе. Предел высоты
   без прокрутки — не предел, а обещание, которого никто не держит.
   ПРОКРУЧИВАЕТСЯ СПИСОК, А НЕ ВСЯ ПЛАШКА: поле поиска обязано остаться на
   виду, иначе оно уезжает вверх ровно тогда, когда им начали пользоваться.
   Отсюда колонка-флексбокс; `min-height:0` обязателен — без него потомок
   флексбокса не сжимается ниже своего содержимого, и прокрутка не появится.
   `display:flex` безопасен рядом с `hidden`: глобальное
   `[hidden]{display:none!important}` (app.css) сильнее. */
.sh .ms-pop{z-index:70;display:flex;flex-direction:column;min-height:0}
.sh .ms-pop>.ms-list{max-height:none;min-height:0;overflow-y:auto}

/* --- пары, плашки, состояния --- */
.sh .pairs>h3{margin:0 0 var(--s1);font-size:11px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ann2);font-weight:500}
.sh .pair{display:grid;grid-template-columns:15rem minmax(0,1fr);gap:var(--s3);
  align-items:baseline;padding:var(--s1) 0;border-bottom:1px solid var(--hair);font-size:13px}
.sh .pair>.k{color:var(--ann2)}
.sh .pair>.v.is-empty{color:var(--ann2);font-style:italic}
/* НА УЗКОМ ЭКРАНЕ ПАРА СКЛАДЫВАЕТСЯ В ДВЕ СТРОКИ (UI-104).
   Колонка подписей — 15rem, то есть 240px. В окне 390px модал шириной
   330px, и значению остаётся 60–90px: «Тех документация Вихря» падает
   в три строки, а «ООО Кошта» — в две. Пара из двух колонок держится,
   пока колонки соизмеримы; здесь они не соизмеримы, и решётка перестаёт
   быть решёткой.
   Это правило пар, а не блока людей: контакты только вскрыли то, что
   уже ломало паспорт. Замер раскладки этого не видел — переполнения
   нет, есть теснота, и её меряет только глаз. */
@media (max-width: 640px){
  .sh .pair{grid-template-columns:minmax(0,1fr);gap:0;padding:var(--s2) 0}
  .sh .pair>.k{font-size:11px;letter-spacing:.03em}
}

/* КОНТАКТЫ ЧЕЛОВЕКА В РОЛИ (UI-104). Вторая строка внутри значения:
   имя отвечает «кто», контакты — «как достать». Тише имени на ступень
   и мельче на пункт: это не второе значение, а способ им воспользоваться.
   Точка-разделитель — та же, что во всём языке, и она aria-hidden:
   диктору она сказала бы «точка» между двумя адресами.

   РЯД, А НЕ СТРОКА ТЕКСТА: значки служб идут первыми, за ними телефон.
   Выравнивание по центру — у значка 24px высоты, у номера 18, и по
   базовой линии они разошлись бы. Значок здесь МЕНЬШЕ обычных 30px
   языка: он стоит не в ряду действий, а под именем, и не должен
   спорить с ним по весу. На пальце его цель всё равно вырастает
   до 44px — правило ниже, в блоке крупных целей. */
/* UI-151: те же значки стоят в клетке таблицы («Сотрудники → Список»). Контекст —
   `:is(…)`: специфичность берётся по старшему аргументу и остаётся прежней,
   поэтому правила крупных целей ниже по-прежнему перебивают базовые. */
:is(.sh .pair>.v,.sh td) .контакты{display:inline-flex;flex-wrap:wrap;align-items:center;
  gap:2px var(--s1);margin-left:var(--s2);vertical-align:middle;
  font-size:12px;line-height:1.5;color:var(--ann);overflow-wrap:anywhere}
:is(.sh .pair>.v,.sh td) .контакты>.icon-btn{width:24px;min-height:24px}
/* ВЫРАВНИВАНИЕ ПО ЧЕРНИЛАМ, А НЕ ПО КОРОБКЕ. Значок 15px сидит
   в цели 24px, и первый в ряду начинался на 4.5px правее имени над ним.
   Коробки при этом выровнены идеально — но выровненными глаз считает
   не коробки, а то, что в них нарисовано. */
/* ЗНАЧКИ СТОЯТ В СТРОКУ С ИМЕНЕМ, а не под ним (решение владельца
   23.08.2026). Отдельная строка удваивала высоту каждой роли: шесть
   ролей давали двенадцать строк там, где хватает шести. Имя короткое,
   значков не больше четырёх — они помещаются рядом и читаются как
   продолжение имени, а не как отдельное сведение.
   Отрицательного поля больше нет: рядом с текстом внутреннее поле
   значка работает зазором, а не сдвигом. */
/* ТЕЛЕФОН И АДРЕС — ТОЖЕ ЦЕЛИ, хотя выглядят строкой текста. Аудит
   поймал: 108×18 при норме 24. Исключение «ссылка внутри предложения»
   сюда не годится — это не предложение, а ряд элементов управления,
   и промах по нему стоит того же, что промах по значку. Высота
   выравнивается со значками, поэтому ряд от этого не растёт. */
:is(.sh .pair>.v,.sh td) .контакты a:not(.icon-btn){display:inline-flex;align-items:center;
  min-height:24px}
:is(.sh .pair>.v,.sh td) .контакты>.icon-btn>img{width:15px;height:15px}
:is(.sh .pair>.v,.sh td) .контакты .разд{margin:0 .4em;color:var(--ann2)}
:is(.sh .pair>.v,.sh td) .контакты a{color:inherit;text-decoration-color:var(--line)}
/* Телефон и телеграм — АТОМЫ: они переносятся целиком или не переносятся
   вовсе. Замер: «+7 926 000-00-00» разрывался после «+7», и обрывок
   на конце строки перестаёт читаться как номер. Адрес, наоборот,
   переносить можно — он длинный, и целиком на телефон не влезает. */
:is(.sh .pair>.v,.sh td) .контакты a.к--цел{white-space:nowrap}
:is(.sh .pair>.v,.sh td) .контакты a:hover{color:var(--ink);text-decoration-color:currentColor}
/* EMP-16: СТРОКА СОТРУДНИКА (`.pf-сотрудник` — вкладка «Сотрудник» профиля и окно
   поверх карточки проекта). Значки контактов стоят в клетке одни, без имени слева,
   и поле языка перед ними — оно отделяет значки от имени у участников — здесь
   лишнее. Селектор на класс сильнее базового `:is(…) .контакты`. */
.sh .pf-сотрудник .pair>.v .контакты{margin-left:0}
.sh .tag{display:inline-block;padding:2px var(--s2);border-radius:var(--r-hair);
  font-size:11px;font-weight:600;letter-spacing:.02em;line-height:1.5;white-space:nowrap;
  background:none;color:var(--ann2)}
.sh .tag--ok{color:var(--ok)} .sh .tag--warn{color:var(--old)}
.sh .tag--error{color:var(--bad)} .sh .tag--wait{color:var(--none)}
.sh .tag--accent,.sh .tag--info{color:var(--mark)}
/* ПОДСКАЗКА ПУСТОГО ЛИСТА ЧИТАЕМА (UI-109). Была на `--ann2` при 12px —
   контраст 4.28 против нормы 4.5. Ступень темнее даёт 5.6.
   Замер нашёл её только сегодня: она рисуется, лишь когда отбор ничего
   не находит, а такого состояния у заглушки по умолчанию не было. */
.sh .empty{padding:var(--s8) var(--s5);text-align:center;color:var(--ann);font-size:12px}
.sh .empty>.t{font-size:13px;color:var(--ann);font-weight:600;margin-bottom:var(--s1)}
/* `--ann2` РАССЧИТАН НА ЛИСТ, А НЕ НА ГРУНТ, и это надо знать до того,
   как ставить подпись вне белого. Замер 07.09.2026 (UI-143):
     на `--sheet` (#FFFFFF)  — контраст 4,95, AA проходит;
     на `--ground` (#EBEBEB) — 4,28 при норме 4,5, НЕ проходит.
   Разница невелика глазом и решает по правилу. Ставите `.muted` на грунт —
   берите `--ann` (на грунте 5,43). Нашлось на витрине режиссёров: строка
   источников лежит вне листа, и шесть подписей подряд были нечитаемы по AA. */
.sh .muted{color:var(--ann2)}
.sh .link,.sh a{color:var(--mark)}

/* --- ЗНАЧОК ОТБОРА И ЕГО ПОЛОСА (UI-60) ---------------------------
   Кнопка без подписи: воронка плюс счётчик выбранного. Слово несло
   смысл, поэтому вместо него стоит aria-label — кнопка со знаком
   и без имени для скринридера пуста. */
.sh .flt{display:inline-flex;align-items:center;gap:6px;height:30px;min-height:30px;
  padding:0 var(--s2);border:1px solid var(--line);border-radius:var(--r-ctl);
  background:var(--sheet);color:var(--ink);cursor:pointer;flex:none}
.sh .flt:hover{border-color:var(--ann2)}
.sh .flt[aria-expanded="true"]{background:var(--field);border-color:var(--ann2)}
.sh .flt svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linejoin:round;display:block}
.sh .flt .n{font-size:12px;font-weight:600;font-variant-numeric:var(--tab);
  min-width:17px;height:17px;padding:0 4px;border-radius:var(--r-pill);
  background:var(--ink);color:var(--sheet);display:inline-flex;
  align-items:center;justify-content:center}

/* СЧЁТЧИК ВНУТРИ МУЛЬТИВЫБОРА — ЗНАЧЕНИЕ, А НЕ ТРЕВОГА (UI-78).
   У воронки счётчик чёрный и вывернутый намеренно: он говорит про
   отборы, которых НЕ ВИДНО, и это предупреждение — выборка сужена
   молча. Внутри мультивыбора тот же счётчик стоит рядом со своей
   подписью, отбор виден целиком, и выворачивать нечего: это просто
   число, а число в этом языке пишется чернилом.

   До правки счётчик оставался на палитре прежнего языка — подложка
   #E9EEFB, текст #6E6E6E, контраст 4.39 при норме 4.5. На «Проектах»
   не всплывало: там по умолчанию отборов нет и значка тоже.
   Поймано аудитом «Контрактов» в состоянии «отбор раскрыт». */
.sh .ms-btn .n{font-size:11px;font-weight:600;font-variant-numeric:var(--tab);
  min-width:16px;height:16px;padding:0 4px;border-radius:var(--r-pill);
  background:var(--field);color:var(--ink);display:inline-flex;
  align-items:center;justify-content:center}

/* ПОЛЕ ПОЛОСЫ ОДИНАКОВО СВЕРХУ И СНИЗУ. Замер до правки: 12 сверху,
   0 снизу — поле снизу снимал `app.css` под раскладку-доску, и полоса
   читалась как прижатая к нижнему краю. Величина, заданная в двух
   местах, — это две величины. */
.sh .app-body.layout-board .toolbar,
.sh .toolbar{padding:var(--s2) var(--s4);border-bottom:1px solid var(--line);
  margin-bottom:0}
.sh .app-body.layout-board .app-bar{margin-bottom:0}
/* Вторая полоса — продолжение первой, а не отдельная плашка: линия
   между ними была бы делением уже разделённого.

   ЭТО Я НАПИСАЛ И НЕ СДЕЛАЛ. Общее правило `.sh .toolbar` вешало
   нижнюю границу на ОБЕ полосы, и линия между ними всё-таки была —
   а раскрытый отбор прижимался к ней вплотную. Владелец на бою:
   «фильтры прилипают к разделителю, а он там вообще не нужен».
   Полосу закрывает ОДНА линия — нижняя у второй полосы, в обоих
   её состояниях. Первая полоса своей не имеет: делить нечего. */
.sh .app-body.layout-board .toolbar.pj-filters-row,
.sh .toolbar.pj-filters-row{border-bottom:none}
.sh .app-body.layout-board .toolbar.pj-filters-more,
.sh .toolbar.pj-filters-more{border-top:none;padding-top:0;padding-bottom:var(--s2)}
.sh .toolbar.pj-filters-all{gap:var(--s2);flex-wrap:wrap}

/* ВСЕ КОНТРОЛЫ ПОЛОСЫ ОДНОЙ ВЫСОТЫ. Замер: поиск 30, мультивыбор 26 —
   разница видна по тому, как они прилипают к краю подложки. */
.sh .ms-btn{min-height:30px;height:30px;padding:0 var(--s3);
  border:1px solid var(--line);border-radius:var(--r-ctl);
  background:var(--sheet);color:var(--ink);font-size:13px}
.sh .ms-btn:hover{border-color:var(--ann2)}
/* СЕДЬМОЙ КЛАСС СЛОВАРЯ, и он вылез только когда полоса отбора
   перестала прятаться атрибутом: подпись и знак мультиселекта
   красились --n6 прежнего языка и давали 3.34 в синьке. Скрытое
   не измеряется — раскрытие показало то, что было всегда. */
.sh .ms-btn .val{color:var(--ink)}
.sh .ms-btn .arrow{color:var(--ann2)}
.sh .ms-btn.is-empty .val{color:var(--ann)}
.sh .ms-btn .n{color:var(--ann2);font-variant-numeric:var(--tab)}
.sh .ms-btn[aria-expanded="true"]{border-color:var(--heavy);background:var(--field)}

/* --- ШАПКА КАК ШТАМП ЧЕРТЕЖА ------------------------------------- */
/* ПОЛОСА ПРИВЯЗАНА К ВЕРХУ, А НЕ ЦЕНТРИРУЕТ КЛЕТКИ. Подгонять клетки
   под одну высоту бесполезно: самая высокая — трёхстрочный
   «Исполнитель» — всё равно центрируется иначе, и подписи встают
   на двух уровнях (замер: 8 и 3). Верхняя привязка делает строку
   подписей строкой независимо от того, сколько строк в клетке. */
.sh .app-bar{align-items:flex-start;gap:var(--s5);padding:9px var(--s4);
  height:var(--stamp-h);min-height:0;flex-wrap:nowrap;overflow:hidden}
/* КЛЕТКИ ОДНОЙ ВЫСОТЫ И С ВЕРХНЕЙ ПРИВЯЗКОЙ. «Исполнитель»
   трёхстрочный, остальные двухстрочные; при центрировании каждая
   клетка центрируется САМА, и подписи встают на трёх разных уровнях.
   Одна высота плюс привязка к верху делают строку подписей строкой. */
.sh .app-bar .cell{display:flex;flex-direction:column;gap:1px;white-space:nowrap}
.sh .app-bar .cell .k{font-size:10px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--ann2);line-height:1.2}
.sh .app-bar .cell h1{margin:0;font-size:14px;font-weight:600;letter-spacing:-.008em;line-height:1.25}
/* ВСЕ ЗНАЧЕНИЯ ОДНОЙ ВЫСОТЫ, иначе подписи над ними не встают в строку:
   полоса центрирует каждую клетку отдельно, и клетка повыше уводит свою
   подпись вверх. Замер до правки: верх клеток 14, 11 и 7 — три уровня
   там, где должен быть один. */
.sh .app-bar .cell .fact,.sh .app-bar .cell h1{min-height:24px;
  display:flex;align-items:center}
.sh .app-bar .cell .fact{font-size:14px;font-weight:500;color:var(--ink);
  font-variant-numeric:var(--tab);line-height:1.25}

/* --- КЛЕТКИ ШТАМПА (UI-60) ---------------------------------------- */
.sh .app-bar .cell .sub{font-size:11px;color:var(--ann2);line-height:1.2}
.sh .app-bar .cell--right{align-items:flex-end;text-align:right}
/* «ДАННЫЕ НА» — ЖИВАЯ КЛЕТКА. Она отвечает на вопрос «свежее ли это»,
   а не «когда снято»: крупно расстояние во времени, точное время
   в подсказке. И она же снимает данные заново — предмет и действие
   над ним в одном месте, а не кнопка где-то отдельно. */
/* ЖИВАЯ КЛЕТКА — КНОПКА, и габарит цели у неё как у кнопки: 24px
   на точном указателе (WCAG 2.5.8 AA), 44 на грубом. Она выглядела
   подписью и потому осталась 20px — единственная находка, пережившая
   все сорок замеров. Выравнивание по центру, а не по базовой линии:
   иначе прибавка высоты уводит текст вверх от соседних клеток. */
.sh .app-bar .fact--live{display:inline-flex;align-items:center;gap:5px;
  padding:0;min-height:24px;height:auto;border:none;background:none;
  color:var(--ink);font:500 14px var(--sans);cursor:pointer;border-radius:var(--r-hair)}
.sh .app-bar .fact--live .ico{font-size:12px;color:var(--ann2);line-height:1}
.sh .app-bar .fact--live:hover{color:var(--mark)}
.sh .app-bar .fact--live:hover .ico{color:var(--mark)}
.sh .app-bar .fact--live:focus-visible{outline:2px solid var(--mark);outline-offset:2px}

/* ВЕРСИЯ — КЛЕТКА-ССЫЛКА (заметки о выкатках). Габарит цели и палитра
   наведения взяты у живой клетки и по тому же доводу (UI-68): клетка,
   которая что-то делает, выглядит подписью и потому остаётся 20px, если
   ей не сказать иначе. Подчёркивание появляется только по наведению —
   в покое штамп читается строкой равных значений, и одно подчёркнутое
   вырывалось бы из неё, обещая ссылку там, где их четыре.
   `--ink` в покое ОБЯЗАТЕЛЕН: язык красит все `a` в `--mark` (метка
   положения), и без этого правила версия горела бы акцентом посреди
   штампа — цветом, у которого в этом языке своя роль. */
.sh .app-bar .fact--заметки{display:inline-flex;align-items:center;min-height:24px;
  color:var(--ink);text-decoration:none;border-radius:var(--r-hair)}
.sh .app-bar .fact--заметки:hover{color:var(--mark);text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px}
.sh .app-bar .fact--заметки:focus-visible{outline:2px solid var(--mark);outline-offset:2px}
/* UI-148: «ИСПОЛНИТЕЛЬ» — КЛЕТКА-ССЫЛКА В ПРОФИЛЬ С ДВУМЯ СТРОКАМИ ВНУТРИ.
   Почта стоит внутри ссылки, а не под ней: снаружи на грубом указателе цель
   росла до 44, почта съезжала под неё, и клетка вылезала из полосы (замер
   1024×768, касание: клетка 71 при полосе 69). Внутри ссылки обе строки — одна
   цель, и 44 её вмещают. `line-height:24px` и зазор 1px повторяют прежнюю
   раскладку клетки (значение в 24 + 1 + почта), поэтому на обычном указателе
   клетка не сдвигается ни на пиксель. Селектор на класс сильнее правила
   грубого указателя ниже — иначе `align-items:flex-start` оттуда прижал бы
   правую клетку влево. */
.sh .app-bar .cell.cell--right .fact--заметки{flex-direction:column;align-items:flex-end;
  line-height:24px;gap:1px}

/* --- РАМА ЭКРАНА НЕ ИМЕЕТ ПОЛЕЙ (UI-60) ---------------------------
   Замер: штамп прототипа стоит в 0/0/0 — он ЧАСТЬ ВЕРХНЕЙ ГРАНИЦЫ ОКНА.
   Продукт держал его в 20 сверху и справа, и полоса читалась как объект
   НА странице, а не как её край. Разница не косметическая: у объекта
   есть граница со всех сторон, у края её нет по определению, и глаз
   ищет край там, где его нарисовали.

   Поле снимается у СЕКЦИИ, а не у полос: полосы и так рамка, а поле
   принадлежит содержимому. Содержимое своё поле получает само —
   у доски и реестра оно уже есть. */
.sh .app-body.layout-board > .section{padding:0}
.sh .app-body.layout-board > .section > .board,
.sh .app-body.layout-board > .section > .grid-wrap{margin:var(--s3) var(--s4)}
.sh .app-bar,.sh .toolbar{border-radius:0}

/* ССЫЛКА, СТОЯЩАЯ В КЛЕТКЕ ОДНА, — САМОСТОЯТЕЛЬНАЯ ЦЕЛЬ (UI-142).
   Аудит нашёл её на двух листах: `/reports/1` (98×17) и `/directors`
   (193×19 на столе; 30×44 и 36×44 на 390px — там не хватает ШИРИНЫ).

   ПОЧЕМУ ЭТО НЕ «ССЫЛКА В ПРЕДЛОЖЕНИИ». WCAG 2.5.8 выводит из правила
   ссылку внутри фразы, и линейка эту оговорку исполняет: строчная ссылка,
   рядом с которой в родителе есть другой текст, не меряется. Здесь другой
   случай — ссылка ОДНА в клетке, то есть она и есть всё её содержимое.
   Такую цель правило требует, и требует по делу: в неё целятся.

   `inline-flex`, А НЕ `padding`. Отступами высота набирается только у
   однострочной ссылки: имя режиссёра на 390px переносится на две строки,
   и padding дал бы не 44, а 70. Флекс-короб берёт ровно `min-height`,
   когда содержимое меньше, и растёт вместе с ним, когда больше.

   Ширина названа отдельно: `реел` и `vimeo` — ссылки в 30 и 36 пикселей
   при верной высоте, и одна высота их не чинит.

   ОТБИРАЕМ ПО «БЕЗ КЛАССА», А НЕ ПО `:only-child`. Первая редакция брала
   единственного ребёнка клетки и промахнулась мимо половины находок:
   у имени в «Внешних источниках» рядом стоит значок Notion, а «vimeo»
   в клетке бывает несколько. Клетка со ссылками — это список целей,
   сколько бы их ни было. Классы при этом не трогаем: `.dir-ntn`,
   `.pj-lnk` и прочие значки имеют собственные размеры и своё правило
   крупных целей — им наш `min-width` только помешал бы. */
.sh table td > a:not([class]),
.sh .board-card .nm > a{
  display:inline-flex;align-items:center;min-height:24px;min-width:24px}

/* ═══ ГРУБЫЙ УКАЗАТЕЛЬ И УЗКИЙ ЛИСТ (UI-63) ═══════════════════════
   Замер аудита: на 390px двадцать целей меньше 44px, среди них семь
   кнопок обновления в карточках по 30×30 и значок отбора 33×30.
   Правило языка — «на грубом указателе любой контрол получает 44px» —
   было записано и не выполнено его же файлом: словарь дал 40.

   УСЛОВИЕ ДВОЙНОЕ, И ЭТО НЕ ПЕРЕСТРАХОВКА. `pointer: coarse` верно
   по смыслу, но гербарий снимает ШИРИНАМИ и подделать указатель
   не умеет — правило, которое он не видит, не проверяется никем.
   Ширина здесь не замена смыслу, а способ сделать смысл наблюдаемым. */
@media (pointer: coarse), (max-width: 860px){
  .sh button,.sh .btn-primary,.sh .btn-tinted,.sh .btn-quiet,
  .sh .ctl,.sh .flt,.sh .search,.sh input,.sh select,.sh .ms-btn,
  .sh .seg button,.sh .filter,.sh .chip,.sh .menu-item{min-height:44px}
  /* `flex:none` ОБЯЗАТЕЛЕН, и без него width ничего не гарантирует:
     кнопка в шапке модала — флекс-элемент, и она сжимается ниже
     заданной ширины. Замер: объявлено 44, вышло 35.98 — дробное
     число и выдало, что ширина считается содержимым, а не правилом. */
  .sh .icon-btn,.sh .pj-updbtn,.sh .pj-delbtn,.sh .pj-arcbtn{width:44px;min-height:44px;flex:none}
  /* UI-145: СТРОКА ПОИСКА В СПИСКЕ ОТБОРА — ТОЖЕ ЦЕЛЬ ПАЛЬЦА. Общее `.sh input`
     выше её не достаёт: у `.sh .ms-search` своя `min-height:28px` и специфичность
     выше, и на телефоне поле оставалось 28 пикселей. Поймано первым замером,
     открывшим список отбора. */
  .sh .ms-search{min-height:44px}
  /* Значки контактов уменьшены правилом с четырьмя классами (UI-104),
     и оно ПЕРЕВЕШИВАЕТ строку выше: 0-4-0 против 0-2-0. Уменьшение
     касалось только мыши, но специфичность про намерение не знает,
     и на пальце цель осталась бы 24px. Повторяем адрес здесь — там же,
     где живёт само правило крупных целей, а не рядом с уменьшением. */
  /* Знаки среди содержимого (UI-121) уменьшены правилом по МЕСТУ, и оно
     тяжелее строки выше — на пальце цель осталась бы 24px. Повторяем
     адрес здесь, там же, где живёт само правило крупных целей.
     Третий случай той же ловушки за сутки: специфичность про намерение
     не знает. */
  .sh .board-card .icon-btn,.sh .card-corner .icon-btn,
  :is(.sh .pair>.v,.sh td) .контакты>.icon-btn,.sh .ms--один>.ms-x,
  .sh .pj-updbtn{width:44px;height:44px;min-height:44px}
  /* Ссылка, стоящая в клетке одна (UI-142): на пальце норма 44 и по
     высоте, и по ШИРИНЕ — короткие `реел`/`vimeo` не проходили именно
     по ширине. Адрес повторён здесь, в блоке крупных целей, а не рядом
     с базовым правилом: так все нормы грубого указателя лежат вместе. */
  .sh table td > a:not([class]),
  .sh .board-card .nm > a{min-height:44px;min-width:44px}
  /* Крестик сброса — тоже цель. Поле на пальце вырастает до 44px,
     и крестик растёт вместе с ним, оставаясь внутри. */
  .sh .ms--один>.ms-x{width:44px;min-height:44px;right:22px}
  .sh .ms--один>.ms-btn{padding-right:72px}
  :is(.sh .pair>.v,.sh td) .контакты a:not(.icon-btn){min-height:44px}
  .sh .modal .btn-quiet,.sh .modal .btn-tinted,.sh .modal .icon-btn{min-width:44px;flex:none}
  /* Марка — тоже цель: по ней уходят на главную. */
  .sh .app-top > .brand{min-width:44px;min-height:44px}
  /* Живая клетка штампа — кнопка, а не подпись: у неё та же цель. */
  /* ПУТЬ СЧИТАЕТСЯ ПО САМОМУ ДЛИННОМУ. Выравнивание клеток штампа
     объявлено через `.cell .fact` — на класс длиннее, чем это правило,
     и потому молча его перевешивало: на телефоне цель оставалась 24.
     Замер поймал, глаз бы не поймал. Путь здесь тот же длины.

     И растёт ЦЕЛЬ, а не строка — как у флажка ниже. С `center` значение
     «только что» уплывало в середину 44 пикселей, пока соседние
     значения стояли в своих 24: подписи держали одну линию, а сами
     данные её теряли. Строка остаётся в верхних 24, лишние 20
     свисают вниз пустой площадью нажатия. */
  .sh .app-bar .cell .fact--live{min-height:44px;align-items:flex-start;
    line-height:24px}
  .sh .app-bar .cell .fact--live .ico{min-height:24px;display:inline-flex;
    align-items:center}
  /* Клетка-ссылка растёт по тому же правилу и с той же оговоркой:
     растёт ЦЕЛЬ, а не строка, иначе значение уплывёт в середину 44
     пикселей, пока соседние стоят в своих 24. */
  .sh .app-bar .cell .fact--заметки{min-height:44px;align-items:flex-start;
    line-height:24px}
  .sh .flt{min-width:44px}
  /* ПУНКТЫ МЕНЮ — ЦЕЛИ, И ОНИ ТОЖЕ 44. Двенадцать ссылок по 30 пикселей
     в шторке на телефоне. Аудит их не видел: шторка закрыта, а скрытое
     не измеряется — правило записано в коде с UI-67, и я снова его
     не применил. Поймано ручным замером на «Контрактах», болело
     одинаково на обоих экранах. Состояние заведено в аудит. */
  .sh .nav-link{min-height:44px;display:flex;align-items:center}
  /* Тихая кнопка — тоже цель: узкая, 34×44, а норма квадратная, и одной
     стороны мало. */
  .sh .btn-quiet{min-width:44px;min-height:44px}
  .sh .sync-btn.btn-quiet{width:44px;min-width:44px}
  /* Сегменты даты на грубом указателе: 44 по обеим сторонам. */
  .sh .date-seg{min-height:44px}
  .sh .date-seg > input{min-height:44px;min-width:44px}
  .sh .date-seg > .y{min-width:60px}
  .sh .date-seg > .cal-btn{min-width:44px;min-height:44px}
  /* Значок-ссылка на проект в списках удаления и обновления. */
  .sh .pj-lnk{min-width:44px;min-height:44px;display:inline-flex;
    align-items:center;justify-content:center}
  /* Знаку справки площадь даёт поле, а не собственный размер. */
  .sh .how > summary::before{inset:-14px}
  /* Одиночная ссылка-переход («→ статистика») стоит в абзаце ОДНА,
     поэтому исключение «ссылка внутри фразы» на неё не распространяется. */
  .sh .link{display:inline-flex;align-items:center;min-height:44px}
  /* Ссылка в ячейке реестра — самостоятельная цель, а не слово внутри
     фразы: исключение WCAG 2.5.8 про «ссылку в тексте» сюда не тянется,
     в колонке «Документ» она стоит одна. */
  .sh table.grid a{display:inline-flex;align-items:center;min-height:44px}
  .sh .app-top .nav-toggle{width:44px;height:44px;min-height:44px}
  /* Подвал карточки держит ту же высоту, что и ряд значков ЗДЕСЬ:
     на грубом указателе значок 44, и ряд с ним 48, а не 34. Одна
     величина на две стороны — но сторон у неё две РАЗНЫХ, и вторую
     я сначала не заметил: карточка без значков осталась на 14 ниже
     соседок ровно на телефоне. Обе сняты замером. */
  .sh .board-card .foot{min-height:48px}
  .sh .seg{padding:3px}
  .sh .check{min-height:44px}
  /* Чекбоксу растёт ЦЕЛЬ, а не сам квадрат: 44-пиксельный флажок
     выглядел бы поломкой. Площадь даёт обёртка. */
  .sh .opt{min-height:44px;padding-top:var(--s2);padding-bottom:var(--s2)}
}

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

   БЕГЛЕЦЫ ПОКАЗЫВАЮТСЯ ТОЛЬКО ТАМ, ОТКУДА ОНИ БЕЖАЛИ. Слово «перенос»
   в заголовке блока выше я написал, а условие переноса не поставил:
   подвал показывался на ЛЮБОЙ ширине, и на широком экране исполнитель
   стоял дважды — в штампе справа вверху и в подвале слева внизу.
   Найдено владельцем на бою. Дублирование хуже отсутствия: два места
   с одним значением расходятся при первой же правке, и читатель не
   знает, какому верить. Пока клетка есть в штампе — в подвале её нет. */
.nf-sheet{display:none}
.sh .nav-foot{display:none}
.sh .nav-foot{padding:var(--s3) var(--s2) 0;font-size:11px;color:var(--ann2)}
.sh .nav-foot .k{font-size:10px;letter-spacing:.05em;text-transform:uppercase}

/* ═══ СВОРАЧИВАНИЕ ПАПКИ НА ШИРОКОМ ЭКРАНЕ (UI-72) ═══════════════════
   Прежний язык написал прямо: «кнопка навигации существует только там,
   где навигация спрятана; на широком экране панель на экране — кнопка
   „показать“ была бы кнопкой без действия». Я поставил её в контрастный
   угол по просьбе владельца и действия не дал — получилась ровно та
   кнопка без действия, о которой предупреждал прежний язык. Владелец
   на бою: «бургер не работает».

   ДЕЙСТВИЕ: папка уезжает целиком, содержимое занимает всю ширину.
   На реестре в 2985 строк это 212 отвоёванных пикселей, и там они
   видны — колонка «Компания» на 1440 обрезалась на «ООО Стереотакти».

   СОСТОЯНИЕ ОДНО И ЧИТАЕТСЯ ОДИНАКОВО НА ОБЕИХ ШИРИНАХ: `aria-expanded`
   значит «меню раскрыто». На узком раскрытое — выехавшая поверх шторка,
   на широком — пристыкованная папка. Умолчание разное (на широком папка
   раскрыта, на узком закрыта), и его ставит скрипт при загрузке: атрибут
   обязан говорить правду скринридеру с первой секунды, а разметка ширины
   экрана не знает.

   Промежуточной «узкой полоски с иконками» здесь нет намеренно: пунктов
   у нас одиннадцать и значков к ним не нарисовано, а полоска из первых
   букв — это ребус. Два состояния, оба честные. */
@media (min-width:861px){
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="false"]){
    grid-template-columns:minmax(0,1fr)}
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="false"]) .app-side{display:none}
  /* УГОЛ ОСТАЁТСЯ УГЛОМ. Первая редакция растягивала его на всю ширину,
     и владелец назвал это странным — справедливо. Язык говорит: один
     чёрный блок на роль. Полноширинная плашка — это уже не угол листа,
     а шапка окна, то есть ВТОРАЯ роль у того же блока; отсюда и
     ощущение подмены.

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

     Технически: рама одноколонная, лист занимает обе строки, угол
     лежит поверх его левого верхнего края. Штамп получает поле слева
     на ширину угла, чтобы не уходить под него. */
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="false"]) .app-top{
    grid-column:1;grid-row:1;width:var(--rail-w);position:relative;z-index:2}
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="false"]) .app-main{
    grid-column:1;grid-row:1/-1}
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="false"]) .app-bar{
    padding-left:calc(var(--rail-w) + var(--s4))}
}

/* ШТАМП НА УЗКОМ: НИЧЕГО НЕ ПРЯЧЕТСЯ МОЛЧА.
   Замер: на 390 и 320px клетки уходили за край, а `overflow:hidden`
   их отрезал — прокрутки нет, признака нет. Это тот же класс дефекта,
   что UI-56: не «обрезано», а «не о чем догадаться».

   Что остаётся на листе: какой лист, насколько свежи данные, сколько
   показано — три вопроса, которые задают, глядя на доску. Версия
   и исполнитель — справочные, и они переезжают в папку листов,
   где у продукта уже живёт почта. Это перенос, а не сокрытие. */
@media (max-width:860px){
  /* РАМА НА УЗКОМ. Правила словаря сильнее прежних по порядку
     подключения и переживали чужую точку перелома: марка оставалась
     в первой колонке, а содержимое ложилось на неё сверху. Отказ
     тихий — на снимке это выглядит как наложенный текст, а не как
     сломанный грид. Переопределяем свою же раму явно. */
  /* Тот же мёртвый селектор, что и у базовой рамы, и здесь он был
     опаснее: пока базовое правило тоже не работало, узкую раскладку
     держал прежний язык. Как только базовое ожило, оно перекрыло
     чужое узкое по порядку подключения — и на 390 рама осталась
     двухколонной: угол 208, содержимому 182. Аудит этого не видит,
     он мерит контраст и цели; поймал замер по ширинам. */
  :is(.sh .app,.sh.app){grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
  .sh .app-top{grid-column:1;grid-row:1;border-bottom:1px solid var(--line)}
  .sh .app-main{grid-column:1;grid-row:2}
  .sh .app-side{position:fixed;top:0;bottom:0;left:0;width:min(272px,84vw);
    z-index:50;box-shadow:var(--sh-drawer);transform:translateX(-100%);
    visibility:hidden;transition:transform 220ms cubic-bezier(.23,1,.32,1),visibility 220ms}
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="true"]) .app-side{transform:translateX(0);visibility:visible}
  .sh .app-bar{overflow:visible;gap:var(--s4);height:auto;padding:var(--s2) var(--s3)}
  .sh .app-bar .cell--right{display:none}
  .sh .app-bar .cell:nth-child(3){display:none}   /* версия */
  .sh .app-top{height:auto;min-height:52px}
  /* Клетки ушли из штампа — здесь они и появляются, ни секундой раньше. */
  .sh .nav-foot{display:block}
  .sh .nf-sheet{display:flex;flex-direction:column;gap:2px}
}
.sh .nav-foot .k--gap{margin-top:var(--s2)}
.sh .nav-foot .v{font-size:13px;color:var(--ink)}
/* Версия в подвале — тот же перенос клетки штампа (UI-63), значит и та же
   ссылка. Правило нужно ровно по той же причине, что в штампе: язык красит
   все `a` в `--mark`, и без него перенесённая клетка выглядела бы иначе,
   чем та, что она переносит. Цель здесь 44 сразу — подвал появляется
   только на узком листе, а узкий лист и есть грубый указатель. */
/* ЦЕЛЬ ОБНИМАЕТ ЧИСЛО, А НЕ ПОДВАЛ. Подвал — колонка, и растянутая ссылка
   заняла бы всю его ширину: 239 пикселей невидимой площади нажатия под
   почтой, где нажимать нечего. В штампе такого нет — там клетка сама
   шириной в своё содержимое. */
/* `min-width` ЗДЕСЬ РОВНО ТАКОЙ ЖЕ ОБЯЗАТЕЛЬНЫЙ, КАК `min-height`, и его
   не хватало с самого заведения ссылки (03.09.2026). Аудит состояний мерил
   цель на каждом листе и на каждом писал одно и то же: `.v 27×44` при норме
   44 — высота задана, ширину задаёт текст «4.0.39», а это 27 пикселей.
   Норма про площадь пальца, а не про одну её сторону.
   Растягивать по-прежнему нельзя (см. разбор выше), но 44 — это не подвал,
   это по-прежнему «обнять число»: текст остаётся у левого края и не
   сдвигается ни на пиксель, шире становится только область нажатия. */
.sh .nav-foot a.v{text-decoration:none;display:inline-flex;align-items:center;
  align-self:flex-start;min-height:44px;min-width:44px}
.sh .nav-foot a.v:hover{color:var(--mark);text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px}
.sh .nav-foot a.v:focus-visible{outline:2px solid var(--mark);outline-offset:2px}
.sh .nav-foot .m{color:var(--ann2)}

/* ═══ ЗАКРЫТИЕ СЛОВАРЯ (UI-64) ═══════════════════════════════════════
   Шесть классов не попали в словарь, и все восемь провалов контраста
   из аудита — это они. Одна причина, не восемь.                       */

/* МАРКА НА ИНВЕРСИИ. `.brand-text small` и бургер красились --n6
   прежнего языка: 3.49 в светлой и 1.89 в тёмной, потому что тон
   не следует за инверсией угла. Чернило угла — его же поверхность
   листа, приглушение — прозрачностью, а не вторым серым. */
.sh .app-top .brand-text small{color:var(--sheet);opacity:.62}
.sh .app-top .icon-btn,.sh .app-top .nav-toggle{color:var(--sheet);opacity:.72}
.sh .app-top .icon-btn:hover,.sh .app-top .nav-toggle:hover{
  opacity:1;background:var(--wash)}

/* ПОДПИСЬ В ПОДВАЛЕ КАРТОЧКИ. --n6 на листе давал 3.34 в синьке. */
.sh .t-meta{color:var(--ann2);border-color:var(--hair)}

/* РАЗДЕЛИТЕЛИ ПАПКИ ЛИСТОВ — волосяная линия языка. */
.sh .nav-group,.sh .nav-foot{border-color:var(--hair)}

/* ШАПКА КОЛОНКИ ДОСКИ — поверхность зоны, а не своя подложка. */
.sh .board-col > .head{background:transparent}

/* ═══ СОСТОЯНИЕ — ПОЛОСОЙ ПО КРАЮ КАРТОЧКИ (UI-100) ══════════════════
   Решение владельца 22.08.2026. Строка «● активный» стоила карточке
   21 пиксель и повторялась в колонке двенадцать раз подряд: слово
   перестаёт читаться и становится текстурой. Край карточки говорит
   то же самое, не занимая строки, а слово приходит по наведению.

   НА ГРУБОМ УКАЗАТЕЛЕ НАВЕДЕНИЯ НЕ БЫВАЕТ, и там слово остаётся:
   полоса без него была бы цветом без значения — тем самым, что
   владелец назвал условием провала в первый же день. Одна величина
   на две стороны, как у целей нажатия.

   Скринридер получает состояние из `title` в обоих случаях: цвет
   для него не существует вовсе. */
/* ПОЛОСА — ТЕНЬ ВНУТРЬ, А НЕ ПСЕВДОЭЛЕМЕНТ. Первая редакция рисовала
   её absolute и ставила карточке `overflow:hidden`, чтобы скруглить
   край. Это уронило высоту с 228 до 111: у гибкого элемента
   `min-height:auto` равен размеру содержимого и защищает от сжатия,
   а `overflow:hidden` обнуляет эту защиту — карточки сжались внутри
   колонки. Отказ бесшумный: ни ошибки, ни предупреждения, просто
   вдвое меньше.
   Тень внутрь скругляется рамкой сама и минимума не трогает.

   ГРАНЬ СЛЕВА, как было в прототипе и как просил владелец. Слева
   у карточки начинается чтение: глаз входит в неё с этого края,
   и состояние встречает его первым, а не догоняет в конце.

   `flex:none` рядом — не перестраховка, а закрытие того же класса:
   карточка не обязана уметь сжиматься, и полагаться на автоминимум
   значит зависеть от свойства, которое отменяется одной строкой. */
.sh .board-card{flex:none}
.sh .board-card[data-состояние="is-ok"]{box-shadow:inset 3px 0 0 var(--ok)}
.sh .board-card[data-состояние="is-none"]{box-shadow:inset 3px 0 0 var(--none)}
.sh .board-card[data-состояние="is-unusable"]{box-shadow:inset 3px 0 0 var(--bad)}
.sh .board-card[data-состояние]:hover{box-shadow:inset 3px 0 0 currentColor}
.sh .board-card[data-состояние="is-ok"]:hover{box-shadow:inset 3px 0 0 var(--ok)}
.sh .board-card[data-состояние="is-none"]:hover{box-shadow:inset 3px 0 0 var(--none)}
.sh .board-card[data-состояние="is-unusable"]:hover{box-shadow:inset 3px 0 0 var(--bad)}
/* Слово прячется ТОЛЬКО там, где есть чем его позвать. */
@media (hover: hover) and (pointer: fine){
  .sh .board-card .cat{display:none}
  /* И МЕСТО ПОД НЕЁ ТОЖЕ НЕ ДЕРЖИТСЯ. Выше (стр. 839) карточка без
     состояния добирает 21px сверху — ровно ту высоту, которую занимает
     видимая строка состояния, чтобы обе карточки были одного роста.
     Здесь строки нет ни у кого, и та добавка делала ровно обратное:
     замер на доске — 228px у карточки без состояния против 207px у
     карточки с ним. Правило было верным до UI-100 и пережило его
     молча: место держалось под тем, что перестали показывать. */
  .sh .board-card:not(:has(.cat)){padding-top:var(--s3)}
}

/* ЗНАК СОСТОЯНИЯ БЭКОФИСА. Точка красилась палитрой прежнего языка;
   значения близки, но токен чужой — правка нового не дошла бы сюда. */
.sh .board-card .cat .sw.is-ok{background:var(--ok)}
.sh .board-card .cat .sw.is-none{background:var(--none)}
.sh .board-card .cat .sw.is-unusable{background:var(--bad)}

/* ═══ ПЛАШКА-ПРИМЕЧАНИЕ: ЗАСЕЧКА СНЯТА ═══════════════════════════════
   Я защищал полосу слева как приём чертежа. Это была защита задним
   числом: цветная граница с одной стороны — категорийный дефолт,
   и мир его не выбирал. Роль плашки несёт ПОЛЕ, а не полоса, —
   тем же приёмом, которым язык кодирует состояние: заливка плюс
   контур одного цвета, углы прямые. Заодно снимается скругление
   при односторонней границе, которое и было половиной формы. */
.sh .note{background:var(--field);border:1px solid var(--line);
  border-left-width:1px;border-radius:0;padding:var(--s2) var(--s3);
  margin:var(--s3) 0;font-size:12px;line-height:1.55;color:var(--ann)}
.sh .note--warn{background:var(--old-bg);border-color:var(--old);color:var(--old)}
.sh .note--error{background:var(--bad-bg);border-color:var(--bad);color:var(--bad)}
.sh .note--ok{background:var(--ok-bg);border-color:var(--ok);color:var(--ok)}
/* СВЕДЕНИЯ — НЕ СОСТОЯНИЕ, И ЦВЕТА У НИХ НЕТ. Модификатор красил рамку
   акцентом, и пояснение про VPN в профиле читалось как предупреждение:
   синяя рамка обещает, что внутри что-то случилось. Внутри — объяснение,
   зачем это нужно и какой лимит. «Цвет без смысла» владелец назвал
   условием провала в самый первый день; здесь он был ровно такой.
   Предупреждение и отказ цвет сохраняют — им есть что сообщить. */
.sh .note--info{background:var(--field);border-color:var(--line);color:var(--ann)}

/* ═══ ПОВЕРХНОСТИ, КОТОРЫЕ РИСУЕТ БРАУЗЕР ════════════════════════════
   Выделение, каретка, полоса прокрутки и флажок приходят с умолчаниями
   ОС и не принадлежат ни одному языку. На синьке системное синее
   выделение поверх синего фона теряется вовсе. Это самый дешёвый
   признак того, что лист начерчен, а не собран. */
.sh ::selection{background:var(--mark);color:var(--sheet)}
[data-theme="dark"] .sh ::selection{background:var(--mark);color:var(--ground)}
.sh input,.sh textarea{caret-color:var(--mark)}
.sh input[type="checkbox"],.sh input[type="radio"]{accent-color:var(--mark)}
.sh a,.sh .link{text-underline-offset:2px;text-decoration-thickness:1px}
/* ПОЛОСА ПРОКРУТКИ ПОЯВЛЯЕТСЯ ПОД РУКОЙ (UI-73).
   Постоянная полоса — это линия, которую никто не чертил: она делит
   поле, ничего о данных не сообщая, и на листе из шести колонок таких
   линий шесть. Полоса нужна, когда до неё тянутся, и не нужна, когда
   на лист смотрят.

   МЕСТО ПОД НЕЁ ЗАНЯТО ВСЕГДА — прозрачен только ползунок. Полоса,
   которая появляется вместе со своей шириной, толкает содержимое
   на 10 пикселей при каждом заходе курсора; это не «появилась
   полоса», это «поехал текст».

   ЧИТАЕМОСТЬ ПРОКРУТКИ ЭТИМ НЕ ДЕРЖИТСЯ, и это условие, а не оговорка.
   Догадаться, что содержимое продолжается, дают крышки с тенями
   у таблиц (UI-56) и счётчик в шапке колонки — оба видны без курсора.
   Полоса лишь подтверждает то, что уже сказано другим. Если завтра
   появится прокручиваемое место без такого признака — там полосу
   прятать нельзя.

   `:focus-within` рядом с `:hover` намеренно: до колонки доходят
   и с клавиатуры, а курсора при этом нет вовсе. */
.sh *{scrollbar-width:thin;scrollbar-color:transparent transparent}
/* ПУТЬ ТОЙ ЖЕ ДЛИНЫ, ЧТО У ПРЕЖНЕГО ЯЗЫКА. `design.css` называет свои
   прокручиваемые места поимённо (`.tn .grid-wrap` и далее) — это на
   класс длиннее, чем `.sh *`, и без перечисления здесь наши правила
   молча проигрывали бы на доске и в реестре. Проверено замером:
   ползунок оставался виден. */
.sh .app-body,.sh .board,.sh .board-col>.body,.sh .cols,.sh .col,
.sh .grid-wrap,.sh .reg-body,.sh .doc,.sh .ac-list,.sh .ms-list,
.sh .modal>.body,.sh .pane--r{scrollbar-color:transparent transparent}
.sh .app-body:hover,.sh .board:hover,.sh .board-col>.body:hover,.sh .cols:hover,
.sh .col:hover,.sh .grid-wrap:hover,.sh .reg-body:hover,.sh .doc:hover,
.sh .ac-list:hover,.sh .ms-list:hover,.sh .modal>.body:hover,.sh .pane--r:hover,
.sh .app-body:focus-within,.sh .board:focus-within,.sh .board-col>.body:focus-within,
.sh .cols:focus-within,.sh .col:focus-within,.sh .grid-wrap:focus-within,
.sh .reg-body:focus-within,.sh .doc:focus-within,.sh .ac-list:focus-within,
.sh .ms-list:focus-within,.sh .modal>.body:focus-within,.sh .pane--r:focus-within,
.sh *:hover,.sh *:focus-within{scrollbar-color:var(--line) transparent}
.sh ::-webkit-scrollbar{width:10px;height:10px}
.sh ::-webkit-scrollbar-track{background:transparent}
.sh ::-webkit-scrollbar-thumb{background:transparent;border:3px solid transparent;
  background-clip:content-box;border-radius:var(--r-pill);
  transition:background-color 160ms}
.sh *:hover::-webkit-scrollbar-thumb,
.sh *:focus-within::-webkit-scrollbar-thumb{background:var(--line)}
.sh *:hover::-webkit-scrollbar-thumb:hover,
.sh *:focus-within::-webkit-scrollbar-thumb:hover{background:var(--ann2)}

/* ПЕРЕХОД К СОДЕРЖИМОМУ. Не спрятан за `display:none` — скрытое так
   не получает фокуса вовсе. Уведён за край и возвращается на лист,
   когда на него встали. */
/* Габарит цели — как у всех: ссылка достаётся и пальцем на планшете
   с клавиатурой, а не только стрелками. */
.sh .skip{position:fixed;top:0;left:0;z-index:60;transform:translateY(-120%);
  min-height:44px;display:inline-flex;align-items:center;
  padding:var(--s2) var(--s4);background:var(--ink);color:var(--sheet);
  font-size:13px;font-weight:500;text-decoration:none;border-radius:0 0 var(--r-ctl) 0}
.sh .skip:focus-visible{transform:translateY(0);outline:2px solid var(--mark);outline-offset:-4px}
.sh .app-main:focus{outline:none}

/* ═══ ДВИЖЕНИЕ (UI-65) ═══════════════════════════════════════════════
   ЧЕРТЁЖ НЕ ВЪЕЗЖАЕТ — ОН ПЕРЕЧЕРЧИВАЕТСЯ. Отсюда весь словарь
   движения этого языка: ничего не летает, не отскакивает и не ждёт,
   пока его покажут. Движутся ровно три вещи, и каждая — потому что
   без неё человек не понимает, что произошло.

   1. ПЕРЕСЪЁМ — единственная долгая операция экрана. Знак крутится,
      пока идёт работа: остановленный индикатор врёт, и это правило
      унаследовано вместе с продуктом.
   2. РАСКРЫТИЕ ОТБОРА — семь отборов, возникших мгновенно, роняют
      доску вниз без объяснения. Раскрытие показывает, откуда строка.
   3. ОТКЛИК КОНТРОЛА — подтверждение нажатия, и ничего больше.

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

/* 1. Пересъём. Значение гаснет, знак крутится, пока идёт запрос. */
.sh .app-bar .fact--live{transition:color 120ms,opacity 160ms}
.sh .app-bar .fact--live .ico{transition:transform 160ms,color 120ms;display:inline-block}
.sh .app-bar .fact--live:hover .ico{transform:rotate(-45deg)}
.sh .app-bar .fact--live.is-busy{opacity:.55;pointer-events:none}
.sh .app-bar .fact--live.is-busy .ico{animation:sh-съём 900ms linear infinite;transform:none}
@keyframes sh-съём{to{transform:rotate(360deg)}}

/* 2. Раскрытие отбора. `grid-template-rows` 0fr→1fr — единственный
      способ довести высоту до содержимого, не зная её числом. */
/* Селектор несёт раскладку-доску не для красоты: правило полосы для неё
   весит на класс больше, и без этого поля 8/8 переживали свёртку —
   полоса оставалась видимой на 17px. Замерено, не угадано. */
/* `align-items:stretch` ОБЯЗАТЕЛЕН, и это не перестраховка. Полоса
   объявлена `align-items:center` как флекс; при смене на сетку значение
   переживает смену, элемент НЕ растягивается по треку, а центрируется
   в нём — и при нулевом треке рисуется наружу. Замер на 390px:
   .flt-row 148px в родителе 0px, выход за него на 74px, полоса отбора
   поверх доски. Родитель при этом честно отдаёт height=0, поэтому
   проверка по габаритам молчала: смотреть надо на ребёнка. */
.sh .app-body.layout-board .toolbar.pj-filters-all,
.sh .toolbar.pj-filters-all{display:grid;grid-template-rows:0fr;align-items:stretch;
  padding-top:0;padding-bottom:0;border-bottom:1px solid var(--line);
  transition:grid-template-rows 180ms cubic-bezier(.16,1,.3,1),
             padding-bottom 180ms cubic-bezier(.16,1,.3,1)}
/* ОБРЕЗКА НУЖНА ТОЛЬКО НА ВРЕМЯ РАСКРЫТИЯ — И ПЕРЕЖИЛА ЕГО (UI-106).
   `overflow:hidden` здесь стоит ради самой анимации: трек растёт от 0fr
   к 1fr, и без обрезки содержимое рисовалось бы наружу с первого кадра.
   Но раскрытие длится 180мс, а обрезка оставалась навсегда — и резала
   ВЫПАДАЮЩИЕ СПИСКИ отборов, которые выходят за полосу вниз.
   Замер: список 238×110, видно 0 из 110. Меню открывалось, отбор
   работал, человек не видел ничего — владелец прочитал это как
   «клик по фильтрам не работает», и это ровно то, что происходило.

   Урок тот же, что с местом под строку состояния в UI-100: правило,
   заведённое ради одного состояния, продолжает действовать во всех
   остальных, если его об этом не попросить.

   `allow-discrete` даёт переключить необрезаемость ПОСЛЕ раскрытия.
   Где его нет, необрезаемость наступает сразу: меню работает, а
   раскрытие выглядит чуть менее опрятно — отказ в верную сторону. */
.sh .toolbar.pj-filters-all > .flt-row{min-height:0;overflow:hidden;
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  transition:overflow 0s;transition-behavior:allow-discrete}
.sh .toolbar.pj-filters-all.is-open > .flt-row{overflow:visible;
  transition-delay:200ms}
.sh .app-body.layout-board .toolbar.pj-filters-all.is-open,
.sh .toolbar.pj-filters-all.is-open{grid-template-rows:1fr;
  padding-top:0;padding-bottom:var(--s2)}

/* 3. Отклик контрола. Нажатие темнит подложку — форма не меняется:
      сжимать чертёжный контрол значило бы притворяться, что он мягкий. */
.sh .ctl,.sh .flt,.sh .seg button,.sh button,.sh .ms-btn,.sh .idx,.sh .nav-link,
.sh .opt,.sh .menu-item,.sh .board-card{
  transition:background-color 120ms,border-color 120ms,color 120ms}
.sh .ctl:active,.sh .flt:active,.sh .ms-btn:active,.sh button:active{background:var(--field)}
.sh .ctl--go:active{opacity:.88}

/* ═══ ПРИГЛУШЁННОЕ ДВИЖЕНИЕ ══════════════════════════════════════════
   Уходит ПЕРЕМЕЩЕНИЕ КРУПНОЙ ПОВЕРХНОСТИ: папка листов проявляется,
   а не выезжает; отбор появляется, а не раскрывается.
   Остаётся ОТКЛИК: цвет, граница, гашение — они сообщают, что действие
   принято, и без них человек нажимает второй раз.
   Остаётся ВРАЩЕНИЕ ЗНАКА: оно сообщает, что работа идёт. Остановленный
   индикатор врёт, и приглушение движения не повод врать. */
@media (prefers-reduced-motion: reduce){
  .sh .app-side{transition:opacity 120ms,visibility 120ms;transform:none;
    opacity:0;visibility:hidden}
  :is(.sh .app,.sh.app):has(.nav-toggle[aria-expanded="true"]) .app-side{transform:none;opacity:1;visibility:visible}
  /* Специфичность равна базовой: у полосы для раскладки-доски на класс
     больше, и правило приглушения без этого проигрывало ей молча. */
  .sh .app-body.layout-board .toolbar.pj-filters-all,
  .sh .toolbar.pj-filters-all{transition:none}
  /* Кнопка обновления в карточке — компонент продукта, и её сдвиг
     приходит из прежнего языка. На переведённом экране движением
     распоряжается этот язык: отклик остаётся цветом, сдвиг уходит. */
  .sh .pj-updbtn{transition:background-color 120ms;transform:none}
  .sh .collapse svg,.sh .app-bar .fact--live .ico{transition:none}
  .sh .app-bar .fact--live:hover .ico{transform:none}
  .sh .skip{transition:none}
}

/* Окно получает фокус программно и не рисует рамку вокруг себя:
   рамка на всём окне не показывает, куда нажимать (UI-66). */
.sh .modal:focus,.sh .modal:focus-visible{outline:none}

/* ═══ КЛАССЫ 8–11 СЛОВАРЯ (UI-68) ════════════════════════════════════
   Все четыре нашлись только обходом по состояниям: они живут в окне
   карточки, в очереди обновления и в фокусе поиска — местах, куда
   прошлые прогоны не заходили. */

/* Подпись под значением в паспорте проекта: --n7 прежнего языка. */
.sh .t-sub{color:var(--ann)}

/* Шапка всплывшего окна. Прежний язык красил её --n0 и скруглял
   на 8px — в синьке это давало светлую плашку поверх тёмного окна,
   а радиус выпадал из шкалы языка (0/2/3/4). */
.sh .modal > .head{background:var(--sheet);border-bottom-color:var(--hair);
  border-radius:var(--r-ctl) var(--r-ctl) 0 0}

/* Индикатор работы. Кольцо — волосяная линия, бегущая часть —
   аннотация: то же соотношение, что у всех тихих элементов языка. */
.sh .spinner{border-color:var(--hair);border-top-color:var(--ann)}

/* Фокус поля. Рамка бралась прежним акцентом — цветом, у которого
   в этом языке своя роль и своё значение. Метка положения одна. */
.sh input:focus-visible,.sh select:focus-visible,.sh textarea:focus-visible,
.sh .search:focus-visible{border-color:var(--mark)}

/* ═══ ЗАМЕТКИ О ВЫКАТКАХ ══════════════════════════════════════════════
   Список читают в двух местах — окном поверх листа и листом
   `/release-notes`, — и разметка у них одна (`_release_notes.html`).
   Значит и правил одна пара: местных «в окне иначе» здесь нет
   намеренно, иначе два входа в один документ показывали бы два
   документа.

   НОМЕР ВЕРСИИ — ЯКОРЬ БЕГЛОГО ПРОСМОТРА. Человек приходит сюда
   с числом из штампа в голове и ищет глазами ЕГО, а не заголовок.
   Поэтому число крупнее заголовка релиза и стоит первым; заголовок
   читается уже после того, как место найдено. */
.sh .rels{display:flex;flex-direction:column;gap:var(--s6)}
.sh .rel{padding-top:var(--s5);border-top:1px solid var(--hair)}
.sh .rel:first-child{padding-top:0;border-top:0}
.sh .rel-head{display:flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap}
.sh .rel-v{font-size:18px;font-weight:600;letter-spacing:-.01em;color:var(--ink);
  font-variant-numeric:var(--tab)}
.sh .rel-d{font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--ann2)}
.sh .rel-t{margin:var(--s1) 0 0;font-size:14px;font-weight:600;color:var(--ink);
  line-height:1.35}
.sh .rel-note{margin:var(--s3) 0 0}
/* СПИСОК БЕЗ МАРКЕРОВ: маркером служит номер задачи, а два знака подряд
   в начале строки — маркер и номер — читаются как один сбитый отступ. */
.sh .rel-list{list-style:none;margin:var(--s3) 0 0;padding:0;
  display:flex;flex-direction:column;gap:var(--s3)}
.sh .rel-list > li{font-size:13px;line-height:1.5;color:var(--ann)}
/* НОМЕР ЗАДАЧИ — ПЛАШКА В НАЧАЛЕ СТРОКИ, А НЕ ОТДЕЛЬНАЯ КОЛОНКА.
   Колонка встала бы по самому длинному номеру («UI-104, UI-105»)
   и оставила бы пустоту всей остальной сотне строк. Плашка не переносится
   внутри себя: обрывок номера на конце строки перестаёт читаться
   как номер — тот же довод, что у телефона в контактах. */
.sh .rel-id{display:inline-block;margin-right:var(--s2);padding:1px var(--s2);
  border:1px solid var(--hair);border-radius:var(--r-hair);
  font-size:11px;font-weight:600;letter-spacing:.02em;white-space:nowrap;
  color:var(--ann2);font-variant-numeric:var(--tab)}
.sh .rel-txt{color:var(--ann)}
/* ТЕКУЩАЯ ВЫКАТКА ПОМЕЧЕНА ТЕМ ЖЕ, ЧЕМ ПОЛОЖЕНИЕ ВЕЗДЕ В ЯЗЫКЕ, —
   меткой `--mark`, и только ею: заливки у этого блока нет, иначе
   на листе появился бы второй чёрный блок (их один на экран). */
.sh .rel--now .rel-v{color:var(--mark)}

/* PROJ-12: замок у названия архивного проекта. Не декорация: архив это
   больше половины доски, и метка отличает историю от текущей работы.
   Приглушён намеренно — он сообщает состояние, а не требует внимания. */
.sh .nm-lock{opacity:.55;font-size:.9em}
