/* ВЁРСТКА КАБИНЕТА. Пакет design/cabinet-handoff, сверка — pnpm design:check.

   ═══ ПОЧЕМУ ОТДЕЛЬНЫМ ФАЙЛОМ, А НЕ В site.css ═══

   Кабинет за входом, витрина — перед ним, и общего у них ровно токены. Один
   файл на оба означал бы, что каждый посетитель главной грузит стили сайдбара
   и таб-бара, в которые он не войдёт, а вошедший — стили лендинга, который он
   уже прошёл. Подключается только страницами кабинета (`renderCabinetPage`).

   ═══ ЕДИНИЦЫ — ПИКСЕЛИ, И ЭТО НЕ НЕБРЕЖНОСТЬ ═══

   Макет задан в пикселях и сверяется попиксельно (порог 2 %). Перевод в `rem`
   на этапе вёрстки означал бы, что расхождение с эталоном закладывается в
   первой же строке, а искать его потом придётся глазами.

   ═══ ПОРЯДОК ═══

   Сначала мобильная оболочка (она же база), потом планшет 600–1023, потом
   десктоп 1024+ и полный десктоп 1200+. Мобайл базой потому, что у него меньше
   всего исключений: сайдбара нет, шапка одна, сетка одноколоночная. */

/* ═══ ПОЧЕМУ ЗДЕСЬ ЯВНЫЙ line-height ═══

   В прототипе строки списков, пункты навигации, чипы и вкладки — это
   `<button>`, а браузер даёт кнопке `line-height: normal` вместо наследования
   от `body` (там 1.6). Сервис на тех же местах отдаёт ссылки и формы: по ссылке
   переходят, а не нажимают её скриптом.

   Разница в межстрочном на 15,5 px даёт +6 px к каждой строке — и на экране из
   пяти строк это уже 30 px расхождения, накопленного молча. Поэтому там, где в
   макете стоит кнопка, межстрочный назван вслух. Сверено попиксельно: без этой
   строки экран «Ещё» расходился на 21 %, с ней — на доли процента. */

/* ───────── Атомы: иконка, подложка, аватар ───────── */

.ci {
  flex-shrink: 0;
  display: block;
}

/* Подложка смысловой иконки: радиус round(size × .27), значок в половину размера.

   Имя `ichip`, а не `chip`: у витрины в site.css свой `.chip` — фильтр-пилюля с
   рамкой и отступами 7×14. Обе таблицы стилей приходят на страницу кабинета,
   и одинаковое имя красило подложку чужими правилами. */
.ichip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: var(--brand-50);
  color: var(--brand-600);
}
.ichip--36 { width: 36px; height: 36px; border-radius: 10px; }
.ichip--38 { width: 38px; height: 38px; border-radius: 10px; }
.ichip--40 { width: 40px; height: 40px; border-radius: 11px; }
.ichip--44 { width: 44px; height: 44px; border-radius: 12px; }
.ichip--46 { width: 46px; height: 46px; border-radius: 12px; }
.ichip--48 { width: 48px; height: 48px; border-radius: 13px; }
.ichip--64 { width: 64px; height: 64px; border-radius: 17px; }

/* Аватар: инициалы, размер шрифта round(size × .36). Владелец — сплошной teal. */
.ava {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  flex-shrink: 0;
  font-weight: 700;
  letter-spacing: .02em;
  background: var(--brand-100);
  color: var(--brand-700);
}
.ava--own { background: var(--brand-600); color: #fff; }
/* Фотография врача поверх инициалов. Инициалы остаются под ней и показываются
   сами, если картинка не доехала: `alt=""` у сломанной картинки не рисует
   ничего, и заменять её скриптом не нужно (`script-src 'none'`). */
.ava { position: relative; overflow: hidden; }
.ava__p { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ava--26 { width: 26px; height: 26px; font-size: 9px; }
.ava--34 { width: 34px; height: 34px; font-size: 12px; }
.ava--36 { width: 36px; height: 36px; font-size: 13px; }
.ava--44 { width: 44px; height: 44px; font-size: 16px; }
.ava--48 { width: 48px; height: 48px; font-size: 17px; }
.ava--52 { width: 52px; height: 52px; font-size: 19px; }
.ava--56 { width: 56px; height: 56px; font-size: 20px; }

/* ───────── Оболочка: мобайл (база) ───────── */

body.cabinet {
  background: var(--bg-soft);
}

.cab {
  background: var(--bg-soft);
  min-height: 100vh;
}

.cab-side,
.cab-top,
.cab-foot {
  display: none;
}

.cab-main {
  max-width: 390px;
  margin: 0 auto;
  padding-bottom: 100px;
  background: var(--bg-soft);
}

.cab-mtop {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(255, 255, 255, .96);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.cab-mtop__in {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 56px;
  padding: 10px 12px 10px 16px;
}
.cab-mtop__logo { display: flex; align-items: center; }
.cab-mtop__gap { flex: 1; }
.cab-mtop__back {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: -10px;
  color: var(--text);
}
.cab-mtop__title { flex: 1; font-size: 17px; font-weight: 600; }
.cab-mtop__bell {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-2);
}
.cab-mtop__ava { display: flex; }

.cab-mpills { padding: 12px 0 4px; background: #fff; }

.cab-content { padding: 16px 16px 0; }

/* Ряд профилей. Скроллбар скрыт: ряд прокручивается пальцем, а полоса под ним
   съедала бы 15 px высоты, которых в макете нет. */
.cab-pills {
  display: flex;
  gap: 6px;
  align-items: center;
  overflow-x: auto;
  scrollbar-width: none;
}
.cab-pills::-webkit-scrollbar { display: none; }
.cab-pills--m { padding: 0 16px; }
.cab-pill {
  line-height: normal;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 4px 12px 4px 4px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: #fff;
  color: var(--text-2);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  box-sizing: border-box;
}
.cab-pill--all { padding: 6px 14px; }
/* Подстраховка от вырожденного случая: сократить некого (одно длинное слово
   без пробелов) — обрезаем многоточием, а не раздуваем пилюлю. В обычном
   случае (короткое имя после `pillLabel`) до предела не доходит никогда. */
.cab-pill__l { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.cab-pill.is-active {
  border: 1.5px solid var(--brand-600);
  background: var(--brand-50);
  color: var(--brand-700);
}
.cab-pill__add {
  width: 34px;
  height: 34px;
  border: 1px dashed var(--line-strong);
  border-radius: 999px;
  background: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--text-3);
}

/* Таб-бар. Держится за низ окна: содержимое под него не уезжает — у оболочки
   padding-bottom 100. */
.cab-tabs {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0;
  width: 100%;
  max-width: 390px;
  z-index: 55;
  background: rgba(255, 255, 255, .97);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: 6px 4px calc(8px + env(safe-area-inset-bottom));
}
.cab-tab {
  line-height: normal;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 6px 0 2px;
  min-height: 50px;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-3);
  text-decoration: none;
}
.cab-tab.is-active { font-weight: 700; color: var(--brand-700); }
.cab-tab__box {
  width: 44px;
  height: 28px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-2);
}
.cab-tab.is-active .cab-tab__box { background: var(--brand-50); color: var(--brand-700); }

/* Плавающая кнопка. Обёртка ловит ширину оболочки, но не клики — иначе
   прозрачная полоса поверх содержимого перехватывала бы нажатия. */
.cab-fab-box {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 84px;
  width: 100%;
  max-width: 390px;
  z-index: 50;
  pointer-events: none;
  display: flex;
  justify-content: flex-end;
  padding: 0 16px;
}
.cab-fab {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 52px;
  padding: 0 20px 0 16px;
  border-radius: 999px;
  background: var(--cta);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 10px 28px rgba(234, 138, 60, .4);
}
.cab-fab:hover { background: var(--cta-hover); }

/* ───────── Карточка ───────── */

.cab-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-s);
  padding: 24px;
}
/* Карточка-список: боковой отступ у карточки, вертикальный — у строк, чтобы
   разделители шли от края до края. */
.cab-card--rows { padding: 2px 14px; }

/* ───────── Экран «Ещё» ───────── */

.more__who {
  display: flex;
  gap: 14px;
  align-items: center;
  padding: 6px 0 18px;
}
.more__name { font-size: 18px; font-weight: 600; }
.more__mail { font-size: 13.5px; color: var(--text-3); }

.mrow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 0;
  text-decoration: none;
  color: inherit;
  line-height: normal;
}
.cab-card--rows .mrow + .mrow { border-top: 1px solid var(--line); }
/* Строка «Поддержка» в макете — ссылка `mailto:`, а не кнопка, и межстрочный у
   неё наследуется от body. Отсюда она на 8 px выше соседней: так в эталоне. */
.mrow--text { line-height: var(--lh-body); }
.mrow__text { flex: 1; min-width: 0; }
.mrow__title { display: block; font-size: 15.5px; font-weight: 600; color: var(--text); }
.mrow__sub {
  display: block;
  font-size: 13px;
  color: var(--text-3);
}
.mrow__sub--clip {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mrow__badge {
  font-size: 12px;
  font-weight: 700;
  background: var(--brand-50);
  color: var(--brand-700);
  border-radius: 999px;
  padding: 1px 8px;
}
.mrow__chev { color: var(--text-3); }
.mrow__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--ok); flex-shrink: 0; }

.more__second { margin-top: 14px; }

.more__links {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  margin-top: 18px;
}
.more__desktop { font-size: 14px; }
/* Флексом, а не блоком: блок вокруг кнопки строит строку с распоркой по
   базовой линии и вырастает на пиксель выше самой кнопки. В макете на этом
   месте ссылка, у которой распорки нет. */
.more__form { margin: 0; display: flex; }
.more__exit {
  line-height: inherit;
  border: none;
  background: none;
  padding: 0;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-2);
  cursor: pointer;
}
.more__note {
  font-size: 12.5px;
  color: var(--text-3);
  margin-top: 22px;
  line-height: 1.5;
}


/* ───────── Бейдж, пилюля вердикта, кнопки ───────── */

.bdg {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: var(--r-pill);
  padding: 3px 12px;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  /* Межстрочный НЕ задаётся: в макете бейдж это `<span>` без своей строки, и
     высоту ему даёт родитель. Своим `normal` он становился на 4 px ниже, и это
     видно там, где бейдж переносится на отдельную строку. */
}
.bdg--caps { padding: 4px 12px; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; }
/* Мелкий бейдж строк списка: статус документа в макете именно такой. */
.bdg--sm { padding: 2px 9px; font-size: 12px; }
/* Канал напоминания: 11,5 px и отступы 2×8 — свой размер, не «мелкий бейдж». */
.bdg--chan { padding: 2px 8px; font-size: 11.5px; }
.bdg--tiny { padding: 1px 8px; font-size: 11.5px; }
.bdg--brand { background: var(--brand-100); color: var(--brand-700); }
.bdg--cta { background: var(--cta-soft); color: #B05E17; }
.bdg--ok { background: var(--ok-soft); color: var(--ok); }
.bdg--warn { background: var(--warn-soft); color: var(--warn); }
.bdg--bad { background: var(--bad-soft); color: var(--bad); }
.bdg--neutral { background: var(--bg-soft); color: var(--text-2); }
.bdg--dark { background: var(--bg-dark); color: #7BCBE2; }

.vchip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  font-weight: 700;
  white-space: nowrap;
  flex-shrink: 0;
  line-height: normal;
}
.vchip--md { padding: 4px 11px; font-size: 13.5px; }
.vchip--sm { padding: 2px 9px; font-size: 12.5px; }
.vchip__dot { width: 7px; height: 7px; border-radius: 99px; background: currentColor; }
.vchip--ok { background: var(--ok-soft); color: var(--ok); }

/* Статус показателя: свои размеры, см. 03-components.md → StatusPill. */
.spill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.spill--md { padding: 3px 11px; font-size: 13px; }
.spill--sm { padding: 2px 9px; font-size: 12px; }
.spill__dot { width: 7px; height: 7px; border-radius: 99px; background: currentColor; }
.spill--sm .spill__dot { width: 6px; height: 6px; }
.spill--ok { background: var(--ok-soft); color: var(--ok); }
.spill--warn { background: var(--warn-soft); color: var(--warn); }
.spill--bad { background: var(--bad-soft); color: var(--bad); }
.vchip--warn { background: var(--warn-soft); color: var(--warn); }
.vchip--bad { background: var(--bad-soft); color: var(--bad); }

/* Кнопка. Высота задана числом, а не отступами: строка кнопки в макете равна
   её высоте, и от межстрочного она зависеть не должна. */
.cab-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: var(--r-m);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  line-height: normal;
  transition: background .15s ease, color .15s ease;
}
.cab-btn--md { height: 44px; padding: 0 22px; font-size: 15px; }
.cab-btn--sm { height: 36px; padding: 0 16px; font-size: 14px; }
.cab-btn--cta { background: var(--cta); color: #fff; }
.cab-btn--cta:hover { background: var(--cta-hover); color: #fff; }
.cab-btn--cta:active { background: var(--cta-active); }
.cab-btn--brand { background: var(--brand-600); color: #fff; }
.cab-btn--brand:hover { background: var(--brand-700); color: #fff; }
.cab-btn--outline { color: var(--brand-600); border-color: var(--brand-600); }
.cab-btn--outline:hover { background: var(--brand-50); color: var(--brand-600); }
.cab-btn--ghost { color: var(--brand-600); }
.cab-btn--ghost:hover { background: var(--brand-50); color: var(--brand-600); }
.cab-btn--muted { color: var(--text-3); }
.cab-btn--muted:hover { background: var(--bg-soft); color: var(--text-2); }
.cab-btn--block { width: 100%; }
/* Кнопка-форма (покупка пакета/подписки, `actionBtn`): `<form>` не должен
   вносить свой бокс в разметку соседних `<a class="cab-btn">` — `contents`
   отдаёт место в раскладке самой кнопке внутри, как будто формы нет вовсе. */
.cab-btn-f { display: contents; }
/* Выключенная кнопка: прозрачность .45 и курсор по умолчанию — умолчание
   системы для выключенного (07-no-js.md). Кнопка остаётся на своём месте и
   выглядит как в макете, а причина написана рядом словом: пустой угол вместо
   кнопки человек читает как поломку сайта. */
.cab-btn--off { opacity: .45; cursor: default; }
.cab-btn--off:hover { background: none; }
.cab-btn--brand.cab-btn--off:hover { background: var(--brand-600); color: #fff; }
/* Необратимое действие — одно на весь сервис: удаление аккаунта. Оно обязано
   выглядеть иначе, чем «Сохранить», иначе рука нажимает его по привычке. */
.cab-btn--danger { background: var(--bad); color: #fff; }
.cab-btn--danger:hover { background: #b91c1c; color: #fff; }

/* ───────── Вкладки «Профиля»: Данные / Уведомления / Безопасность ───────── */

/* Пилюли в скруглённой коробке — как в эталоне (`01-screens/profile-*.png`), а
   не полоска с подчёркиванием: активная вкладка там белая, с тенью, внутри
   мягкой подложки. Ссылками, а не кнопками: переход обязан работать средним
   кликом и без скриптов. */
.ptabs {
  display: inline-flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 0 0 20px;
  padding: 5px;
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.ptabs__i {
  padding: 8px 16px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--text-2);
  text-decoration: none;
  border-radius: 8px;
  line-height: normal;
}
.ptabs__i:hover { color: var(--brand-600); }
.ptabs__i--on { color: var(--brand-600); background: #fff; box-shadow: 0 1px 2px rgba(16, 24, 40, .06); }

/* ───────── Уведомления ───────── */

.ntf__f { display: flex; flex-direction: column; gap: 18px; }
.ntf__row { display: flex; gap: 12px; align-items: flex-start; cursor: pointer; }
.ntf__box { width: 20px; height: 20px; margin-top: 2px; flex: none; accent-color: var(--brand-600); }
.ntf__b { display: flex; flex-direction: column; gap: 2px; }
.ntf__t { font-size: 15px; }
.ntf__x { font-size: 13.5px; color: var(--text-2); }
.ntf__none { font-size: 13.5px; color: var(--bad); }
.seg--col { flex-direction: column; align-items: stretch; }

/* ───────── Безопасность и данные ───────── */

/* `min-width: 0` — колонка сетки `.prof__g` без него не сжимается ниже
   min-content своего длинного текста (та же ловушка, что и у самой `.prof__g`,
   доделка 16.09.2026): текст квиза «Заботы о здоровье» на 375 px уезжал за
   край вместо переноса. */
.prof__main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.prof--narrow { max-width: 640px; }
.sec__ls { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; }
.sec__l { display: flex; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
.sec__l:last-child { border-bottom: none; }
.sec__l-w { color: var(--text-2); flex: none; min-width: 148px; }
.sec__l-h { color: var(--text-3); }
.sec__l--bad .sec__l-t { color: var(--bad); }
.sec__del { margin-top: 10px; }
.sec__conf { display: flex; flex-direction: column; gap: 16px; margin-top: 16px; }
/* Прощание после удаления: оболочки кабинета у страницы нет — все её пункты
   вели бы в 404, — поэтому свои поля. */
.cab-gone { padding: 40px 20px; max-width: 720px; margin: 0 auto; }

/* ───────── Заголовок блока, пустое состояние, дисклеймер ───────── */

.blk { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.blk__t { font-size: 15.5px; flex: 1; font-weight: 700; }
.blk__link { font-size: 13.5px; }

.empty {
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--r-l);
  padding: 36px 24px;
  text-align: center;
  background: var(--bg-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.empty--compact { padding: 22px 18px; }
.empty__t { font-size: 16px; font-weight: 600; }
.empty__x { font-size: 14.5px; color: var(--text-2); max-width: 420px; line-height: 1.5; }
.empty__a { margin-top: 6px; }

.disc {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13.5px;
  color: var(--text-3);
  margin-top: 10px;
  line-height: 1.5;
}

/* ───────── Шкала квоты и кольцо скоринга ───────── */

.quota__row { display: flex; justify-content: space-between; font-size: 13.5px; color: var(--text-2); margin-bottom: 6px; }
.quota__row b { color: var(--text); }
.quota__bar { display: block; width: 100%; height: 8px; }
.quota__track { fill: var(--brand-100); }
.quota__fill { fill: var(--brand-600); }

.ring { display: block; flex-shrink: 0; }
.ring__track { stroke: var(--brand-100); }
.ring__fill { stroke: var(--brand-600); }
.ring__v { font-size: 30px; font-weight: 700; fill: var(--text); }
.ring__of { font-size: 11px; fill: var(--text-3); }

/* ───────── График динамики ───────── */

.tr { display: block; width: 100%; height: auto; overflow: visible; }
/* Из двух холстов виден один: узкий на мобайле, широкий на десктопе. */
.tr--narrow { display: none; }
/* Годовая ось — своя натуральная ширина в пикселях (год = YEAR_COL в
   `parts.ts`), не растягивается: растёт вместо неё прокрутка контейнера. */
.tr.tr--scroll { width: auto; height: auto; flex-shrink: 0; }
/* Десктопный холст без годовой оси не растягивается на всю широкую карточку —
   иначе весь текст внутри (значения, даты, «норма») масштабируется вместе с
   `viewBox` и становится крупнее заголовка показателя (докладная владельца,
   «значения набраны непропорционально крупно»). */
.tr--wide:not(.tr--scroll) { max-width: 640px; }

/* Непрерывная ось лет шире контейнера чаще, чем нет — контейнер прокручивает
   её сам, горизонтально, страница вбок не едет. Открыта по умолчанию на
   последнем (текущем) годе: `direction: rtl` у контейнера делает ПРАВЫЙ край
   его «началом» (там, где браузер без единой строки скрипта держит позицию
   прокрутки при первой отрисовке) — а `direction: ltr`, возвращённый самому
   графику, читает годы и числа как обычно, слева направо; зеркалится только
   прокрутка, не содержимое. */
.tr-scroll {
  overflow-x: auto;
  max-width: 100%;
  direction: rtl;
  -webkit-overflow-scrolling: touch;
}
.tr-scroll > svg { direction: ltr; }

.tr__norm { fill: var(--ok-soft); }
/* До четырёх подписей слева: низ шкалы, нижняя норма, верхняя норма, верх
   шкалы — считает и прореживает `yAxisLabels` в `parts.ts`. */
.tr__yaxis { font-size: 10.5px; fill: var(--text-3); }
/* «норма 30–45» одной строкой у полосы — вместо оторванного от чисел слова
   «норма» в углу (докладная владельца). */
.tr__normtag { font-size: 10px; font-weight: 600; fill: var(--ok); }
.tr__ms { stroke: var(--brand-300); stroke-dasharray: 3 4; stroke-width: 1.3; }
.tr__msbox { fill: var(--brand-100); }
.tr__mstext { font-size: 10.5px; font-weight: 600; fill: var(--brand-700); }
.tr__seg { stroke: var(--brand-600); stroke-width: 2.5; stroke-linecap: round; }
/* Разрыв больше года между соседними сдачами — пунктиром: связь есть,
   наблюдений между точками не было, и это правда (решение §146 — придуманную
   ломаную/размытую кривую между ними НЕ рисуем, см. заметку в `parts.ts`). */
.tr__seg--gap { stroke-dasharray: 5 4; }
.tr__dot { fill: #fff; stroke-width: 2.5; }
.tr__dot--ok { stroke: var(--ok); }
.tr__dot--warn { stroke: var(--warn); }
.tr__dot--bad { stroke: var(--bad); }
/* Вне нормы — точка ЗАЛИТА цветом зоны, не только обведена цветом: смысл не
   держится на одном канале различения (докладная владельца). */
.tr__dot--fill.tr__dot--warn { fill: var(--warn); }
.tr__dot--fill.tr__dot--bad { fill: var(--bad); }
.tr__val { font-size: 11px; font-weight: 700; }
.tr__val--ok { fill: var(--ok); }
.tr__val--warn { fill: var(--warn); }
.tr__val--bad { fill: var(--bad); }
.tr__date { font-size: 10.5px; fill: var(--text-3); }
/* Год без единого измерения — знак вопроса на оси, приглушённым цветом:
   честный факт «не сдавали», а не догадка о значении (решение §146). */
.tr__gapmark circle { fill: #fff; stroke: var(--line-strong); stroke-width: 1; }
.tr__gapmark text { font-size: 9px; font-weight: 700; fill: var(--text-3); }

/* Таблица измерений под графиком — резерв доступности: график сам
   `aria-hidden` (его не прочтёт скринридер), и то же самое, что подсказка
   `<title>` даёт мыши, здесь читается текстом без единой строки скрипта. */
.tr-table { margin-top: 10px; font-size: 13px; color: var(--text-2); }
.tr-table summary { cursor: pointer; color: var(--brand-700); font-weight: 600; }
.tr-table__t { width: 100%; border-collapse: collapse; margin-top: 8px; }
.tr-table__t th,
.tr-table__t td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }
.tr-table__t th { font-weight: 600; color: var(--text-3); font-size: 12px; }

/* ───────── Строка документа ───────── */

.drow {
  /* ═══ ПРАВЫЙ КРАЙ СТРОКИ — КОЛОНКИ, А НЕ ПОТОК ═══

     Ширины гнёзд правого края объявлены здесь, а сами гнёзда — у карточки
     списка (`.dgroup__c`): в списке строк десятки, и столбцы у него ищут
     глазами. Числа названы САМЫМ ШИРОКИМ содержимым гнезда и замерены, а не
     подобраны на глаз:

       --drow-lab      152 — плашка незнакомой лаборатории. Замер в браузере:
                       «Национальный…» 115 px, «Поликлиника №12…» 132 px,
                       «Клинико-диагностическа…» 169 px. Знаки известных
                       лабораторий у́же: самый широкий, «Гемотест» (3,4 : 1),
                       при высоте 26 занимает 88 px (перемерено 19.09.2026,
                       когда знак вырос с 20 до 26 — см. блок про колонку);
       --drow-verdict   80 — «10 из 10», единственная двузначная оценка шкалы;
       --drow-status   106 — «Консилиум ИИ», длиннейший из четырёх `DOC_STATUS`.

     Ширина задана НИЖНЕЙ ГРАНИЦЕЙ, а не жёстким числом (`min-width` у гнёзд,
     `minmax()` у колонки знака): содержимое длиннее ожидаемого раздвинет
     гнездо, а не обрежется. Ровный столбец — цель, но не ценой обрезанного
     слова (§78: обрывок имени читается как ошибка сервиса). */
  --drow-lab: 152px;
  --drow-verdict: 80px;
  --drow-status: 106px;

  position: relative;
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 12px 4px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.drow--compact { padding: 10px 4px; }
.drow__body { flex: 1; min-width: 0; }

.drow__title {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.3;
}
.drow__who {
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-700);
  background: var(--brand-50);
  border-radius: 999px;
  padding: 1px 8px;
}
/* Знак лаборатории в строке документа — сверх макета (решение владельца
   04.09.2026). Здесь — общий вид знака: высота общая, ширина по пропорции,
   потому что знаки чужие и разной формы, а растянутый чужой логотип —
   испорченный чужой логотип.

   ЭТО ВИД ПО УМОЛЧАНИЮ: знак стоит в подписи строки, перед датой. Так он
   выглядит на Обзоре и в хронологии дневника — там строка вдвое уже, и
   отдельной колонки знаку в ней нет. В СПИСКЕ документов у него своя колонка
   у правого края подписи, и она объявлена ниже, рядом с карточкой списка.

   `mix-blend-mode: multiply` — потому что переданные владельцем файлы растровые
   и с белым фоном; на подложке карточки белый прямоугольник вокруг знака виден
   сразу. Тем же способом стоит лента лабораторий на лендинге. */
.drow__lab {
  height: 14px;
  width: auto;
  max-width: 80px;
  object-fit: contain;
  vertical-align: -2px;
  margin-right: 7px;
  mix-blend-mode: multiply;
}
/* Плашка незнакомой лаборатории стоит на месте знака и в том же ряду подписи:
   строка не должна прыгать оттого, что логотипа этой лаборатории у нас нет. */
.drow__lab--text {
  display: inline-block;
  vertical-align: -1px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-3);
  background: var(--bg-soft);
  border-radius: 4px;
  padding: 0 5px;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drow__note { display: block; font-size: 13px; color: var(--text-3); margin-top: 2px; }
.drow__chev { color: var(--text-3); }
/* Ссылка заголовка накрывает всю строку — кроме кнопки, которая лежит выше. */
.drow__link { color: inherit; text-decoration: none; }
.drow__link::after { content: ""; position: absolute; inset: 0; }
.drow__act { position: relative; z-index: 1; }

/* ───────── Обзор ───────── */

.head { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; }
.head__b { flex: 1; min-width: 0; }
.head__t { font-size: 28px; line-height: 1.2; }
.head__s { font-size: 15px; color: var(--text-3); margin-top: 4px; }
.head__act { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

.ov-serv { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 20px; }
.tile {
  display: flex;
  gap: 14px;
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-l);
  box-shadow: var(--shadow-s);
  padding: 14px 16px;
  text-decoration: none;
  color: inherit;
  line-height: normal;
  transition: box-shadow .18s ease, transform .18s ease;
}
.tile:hover { box-shadow: var(--shadow-l); transform: translateY(-2px); }
.tile__body { min-width: 0; }
.tile__t { display: block; font-size: 15px; font-weight: 600; line-height: 1.3; }
.tile__s { display: block; font-size: 13px; color: var(--text-3); margin-top: 2px; line-height: 1.35; }

/* `minmax(0, 1fr)`, а не `1fr`: колонка сетки по умолчанию не сжимается ниже
   своего содержимого, и строка документа с длинным названием, бейджем и
   кнопкой уносила правую колонку за край экрана. На эталонных данных этого не
   видно — там названия короткие; поймано прогоном на трудном содержимом. */
/* Карточка заботы о здоровье — «ненавязчивый блок», не уведомление: та же
   плашка `cab-card`, что и соседние блоки Обзора, без рамки тревоги и без
   крестика в углу. Полная ширина, вне `.ov-cols` — она не про одну из двух
   колонок, а про профиль целиком. */
.ov-care { padding: 20px 22px; margin-bottom: 20px; }
.care__src { font-size: 13px; color: var(--text-3); margin-top: 8px; }
.ov-cols { display: grid; grid-template-columns: 380px minmax(0, 1fr); gap: 20px; align-items: start; }
.ov-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.ov-score, .ov-plan, .ov-up, .ov-dyn { padding: 22px; }
.ov-plan, .ov-up, .ov-dyn { padding: 20px; }
.ov-docs { padding: 8px 20px 12px; }
.blk--docs { padding: 12px 0 4px; margin-bottom: 0; }
.ov-docs .drow + .drow { border-top: 1px solid var(--line); }
.ov-docs__empty { padding: 8px 0 12px; }

.score { display: flex; gap: 14px; align-items: center; }
.score__b { flex: 1; min-width: 0; }
/* Блоком, а не строкой: у строчного элемента высоту строки задаёт РОДИТЕЛЬ
   (17 px от body), и строка становится на 6 px выше своей буквы, сдвигая
   заметку под ней. В макете это `<div>`. */
.score__d { display: block; font-size: 13.5px; font-weight: 600; color: var(--text-3); }
.score__d--up { color: var(--ok); }
.score__d--down { color: var(--warn); }
.score__note { font-size: 14px; color: var(--text-2); margin-top: 4px; line-height: 1.45; }

.pl { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }
.pl__h { font-size: 14px; }
.pl__i { display: flex; gap: 10px; padding: 10px 0; }
.pl__i + .pl__i { border-top: 1px solid var(--line); }
.pl__n {
  width: 22px;
  height: 22px;
  border-radius: 99px;
  background: var(--brand-50);
  color: var(--brand-700);
  font-size: 12px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 1px;
}
.pl__b { flex: 1; min-width: 0; }
.pl__t { font-size: 14px; line-height: 1.45; color: var(--text-2); }
.pl__a { margin-top: 8px; }

.plan__desc { font-size: 14px; color: var(--text-2); line-height: 1.5; }
.plan__renew { font-size: 13px; color: var(--text-3); margin-top: 8px; }
.plan__prev { font-size: 13.5px; color: var(--text-2); margin-top: 10px; }
.plan__acts { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* Краткий выбор тарифа в плашке (15.09.2026): три строки вместо похода на
   страницу цен. Список тесный — карточка Обзора узкая, а `.bil__s` странице
   цен верстался под сетку `1fr 150px 170px`, которая сюда не влезает. */
.plan__opts { list-style: none; margin: 14px 0 0; padding: 0; }
.plan__opt { padding: 12px 0; }
.plan__opt + .plan__opt { border-top: 1px solid var(--line); }
.plan__opt-h { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.plan__opt-n { font-size: 14px; font-weight: 600; }
.plan__opt-p { font-size: 14px; font-weight: 700; white-space: nowrap; }
.plan__opt-i { display: block; font-size: 13px; color: var(--text-2); line-height: 1.4; margin-top: 2px; }
.plan__opt-a { margin-top: 8px; }
.plan__opt-no { font-size: 12px; color: var(--text-3); line-height: 1.4; margin-top: 6px; }

.up__i { display: flex; gap: 12px; align-items: flex-start; padding: 10px 0; }
.up__i + .up__i { border-top: 1px solid var(--line); }
.up__d {
  width: 44px;
  text-align: center;
  flex-shrink: 0;
  background: var(--bg-soft);
  border-radius: 10px;
  padding: 4px 0;
  line-height: 1.15;
}
.up__d b { font-size: 17px; display: block; color: var(--text); }
.up__d span { font-size: 11px; color: var(--text-3); }
.up__d--soon { background: var(--brand-50); }
.up__d--soon b { color: var(--brand-700); }
.up__b { flex: 1; min-width: 0; }
.up__t { font-size: 14.5px; font-weight: 600; display: flex; gap: 8px; align-items: center; }
.up__n { font-size: 13px; color: var(--text-3); margin-top: 2px; line-height: 1.4; }

.dyn__note { font-size: 12.5px; color: var(--text-3); margin-top: 6px; }
.dyn__lock {
  display: flex;
  gap: 14px;
  align-items: center;
  background: var(--bg-soft);
  border-radius: var(--r-l);
  padding: 18px 20px;
}
.dyn__ico { color: var(--brand-700); }
.dyn__lb { flex: 1; }
.dyn__lt { font-size: 15px; }
.dyn__lx { font-size: 14px; color: var(--text-2); margin-top: 4px; line-height: 1.45; }

.ov__disc { font-size: 13px; color: var(--text-3); text-align: center; margin-top: 36px; }

/* ───────── Планшет: та же оболочка во всю ширину ───────── */

@media (min-width: 600px) and (max-width: 1023px) {
  .cab-main { max-width: none; }
  .cab-mtop__in { padding: 10px 20px 10px 24px; }
  .cab-pills--m { padding: 0 24px; }
  .cab-content { padding: 16px 24px 0; }
  .cab-tabs { max-width: none; }
  .cab-fab-box { max-width: none; padding: 0 24px; }
}

/* ───────── Десктоп: сайдбар вместо таб-бара ───────── */

@media (min-width: 1024px) {
  .cab { display: flex; }
  .cab-mtop,
  .cab-mpills,
  .cab-tabs,
  .cab-fab-box { display: none; }

  .cab-side {
    display: flex;
    flex-direction: column;
    width: 72px;
    flex-shrink: 0;
    background: #fff;
    border-right: 1px solid var(--line);
    position: sticky;
    top: 0;
    height: 100vh;
    padding: 22px 12px 18px;
  }
  .cab-side__logo { display: flex; align-items: center; justify-content: center; }
  .cab-side__logo-full { display: none; }
  .cab-side__logo-mark { display: block; border-radius: 8px; }

  .cab-nav { margin-top: 30px; display: flex; flex-direction: column; gap: 2px; }
  .cab-nav__item {
    line-height: normal;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    border-radius: var(--r-l);
    padding: 12px 0;
    font-size: 15px;
    font-weight: 500;
    color: var(--text-2);
    text-decoration: none;
    white-space: nowrap;
    position: relative;
    transition: background .15s ease;
  }
  .cab-nav__item:hover { background: var(--bg-soft); }
  .cab-nav__item.is-active { background: var(--brand-50); color: var(--brand-700); font-weight: 700; }
  .cab-nav__label { display: none; }
  .cab-nav__count {
    position: absolute;
    top: 6px;
    right: 8px;
    font-size: 11px;
    font-weight: 700;
    background: var(--brand-600);
    color: #fff;
    border-radius: 999px;
    padding: 0 6px;
    line-height: 16px;
  }

  .cab-side__foot {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
    border-top: 1px solid var(--line);
    padding-top: 14px;
    align-items: center;
  }
  .cab-side__row,
  .cab-side__exit {
    display: flex;
    align-items: center;
    gap: 10px;
    border-radius: var(--r-m);
    padding: 9px 0;
    font-size: 14px;
    color: var(--text-2);
    text-decoration: none;
  }
  .cab-side__form { border: none; padding: 0; margin: 0; background: none; }
  .cab-side__exit {
    width: 100%;
    justify-content: center;
    text-align: left;
    border: none;
    background: transparent;
    font-family: var(--font-body);
    cursor: pointer;
  }
  .cab-side__label { display: none; }
  .cab-side__dot { display: none; }
  .cab-side__mobile { display: none; }

  .cab-main {
    max-width: none;
    margin: 0;
    padding-bottom: 0;
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }

  .cab-top {
    display: block;
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgba(255, 255, 255, .94);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--line);
  }
  .cab-top__in {
    padding: 0 24px;
    height: 68px;
    display: flex;
    align-items: center;
    gap: 16px;
  }
  .cab-top__act { margin-left: auto; display: flex; gap: 10px; align-items: center; flex-shrink: 0; }
  .cab-icon-btn {
    width: 40px;
    height: 40px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-2);
  }
  .cab-icon-btn:hover { background: var(--bg-soft); }
  /* Кнопка шапки объявлена выше вместе с остальными: здесь ей нужен только
     размер по умолчанию — «Загрузить документ» в макете средняя. */
  .cab-top__act .cab-btn { height: 44px; padding: 0 22px; font-size: 15px; }

  .cab-content {
    width: 100%;
    padding: 24px 24px 64px;
    flex: 1;
  }

  .cab-foot {
    display: flex;
    border-top: 1px solid var(--line);
    padding: 18px 32px;
    gap: 24px;
    justify-content: center;
    font-size: 12.5px;
    color: var(--text-3);
    flex-wrap: wrap;
  }
}

/* ───────── Полный десктоп: подписи навигации ───────── */

/*  ═══ КОЛОНКА НЕ ОГРАНИЧЕНА ПО ШИРИНЕ — ОТСТУПЛЕНИЕ ОТ МАКЕТА (DECISIONS §68) ═══

    В макете правило одно: «контент max-width 1120 по центру» (05-responsive.md,
    строка про ≥ 1200). У сайдбара оно даёт справа пустое место: на 1440 — по
    26 px с каждой стороны колонки, на 1920 — по 266. Владелец увидел это на
    стенде и решил снять ограничение: контент занимает всю ширину рядом с
    сайдбаром, `.cab-main` растягивается флексом, а колонке остаются только
    поля (24 на 1024–1199, 32 от 1200).

    Раньше здесь стояла заморозка зазора на 26 px от 1441 — полумера: на самом
    эталоне 1440 пустое место оставалось. Правила нет вовсе.

    ЧЕМ ЭТО ОПЛАЧЕНО. Эталоны 1440 расходятся: колонка шире на 52 px и начата
    на 26 px левее. Сверка про это знает и меряет вёрстку в макетной ширине
    колонки — `scripts/design/frame.ts`; отступление объявлено там одним числом
    и не растворяется в допуске. */
@media (min-width: 1200px) {
  .cab-side { width: 268px; padding: 22px 14px 18px; }
  .cab-side__logo { justify-content: flex-start; padding: 0 10px; }
  .cab-side__logo-full { display: block; }
  .cab-side__logo-mark { display: none; }

  .cab-nav__item { justify-content: flex-start; padding: 10px 12px; }
  .cab-nav__label { display: block; flex: 1; }
  .cab-nav__count {
    position: static;
    font-size: 12px;
    background: var(--brand-50);
    color: var(--brand-700);
    padding: 1px 8px;
    line-height: normal;
  }
  .cab-nav__item.is-active .cab-nav__count { background: #fff; }

  .cab-side__foot { align-items: stretch; }
  .cab-side__row,
  .cab-side__exit { padding: 9px 12px; justify-content: flex-start; }
  .cab-side__label { display: block; flex: 1; }
  .cab-side__dot { display: block; width: 8px; height: 8px; border-radius: 999px; background: var(--ok); }
  .cab-side__mobile { display: block; padding: 10px 12px 0; font-size: 13px; color: var(--text-3); }

  .cab-top__in { padding: 0 32px; }
  .cab-content { padding: 28px 32px 72px; }
}


/* Вспышка-сообщение. В макете это тост у нижнего края; без скриптов он
   приходит вместе со страницей после редиректа, поэтому стоит вверху раздела —
   там, куда человек смотрит после отправки формы. */
.toast {
  background: rgba(11, 37, 48, .94);
  color: var(--text-inverse);
  border-radius: var(--r-m);
  padding: 11px 18px;
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 16px;
}
.toast--error { background: var(--bad); }

/* ───────── Документы ───────── */

.dfilters { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }

/* Ряд фильтр-пилюль. Прокручивается пальцем, полоса прокрутки скрыта.

   Имя `fchip`, а не `chip`: подложка смысловой иконки уже занимает `.ichip`, и
   одинаковое имя делало круг из скруглённого квадрата в КАЖДОЙ строке списка.
   Найдено сверкой, а не глазами. */
.fchips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; }
.fchips::-webkit-scrollbar { display: none; }
.fchip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: #fff;
  color: var(--text-2);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  line-height: normal;
  transition: background .15s ease;
}
.fchip.is-active { border-color: var(--brand-600); background: var(--brand-50); color: var(--brand-700); }
.fchip__n { font-weight: 500; color: var(--text-3); }
.fchip.is-active .fchip__n { color: var(--brand-600); }

/* Сегмент-контрол: вкладки-переключатель одной строкой. */
.seg {
  display: inline-flex;
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  padding: 3px;
  gap: 2px;
}
.seg__i {
  border-radius: 9px;
  padding: 7px 14px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  line-height: normal;
  transition: background .15s ease;
}
.seg--sm .seg__i { padding: 5px 10px; font-size: 13px; }
.seg__i.is-active { background: #fff; color: var(--brand-700); box-shadow: var(--shadow-s); }
.seg__i:hover { color: var(--text); }
.seg__i.is-active:hover { color: var(--brand-700); }

/* Поиск. Лупа — фон поля: значок поверх пришлось бы ставить абсолютно. */
.dsearch { margin-left: auto; }
.dsearch__i {
  height: 44px;
  width: 300px;
  padding: 0 14px 0 38px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  background: #fff 12px center / 17px no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
  font-family: var(--font-body);
  font-size: 14.5px;
  color: var(--text);
}
.dsearch__i:focus { outline: none; border-color: var(--brand-600); box-shadow: 0 0 0 3px var(--focus-ring); }

/* Допродажа пакета: мягкая карточка над списком. */
.ups { display: flex; gap: 14px; align-items: center; padding: 16px; margin-bottom: 16px; }
.cab-card--soft { background: var(--bg-soft); box-shadow: none; }
.ups__t { flex: 1; font-size: 14px; color: var(--text-2); line-height: 1.5; }
.ups__t b { color: var(--text); }

.dgroup { margin-bottom: 18px; }
.dgroup__m {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin: 0 0 8px 4px;
}
.dgroup__c { padding: 2px 18px; }
.dgroup__c .drow + .drow { border-top: 1px solid var(--line); }

/* ───────── Правый край строки списка: одно правило на все гнёзда ─────────

   Замечание владельца 08.09.2026: «у строк с разными кнопками — превью, полный
   отчёт, круглые обводки — отступы разные». Так и было, и виноваты не отступы
   — они везде по 12 px, — а ШИРИНА ГНЕЗДА: её задавало содержимое. Бейдж
   «Превью» уже «Полного отчёта» на 37 px, и ровно на столько же прыгала
   влево-вправо пилюля внимания у соседних строк. Замерено на бланках стенда:
   при ширине окна 1440 пилюля начиналась то на 1170, то на 1207.

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

   ═══ ПОЧЕМУ ПРАВИЛО У КАРТОЧКИ СПИСКА, А НЕ У СТРОКИ ВООБЩЕ ═══

   Строка документа стоит ещё на Обзоре и в хронологии дневника, и там карточка
   вдвое уже. Та же резервация двигала там подпись на вторую строку: Обзор
   расходился с эталоном на 3,67 % против 1,16 %. Столбцы ищут глазами там, где
   строк десятки; на Обзоре их четыре. */
.dgroup__c .drow > .vchip { min-width: var(--drow-verdict); justify-content: center; }
/* Выравнивание — `text-align`, а НЕ `display: flex`: гнездо статуса на экране
   уже 1024 скрыто макетом (`display: none` в медиазапросе ниже), а правило с
   двумя классами перебило бы его по весу — бейдж «Превью» вернулся бы на
   мобайл и съел 118 px подписи. Поймано сверкой: 768 разошлось на 3,61 %,
   375 — на 7,52 %, подпись сжималась до двух пикселей ширины. */
.dgroup__c .drow__status { text-align: right; min-width: var(--drow-status); }

/* ───────── Знак лаборатории в списке: колонка у правого края подписи ─────────

   Решение владельца 08.09.2026, поверх §78. До него знак стоял иконкой в 14 px
   у левого края подписи, вплотную к дате. Владелец посмотрел список на живых
   бланках: знак там теряется — он мельче стоящей рядом даты и попадает в самое
   пёстрое место строки. Теперь у него КОЛОНКА в правом конце подписи, и в ней
   он стоит один: слева пусто, справа край тела строки.

   ═══ ПОЧЕМУ КОЛОНКА У ПОДПИСИ, А НЕ У ВСЕЙ СТРОКИ ═══

   Колонка отнимает 164 px, и отнимать их у ЗАГОЛОВКА нельзя: названия
   настоящих бланков длинные («Общий анализ крови (развёрнутый, с лейкоцитарной
   формулой)»), и строка начала бы переноситься там, где не переносилась.
   Замерено сверкой: колонка на всё тело строки заставляла переноситься
   заголовок на 1024, список под ним съезжал на 25 px, и расхождение с макетом
   росло с 1,42 % до 4,65 %. У ПОДПИСИ запас есть: «12 авг 2026 · 3 отклонения»
   занимает треть строки.

   ═══ ПОЧЕМУ ТОЛЬКО В СПИСКЕ ═══

   На Обзоре и в дневнике карточка вдвое уже — колонка съела бы подпись целиком
   (проверено: Обзор расходился с эталоном на 5,52 % против 0,94 %). Там знак
   остаётся в подписи, как стоял. Столбец знаков ищут глазами в списке.

   ═══ ПОЧЕМУ ВЫСОТА 26, А НЕ 20 (ПЕРЕМЕРЕНО 19.09.2026) ═══

   Строка подписи занимает 20,8 px, и до 19.09.2026 знак был ровно 20: всё,
   что есть у подписи ДАРОМ. Выше — и раздвигалась КАЖДАЯ строка списка;
   цена замерена дважды, на разной вёрстке и с одинаковым ответом. Замер
   19.09.2026 на нынешнем списке, экран «Результаты», стенд с настоящими
   бланками:

     высота знака   1440     1024     768      375
     20 (как было)  1,84 %   2,17 %   1,77 %   2,49 %
     22             3,91 %   4,86 %   4,36 %   2,57 %
     24             5,33 %   6,77 %   5,51 %   7,17 %

   То есть ограничение НЕ снялось: +2 px к высоте по-прежнему удваивают
   расхождение с макетом, и не оттого, что знак больше, а оттого, что список
   под ним съезжает. Сумма высот шести строк: 396,8 px при знаке 20, 404,1
   при 22, 416,1 при 24.

   ═══ И ВСЁ-ТАКИ ЗНАК ВЫРОС — ЗА СЧЁТ ПОЛЯ СТРОКИ, А НЕ ЕЁ ВЫСОТЫ ═══

   У строки под подписью есть СВОЁ поле: 12 px на 1440/1024, 10 px на 768
   (замерено браузером). Знак забирает часть этого поля, СВИСАЯ вниз:
   `height: 26px` и `margin-bottom: -5.2px` — коробка полей у знака остаётся
   те же 20,8 px, клетка сетки не растёт, строка не двигается ни на пиксель.
   Просвет до края строки остаётся 6,8 px на 1440/1024 и 4,8 px на 768.

   Свисает ТОЛЬКО ВНИЗ, а не в обе стороны: над знаком идёт заголовок, и на
   1024 и 768 длинные названия бланков заходят правым краем В КОЛОНКУ ЗНАКА
   (замерено: зазор доходит до −113 px). Вверх знак упёрся бы в их буквы.

   Что это дало: расхождение с макетом 1,88 % / 2,22 % / 1,83 % / 2,49 % —
   те же доли, что при знаке 20 (±0,06 п.п.), сумма высот строк 396,9 против
   396,8. Ширина знака при этом выросла на треть: ситилаб 32 → 41,7, инвитро
   41 → 53,4, гемотест 67,6 → 87,9, KDL 44,4 → 57,7. Слабейший из знаков
   (ситилаб: в его рамку входит редкое облако точек, §108) стал ровно той
   ширины, какой при высоте 20 был инвитро.

   Выше 26 не пошёл: при 28 просвет до края строки на 768 остаётся 2,8 px —
   знак читался бы приклеенным к разделителю. Это последний рубеж, за
   которым правится уже высота строки списка, а это отдельное решение
   владельца (§108).

   Против прежних 14 это почти вдвое крупнее, а главное — знак больше ни с
   чем не соседствует. Терялся он не от размера, а от соседства с датой. */
.dgroup__c .drow__note {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(var(--drow-lab), auto);
  column-gap: 12px;
  align-items: center;
}
/* Знак назначен во ВТОРУЮ колонку явно, а текст подписи попадает в первую сам:
   он безымянный узел, и сетка размещает его в первую свободную клетку. */
.dgroup__c .drow__lab {
  grid-column: 2;
  /* `order` двигает знак в КОНЕЦ порядка размещения. Без него сетка ставила
     текст подписи во ВТОРУЮ строку: знак стоит в разметке первым, размещается
     первым, и курсор авторазмещения уже не возвращался в первую клетку —
     подпись становилась двухстрочной, а строка выше на 20 px. Поймано замером:
     `grid-template-rows` показал две строки вместо одной.

     Порядок в разметке при этом не тронут намеренно: знак стоит там перед
     датой, и таким его берут Обзор и дневник, у которых сетки нет. */
  order: 1;
  justify-self: center;
  /* 26 при клетке в 20,8: лишние 5,2 px знак забирает у СОБСТВЕННОГО поля
     строки, свисая вниз, — см. блок выше. Строка от этого не растёт. */
  height: 26px;
  margin-bottom: -5.2px;
  max-width: 100%;
  margin-right: 0;
  vertical-align: baseline;
}
/* Плашка незнакомой лаборатории — в той же колонке и той же высоты, включая
   свисание вниз: иначе столбец шёл бы вперемешку знаками в 26 px и плашками
   в 20, и ровным он быть перестал бы. Кегль прежний (11,5): растёт коробка,
   а не буквы. */
.dgroup__c .drow__lab--text {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  margin-bottom: -5.2px;
  border-radius: 6px;
  padding: 0 8px;
  /* Потолок — на 11 px шире самой длинной ЗАМЕРЕННОЙ плашки (169). Он не для
     них, а для имени из одних широких букв: 22 знака «Ш» дают 270 px и съели
     бы подпись. Такое имя обрежется, настоящие — нет. */
  max-width: 180px;
  vertical-align: baseline;
}
.dscreen__note { font-size: 13px; color: var(--text-3); text-align: center; margin-top: 8px; }

/* ───────── Пачка: список загрузок и состояние одной ─────────

   Строка пачки — сетка `auto 1fr auto auto`: номер, имя с подписью, пилюля
   состояния, кнопка повтора. Именно сетка, а не flex с `gap`: пилюли состояний
   разной ширины («В очереди» и «Разобран»), и при flex они встают на разном
   расстоянии от правого края — глазом это читается как криво собранный список.

   Полоса выполнения — SVG, а не `<div>` с процентом в `style`: у сервиса
   `style-src 'self'` без `unsafe-inline`, и инлайновая ширина не применилась бы
   вовсе. Ширина прямоугольника это атрибут, политики он не касается — тот же
   приём, что у `.quota__fill`. Перехода нет и не нужно: страница
   перезагружается целиком, и `transition` на новом элементе не срабатывает. */

.bgroup { padding: 2px 18px; }

/* ───────── Сумма массовой загрузки (DECISIONS §181) ─────────
   Оценка, пока разбор идёт, и форма оплаты, когда он закончился. */
.bpay { padding: 16px 18px; margin-bottom: 16px; }
.bpay__sum { font-size: 15px; font-weight: 600; color: var(--text); margin-top: 8px; }
.bpay__n { font-size: 13.5px; color: var(--text-2); line-height: 1.5; margin-top: 8px; }
.bpay__f { display: flex; flex-direction: column; gap: 12px; margin-top: 10px; max-width: 520px; }
.bpay__modes { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.bpay__lg { font-size: 14px; color: var(--text-2); padding: 0; margin-bottom: 2px; }
.bpay__opt { align-items: flex-start; line-height: 1.45; }
.bpay__opt input { margin-top: 2px; flex: none; }
.bpay .cab-btn { align-self: flex-start; }
.bgroup .brow + .brow { border-top: 1px solid var(--line); }

.brow {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  padding: 13px 0;
}
.brow--link { text-decoration: none; color: inherit; grid-template-columns: auto minmax(0, 1fr) auto auto; }
.brow--link:hover .brow__n { color: var(--brand); }
.brow__i { font-size: 13px; color: var(--text-3); text-align: right; }
.brow__b { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.brow__n {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  /* Имя переносится по словам, а не обрезается: у бланков из архива имена
     длинные и различаются концом («скан_2019_03_12.pdf»). */
  overflow-wrap: anywhere;
}
a.brow__n { text-decoration: none; }
a.brow__n:hover { color: var(--brand); }
.brow__p { font-size: 13px; color: var(--text-3); }
.brow__r { font-size: 13px; color: var(--text-2); line-height: 1.45; margin-top: 2px; }
.brow__a { margin: 0; }

.bprog { display: block; width: 100%; height: 6px; margin-bottom: 16px; }
.bprog__track { fill: var(--bg-soft); }
.bprog__fill { fill: var(--brand); }

/* ───────── Telegram-бот ─────────

   Код набирают руками с экрана компьютера в телефон, поэтому он крупный, с
   разрядкой и моноширинным начертанием (`.num` даёт табличные цифры, разрядку
   добавляет letter-spacing): «RQ3M» слитно и «RQ 3M» вразрядку переписываются
   с разной точностью, и это видно на первой же попытке. */

.tg { max-width: 720px; }
.tg__card { padding: 20px; margin-bottom: 16px; display: flex; gap: 14px; align-items: flex-start; }
.tg__card--code { flex-direction: column; gap: 8px; }
.tg__st { flex: 1; min-width: 0; }
.tg__st-t { display: flex; align-items: center; gap: 8px; font-size: 16px; font-weight: 600; }
.tg__st-x { font-size: 14px; color: var(--text-2); line-height: 1.5; margin-top: 4px; }
.tg__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--ok); flex-shrink: 0; }
.tg__off { margin: 0; }

.tg__fallback { width: 100%; margin-top: 8px; }
.tg__fallback summary { cursor: pointer; color: var(--text-2); font-size: 14px; }
.tg__fallback[open] summary { margin-bottom: 10px; }
.tg__code {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.18em;
  /* Перенос по символам: восемь знаков вразрядку не помещаются в 320 px, и
     без этого код уехал бы за край карточки. */
  overflow-wrap: anywhere;
}
.tg__code-a { margin: 4px 0; }
.tg__code-x { font-size: 13.5px; color: var(--text-2); line-height: 1.5; }
.tg__code-x--warn { color: var(--text-3); }

.tg__on { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
.tg__on-x { font-size: 13.5px; color: var(--text-3); flex: 1; min-width: 220px; }

.tg__how-t { font-size: 15px; font-weight: 600; }
.tg__how { margin: 10px 0 0 18px; font-size: 14px; color: var(--text-2); line-height: 1.7; }
.tg__how-x { font-size: 13.5px; color: var(--text-3); line-height: 1.5; margin-top: 10px; }

/* Кнопка «Загрузить» в шапке — только на узком экране: на десктопе она в шапке
   кабинета, и двух одинаковых кнопок на экране быть не должно. */
.head__act--m { display: none; }
/* Обратное: кнопка, которой на узком экране нет. */
.head__act--wide { display: inline-flex; }

/* ───────── Дневник ───────── */

.diary__body { margin-top: 16px; }

/* Приём выгрузки: форма в карточке, шириной с текст, с кнопкой выбора файла. */
.imp__c { max-width: 620px; }
.imp { display: flex; flex-direction: column; gap: 14px; }
.imp__t { font-size: 16px; font-weight: 600; }
.imp__l { font-size: 14px; color: var(--text-2); line-height: 1.55; }
.imp__n { font-size: 13px; color: var(--text-3); line-height: 1.5; }
.imp__a { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* ═══ Выгрузка: правила, список загрузок, предпросмотр (DECISIONS §211) ═══ */
.imp__c--wide { max-width: 980px; }
.imp__list { margin-top: 16px; }
.imp__m { font-size: 13px; color: var(--text-3); line-height: 1.5; overflow-wrap: anywhere; }
.imp__rules { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; color: var(--text-2); line-height: 1.5; }
.imp select.fld__i { height: 44px; }

.impl { list-style: none; margin: 8px 0 0; padding: 0; }
.impl__i { display: flex; gap: 12px; align-items: center; justify-content: space-between; padding: 12px 0; border-top: 1px solid var(--line); }
.impl__i:first-child { border-top: 0; }
.impl__b { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.impl__t { font-size: 14.5px; font-weight: 600; color: var(--text); text-decoration: none; }
.impl__t:hover { color: var(--brand-700); text-decoration: underline; }
.impl__m { font-size: 13px; color: var(--text-3); overflow-wrap: anywhere; }
.impl__a { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }

.imps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.imps__i { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px; border-radius: var(--r-m); background: var(--bg-soft); border: 1px solid var(--line); }
.imps__i--ok { background: var(--ok-soft); border-color: transparent; }
.imps__i--warn { background: var(--warn-soft); border-color: transparent; }
.imps__i--bad { background: var(--bad-soft); border-color: transparent; }
.imps__v { font-size: 22px; font-weight: 700; line-height: 1.2; }
.imps__l { font-size: 12.5px; color: var(--text-2); }

.impm { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.impm__t { font-size: 15px; font-weight: 600; padding: 0; margin-bottom: 2px; }
.impm__i { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-m); cursor: pointer; }
.impm__i input { margin-top: 3px; width: 18px; height: 18px; accent-color: var(--brand-600); flex-shrink: 0; }
.impm__b { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.impm__n { font-size: 14px; overflow-wrap: anywhere; }
.impm__to { color: var(--text-3); }
.impm__m { font-size: 12.5px; color: var(--text-3); }

.impt__w { overflow-x: auto; margin-top: 14px; -webkit-overflow-scrolling: touch; }
.impt { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.impt__c { caption-side: top; text-align: left; font-size: 13px; color: var(--text-3); padding-bottom: 6px; }
.impt th { text-align: left; font-weight: 500; color: var(--text-3); font-size: 12.5px; padding: 8px 10px 8px 0; border-bottom: 1px solid var(--line); white-space: nowrap; }
.impt td { padding: 9px 10px 9px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.impt td.num { white-space: nowrap; font-variant-numeric: tabular-nums; }
.impt__was { display: block; font-size: 12px; color: var(--text-3); }
.impt__raw { color: var(--text-3); overflow-wrap: anywhere; }
.impt--rej td:first-child { width: 64px; }
.impp { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.impp__l { font-size: 13px; color: var(--text-3); margin-right: auto; }
.impx { margin-top: 10px; }
.impr .blk { margin-bottom: 6px; }

@media (max-width: 720px) {
  .imps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .impl__i { flex-direction: column; align-items: flex-start; }
  .impt { font-size: 13px; }
}

/* Результаты словами под графиком и карточка ряда из одних слов. */
.hchart__table--texts th:nth-child(2),
.hchart__table--texts td:nth-child(2) { text-align: left; padding-right: 0; }
.hchart__value--text { font-size: 28px; }

.rem__intro { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.rem__text { font-size: 14px; color: var(--text-2); flex: 1; min-width: 200px; }
.rem__c { padding: 2px 18px; }

.rrow { display: flex; gap: 12px; align-items: center; padding: 12px 0; }
.rrow + .rrow { border-top: 1px solid var(--line); }
.rrow__b { flex: 1; min-width: 0; }
.rrow__t {
  font-size: 14.5px;
  font-weight: 600;
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.rrow__n { font-size: 13px; color: var(--text-3); margin-top: 2px; }
.rrow__ch { display: flex; gap: 6px; }

/* ───────── Хронология ───────── */

.tl-top { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
/* Год-чипы ПЕРЕНОСЯТСЯ, а не прокручиваются: в макете это обычный флекс с
   `flex-wrap`, и прокручиваемый контейнер ведёт себя иначе по высоте. */
.tl-years { flex: 0 1 auto; overflow-x: visible; flex-wrap: wrap; }
.tl-filter { margin-left: auto; }

.tl-h { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.tl-future .tl-h { margin: 6px 0; }
.tl-year .tl-h { margin: 18px 0 4px; }
.tl-h__t { font-family: var(--font-display); font-weight: 600; font-size: 22px; }
.tl-h__t--soon { color: var(--brand-700); }
.tl-h__y { font-family: var(--font-display); font-weight: 600; font-size: 36px; line-height: 1; color: var(--brand-700); }
.tl-h__n { font-size: 13.5px; color: var(--text-3); }
.tl-future { margin-bottom: 8px; }

/* Месяц: короткий отрезок линии в колонке рейла и заглавная подпись. */
.tl-m {
  display: grid;
  grid-template-columns: 74px 24px minmax(0, 1fr);
  gap: 0 12px;
  align-items: center;
  margin: 10px 0 2px;
}
.tl-m__s { grid-column: 2; display: flex; justify-content: center; }
.tl-m__tick { width: 2px; height: 14px; background: var(--line); }
.tl-m__l {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* Рейл: дата, линия с точкой, содержимое. */
.rail {
  display: grid;
  grid-template-columns: 74px 24px minmax(0, 1fr);
  gap: 0 12px;
  align-items: start;
}
.rail__d {
  text-align: right;
  font-size: 13.5px;
  color: var(--text-3);
  padding-top: 14px;
  white-space: nowrap;
}
.rail__l { position: relative; align-self: stretch; display: flex; justify-content: center; }
.rail__l::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--line);
}
/* У последней записи линия обрывается на половине: продолжать её некуда. */
.rail__l--last::before { bottom: 50%; }
.rail__dot {
  position: relative;
  margin-top: 15px;
  width: 12px;
  height: 12px;
  border-radius: 99px;
  box-shadow: 0 0 0 3px #fff;
}
.rail__dot--doc { background: var(--brand-600); border: 2.5px solid var(--brand-600); }
.rail__dot--milestone { background: #fff; border: 2.5px solid var(--brand-300); }
.rail__dot--future { background: #fff; border: 2.5px solid var(--line-strong); }
.rail__dot--import { background: var(--brand-300); border: 2.5px solid var(--brand-300); }
.rail__b { padding-bottom: 10px; min-width: 0; }
.tl-doc { padding: 2px 16px; }

/* Веха и предстоящее событие. */
.msrow {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 11px 14px;
  background: var(--brand-50);
  border: 1px solid var(--brand-100);
  border-radius: var(--r-m);
}
.msrow__i { color: var(--brand-700); }
.msrow__b { flex: 1; min-width: 0; }
.msrow__t { font-size: 14.5px; font-weight: 600; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.msrow__who { font-size: 12px; font-weight: 600; color: var(--brand-700); background: #fff; border-radius: 999px; padding: 1px 8px; }
.msrow__n { font-size: 13px; color: var(--text-2); margin-top: 2px; }
/* Выгрузка за месяц: та же строка, но светлая и ссылкой в таблицу года. */
.msrow--imp { background: #fff; border-color: var(--line); color: inherit; text-decoration: none; }
.msrow--imp:hover { border-color: var(--brand-300); }
.msrow--imp:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }

.frow {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 11px 14px;
  background: #fff;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--r-m);
}
.frow__i { color: var(--brand-600); }
.frow__b { flex: 1; min-width: 0; }
.frow__t { font-size: 14.5px; font-weight: 600; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.frow__n { font-size: 13px; color: var(--text-2); margin-top: 2px; }

/* ───────── Показатели ───────── */

/* `.ichart` держит только запертое состояние (подписки нет — плашка вместо
   графика, `ichart--locked` ниже в файле): сам график ушёл в `.hchart`,
   карточку эталона владельца (design/chart-handoff/medical-chart-package/). */
.ichart { padding: 22px; margin-bottom: 16px; }

/* ───────── Карточка графика (эталон владельца, design/chart-handoff) ─────────
   Токены — свои, где у кабинета уже есть значение той же роли: --hc-teal
   эталона === var(--brand-600) день в день, --hc-soft === var(--bg-soft),
   --hc-border ≈ var(--line), --hc-muted ≈ var(--text-3), --hc-ink → var(--text)
   (эталон чуть синее), --hc-green → var(--ok) (эталон приглушённее),
   --hc-band ≈ var(--ok-soft). Шрифт — var(--font-body) (Golos Text), эталон
   был на Inter: расхождение шрифта и части оттенков названо в отчёте и в
   DECISIONS, не подобрано незаметно. Числа, которых в токенах кабинета нет
   вовсе (36 px значения, 21 px заголовка, 16 px тултипа и т.п.), оставлены
   буквально эталонными — это решения ЭТОЙ карточки, не общего масштаба. */
.hchart {
  padding: 0;
  margin-bottom: 16px;
  overflow: hidden;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
}
.hchart__head { padding: 24px 26px 0; display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; }
.hchart__eyebrow { color: var(--text-3); font-size: 12px; margin-bottom: 5px; }
.hchart__title { font-family: var(--font-body); font-size: 21px; font-weight: 600; letter-spacing: -.4px; margin: 0; line-height: 1.3; }
.hchart__unit-inline { color: var(--text-3); font-size: 16px; font-weight: 400; }
.hchart__periods { flex-shrink: 0; }

.hchart__summary { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 20px; padding: 20px 26px; }
.hchart__latest { display: flex; align-items: baseline; gap: 7px; font-variant-numeric: tabular-nums; }
.hchart__value { font-size: 36px; font-weight: 600; letter-spacing: -1.2px; line-height: 1.1; }
.hchart__date { color: var(--text-3); font-size: 12px; margin-top: 5px; }
.hchart__status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: var(--r-pill);
  font-size: 12px;
  padding: 5px 10px;
  font-weight: 500;
}
.hchart__status--ok { color: var(--ok); background: var(--ok-soft); }
.hchart__status--warn { color: var(--warn); background: var(--warn-soft); }
.hchart__status--bad { color: var(--bad); background: var(--bad-soft); }
.hchart__reference { margin-left: auto; text-align: right; font-size: 13px; line-height: 1.65; }
.hchart__reference small { display: block; color: var(--text-3); font-size: 12px; }
.hchart__reference-none { font-weight: 400; font-size: 12px; color: var(--text-3); }

.hchart__plot { position: relative; margin: 0 14px; min-width: 0; }
/* Поле графика — это и отступы эталона (L=43, R=21 у `.hc-svg`), и контейнер
   для запросов ширины. Отступы здесь, а не в координатах SVG, потому что
   координата внутри холста — ПРОЦЕНТ его собственной ширины, и «43 пикселя
   плюс доля остатка» одним атрибутом не пишется (ui/cabinet/health-chart.ts). */
.hchart__field { margin: 0 21px 0 43px; min-width: 0; container-type: inline-size; container-name: hplot; }
/* Ни `viewBox`, ни масштаба: юнит холста равен пикселю CSS, высота — те же
   298 px, что у эталона, ширину даёт `width: 100%`, а тянутся только
   координаты X — они заданы процентами. Поэтому кегль подписи остаётся
   двенадцатью пикселями на любой ширине карточки. */
.hchart__svg { display: block; width: 100%; height: 298px; overflow: visible; font-variant-numeric: tabular-nums; }
.hchart__grid { stroke: var(--line); stroke-width: 1; }
.hchart__grid--edge { stroke: var(--ok); stroke-opacity: .45; }
.hchart__tick { stroke: var(--line); stroke-width: 1; }
.hchart__tick--major { stroke: var(--line-strong); }
.hchart__band { fill: var(--ok-soft); }
.hchart__seg { stroke: var(--brand-600); stroke-width: 2; }
/* Разрыв больше года — тонкая полупрозрачная линия, а не пунктир бренда: тот
   же довод, что и раньше (график не утверждает формы, которой не видел), но
   вид — из этого эталона буквально (докладная владельца). */
.hchart__seg--gap { stroke: var(--line-strong); stroke-width: 1.25; stroke-linecap: round; opacity: .72; }
.hchart__gapmark { overflow: visible; }
.hchart__gapmark circle { fill: #fff; opacity: .92; }
.hchart__drop { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 2 5; }
.hchart__halo { fill: var(--brand-100); }
.hchart__dot { fill: #fff; stroke: var(--brand-600); stroke-width: 2; }
.hchart__dot--last { fill: var(--brand-600); }

/* ─── Текст графика ───────────────────────────────────────────────────────
   Живой отказ 17.09.2026: текст пробовали вынести HTML-слоем поверх SVG и
   расставить `style="left:%;top:%"` — у сервиса `style-src 'self'` без
   `unsafe-inline`, браузер инлайновый стиль НЕ ПРИМЕНЯЕТ, и все 27 подписей
   карточки схлопнулись в одну точку. Координата тут выражается только
   атрибутом SVG, а размер и цвет — классом. */
.hchart__gridlabel { fill: var(--text-3); font-size: 12px; font-weight: 400; }
.hchart__gridlabel--edge { fill: var(--ok); font-weight: 600; }
.hchart__unitcorner { fill: var(--text-3); font-size: 12px; }
.hchart__xlabel { fill: var(--text-3); font-size: 12px; }
.hchart__caption { fill: var(--text-3); font-size: 11px; }
.hchart__val { fill: var(--text); font-size: 14px; font-weight: 600; }
.hchart__gapmark-q { fill: var(--text-3); font-size: 12px; font-weight: 500; }

/* Прореживание подписей: сколько их влезет, знает браузер, а не сервер.
   Каждая «лишняя» подпись несёт ступень ширины ПОЛЯ, с которой её показывать
   (`--w560` — от 560 px). Ступени те же, что в `FIELD_STEPS`
   (ui/cabinet/health-chart.ts); наборы вложены, поэтому подписи только
   добавляются с ростом ширины. */
.hchart__xlabel--w320, .hchart__val--w320,
.hchart__xlabel--w420, .hchart__val--w420,
.hchart__xlabel--w560, .hchart__val--w560,
.hchart__xlabel--w740, .hchart__val--w740,
.hchart__xlabel--w980, .hchart__val--w980,
.hchart__xlabel--w1300, .hchart__val--w1300 { display: none; }
@container hplot (min-width: 320px) { .hchart__xlabel--w320, .hchart__val--w320 { display: inline; } }
@container hplot (min-width: 420px) { .hchart__xlabel--w420, .hchart__val--w420 { display: inline; } }
@container hplot (min-width: 560px) { .hchart__xlabel--w560, .hchart__val--w560 { display: inline; } }
@container hplot (min-width: 740px) { .hchart__xlabel--w740, .hchart__val--w740 { display: inline; } }
@container hplot (min-width: 980px) { .hchart__xlabel--w980, .hchart__val--w980 { display: inline; } }
@container hplot (min-width: 1300px) { .hchart__xlabel--w1300, .hchart__val--w1300 { display: inline; } }

/* ─── Точка: вложенный холст с началом координат в самой точке ───
   Кружок, ореол, проекция, подпись и подсказка написаны пикселями от центра;
   тянется только атрибут `x` вложенного `<svg>` — он в процентах. */
.hchart__pt { overflow: visible; }
.hchart__hit { fill: transparent; pointer-events: all; }
/* Подсказка — тот же приём, что был: спрятана непрозрачностью, открыта
   наведением на вложенный холст точки. Внутри SVG, потому что снаружи её
   некуда поставить без инлайнового стиля (см. выше). */
.hchart__tip { opacity: 0; pointer-events: none; transition: opacity .12s; }
.hchart__pt:hover .hchart__tip { opacity: 1; }
.hchart__tip-box { fill: var(--bg-dark-2); }
.hchart__tip-date,
.hchart__tip-ref { fill: var(--text-inverse-2); font-size: 12px; }
.hchart__tip-value { fill: #fff; font-size: 17px; font-weight: 600; }
.hchart__tip-mark { fill: #fff; font-size: 12px; font-weight: 600; }

/* ─── Веха дневника: пунктир под данными, отметка с подсказкой над ними ───
   Пунктир и отметка — РАЗНЫЕ узлы холста (см. health-chart.ts): полоса
   обязана лежать под линией показателя, отметка с подсказкой — над ней.
   Подсказка вехи спрятана `display:none`, а не непрозрачностью: у точки
   подсказка переезжает плавно и висит над ней всегда, а отметка вехи стоит
   ВЫШЕ поля, и невидимый прямоугольник в этом месте перехватывал бы
   наведение у верхних точек графика. */
.hchart__ms { overflow: visible; }
.hchart__msline { stroke: var(--brand-300); stroke-width: 1.3; stroke-dasharray: 3 4; }
.hchart__msflag { fill: var(--brand-100); stroke: var(--brand-600); stroke-width: 1.5; }
.hchart__mshit { fill: transparent; pointer-events: all; }
.hchart__mstip { display: none; pointer-events: none; }
.hchart__ms:hover .hchart__mstip { display: block; }

.hchart__legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; padding: 6px 26px 20px; font-size: 12px; color: var(--text-3); }
.hchart__legend span { display: flex; align-items: center; gap: 7px; }
.hchart__swatch { width: 14px; height: 10px; background: var(--ok-soft); border-radius: 2px; display: inline-block; }
.hchart__dot-sample { width: 7px; height: 7px; background: var(--brand-600); border-radius: 50%; display: inline-block; }
/* Образец вехи в легенде — тот же пунктир, что на поле, только вертикальный
   отрезок в строку текста. Цвет и шаг штриха совпадают с `.hchart__msline`. */
.hchart__ms-sample { width: 0; height: 12px; border-left: 1.3px dashed var(--brand-300); display: inline-block; }

.hchart__details { border-top: 1px solid var(--line); padding: 0 26px; }
.hchart__details summary { list-style: none; display: flex; align-items: center; justify-content: flex-start; gap: 8px; color: var(--brand-600); font-size: 13px; padding: 17px 0; font-weight: 500; cursor: pointer; }
/* «Все измерения · 3» держатся вместе слева, шеврон уходит вправо. Без
   этих двух строк `justify-content: space-between` дизайн-системы
   растаскивает три части строки по всей ширине, и счётчик уезжает на
   середину — у эталона он стоит сразу за названием. */
.hchart__details summary::after { margin-left: auto; }
.hchart__details summary::-webkit-details-marker { display: none; }
.hchart__count { color: var(--text-3); font-weight: 400; }
/* Шеврона у строки СВОЕГО нет: его рисует дизайн-система на любом `summary`
   (`site.css`, `summary::after` — значок, поворачивающийся на 180° при
   раскрытии). Свой знак «⌄» в разметке давал ВТОРОЙ шеврон рядом с первым —
   видно сверкой с эталоном пакета 17.09.2026. Разводит их по краям строки
   `justify-content: space-between` оттуда же. */
.hchart__table { width: 100%; border-collapse: collapse; font-size: 12px; margin-bottom: 18px; }
.hchart__table th { color: var(--text-3); font-weight: 400; text-align: left; padding: 8px 0; }
.hchart__table td { padding: 10px 0; border-top: 1px solid var(--line); }
.hchart__table th:nth-child(2),
.hchart__table td:nth-child(2) { text-align: right; font-variant-numeric: tabular-nums; padding-right: 18px; }
.hchart__mark--ok { color: var(--ok); }
.hchart__mark--warn { color: var(--warn); }
.hchart__mark--bad { color: var(--bad); }

.hchart__footnote { font-size: 12px; color: var(--text-3); margin: 12px 2px 16px; line-height: 1.5; }

@media (pointer: coarse) {
  .hchart__details summary { min-height: 48px; }
}
@media (prefers-reduced-motion: reduce) {
  .hchart * { transition: none !important; }
}

.ichips { margin-bottom: 12px; padding-bottom: 2px; }

/* Список показателей: каждый — целой карточкой во всю ширину, той же, что
   раскрыта сверху (§155). До 18.09.2026 здесь была сетка `.igrid` из плиток
   `.icard` со своим мини-графиком — доэталонная вёрстка, не знавшая ни
   подписей границ нормы, ни знаков вопроса на пустых годах; двум
   реализациям одного графика пришлось бы чинить каждую поломку дважды.
   Своей ширины у списка нет: карточка и так во всю колонку на любом экране,
   и на 375 менять было нечего. */
.ilist { display: flex; flex-direction: column; }
.ilist__more { margin: 4px 0 20px; }

/* ───────── Панель документа поверх экрана ─────────

   Эталон `modals/modal-doc`: справа белая колонка 460 px во всю высоту, слева
   затемнение. Открывается параметром адреса (`?dok=`), закрывается ссылкой —
   скриптов здесь нет ни строчки (DECISIONS §82).

   Подложка объявлена в разметке ПЕРВОЙ и потому лежит под панелью: порядок
   узлов и есть порядок слоёв, отдельный `z-index` внутри не нужен. Наружу
   `z-index` всё-таки назван — панель обязана накрыть и таб-бар (55), и
   плавающую кнопку (50), и мобильную шапку (60). */

.dpanel {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  justify-content: flex-end;
}
.dpanel__back {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, .48);
}
.dpanel__c {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 460px;
  max-width: 100%;
  height: 100%;
  overflow-y: auto;
  background: #fff;
  box-shadow: -8px 0 32px rgba(15, 23, 42, .18);
}
.dpanel__h {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 24px 24px 10px;
}
.dpanel__t { flex: 1; font-size: 20px; font-weight: 700; line-height: 1.3; }
.dpanel__x {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  color: var(--text-2);
  background: var(--bg-soft);
}
.dpanel__x:hover { background: var(--line); color: var(--text); }
/* Внутри панели карточка рамки не носит: панель и есть поверхность. */
.dpanel .dcard { padding: 0 24px 24px; }
.dpanel .dcard__c {
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}

/* Панель настроек профиля (ТЗ, часть 2 и 4) — то же содержимое, что у страницы
   «Профиль» (`profileTabBody`/`notifyTabBody`/`securityTabBody`), только без
   страничных отступов, которые там даёт оболочка кабинета: панель их не
   наследует, и без своих человек увидел бы форму, прижатую к краю. */
.dpanel__body { padding: 0 24px 24px; }
.dpanel__body .prof__g { grid-template-columns: 1fr; gap: 16px; }

/* Панель «Настроить» шире панели документа (доделка 16.09.2026, ТЗ часть 2:
   «половина не влезает»): вплоть до половины экрана, а не фиксированных
   460 px. Два столбца формы («Данные»/доступ, журнал/боковая карточка)
   помещаются рядом уже от 1024 px — той же ширины, с которой они стоят рядом
   на самой странице (`.prof__g`, 380 px боковая колонка). Ниже 1024 либо на
   любой ширине без класса `--wide` (панель документа, панель открепления)
   столбец остаётся один — тем же правилом, что у самой узкой страницы. */
.dpanel__c--wide { width: max(460px, min(50vw, 760px)); }
@media (min-width: 1024px) {
  .dpanel__c--wide .dpanel__body .prof__g { grid-template-columns: minmax(0, 1fr) 320px; }
}

/* Панель «Настроить» РЯДОМ с кабинетом, а не поверх него (доделка 16.09.2026,
   требование владельца): затемнение и `position: fixed` закрывали список
   «Семьи» целиком, хотя место для обеих колонок на экране есть. Ниже 1024 px
   правило не срабатывает — там панель по-прежнему во весь экран (ТЗ, часть 2),
   узкому экрану делить нечего.

   `.dpanel` при этом — обычный ребёнок флекс-ряда `.cab` (родитель уже
   `display: flex` на этой ширине, см. выше): `.cab-main` держит `flex: 1;
   min-width: 0` и подожмётся сам, отдельного правила ему не нужно.

   ═══ ШИРИНА ПАНЕЛИ РЯДОМ — СВОЯ, УЖЕ ОВЕРЛЕЙНОЙ ═══

   `max(460px, min(50vw, 760px))` у `.dpanel__c--wide` рассчитан на панель НАД
   пустым затемнением — там она вправе забрать хоть половину экрана. Рядом с
   настоящим контентом та же формула на 1440 (частый ноутбучный экран) оставляла
   `.cab-main` меньше 460 px — шапка «Семьи» переносилась по одному слову в
   строке, а не по словам естественно. `min(40vw, 620px)` c тем же полом 460 px
   отдаёт панели меньше, а `.cab-main` — заметно больше на тех же 1440 (596 px
   вместо 452). Внутренние две колонки самой панели (`.prof__g` ниже) при такой
   ширине панели уже не поместятся — им и не положено: правило `1fr 320px`
   держится за ШИРИНУ ЭКРАНА (`min-width: 1024px`), а не за ширину панели
   (`@container` здесь не заведён), и на узкой рядом-панели его сняли явно. */
@media (min-width: 1024px) {
  .dpanel--wide {
    position: static;
    inset: auto;
    z-index: auto;
    flex-shrink: 0;
  }
  .dpanel--wide .dpanel__back { display: none; }
  .dpanel--wide .dpanel__c {
    position: sticky;
    top: 0;
    height: 100vh;
    box-shadow: none;
    border-left: 1px solid var(--line);
  }
  .dpanel--wide.dpanel .dpanel__c--wide { width: max(460px, min(40vw, 620px)); }
  .dpanel--wide .dpanel__c--wide .dpanel__body .prof__g { grid-template-columns: 1fr; }
}

/* ───────── Карточка документа ───────── */

.dcard__c { padding: 24px; display: flex; flex-direction: column; gap: 18px; }
.dcard__head { display: flex; gap: 12px; align-items: flex-start; }
.dcard__hb { flex: 1; min-width: 0; }
.dcard__kind { font-size: 14.5px; color: var(--text-2); }
.dcard__note { font-size: 13.5px; color: var(--text-3); margin-top: 2px; }
.dcard__why { font-size: 14px; color: var(--text-2); line-height: 1.5; }
.dcard__why b { color: var(--text); }
.dcard__wait { font-size: 14px; color: var(--text-2); line-height: 1.5; }
.dcard__acts { display: flex; flex-direction: column; gap: 10px; }

/* Распоряжение документом: архив, возврат, удаление насовсем (решение 100).
   Отделено чертой и уведено вниз намеренно: «Открыть отчёт» и «Удалить
   насовсем» в одном ряду — это соседство, при котором промахиваются. */
.dcard__manage { border-top: 1px solid var(--line); padding-top: 14px; }
.dcard__mrow { display: flex; flex-wrap: wrap; gap: 8px; }
.dcard__mnote { margin-top: 8px; line-height: 1.5; }

/* Шкала уровня внимания: одиннадцать сегментов, окрашенных до значения. */
.ascale__caps {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
}
.ascale__head { display: flex; align-items: baseline; gap: 10px; margin-top: 4px; }
.ascale__v { font-size: 46px; font-weight: 700; line-height: 1; }
.ascale__v--ok { color: var(--ok); }
.ascale__v--warn { color: var(--warn); }
.ascale__v--bad { color: var(--bad); }
.ascale__of { font-size: 15px; color: var(--text-2); }
.ascale__zone--ok { color: var(--ok); }
.ascale__zone--warn { color: var(--warn); }
.ascale__zone--bad { color: var(--bad); }
.ascale__bar { display: flex; gap: 3px; margin-top: 12px; }
.ascale__s { flex: 1; height: 8px; border-radius: 3px; background: var(--line); }
.ascale__s--ok { background: var(--ok); }
.ascale__s--warn { background: var(--warn); }
.ascale__s--bad { background: var(--bad); }
.ascale__ends {
  display: flex;
  justify-content: space-between;
  font-size: 11.5px;
  color: var(--text-3);
  margin-top: 5px;
}

/* ───────── Запись к врачу и на исследование ───────── */

/* Переключатель двух разделов — ТОЛЬКО на узком экране: на десктопе у каждого
   свой пункт сайдбара, а в таб-баре колонка одна на двоих (05-responsive.md). */
.zseg { display: none; }

/* Строка совета из отчёта: пилюля внимания, текст, ссылка. Переносится — на
   375 текст занимает три строки, и ссылка уходит под него, как в макете. */
.zadvice {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 14px;
  margin-bottom: 14px;
}
.zadvice__t { flex: 1; min-width: 200px; font-size: 14px; color: var(--text-2); line-height: 1.45; }
.zadvice__t b { color: var(--text); }

/* Фильтры: город раскрывающимся списком, специальность ссылками. */
.zfilters { padding: 20px; margin-bottom: 16px; }
/* Город и метро — РАВНЫЕ ПОЛОВИНЫ ряда, как в макете (`doctors-1440.png`:
   «Город» слева, «Метро» справа с плашкой «Любая станция»).

   Прежде здесь стояло, что метро у нас нет: разведка не зафиксировала имена
   полей. Метро есть — `/metro/city/{id}` отдаёт справочник станций, — но
   только в семи городах покрытия из 41. */
.zcity { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: end; }

/* Город БЕЗ метро занимает ряд целиком. Оставить его в половине значило бы
   держать пустоту справа в 34 городах из 41 — ради поля, которого там не будет
   никогда, а не «пока». */
.zcity > .fld:only-child { grid-column: 1 / -1; }

/* ═══ ВЫБОР ГОРОДА ═══
 *
 * Кнопки «Показать» рядом с городом нет ни здесь, ни в макете: `<select>` без
 * скрипта сам ничего не отправляет, и подтверждать выбор было нечем, кроме
 * кнопки. Здесь закрытый `<details>` выглядит тем же полем, а каждый город
 * внутри — ссылка: нажал, и врачи показались.
 */
.zpick { position: relative; }
.zpick__s {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  height: 46px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  background: #fff;
  font-size: 16px;
  color: var(--text);
  cursor: pointer;
  list-style: none;
}
.zpick__s::-webkit-details-marker { display: none; }
.zpick__s::after {
  content: "";
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background: center/18px no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='m6 9 6 6 6-6' stroke='%230E7490' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  transition: transform .18s ease;
}
.zpick[open] .zpick__s::after { transform: rotate(180deg); }
.zpick__s:focus-visible { outline: none; border-color: var(--brand-600); box-shadow: 0 0 0 3px var(--focus-ring); }
/* Список раскрывается ПОВЕРХ, а не раздвигает фильтры: иначе ряд специальностей
   и вся выдача уезжали бы вниз на пол-экрана при каждом открытии. */
.zpick__m {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  margin-top: 4px;
  max-height: min(46vh, 320px);
  overflow-y: auto;
  padding: 6px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 2px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  box-shadow: var(--shadow-m, 0 12px 28px rgba(16, 52, 70, .14));
}
.zpick__i {
  display: block;
  padding: 9px 10px;
  border-radius: var(--r-s);
  color: var(--text);
  text-decoration: none;
  font-size: 14.5px;
  min-height: 36px;
}
.zpick__i:hover { background: var(--bg-soft); }
.zpick__i:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }
.zpick__i.is-active { background: var(--brand-50); color: var(--brand-700); font-weight: 600; }
.fld { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 0; }
.fld__l { font-size: 14px; font-weight: 600; color: var(--text); }
.fld__i {
  height: 46px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  background: #fff;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--text);
}
.fld__i:focus { outline: none; border-color: var(--brand-600); box-shadow: 0 0 0 3px var(--focus-ring); }
/* Keep the native filename and keyboard/screen-reader control. Only its
   system button is hidden; the associated label supplies our button.
   :valid switches the empty caption without JavaScript (CSP script-src 'none'). */
.cab-file {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  align-items: center;
  gap: 8px 12px;
  min-width: 0;
}
.cab-file__pick { grid-area: 1 / 1; cursor: pointer; }
.cab-file__input {
  grid-area: 1 / 2;
  width: 100%;
  min-width: 0;
  padding: 10px 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  font: inherit;
  font-size: 14px;
  color: transparent;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cab-file__input::file-selector-button { display: none; }
.cab-file__input:focus-visible { outline: none; }
.cab-file:has(.cab-file__input:focus-visible) .cab-file__pick {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}
.cab-file__empty {
  grid-area: 1 / 2;
  min-width: 0;
  color: var(--text-3);
  font-size: 14px;
  pointer-events: none;
}
.cab-file__input:valid { color: var(--text); }
.cab-file__input:valid + .cab-file__empty { display: none; }
.cab-file:has(.cab-file__input:user-invalid) .cab-file__pick,
.cab-file:has(.cab-file__input[aria-invalid="true"]:invalid) .cab-file__pick {
  border-color: var(--bad);
  color: var(--bad);
}
@media (max-width: 480px) {
  .cab-file { grid-template-columns: minmax(0, 1fr); }
  .cab-file__input, .cab-file__empty { grid-area: 2 / 1; }
  .cab-file__input { padding: 0; min-height: 24px; }
}
@media (forced-colors: active) {
  .cab-file__input:invalid { forced-color-adjust: none; }
  .cab-file:has(.cab-file__input:focus-visible) .cab-file__pick { outline-color: Highlight; }
}
/* Красная подсветка неверной почты (ТЗ, часть 4) — без единой строки скрипта:
   `type="email"` уже даёт браузеру знать, что строка неверна, `:invalid`
   ловит это состоянием CSS. `:not(:placeholder-shown)` — не раньше, чем
   человек хоть что-то напишет: пустое обязательное поле тоже `:invalid`, и
   красная рамка на нетронутой форме выглядела бы обвинением. `:not(:focus)`
   — не во время печати: подсвечивать ошибку в процессе набора рано, ровно
   так же ведёт себя нативная проверка браузера при попытке отправить форму. */
.fld__i:invalid:not(:placeholder-shown) { border-color: var(--bad); }
.fld__i:invalid:not(:placeholder-shown):focus {
  border-color: var(--bad);
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .16);
}

/* Дата рождения тремя полями (ТЗ, часть 4) — точки в разметке, не во вводе. */
.bdate { display: flex; align-items: center; gap: 6px; }
.bdate__i { width: 52px; padding: 0 4px; text-align: center; flex: none; }
.bdate__i--y { width: 74px; }
.bdate__sep { color: var(--text-3); font-weight: 600; font-size: 16px; }
/* Ряд специальностей переносится, а не прокручивается: их девять, на 1440 они
   в строку не встают, и последний чип уходил под край без всякого признака,
   что там что-то есть. На узком экране остаётся прокрутка — как в макете. */
.zspec { margin-top: 14px; }
.zspec .fchips { flex-wrap: wrap; overflow-x: visible; }
.zspec__l { display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; }

/* ═══ ПЛИТКИ ВИДОВ ДИАГНОСТИКИ ═══
 *
 * Как в макете: значок, название, сетка по четыре на широком экране. Цены
 * «от 350 ₽» на плитках нет — партнёр такой цены не отдаёт, и точная стоит в
 * карточке центра.
 */
.zkinds { margin-bottom: 14px; }
.ztiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px;
  margin-top: 8px;
}
.ztile {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: #fff;
  color: var(--text);
  text-decoration: none;
  min-height: 68px;
}
.ztile:hover { border-color: var(--brand-600); }
.ztile.is-active { border-color: var(--brand-600); box-shadow: 0 0 0 1px var(--brand-600) inset; }
.ztile__t { font-size: 14.5px; font-weight: 600; line-height: 1.3; }
/* У исследований в ряду фильтров два поля — «что именно» и город. */
.zcity--studies { margin-top: 14px; }
/* Рейтинг центра — у самого края карточки, как в макете. */
.zrate { display: inline-flex; align-items: center; gap: 4px; font-size: 13.5px; font-weight: 600; color: var(--text-2); }
.zrate__i { color: var(--warn, #E8A33D); }

/* Выдача. Две колонки — так в макете на 1024 и 1440. */
.zfound { font-size: 14px; color: var(--text-3); margin-bottom: 10px; }
.zgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
.zgrid__note { font-size: 13px; color: var(--text-3); margin-top: 10px; }

.zdoc { padding: 18px; }
.zdoc__h { display: flex; gap: 12px; align-items: center; }
.zdoc__n { flex: 1; min-width: 0; }
.zdoc__nm { display: block; font-size: 15.5px; font-weight: 600; }
.zdoc__sp { font-size: 13.5px; color: var(--text-2); }
.zdoc__rows { display: flex; flex-direction: column; gap: 4px; margin-top: 12px; }
.zrow { display: flex; gap: 7px; align-items: flex-start; font-size: 13.5px; color: var(--text-2); }
.zrow b { color: var(--text); }
.zrow__i { color: var(--brand-600); margin-top: 2px; }
.zdoc__cap {
  margin-top: 12px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* Окна времени. Ссылка, а не кнопка: своей формы записи у сервиса нет — заявку
   оформляет партнёр, у которого разрешено спрашивать телефон. */
.zslots { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 8px; }
.zslot {
  border: 1px solid var(--brand-600);
  background: #fff;
  color: var(--brand-700);
  border-radius: 999px;
  padding: 7px 13px;
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  line-height: normal;
  white-space: nowrap;
  transition: background .15s ease;
}
.zslot:hover { background: var(--brand-50); }
.zslot__all {
  color: var(--brand-600);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  padding: 7px 6px;
  line-height: normal;
}
.zslot__none { font-size: 13.5px; color: var(--text-3); }

/* Состояние вместо выдачи: партнёра нет, город не выбран, город вне покрытия. */
.zstate { display: flex; gap: 14px; align-items: flex-start; padding: 24px; margin-bottom: 16px; }
.zstate__b { flex: 1; min-width: 0; }
.zstate__t { display: block; font-size: 17px; font-weight: 600; }
.zstate__x { font-size: 14.5px; color: var(--text-2); line-height: 1.5; margin-top: 6px; }
.zstate__a { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }

/* Знак партнёра. Растр с белым фоном — `multiply` убирает подложку и на белой
   карточке, и на подложке страницы (DECISIONS §26: вектора у владельца нет). */
.zpartner {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--text-3);
}
.zpartner img { mix-blend-mode: multiply; height: 26px; width: auto; }

/* Оценка врача — под именем и специальностью, шкала пятибалльная.
   У центра шкала десятибалльная и своя строка: см. `.zrate` в карточке центра. */
.zdoc__r { display: inline-flex; align-items: center; gap: 3px; margin-top: 3px; font-size: 12.5px; }

/* Список «что досдать»: показатель и зачем. */
.zret__c { padding: 20px; margin-bottom: 16px; }
.zrets { list-style: none; margin: 12px 0 0; padding: 0; }
.zret { display: flex; flex-direction: column; padding: 12px 0; }
.zret + .zret { border-top: 1px solid var(--line); }
.zret__n { font-size: 14.5px; font-weight: 600; }
.zret__w { font-size: 13.5px; color: var(--text-2); line-height: 1.45; margin-top: 2px; }

/* Подготовка к приёму и к исследованию: текст партнёра.

   Разметка внутри `.zprep__t` ЧУЖАЯ — она пересобрана по белому перечню тегов
   (product/report/guidelines.ts), но состав абзацев и списков в ней партнёрский.
   Поэтому стили заданы потомкам явно: наследовать типографику неоткуда, а без
   правил браузерные умолчания (margin 1em, отступ списка 40px) выбиваются из
   карточки. */
.zprep { padding: 20px; margin-bottom: 16px; }
.zprep__i {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
  border-radius: 10px;
  margin: 12px 0 4px;
}
.zprep__t { font-size: 14.5px; line-height: 1.55; color: var(--text-1); }
.zprep__t p { margin: 10px 0 0; }
.zprep__t p:first-child { margin-top: 12px; }
.zprep__t ul,
.zprep__t ol { margin: 10px 0 0; padding-left: 20px; }
.zprep__t li { margin-top: 4px; }
.zprep__t b,
.zprep__t strong { font-weight: 600; }
.zprep__f { font-size: 12.5px; color: var(--text-3); margin-top: 14px; }

/* Дисклеймер раздела — по центру и внизу, как в макете. */
.zscreen > .disc { text-align: center; margin-top: 28px; }

/* ───────── Семья ───────── */

.fam__g { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
.fam__c { padding: 20px; display: flex; flex-direction: column; }

.fam__h { display: flex; gap: 12px; align-items: center; }
.fam__n { flex: 1; min-width: 0; }
.fam__nm { display: block; font-size: 16px; font-weight: 600; }
.fam__meta { font-size: 13.5px; color: var(--text-2); }

/* Строка документов и пилюля внимания — в один ряд, как в макете. */
.fam__docs {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
  font-size: 13.5px;
  color: var(--text-2);
}
.fam__acc { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.fam__mail { font-size: 13px; color: var(--text-3); overflow-wrap: anywhere; }
/* Своя карточка: на месте адреса стоит пояснение, зачем этот профиль нужен.
   Пустое место на строке доступа человек прочитал бы как «чего-то не хватает». */
.fam__own { font-size: 13px; color: var(--text-3); }

/* Действия прижаты к низу: карточки в ряду разной высоты, а кнопки в макете
   стоят на одной линии. */
.fam__a { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: auto; padding-top: 14px; }
.fam__cancel { display: contents; }

/* Плашка «запрос отправлен» — 03-components.md: мягкая подложка, часы, кнопка
   отмены. Не строкой текста: состояние, которое можно отменить, обязано
   показывать чем. */
.fam__wait {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--bg-soft);
  border-radius: 12px;
  padding: 10px 12px;
  margin-top: 12px;
  font-size: 13px;
  color: var(--text-2);
}
.fam__wait-t { flex: 1; min-width: 140px; }
.fam__wait .ci { color: var(--text-3); flex-shrink: 0; }

/* Баннер входящего запроса. Контур брендовый, а не серый: это не сведение, а
   вопрос, на который человек обязан ответить, — и он выделен так же, как
   выделен бы вопрос в макете. */
.fam__req {
  align-items: center;
  gap: 14px;
  padding: 20px 24px;
  border-color: var(--brand-600);
}
.fam__req-a { flex-shrink: 0; }

/* Бланки без профиля. */
.fam__loose { padding: 20px; margin-top: 16px; }
.fam__note { font-size: 13.5px; color: var(--text-2); margin-top: 6px; }
.fam__ls { list-style: none; margin: 10px 0 0; padding: 0; }
.fam__l { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; padding: 12px 0; }
.fam__l + .fam__l { border-top: 1px solid var(--line); }
.fam__l-t { flex: 1; min-width: 200px; font-size: 14px; }
.fam__l-w { display: block; font-size: 13px; color: var(--text-3); }
.fam__l-f { display: flex; gap: 8px; align-items: center; }
.fld__i--sm { height: 38px; font-size: 14px; }

/* След открепления. */
.fam__det { padding: 18px 20px; margin-top: 16px; }
.fam__ds { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13.5px; color: var(--text-2); }
.fam__ds li + li { margin-top: 6px; }

/* Справка: три колонки на широком, одна на узком. */
.fam__help { padding: 20px 24px 24px; margin-top: 16px; }
.fam__hs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 14px; }
.fam__hi-i { color: var(--brand-600); }
.fam__hi-t { display: block; font-size: 14.5px; font-weight: 600; margin-top: 6px; }
.fam__hi-x { font-size: 13.5px; color: var(--text-2); line-height: 1.5; margin-top: 4px; }

/* Заведение профиля: узкая колонка — форма из трёх полей во всю ширину экрана
   читается как анкета, а не как одно действие. */
.fam--new { max-width: 560px; }
.fam__new { padding: 22px; }
.fam__f { display: flex; flex-direction: column; gap: 16px; }
.fam__f-a { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.fld__h { font-size: 13px; color: var(--text-3); }
/* Указание авторства базы геоданных — условие её лицензии. Тише подсказки,
   рядом с которой стоит: это сноска, а не сообщение человеку. */
.zgeo { color: var(--text-3); opacity: .8; }
.zgeo a { color: inherit; }
.fld--set { border: 0; padding: 0; margin: 0; }
.rads { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 6px; }
.rad { display: flex; gap: 7px; align-items: center; font-size: 14.5px; cursor: pointer; }
.rad input { accent-color: var(--brand-600); width: 18px; height: 18px; }
/* Ошибка формы — цветом отказа, а не бренда: подложка `--brand-50` у значка
   читается как подсказка, и человек прокручивает мимо того, что его остановило. */
.fam__err {
  display: flex;
  gap: 12px;
  align-items: center;
  background: var(--bad-soft);
  border-radius: 12px;
  padding: 12px 14px;
  font-size: 14.5px;
  color: var(--text);
  margin-bottom: 18px;
}
.fam__err .ichip { background: #fff; border-color: var(--bad); }
.fam__err .ichip .ci { color: var(--bad); }
.fam--new .disc { text-align: center; margin-top: 18px; }

/* ───────── Форма загрузки внутри кабинета (16.09.2026, п. 2) ─────────
   `${widget}` в `uploadPage` (`ui/pages.ts`) — та же разметка `.upload`/
   `.field`/`.checkbox`, что и у гостя (`site.css`), без изменений в HTML.
   Здесь только масштаб: свою рамку и тень формы гасит `.cab-card` вокруг,
   а поля и галка уменьшены до размера остального кабинета (ср. `.det__chk`,
   `.fld__i`). Гостевую `.upload` в `site.css` эти правила не задевают —
   селектор всегда начинается с `.upl`, а он стоит только в этой разметке. */
.upl { max-width: 620px; }
.upl .upload { background: transparent; border: 0; box-shadow: none; padding: 0; gap: 12px; }
.upl .upload__drop { padding: 16px; }
.upl .upload__empty img { width: 44px; height: 44px; }
.upl .upload__title { font-size: 14.5px; }
.upl .upload__sub { font-size: 13px; }
.upl .upload__micro { font-size: 13px; }
.upl .field { gap: 4px; }
.upl .field__label { font-size: 13.5px; }
.upl .field input[type=email],
.upl .field input[type=text],
.upl .field input[type=password],
.upl .field select { height: 40px; font-size: 14.5px; padding: 0 12px; }
.upl .field__hint { font-size: 12.5px; }
.upl .checkbox { font-size: 13.5px; gap: 8px; padding-block: 4px; }
.upl .checkbox input[type=checkbox] { width: 18px; height: 18px; margin-top: 0; }
.upl .upload .btn--lg { height: 44px; padding-inline: 22px; font-size: 15px; }
.upl__tips { margin-top: 4px; font-size: 13.5px; }
.upl__limit { margin-top: 10px; font-size: 12.5px; color: var(--text-3); }

/* ───────── Профиль ───────── */

/* Две колонки: слева данные, справа доступ и семья — так в макете на 1024
   и 1440. Правая уже: в ней текст и одна кнопка, а не форма. */
/* `min-width: 0` — не для самой сетки (её колонки уже `minmax(0, 1fr)`), а для
   случая, где `.prof__g` сама сидит внутри чужого флекс-ряда/колонки
   (`уведомления`, доделка 16.09.2026: вся вкладка теперь в одной `<form>` с
   `display: flex`). Без него флекс-родитель меряет `.prof__g` по ширине
   НЕПЕРЕНОСИМОГО текста внутри и раздувает её шире контейнера — длинные
   подписи квиза уезжали за правый край на 375 px, вместо переноса. */
.prof__g { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 20px; align-items: start; min-width: 0; }
.prof__side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* Ряд выбора «чей профиль» стоит на месте кнопки шапки. */
.prof__pick { display: flex; gap: 10px; align-items: end; }
.prof__pick .fld { min-width: 220px; }
.prof__pick .cab-btn { height: 46px; }

.prof__data { padding: 24px; }
.prof__h { display: flex; gap: 14px; align-items: center; margin-bottom: 18px; }
.prof__n { min-width: 0; }
.prof__nm { display: block; font-size: 17px; font-weight: 600; }
.prof__meta { font-size: 13.5px; color: var(--text-2); }

.prof__f { display: flex; flex-direction: column; gap: 16px; }
.prof__f-a { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.prof__why { font-size: 13px; color: var(--text-3); line-height: 1.5; }

/* Поле, которое не правится, обязано ВЫГЛЯДЕТЬ неправимым: одинаковое с
   правимым поле обещает правку и получает жалобу «не сохраняется». */
.fld__i[readonly] { background: var(--bg-soft); color: var(--text-2); cursor: default; }
.fld__i[readonly]:focus { border-color: var(--line-strong); box-shadow: none; }

/* ═══ СОСТОЯНИЕ И ПРЕПАРАТЫ (DECISIONS §178) ═══
   Своей строкой под сеткой данных и доступа: колонка «Доступ» узкая (380),
   а формы добавления — с датами и подсказками. */
#sostoyanie { margin-top: 20px; min-width: 0; }
.prof__cond { padding: 24px; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.cond__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.cond__it { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; min-width: 0; }
.cond__it--off { background: var(--bg-soft); }
.cond__t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cond__t b { font-size: 15px; font-weight: 600; }
.cond__t span { font-size: 13.5px; color: var(--text-2); overflow-wrap: anywhere; }
.cond__a { display: flex; gap: 6px; flex-wrap: wrap; }
.cond__g { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; align-items: start; }
.cond__h { font-size: 14px; font-weight: 600; margin-top: 4px; }
/* Глобальные правила `details` дизайн-системы (site.css) дают отступ между
   соседями и поля — в сетке они сдвигали первую карточку и раздували все. */
.cond__add { border: 1px solid var(--line); border-radius: 12px; min-width: 0; padding: 0; margin: 0; background: var(--surface, #fff); }
.cond__add + .cond__add { margin-top: 0; }
.cond__add summary { padding: 10px 14px; font-size: 14.5px; font-weight: 600; color: var(--brand-600);
  min-height: 44px; box-sizing: border-box; margin: 0; }
.cond__add[open] summary { border-bottom: 1px solid var(--line); margin-bottom: 0; }
.cond__f { display: flex; flex-direction: column; gap: 12px; padding: 14px; }
.cond__f .cab-btn { align-self: flex-start; }
.cond__i { max-width: 220px; }
.cond__or { font-size: 13px; color: var(--text-3); }

/* «Изменения сохранены» — вместо тоста макета: тост требует скрипта. */
.prof__ok {
  display: flex;
  gap: 8px;
  align-items: center;
  background: var(--ok-soft);
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 18px;
  font-size: 14px;
}
.prof__ok .ci { color: var(--ok); }

/* Пол сегментом: радиокнопки, нарисованные как переключатель (03-components). */
/* ═══ ТРИ ВАРИАНТА ВМЕСТО ДВУХ — И ОНИ НЕ ВЛЕЗАЛИ ═══

   В эталоне у пола две кнопки; у сервиса их три: «Не указывать» — законный
   ответ, а не пропуск (`patient.ts`). Третья не помещалась в половину колонки
   и наезжала на поле даты рождения — видно кадром сверки при 1024. Перенос
   строкой вместо наезда: сегмент из двух строк читается, наложенный текст —
   нет. */
.seg--radio { display: inline-flex; gap: 2px; flex-wrap: wrap; max-width: 100%; }
.seg__r { position: relative; display: flex; }
.seg__r input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.seg__r span {
  padding: 7px 14px;
  border-radius: 9px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
}
.seg__r input:checked + span { background: #fff; color: var(--brand-700); box-shadow: var(--shadow-s); }
.seg__r input:focus-visible + span { outline: 2px solid var(--brand-600); outline-offset: 1px; }

.prof__acc, .prof__fam { padding: 20px; }
.prof__ax { font-size: 14px; color: var(--text-2); line-height: 1.55; margin-top: 10px; }
.prof__note { font-size: 13px; color: var(--text-3); line-height: 1.5; margin-top: 10px; }
.prof__mail { display: flex; gap: 8px; align-items: end; margin-top: 14px; }
.prof__mail .fld { flex: 1; min-width: 0; }

/* Смена пароля: те же поля колонкой, кнопка по левому краю. */
.prof__pass { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; margin-top: 12px; }
.prof__pass .fld { align-self: stretch; }
.prof__mail .cab-btn { height: 46px; }
.prof__detach { margin-top: 14px; }

/* Входящий запрос: подтвердить или отклонить, и оба ответа рядом. */
.prof__ask { background: var(--bg-soft); border-radius: 12px; padding: 12px 14px; margin-top: 14px; }
.prof__ask-t { font-size: 13.5px; color: var(--text-2); line-height: 1.5; }
.prof__ask-a { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* ───────── Тарифы и услуги ───────── */

/* Текущий тариф — контур брендовый 2px и тень, как в макете: это не строка
   списка, а ответ на вопрос «что у меня сейчас». Карточка есть ВСЕГДА, даже
   когда человек ничего не покупал (требование владельца 04.09.2026). */
.bil__cur {
  padding: 24px;
  border: 2px solid var(--brand-600);
  box-shadow: var(--shadow-l);
  margin-bottom: 24px;
}
.bil__cur-b { flex: 1; min-width: 0; }
.bil__cur-n { display: block; font-size: 22px; font-weight: 700; margin-top: 10px; }
.bil__cur-x { font-size: 14.5px; color: var(--text-2); line-height: 1.5; margin-top: 6px; }
.bil__wait { font-size: 13.5px; color: var(--text-2); margin-top: 10px; }

/* Причина, по которой действие заперто. Тем же кеглем, что подпись, и без
   цвета тревоги: это не ошибка, а положение дел. */
.bil__why { font-size: 13px; color: var(--text-3); line-height: 1.45; margin-top: 10px; }

/* Строка услуги — сетка макета `1fr 150px 170px`: цена и действие стоят
   столбцами и не ездят вслед за длиной названия. */
.bil__svc { padding: 2px 24px; margin-bottom: 12px; }
.bil__ss { list-style: none; margin: 0; padding: 0; }
.bil__s {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 150px 170px;
  gap: 16px;
  align-items: center;
  padding: 14px 0;
}
.bil__s + .bil__s { border-top: 1px solid var(--line); }
.bil__s-b { min-width: 0; }
.bil__s-n { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 15px; font-weight: 600; }
.bil__s-i { display: block; font-size: 13.5px; color: var(--text-2); margin-top: 2px; line-height: 1.45; }
.bil__s-p { font-size: 19px; font-weight: 700; white-space: nowrap; }
.bil__s-a { display: flex; justify-content: flex-end; }
.bil__s-no { display: block; font-size: 12.5px; color: var(--text-3); line-height: 1.4; margin-top: 3px; }

.bil__bk { align-items: center; gap: 12px; padding: 16px; margin-bottom: 28px; }
.bil__bk-a { display: flex; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }

.bil__pt { margin-bottom: 12px; }

/* ── Промокод ── */

.bil__promo-f { flex: 1; }
/* Поле промокода ниже обычного (40 против 46) — так в макете. */
.bil__promo-f .fld__i { height: 40px; font-size: 14.5px; }

.bil__hist { padding: 20px 24px; }
.bil__hs { list-style: none; margin: 0; padding: 0; }
.bil__h { display: flex; gap: 16px; align-items: center; padding: 12px 0; font-size: 14px; }
.bil__h + .bil__h { border-top: 1px solid var(--line); }
.bil__h-d { color: var(--text-3); white-space: nowrap; }
.bil__h-t { flex: 1; min-width: 0; }
.bil__h-a { color: var(--brand-600); font-weight: 600; text-decoration: none; white-space: nowrap; }
.bil__note { font-size: 13px; color: var(--text-3); margin-top: 12px; }
.bil__empty { font-size: 14px; color: var(--text-2); line-height: 1.6; }

/* ───────── Главный блок «Тарифов»: выбор и покупка в первом экране
   (докладная владельца 16.09.2026, п.7) ───────── */
.bil__hero-f { margin-top: 18px; display: flex; flex-direction: column; gap: 14px; }
.bil__hero-opts { display: flex; flex-direction: column; gap: 10px; }
.bil__hero-opt {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  cursor: pointer;
}
.bil__hero-opt:has(.bil__hero-radio:checked) { border-color: var(--brand-600); box-shadow: 0 0 0 1px var(--brand-600) inset; }
.bil__hero-opt:has(.bil__hero-radio:disabled) { cursor: not-allowed; opacity: .55; }
.bil__hero-radio { margin-top: 3px; flex-shrink: 0; width: 18px; height: 18px; accent-color: var(--brand-600); }
.bil__hero-opt-b { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.bil__hero-opt-n { font-size: 15px; font-weight: 700; }
.bil__hero-select { height: 40px; font-size: 14.5px; max-width: 320px; width: 100%; }
.bil__hero-steps { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13.5px; color: var(--text-2); }
.bil__hero-step-l { min-width: 130px; }
/* Ссылки шага и число между ними не рвутся сами по себе на 375: подпись
   переносится отдельно, а «− N +» остаются одной группой. */
.bil__hero-step-grp { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.bil__hero-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-weight: 700;
  text-decoration: none;
  color: inherit;
}
.bil__hero-step--off { opacity: .35; border-style: dashed; }
.bil__hero-step-v { min-width: 22px; text-align: center; }
.bil__hero-opt-note { font-size: 13px; color: var(--text-2); line-height: 1.4; }
.bil__hero-opt-p { font-size: 18px; font-weight: 700; }

.bil__promow { flex-direction: column; align-items: stretch; gap: 10px; }
.bil__promo-bar { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.bil__promo-bar .fld { flex: 1; min-width: 200px; }
.bil__promo-bar .fld__i { height: 40px; font-size: 14.5px; }
.bil__promo-msg { font-size: 13.5px; margin: 0; }
.bil__promo-msg--ok { color: var(--ok); }
.bil__promo-msg--no { color: var(--text-2); }

/* ───────── Открепление профиля ───────── */

/* Узкая колонка: это диалог, а не раздел. Во всю ширину экрана он читался бы
   как страница со списком, которого у него нет. */
.det { max-width: 620px; }
.det__c { padding: 24px; }
.det__h { display: flex; gap: 12px; align-items: center; }
.det__n { min-width: 0; }
.det__nm { display: block; font-size: 16px; font-weight: 600; }
.det__mail { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 13px; color: var(--text-3); margin-top: 4px; }
.det__lead { font-size: 15px; font-weight: 600; margin-top: 16px; }
.det__t { font-size: 14.5px; color: var(--text-2); line-height: 1.55; margin-top: 14px; }

/* Сколько документов уйдёт — фактом, а не обещанием: число считается по профилю. */
.det__d {
  display: flex;
  gap: 8px;
  align-items: center;
  background: var(--bg-soft);
  border-radius: 12px;
  padding: 12px 14px;
  margin-top: 14px;
  font-size: 13.5px;
  color: var(--text-2);
}
.det__d .ci { color: var(--text-3); flex-shrink: 0; }

.det__f { margin-top: 18px; }
/* Галочка согласия крупнее обычной: её нажимают один раз в жизни профиля. */
.det__chk { display: flex; gap: 10px; align-items: flex-start; font-size: 14px; line-height: 1.5; cursor: pointer; }
.det__chk input { accent-color: var(--brand-600); width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px; }
.det__a { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 18px; }

/* Панель владельца над «Семьёй» (ТЗ, часть 1) — сводка для опознания человека,
   поверх которой стоит то же самое `.det__*`: панель и страница показывают
   один и тот же разговор, поэтому шрифты и отступы у них общие. */
.detp__who {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--brand-700);
  margin-top: 12px;
}
.detp__who .ci { color: var(--brand-600); flex-shrink: 0; }

/* Сводка — колонка подпись/значение, не таблица: строк то восемь, то пять
   (последний вход или привязка почты есть не всегда), и таблица с пустыми
   ячейками выглядела бы недоделкой там, где факта просто нет. */
.detp__facts { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; padding: 14px; background: var(--bg-soft); border-radius: 12px; }
.detp__f { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; }
.detp__fk { color: var(--text-3); }
.detp__fv { color: var(--text); font-weight: 600; text-align: right; }

.detp__note { font-size: 13px; color: var(--text-3); line-height: 1.5; margin-top: 14px; }

/* ───────── Обзор на узком экране ───────── */

@media (max-width: 1023px) {
  .head { flex-direction: column; align-items: flex-start; gap: 10px; margin-bottom: 16px; }
  .head__t { font-size: 24px; }
  .head__s { font-size: 14px; }
  /* «Импорт из Госуслуг» и «Добавить веху» на узком экране скрыты — но только
     НА ОБЗОРЕ: импорт доступен из модалки загрузки и из «Документов»
     (05-responsive.md). У «Документов» своя шапка, и правило её не касается. */
  .ov .head__act { display: none; }


  /* Документы на узком экране: чипы во всю ширину с прокруткой, сегмент на всю
     ширину, поиск скрыт (05-responsive.md), в шапке появляется «Загрузить». */
  .fchips { flex: 1 1 100%; padding-bottom: 2px; }
  .seg { display: flex; width: 100%; }
  .seg__i { flex: 1; text-align: center; }
  .dsearch { display: none; }
  /* В шапке «Документов» на узком экране появляется «Загрузить»; кнопка
     импорта остаётся — так в макете (`CabDocs.jsx`). */
  .dscreen .head__act--m { display: inline-flex; }
  .dgroup__c { padding: 2px 12px; }
  .ups { flex-direction: column; text-align: center; padding: 14px; }

  .ov-serv { grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
  .tile { flex-direction: column; align-items: flex-start; gap: 10px; padding: 12px 12px 13px; }
  .tile__t { font-size: 14px; }
  .tile__s { font-size: 12.5px; }

  /* Колонки раскрываются, карточки становятся детьми одной ленты — только так
     «Ближайшее» встаёт ПОСЛЕ документов, оставаясь в разметке один раз. */
  /* `align-items: stretch` названо вслух: базовое правило колонок ставит
     `start` (там это выравнивание по верху в сетке), и в колоночном флексе оно
     превращается в «по ширине содержимого» — карточка графика ужималась до
     ширины подписи, потому что у SVG своей ширины нет. */
  .ov-cols { display: flex; flex-direction: column; gap: 14px; align-items: stretch; }
  .ov-col { display: contents; }
  .ov-score { order: 1; }
  .ov-plan { order: 2; }
  .ov-docs { order: 3; }
  .ov-up { order: 4; }
  .ov-dyn { order: 5; }

  .ov-score { padding: 18px; }
  .ov-plan, .ov-up, .ov-dyn { padding: 16px; }
  .ov-docs { padding: 6px 14px 10px; }

  /* Плитка витрины на узком экране: значок над текстом, подложка 42. Размер
     задан разметкой (48), поэтому здесь он переопределяется — второй вызов
     `iconChip` в разметке означал бы два места, где живёт одна плитка. */
  .head__act--wide { display: none; }

  /* Хронология на узком экране: рейл в две колонки, дата НАД карточкой. */
  .tl-filter { margin-left: 0; flex: 1 1 100%; }
  .tl-years { flex: 1 1 100%; }
  .tl-h__t { font-size: 20px; }
  .tl-h__y { font-size: 30px; }
  .tl-m { grid-template-columns: 20px minmax(0, 1fr); gap: 0 8px; }
  .tl-m__s { grid-column: 1; }
  .rail { grid-template-columns: 20px minmax(0, 1fr); gap: 0 8px; }
  .rail__d {
    grid-column: 2;
    grid-row: 1;
    text-align: left;
    padding-top: 0;
    font-size: 12px;
    font-weight: 600;
    line-height: 20px;
    margin-bottom: 4px;
  }
  .rail__l { grid-column: 1; grid-row: 1 / span 2; }
  .rail__dot { margin-top: 4px; }
  .rail__b { grid-column: 2; grid-row: 2; padding-bottom: 12px; }
  .tl-doc { padding: 2px 12px; }
  .frow { flex-wrap: wrap; gap: 8px 12px; }
  .frow__b { flex: 1 1 200px; }
  .frow__a { flex: 0 0 100%; display: flex; justify-content: flex-end; }

  /* Показатели на узком экране: две карточки в ряд, без статуса в шапке. */
  .ichart { padding: 16px; }

  /* Карточка графика на узком экране — своя, более тесная сетка отступов, тем
     же приёмом, что и остальные карточки раздела: сервер ширины браузера не
     знает и переключается по медиазапросу, как везде в кабинете. Порог
     эталона (520 px ШИРИНЫ КАРТОЧКИ) здесь заменён на порог ширины ЭКРАНА —
     точного совпадения без скрипта нет, названо в отчёте. */
  .hchart__head { padding: 20px 16px 0; flex-wrap: wrap; }
  .hchart__summary { padding: 18px 16px; gap: 12px; }
  .hchart__reference { margin-left: 0; text-align: left; width: 100%; display: flex; gap: 7px; }
  .hchart__reference small { display: inline; }
  .hchart__plot { margin: 0 4px; }
  .hchart__legend { padding: 6px 16px 18px; gap: 8px 14px; }
  .hchart__details { padding: 0 16px; }
  .hchart__periods .seg__i { padding: 7px 12px; }
  .hchart__value { font-size: 32px; }

  /* Дневник на узком экране: переключатель во всю ширину, строки переносятся. */
  .rem__c { padding: 2px 12px; }
  .rrow { flex-wrap: wrap; }
  .rrow__b { min-width: 60%; }

  .tile .ichip { width: 42px; height: 42px; border-radius: 11px; }
  .tile .ichip .ci { width: 21px; height: 21px; }

  /* Строка документа компактная: подложка 36, статус и шеврон скрыты — их
     место в карточке документа (05-responsive.md). */
  .drow { padding: 10px 4px; }
  /* Правому краю на узком экране остаётся одна пилюля внимания — гнёзда
     статуса и кнопки скрыты ниже. Её колонка сохраняется: столбец оценок
     ровный и здесь. */
  .drow .ichip { width: 36px; height: 36px; border-radius: 10px; }
  .drow .ichip .ci { width: 18px; height: 18px; }
  .drow__status,
  .drow__act,
  .drow__chev { display: none; }
  /* Пятая строка уходит: на узком экране в карточке их четыре. */
  .drow--extra { display: none; }

  .tr--wide { display: none; }
  .tr--narrow { display: block; }

  .dyn__lock { flex-direction: column; text-align: center; padding: 16px; }
  .ov__disc { margin-top: 24px; }

  /* Запись на узком экране: сегмент во всю ширину, выдача в одну колонку,
     карточка врача теснее (03-components.md: padding 14 вместо 18). */
  .zseg { display: flex; margin-bottom: 14px; }
  .zfilters { padding: 14px; }
  .zcity { grid-template-columns: 1fr; }
  .zspec .fchips { flex-wrap: nowrap; overflow-x: auto; }
  .zgrid { grid-template-columns: 1fr; }
  .zdoc { padding: 14px; }
  .zstate { flex-direction: column; padding: 18px; }
  .zret__c { padding: 16px; }
  .zprep { padding: 16px; }

  /* Семья на узком экране: одна колонка везде, справка столбиком. */
  .fam__g { grid-template-columns: 1fr; }
  .fam__c { padding: 16px; }
  .fam__g { gap: 12px; }
  .fam__hs { grid-template-columns: 1fr; gap: 16px; }
  /* Баннер запроса на узком экране остаётся РЯДОМ: `.zstate` в столбик — это
     про состояния записи, где значок крупный и сам по себе. Здесь слева стоит
     аватар того, кто просит, и оторвать его от имени значит спросить «кто?». */
  .fam__req { flex-direction: row; align-items: flex-start; flex-wrap: wrap; padding: 16px; }
  /* Кнопка уезжает под текст ЦЕЛОЙ строкой (`flex-basis: 100%`), а не сжимает
     его до одного слова в строке: в ряду без переноса `width: 100%` у кнопки
     отбирал у заголовка всю ширину, и «попросил открепить свой профиль»
     вставало столбиком. Поймано прогоном `--live` на длинном имени. */
  .fam__req-a { flex: 1 0 100%; }
  .fam__loose, .fam__help { padding: 16px; }
  .fam__l-f { width: 100%; }
  .fam__l-f .fld__i--sm { flex: 1; min-width: 0; }

  /* Профиль на узком экране: одна колонка, выбор профиля во всю ширину. */
  .prof__g { grid-template-columns: 1fr; gap: 16px; }
  .prof__data { padding: 18px; }
  .prof__mail { flex-direction: column; align-items: stretch; }
  .prof__mail .cab-btn { justify-content: center; }

  /* Тарифы на узком экране (05-responsive.md): подписки в столбик, карточка
     тарифа в столбик, строка услуги — сетка `1fr auto` с ценой справа в первой
     строке и кнопкой во всю ширину под названием. */
  .bil__ps { grid-template-columns: 1fr; gap: 14px; }
  .bil__cur, .bil__hist, .bil__p { padding: 18px; }
  .bil__svc { padding: 2px 16px; }
  .bil__cur-w { flex-direction: column; }
  .bil__cur-a { min-width: 100%; }
  .bil__cur-n { font-size: 20px; }
  .bil__up { grid-template-columns: minmax(0, 1fr) auto; }
  .bil__up-g { grid-column: 1 / -1; }

  .bil__s { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 12px; }
  .bil__s-p { font-size: 17px; text-align: right; grid-row: 1; grid-column: 2; }
  .bil__s-a { grid-column: 1 / -1; }
  .bil__s-a .cab-btn { width: 100%; }

  .bil__bk { flex-direction: column; align-items: flex-start; padding: 14px; }
  .bil__bk-a { width: 100%; }
  .bil__promo { width: 100%; }

  .det__c { padding: 18px; }

  /* Пачка на узком экране: номер уходит, пилюля и кнопка встают под именем.
     Четыре колонки в 375 px не помещаются — имя файла сжалось бы до трёх
     символов, а именно по нему человек и узнаёт свой бланк. */
  .bgroup { padding: 2px 16px; }
  .brow { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 10px; }
  .brow__i { display: none; }
  .brow__a { grid-column: 1 / -1; }
  .brow__a .cab-btn { width: 100%; }

  /* Telegram-бот на узком экране: карточка состояния в столбик, кнопка
     «Отвязать» во всю ширину — иначе она жмётся к правому краю и попадает под
     большой палец случайно. */
  .tg__card { flex-direction: column; padding: 18px; }
  .tg__off { width: 100%; }
  .tg__off .cab-btn { width: 100%; }
  .tg__code { font-size: 26px; }
  .tg__on .cab-btn { width: 100%; }

  /* Панель на узком экране — во весь экран: так на эталоне `modal-doc-375`,
     и так же ведут себя все окна макета на мобайле (05-responsive). */
  .dpanel__c { width: 100%; }
  .dpanel__h { padding: 18px 18px 8px; }
  .dpanel .dcard { padding: 0 18px 18px; }
  .dpanel__body { padding: 0 18px 18px; }
}

/* ───────── Консилиум ИИ ─────────

   Разметка собрана из тех же узлов, что у остальных разделов (`cab-card`,
   `fchip`, `disc`, `empty`) — здесь только то немногое, у чего своего аналога
   ещё не было: строка показателя с пилюлей, группа причин, тизер-текст под
   блюром и форма заказа. */

.konsilium__who { font-size: 14px; color: var(--text-2); margin-bottom: 14px; }

.konsilium__ind {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  font-size: 14.5px;
  color: var(--text);
}
.konsilium__ind + .konsilium__ind { border-top: 1px solid var(--line); }

.konsilium__group { margin-top: 4px; }
.konsilium__group + .konsilium__group { margin-top: 14px; }
.konsilium__group b { font-size: 13.5px; font-weight: 700; }
.konsilium__group ul { margin: 6px 0 0; padding-left: 18px; }
.konsilium__group li { font-size: 14px; color: var(--text-2); line-height: 1.55; }
.konsilium__group li + li { margin-top: 4px; }

/* Карточка одного голоса роли («Терапевт», «Скептик») — задача владельца
   15.09.2026, часть 2. Тот же ритм, что у `.konsilium__group`: подпись
   жирным, текст ниже, отступ между карточками. */
.konsilium__role { margin-top: 4px; }
.konsilium__role + .konsilium__role { margin-top: 14px; }
.konsilium__role b { font-size: 13.5px; font-weight: 700; }
.konsilium__role p { margin: 4px 0 0; font-size: 14px; color: var(--text-2); line-height: 1.55; }

.konsilium__empty,
.konsilium__locked {
  font-size: 14px;
  color: var(--text-3);
  line-height: 1.55;
}

.konsilium__price { font-size: 22px; font-weight: 700; }
.konsilium__note { font-size: 13px; color: var(--text-3); margin-top: 4px; line-height: 1.5; }

.konsilium__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}
.konsilium__form input[type="email"] {
  flex: 1 1 220px;
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-m);
  font-family: var(--font-body);
  font-size: 14.5px;
  color: var(--text);
}
.konsilium__form input[type="email"]:focus { outline: none; border-color: var(--brand-600); box-shadow: 0 0 0 3px var(--focus-ring); }

@media (max-width: 599px) {
  .konsilium__form { flex-direction: column; align-items: stretch; }
  .konsilium__form .cab-btn { width: 100%; }
}

/* ───────── Самый узкий экран: колонка знака сжимается до самого знака ─────────

   Правило то же — знак в правом конце подписи, — а вот ПОЛ у колонки здесь
   нулевой: тело строки на 375 всего 175 px, и колонка в 152 не оставила бы
   подписи ничего. Знак прижимается к правому краю подписи и занимает ровно
   свою ширину; ровным остаётся правый край столбца, а не оба.

   Высота 18, а не 26, и свисания вниз тут нет: у колонки на этой ширине нет
   пола, знак занимает свою натуральную ширину — и растёт он не только вверх,
   а и ВБОК, отнимая место у текста подписи. Замерено: знак 22 px на 375
   заставляет подпись одной из строк перенестись на вторую строку, и список
   вырастает на 20,8 px (788,4 → 809,2). Упирается тут ширина, а её полем
   строки не оплатить. Против прежних 14 всё равно крупнее. */
@media (max-width: 599px) {
  .dgroup__c .drow { --drow-lab: 0px; }
  .dgroup__c .drow__lab { height: 18px; margin-bottom: 0; }
  /* Плашке нужен свой потолок: 180 px в теле шириной 175 не оставили бы
     подписи ни пикселя. Что не влезло — в подсказке `title`, как и было. */
  .dgroup__c .drow__lab--text { min-height: 18px; margin-bottom: 0; max-width: 110px; }
}

/* ═══ СВОДНАЯ ТАБЛИЦА ДИНАМИКИ ═══════════════════════ ui/cabinet/dynamics.ts
   Строки — показатели, столбцы — даты сдачи. Таблица прокручивается В СВОЁМ
   контейнере, имя показателя прилипает к левому краю; страница вбок не едет. */

.dyn-list { display: grid; gap: 16px; min-width: 0; }
.dyn__lead { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-2); max-width: 72ch; }
.dyn-toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.dyn-toc a {
  display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px;
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg);
  color: var(--text); font-size: 13px; font-weight: 600; text-decoration: none; line-height: 1.3;
}
.dyn-toc a:hover { border-color: var(--brand-600); color: var(--brand-700); }
.dyn-toc__who { color: var(--text-2); font-weight: 500; }
.dyn-toc__n { color: var(--text-3); font-weight: 400; }

.dyn { padding: 20px 0 16px; min-width: 0; scroll-margin-top: 16px; }
.dyn__head {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; justify-content: space-between;
  padding: 0 24px 14px;
}
.dyn__hb { min-width: 0; }
.dyn__who { margin: 0 0 2px; font-size: 12px; color: var(--text-3); }
.dyn__t { margin: 0; font-size: 17px; font-weight: 700; color: var(--text); }
.dyn__s { margin: 4px 0 0; font-size: 13px; color: var(--text-3); }

/* `position: relative` обязателен: подпись для экранного чтеца (`.vh`) —
   абсолютный элемент, и без позиционированного предка он отсчитывается от
   документа — клетка за краем прокрутки растягивала страницу вбок на 40–55 px
   (замерено на 320 по бланкам стенда). */
.dyn__scroll {
  position: relative;
  overflow-x: auto; overscroll-behavior-x: contain;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.dyn__scroll:focus-visible { outline: 2px solid var(--brand-600); outline-offset: -2px; }
/* Ширина — по содержимому: растянутая на карточку, таблица отрывала
   референс от значений на пол-экрана (замерено на 1440). */
.dyn__table { border-collapse: separate; border-spacing: 0; font-size: 13px; }
.dyn__table .dyn__date, .dyn__table .dyn__v { min-width: 76px; }
.dyn__table th,
.dyn__table td { padding: 9px 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
.dyn__table tbody tr:last-child > * { border-bottom: 0; }
.dyn__table thead th {
  background: var(--bg-soft); color: var(--text-3); font-size: 12px; font-weight: 500; white-space: nowrap;
}
.dyn__name {
  position: sticky; left: 0; z-index: 1;
  min-width: 150px; max-width: 220px;
  background: var(--bg); color: var(--text); font-weight: 600;
  box-shadow: 1px 0 0 var(--line);
}
.dyn__table thead .dyn__name { background: var(--bg-soft); z-index: 2; }
.dyn__unit { display: block; margin-top: 2px; font-size: 12px; font-weight: 400; color: var(--text-3); }
/* Метод, от которого зависят числа: объясняет, почему у показателя два ряда (DECISIONS §185). */
.dyn__method { display: block; margin-top: 2px; font-size: 12px; font-weight: 400; font-style: italic; color: var(--text-2); }
.dyn__ref { min-width: 96px; max-width: 170px; font-size: 12px; color: var(--text-2); }
.dyn__table .dyn__date,
.dyn__table .dyn__v { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.dyn__star { margin-left: 2px; color: var(--text-2); }
.dyn__v--high,
.dyn__v--low { color: var(--bad); font-weight: 600; }
td.dyn__v--high,
td.dyn__v--low { background: var(--bad-soft); }
.dyn__m { margin-left: 3px; }
.dyn__v--none { color: var(--text-3); }
.dyn__legend { margin: 0; padding: 12px 24px 0; font-size: 12px; line-height: 1.5; color: var(--text-3); }
.dyn__note { margin: 0; font-size: 13px; color: var(--text-3); }

/* Вид «для врача»: без оболочки кабинета, только таблица. */
.dyn-show { page: dyn; max-width: 1200px; margin: 0 auto; padding: 16px 24px 32px; }
.dyn-show__bar {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 20px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line); font-size: 13px; color: var(--text-3);
}
.dyn-show__back { color: var(--brand-600); font-weight: 600; text-decoration: none; }
.dyn-show__hint { margin: 0; }
.dyn-show__head { padding: 20px 0 14px; }
.dyn-show__who { margin: 0 0 4px; font-size: 16px; font-weight: 600; color: var(--text); }
.dyn-show__t { margin: 0; font-size: 22px; font-weight: 700; color: var(--text); }
.dyn-show__s { margin: 6px 0 0; font-size: 13px; color: var(--text-3); }
.dyn--show { padding: 0; }
.dyn--show .dyn__scroll { border: 1px solid var(--line); border-radius: var(--r-m); }
.dyn--show .dyn__legend { padding: 10px 0 0; }
.dyn-show__note { margin: 16px 0 0; font-size: 12px; line-height: 1.5; color: var(--text-3); }

@media (max-width: 599px) {
  .dyn__head { padding: 0 16px 12px; }
  .dyn__legend { padding: 10px 16px 0; }
  /* На 320 имя и референс не должны съесть всю ширину: хотя бы одна дата
     обязана быть видна целиком без прокрутки. */
  .dyn__name { min-width: 104px; max-width: 118px; }
  .dyn__ref { min-width: 64px; max-width: 84px; }
  .dyn__table { font-size: 12.5px; }
  .dyn__table th,
  .dyn__table td { padding: 8px 8px; }
  .dyn-show { padding: 12px 16px 24px; }
  .dyn-show__t { font-size: 19px; }
  /* Четыре вкладки дневника в одну строку на 320 не встают: две на две. */
  .seg.seg--diary { display: grid; grid-template-columns: 1fr 1fr; }
}

/* Печать: альбомный лист только у вида «для врача» (именованная страница),
   таблица без прокрутки и без липкой колонки, отметка — стрелкой, а не цветом:
   лист часто печатают чёрно-белым. */
@page dyn { size: A4 landscape; margin: 12mm; }
@media print {
  .dyn-show__bar { display: none; }
  .dyn-show { max-width: none; padding: 0; }
  .dyn__scroll { overflow: visible; border: 0 !important; }
  .dyn__table { width: 100%; font-size: 9.5pt; }
  .dyn__table thead { display: table-header-group; }
  .dyn__table tr { break-inside: avoid; }
  .dyn__table th,
  .dyn__table td { padding: 3pt 5pt; border-bottom: 0.5pt solid #999; }
  .dyn__name { position: static; box-shadow: none; }
  /* Единица — в строку с именем: двадцать строк ОАК встают на один лист. */
  .dyn__unit { display: inline; margin: 0 0 0 4pt; }
  .dyn__method { display: inline; margin: 0 0 0 4pt; color: #000; }
  td.dyn__v--high,
  td.dyn__v--low { background: none; color: #000; }
  /* Вкладка кабинета тоже печатается без обвязки: только таблицы. */
  .cab-side, .cab-top, .cab-mtop, .cab-tabs, .cab-fab-box, .skip-link,
  .diary .head, .seg--diary, .dyn-toc, .dyn__act, .dyn__lead { display: none !important; }
  .dyn { break-inside: auto; box-shadow: none; border: 0; }
}

/* Бесплатная дорога по ОМС в разделах записи (§197). Вид карточки — из
   site.css (`.oms`), здесь только её место в колонке экрана. */
.zscreen .oms { margin-top: 16px; margin-bottom: 16px; }
.zscreen .oms-roads { margin-bottom: 8px; }

/* Сохранённый разговор консилиума: один голос на карточку. */
.konsilium__chat { display: grid; gap: 12px; margin-top: 14px; }
.konsilium__message { max-width: 88%; padding: 14px 16px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface, #fff); overflow-wrap: anywhere; }
.konsilium__message:nth-child(even) { margin-left: auto; background: var(--brand-50, #f3f8f5); }
.konsilium__speaker { font-weight: 700; font-size: 14px; }
.konsilium__speaker span { font-weight: 400; color: var(--text-3); margin-left: 8px; }
.konsilium__reply { font-size: 12px; color: var(--text-3); margin: 5px 0 0; }
.konsilium__message p:last-child { margin: 8px 0 0; line-height: 1.55; white-space: normal; }
.konsilium__message--latest p:last-child { clip-path: inset(0 100% 0 0); animation: konsilium-type 2s steps(35, end) forwards; }
@keyframes konsilium-type { to { clip-path: inset(0 0 0 0); } }
@media (prefers-reduced-motion: reduce) { .konsilium__message--latest p:last-child { animation: none; clip-path: none; } }
@media (max-width: 600px) { .konsilium__message { max-width: 100%; } }
.konsilium__sources { font-size: 12px; color: var(--text-3); }
