/* Дентал Порт — слой поверх Tailwind.
   Палитра объявлена один раз в @theme на странице; Tailwind сам выкладывает
   её значения в :root как --color-*, и этот файл берёт их оттуда.
   Ни одного hex здесь и ни одного hex в разметке. */

/* Единиц rem в этом файле нет ни одной, и это правило, а не стиль
   записи. Кегль документа в Chrome настраивается, и на машине с крупным
   системным шрифтом (24px вместо 16) всё, что задано в rem, вырастает
   в полтора раза: кнопка 3.25rem превращалась в 78px против 52, поля
   формы — в 84 против 56, а логотип и иконки, заданные в px, оставались
   прежними и выглядели рядом с ними игрушечными. Половина системы в rem,
   половина в px — это вёрстка, которая цела у автора и разъезжается
   у части посетителей. Тот же расчёт стоит за --spacing: 4px в @theme. */

/* Все кегли сайта умножены на --k, и это не запас «на будущее»: на нём
   держится крупный шрифт версии для слабовидящих. Литеральных font-size
   в разметке нет ни одного (утилит text-* Tailwind в шести страницах
   ноль), поэтому один множитель здесь поднимает весь текст целиком —
   и заголовки, и подписи в доке, и строки прайса. Межстрочные заданы
   безразмерно, трекинг в em: оба масштабируются сами.
   Исключение одно — .ghost-num: подложечная цифра секции это графика,
   а не текст, и растить её вместе с абзацами незачем. */
:root {
  --k: 1;
}

/* ── Типографика ───────────────────────────────────────────────── */

/* scroll-padding-top держит запас под липкой шапкой (88px + воздух):
   без него якорь ставил заголовок секции ровно под её нижний край,
   и запас держался только полями секции — один пиксель. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 112px;
}

/* Снимки уходят за край экрана отрицательными полями. Защита от
   горизонтальной прокрутки — clip, а не hidden: hidden превратил бы
   обёртку в scroll-контейнер и липкая шапка залипла бы относительно неё. */
html,
body {
  overflow-x: clip;
}

body {
  font-size: calc(17.5px * var(--k));
  line-height: 1.6;
  letter-spacing: -0.005em;
  font-variant-numeric: tabular-nums;
}

.h1,
.h2,
.h3 {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--color-ink-900);
  text-wrap: balance;
}

.h1 {
  font-size: calc(clamp(34px, 6.2vw, 56px) * var(--k)); /* 34 → 56px */
  line-height: 1.02;
  letter-spacing: -0.03em;
}

.h2 {
  font-size: calc(clamp(32px, 4.2vw, 46px) * var(--k)); /* 32 → 46px */
  line-height: 1.08;
  letter-spacing: -0.025em;
}

.h3 {
  font-size: calc(clamp(24px, 2.4vw, 28px) * var(--k)); /* 24 → 28px */
  line-height: 1.2;
  letter-spacing: -0.02em;
}

/* Явный разрыв между уровнями: 56 → 46 → 28 → 21 → 17.5. */

.lead {
  font-size: calc(clamp(18px, 1.5vw, 21px) * var(--k)); /* 18 → 21px */
  line-height: 1.55;
  color: var(--color-ink-600);
  max-width: 44ch;
}

/* Тёмная плоскость. Заголовочные классы задают color сами, и утилита
   text-paper-elev на том же элементе им проигрывает при равной
   специфичности — ровно та же ловушка, что с .btn и .hidden. Поэтому
   у компонента есть вариант, а не борьба весами в разметке:
   класс ставится на секцию, а не на каждый заголовок. */
.on-dark .h1,
.on-dark .h2,
.on-dark .h3,
.on-dark .card-t {
  color: var(--color-paper-elev);
}
.on-dark .lead,
.on-dark .prose-dp p {
  color: var(--color-mint-300);
}

/* То же для лимонной плоскости. Утилита text-sun-ink на .h2 не работает
   и работать не может: dp.css лежит вне слоёв Tailwind и бьёт утилиты
   при равной специфичности — заголовок на жёлтом остаётся тушью ink-900,
   хотя в разметке написано другое. Контраст при этом высокий, и поломки
   не видно, поэтому расхождение и дожило: тот же класс стоит на «Чего
   у нас не бывает» на Детской и там тоже ничего не красит.
   Вариант компонента чинит правило вместо того, чтобы делать вид,
   что оно соблюдается. */
.on-sun .h1,
.on-sun .h2,
.on-sun .h3,
.on-sun .card-t {
  color: var(--color-sun-ink);
}
.on-sun .lead {
  color: var(--color-sun-ink-2);
}

.prose-dp p {
  max-width: 68ch;
  color: var(--color-ink-600);
}

.num {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
/* Реквизиты — тот же .num, но переносить их можно и нужно: nowrap здесь
   писался под «7 000 ₽» и телефон, где разрыв внутри значения читается
   ошибкой. В блоке реквизитов значений несколько, они длинные, и разметка
   уже просит переноса утилитой break-words — но nowrap из .num её бил,
   и строка «ИНН 9721257880 · ОГРН 1257700446080» на крупном кегле
   в режиме слабовидящих выходила за колонку и ложилась на соседнюю.
   Селектор (0,2,0) весит больше .num и включается ровно там, где
   разметка сама попросила перенос. */
.num.break-words {
  white-space: normal;
}

/* Заголовок карточки — гротеск, а не антиква. Prata на 28px в колонке
   шириной 250px рвёт слова и спорит с h2; ступень 21/500 держит
   иерархию 56 → 46 → 28 → 21 → 17.5 без наложений. */
.card-t {
  font-family: var(--font-sans);
  font-size: calc(21px * var(--k));
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--color-ink-900);
}

/* Ступени ниже основного текста и одна над ним. Литеральных кеглей
   в разметке нет: иначе на странице заводится десяток соседних
   значений вроде 14.5 / 15 / 15.5, которые ничем не отличаются. */
.t-xl {
  font-size: calc(21px * var(--k));
  line-height: 1.4;
}
.t-lg {
  font-size: calc(19px * var(--k));
  line-height: 1.45;
}
.t-md {
  font-size: calc(16px * var(--k));
  line-height: 1.5;
}
.t-sm {
  font-size: calc(15px * var(--k));
  line-height: 1.5;
}
.t-xs {
  font-size: calc(13.5px * var(--k));
  line-height: 1.3;
}

/* Кнопка возраста: на узком экране ступень 15, на широком — основной текст. */
.t-tab {
  font-size: calc(15px * var(--k));
}
@media (min-width: 640px) {
  .t-tab {
    font-size: calc(17.5px * var(--k));
  }
}

/* Первый экран Главной набран крупнее остальных заголовков: в референсе
   display-1 равен 72px против 44 у заголовков секций. Ступень своя, а не
   раздутая .h1 — иначе первый экран Детской уехал бы вместе с ней. */
.h1-hero {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--color-ink-900);
  /* Потолок 60px, а не 72 из референса: там заголовок английский
     («Your Smile.»), а «доверяют» в колонке 471px на 72px рвётся
     на третью строку. Правило двух строк равной длины важнее кегля. */
  font-size: calc(clamp(38px, 4.4vw, 60px) * var(--k));
  line-height: 1.06;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

/* Пилюля-надзаголовок. Единственный uppercase на странице — так же,
   как в референсе, где заглавными набран только eyebrow. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 999px;
  background-color: var(--color-mint-100);
  color: var(--color-brand-700);
  font-size: calc(13.5px * var(--k));
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Четыре крупных знака страницы, каждый со своей ролью. */
.seal-num {
  font-family: var(--font-display);
  font-size: calc(clamp(34px, 4vw, 38px) * var(--k));
  line-height: 1;
}
.price-hero {
  font-family: var(--font-display);
  font-size: calc(clamp(40px, 4.5vw, 56px) * var(--k));
  line-height: 1;
}
.quote {
  font-family: var(--font-display);
  font-size: calc(clamp(19px, 1.7vw, 23px) * var(--k));
  line-height: 1.35;
}
/* Подложечная «20» — не текст, а графика секции. */
.ghost-num {
  font-family: var(--font-display);
  font-size: clamp(120px, 14vw, 200px);
  line-height: 1;
}

/* ── Печать «0 ₽» ──────────────────────────────────────────────── */

/* Первая сумма страницы стоит на первом экране отдельным знаком.
   Раньше знак лежал в лимонном круге, и круг был ошибкой дважды:
   жёлтая заливка рядом с врачом и ребёнком читалась как жёлтый зуб —
   ровно то состояние, из-за которого к нам и идут, — а сама форма
   была ничьей: круг не говорит о клинике ничего.

   Форма теперь зубная, и это не новая фигура: путь `dp-tooth-d` тот же,
   что у кнопки и панели дока (`tools/parts/tooth-def.html`). Печать,
   кнопка связи и раскрытая панель — один контур в трёх коробках.

   Эмаль белая, контур и знак — зелень бренда. Белое на бумаге само
   по себе не читается, поэтому фигуру держат обводка 2px и тень;
   лимон остаётся там, где он и значит выгоду, — на акциях.

   Коробка печати (`.seal`) своих правил не имеет намеренно. position
   задаётся в разметке (absolute): на Главной она висит
   по краю снимка, на Детской — по его середине. Объявленный здесь
   position побил бы утилиту (dp.css лежит вне слоёв Tailwind) и оторвал
   бы печать от кадра сразу на обеих страницах. Но absolute обязателен:
   от него считается безопасное поле внутри коронки.

   display здесь тоже не объявлен, и по той же причине: dp.css бьёт
   утилиты при равной специфичности, и объявленный тут display выключил
   бы `hidden` — на Детской печать показывалась бы на мобильном поверх
   плашки, которая её там заменяет. */

/* Строки лежат в коронке, а не по середине коробки: середина у зуба
   приходится на развилку корней. Поле задано процентами от самой
   печати — она меняет размер трижды по брейкпоинтам, и пересчитывать
   отступы в пикселях на каждом значило бы держать три версии одного
   правила. Дно поля — 27% снизу, там кончается коронка; бока по 9%,
   там контур уходит внутрь. */
.seal-body {
  position: absolute;
  inset: 5% 9% 27%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.seal-tooth {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: var(--color-paper-elev);
  stroke: var(--color-brand-700);
  stroke-width: 2;
  filter: drop-shadow(0 12px 26px rgb(18 51 42 / 0.26))
    drop-shadow(0 2px 6px rgb(18 51 42 / 0.12));
}



/* ── Кнопки ────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 24px;
  border-radius: 999px;
  font-size: calc(17px * var(--k));
  font-weight: 500;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
  transition: background-color 180ms ease-out, color 180ms ease-out,
    border-color 180ms ease-out;
}

/* Кнопка с длинной подписью: на 375px «Записать к Виолетте Григорьевне»
   в одну строку даёт 527px и уезжает за экран. */
.btn-wrap {
  white-space: normal;
  padding-top: 14px;
  padding-bottom: 14px;
  line-height: 1.3;
}

/* Кнопка в шапке. Высота 48 вместо 52 — ровно круг трубки, марки
   и бургера, чтобы правый блок читался одной деталью, а не лесенкой.
   Это вариант компонента, а не утилита в разметке: утилиты Tailwind
   лежат в @layer, dp.css — вне слоёв, и неслоёное правило .btn
   перебивает h-12 при любой специфичности. */
.btn-bar {
  min-height: 48px;
  padding: 0 20px;
}

.btn-primary {
  background-color: var(--color-brand-700);
  color: var(--color-paper-elev);
}
.btn-primary:hover {
  background-color: var(--color-brand-800);
}

.btn-sun {
  background-color: var(--color-sun-200);
  color: var(--color-sun-ink);
  border: 1px solid var(--color-sun-ink);
}
.btn-sun:hover {
  background-color: var(--color-sun-400);
}

.btn-ghost {
  background-color: transparent;
  color: var(--color-ink-900);
  border: 1px solid var(--color-line);
}
.btn-ghost:hover {
  background-color: var(--color-mint-100);
  border-color: var(--color-mint-300);
}

/* Кнопка на мятной плоскости. .btn-ghost прозрачна, и на мяте она
   читается не кнопкой, а подчёркнутой строкой; заливку при этом нельзя
   добавить утилитой bg-paper-elev — dp.css лежит вне слоёв и выигрывает
   у утилит при равной специфичности, ровно та же ловушка, что с .hidden.
   Поэтому вариант компонента, а не класс в разметке. */
.btn-paper {
  background-color: var(--color-paper-elev);
  color: var(--color-ink-900);
  border: 1px solid var(--color-line);
}
.btn-paper:hover {
  background-color: var(--color-mint-200);
  border-color: var(--color-mint-500);
}

/* Фокус виден на любой подложке: тёмное кольцо плюс бумажный зазор. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--color-brand-700);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── Поля формы ────────────────────────────────────────────────── */

.field {
  width: 100%;
  min-height: 56px;
  padding: 0 20px;
  border-radius: 999px;
  border: 1px solid var(--color-line-input);
  background-color: var(--color-paper-elev);
  color: var(--color-ink-900);
  font-size: calc(17px * var(--k));
}
.field::placeholder {
  color: var(--color-ink-400);
}
.field:focus {
  border-color: var(--color-brand-700);
}

/* Подпись стоит над полем и видна всегда: placeholder исчезает при вводе
   и подписью работать не может. Звёздочка — на обязательном, слово
   «необязательно» — на остальных, чтобы правило читалось в обе стороны. */
/* Компактная форма. Поле 48px — нижняя допустимая граница; кегль внутри
   остаётся 17px, иначе iOS зумит страницу при фокусе. Применяется там,
   где форма стоит рядом с картой и высота секции дороже воздуха. */
.form-tight .field {
  min-height: 48px;
}
.form-tight .f-label {
  margin-bottom: 6px;
  font-size: calc(15px * var(--k));
}

/* Форма, собранная в карточку. Ставится там, где форма стоит в одном ряду
   с блоком, который выше её: карта с панелью места на Главной идёт на 70px
   длиннее формы.
   Здесь раньше стоял .form-fill — форма растягивалась на высоту соседней
   колонки, а остаток раздавался между группами полей. Кромки ряда сходились,
   но шаг между полями уезжал с заданных 16px до 47–52, и бланк рассыпался
   на пять отдельных вещей. Разницу высот держит теперь край карточки:
   у формы своя плоскость и свой радиус, и то, что она кончается выше панели,
   читается формой карточки, а не провалом.
   Верхнее поле формы снимается: оно рассчитано на заголовок над формой,
   а здесь форма начинается сразу от кромки карточки.
   Кнопка — во всю ширину карточки: у нижней кромки бланка должен быть край,
   а не пилюля, прижатая к левому полю. */
.form-card > form {
  margin-top: 0;
}
.form-card > form > button {
  width: 100%;
}

.f-label {
  display: block;
  margin-bottom: 8px;
  font-size: calc(16px * var(--k));
  font-weight: 500;
  line-height: 1.4;
  color: var(--color-ink-900);
}
.f-hint {
  font-weight: 400;
  color: var(--color-ink-400);
}
.f-req {
  color: var(--color-alert);
}

/* Ошибка — под своим полем и словами о том, что именно не так.
   Одной сводкой над формой человек не узнает, какое поле чинить. */
.f-err {
  margin-top: 6px;
  font-size: calc(15px * var(--k));
  line-height: 1.4;
  color: var(--color-alert);
}
.field[aria-invalid='true'] {
  border-color: var(--color-alert);
  background-color: var(--color-alert-bg);
}

/* Select остаётся select — снимаем только системную стрелку, вместо неё
   поверх лежит наша двухцветная иконка. Фоновой картинкой её не задать:
   data-URI притащил бы в CSS hex, которого здесь нет ни одного. */
.field-select {
  appearance: none;
  padding-right: 56px;
  cursor: pointer;
}
/* Пока стоит подсказка «Выберите услугу», поле пустое и по цвету —
   подсказка, а не ответ. */
.field-select:invalid {
  color: var(--color-ink-400);
}

/* Чекбокс родной, увеличенный. Своя рисованная галочка здесь запрещена:
   согласие по 152-ФЗ обязано выглядеть как чекбокс, а не как украшение. */
.consent {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding-block: 6px;
}
/* Курсор — на подписи и квадрате, но не на ссылке рядом с ними. */
.consent label {
  cursor: pointer;
}
/* 26px, а не 22: это единственный чекбокс сайта и он стоит перед
   отправкой формы — на телефоне в него надо попадать пальцем. Подпись
   рядом — тоже цель: label охватывает всю строку текста. */
.consent input[type='checkbox'] {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  margin-top: 1px;
  accent-color: var(--color-brand-700);
  cursor: pointer;
}

/* ── Чипы сценария (radio без вида radio) ──────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--color-line);
  background-color: var(--color-paper-elev);
  color: var(--color-ink-600);
  font-size: calc(16px * var(--k));
  line-height: 1.3;
  cursor: pointer;
  transition: background-color 180ms ease-out, color 180ms ease-out,
    border-color 180ms ease-out;
}
.chip:hover {
  background-color: var(--color-mint-100);
  border-color: var(--color-mint-300);
}
.chip:has(input:checked) {
  background-color: var(--color-brand-700);
  border-color: var(--color-brand-700);
  color: var(--color-paper-elev);
}
.chip:has(input:focus-visible) {
  outline: 3px solid var(--color-brand-700);
  outline-offset: 3px;
}

/* ── Работы врача ──────────────────────────────────────────────
   Перечень направлений в панели картотеки: строка на работу, две
   колонки на широком экране, одна на узком. Обведённых пилюль здесь
   нет — чип в этой системе переключатель, и капсула, на которую
   нельзя нажать, обещает интерактивность, которой нет.

   Метка — нарисованный кружок 5px, а не типографская точка, не иконка
   и не ::marker: маркер списка здесь графика, и он обязан выглядеть
   одинаково во всех браузерах, включая те, где ::marker правится
   наполовину. Мята в своей роли подложки — метка не спорит с самими
   словами, набранными тушью.

   ⚠️ Строчный вариант .does (работы одной строкой через точку) стоял
   здесь с 21 августа 2026 и снят в тот же день: он жил в разворотах
   досье, а досье слито с картотекой, и в панели работы идут столбцом.
   Мёртвого правила в файле не осталось. */
.does-col {
  display: grid;
  gap: 8px 24px;
}

@media (min-width: 640px) {
  .does-col {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.does-col li {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.does-col li::before {
  content: '';
  flex: none;
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background-color: var(--color-mint-500);
  transform: translateY(-4px);
}

/* ── Прайс ─────────────────────────────────────────────────────
   Строка прайса набрана как строка меню: название слева, сумма справа,
   между ними протянут точечный лидер. Он не украшение — на ширине
   в 700px глаз терял, какой сумме принадлежит название, и цену
   приходилось вести пальцем. Точки, а не сплошная линия: сплошная
   читалась бы рамкой, а рамок в этой системе нет. */

.leader {
  flex: 1 1 auto;
  min-width: 24px;
  align-self: center;
  height: 1px;
  background-image: radial-gradient(
    circle,
    var(--color-mint-300) 1px,
    transparent 1px
  );
  background-size: 7px 1px;
  background-repeat: repeat-x;
}

/* На лимонной плоскости точки лидера набираются линией по лимону:
   мята по жёлтому не читается, а цвет элемента в этой системе
   зависит от плоскости, на которой он лежит, а не от набора. */
.on-sun .leader {
  background-image: radial-gradient(
    circle,
    var(--color-sun-line) 1px,
    transparent 1px
  );
}

/* Найденное слово подсвечивается мятой, а не жёлтым: жёлтый в этой
   системе помечает выгоду, и подсветка совпадения отняла бы у него
   единственную работу. */
.price-scope mark {
  background-color: var(--color-mint-200);
  color: inherit;
  border-radius: 4px; /* ступень focus из шкалы скруглений */
  padding: 0 2px;
}

/* Фильтр «детям» держат radio и :has — без JS он работает так же,
   как с ним. Раздел, в котором не осталось ни одной детской позиции,
   уходит целиком: пустая шапка над пустым списком выглядела бы
   поломкой. В Safari ниже 15.4 и Firefox ниже 121 :has не поддержан —
   там фильтр не сработает, но все 97 позиций останутся видимыми,
   то есть страница деградирует в полный прайс, а не в пустоту. */
.price-scope:has(#pf-kids:checked) [data-row]:not([data-kids]),
.price-scope:has(#pf-kids:checked) [data-group]:not(:has([data-kids])) {
  display: none;
}

/* Текущий раздел в списке слева. Отметку ставит scrollspy из dp.js
   атрибутом `aria-current` — тем же, которым шапка отмечает текущую
   страницу, так что состояние читает и скринридер, а не только глаз.
   Меняется фон, а не фон и текст сразу через классы состояния: правило
   одного цвета в состоянии. Цвет текста здесь принадлежит самому
   правилу, а не утилите на элементе, поэтому конфликта не возникает. */
.price-scope [aria-current='true'] {
  background-color: var(--color-mint-200);
  color: var(--color-brand-700);
  font-weight: 500;
}

/* Приглушения пунктов, выпавших из выдачи, здесь больше нет. Оно читалось
   блокировкой: чип «Детям» гасил девять разделов из тринадцати, и прайс
   выглядел запертым. Список слева — карта клиники, он всегда полный
   и всегда кликабельный; пункт скрытого раздела сам снимает отбор
   (см. dp.js). */

/* Поиск прячет строки своим атрибутом, а не инлайновым display: иначе
   он и фильтр выше спорили бы за одно и то же свойство, и снятие запроса
   возвращало бы на экран строки, скрытые чипом «Детям». */
.price-scope [data-off] {
  display: none;
}

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

.lift {
  transition: transform 200ms ease-out, box-shadow 200ms ease-out;
  will-change: transform;
}
@media (hover: hover) {
  .lift:hover {
    transform: translateY(-4px) scale(1.012);
    box-shadow: 0 18px 38px -22px rgb(18 51 42 / 0.42),
      0 4px 10px -6px rgb(18 51 42 / 0.18);
  }
}

/* ── Скип-ссылка ───────────────────────────────────────────────── */

.skip {
  position: absolute;
  left: 16px;
  top: -96px;
  z-index: 100;
  padding: 14px 24px;
  border-radius: 999px;
  background-color: var(--color-brand-700);
  color: var(--color-paper-elev);
  transition: top 150ms ease-out;
}
.skip:focus {
  top: 16px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ── Возрастная линейка: сегмент шириной в длину отрезка ───────── */

.ruler [role='tab'] {
  transition: background-color 180ms ease-out, color 180ms ease-out;
}

/* ── Паттерн под прайсом ───────────────────────────────────────── */

.dots {
  background-image: radial-gradient(
    var(--color-mint-300) 1.5px,
    transparent 1.5px
  );
  background-size: 26px 26px;
  background-position: -8px -8px;
}

/* ── Аккордеон: перекрываем дефолт Flowbite целиком ────────────── */

.acc button[data-accordion-target] {
  transition: background-color 180ms ease-out;
}
/* Flowbite снимает и вешает `rotate-180` сам: в Tailwind 4 это свойство
   `rotate`, а не `transform`, — переход обязан назвать оба. */
.acc [data-accordion-icon] {
  transition: rotate 220ms ease-out, transform 220ms ease-out;
}

/* ── Фильтр тем в вопросах ─────────────────────────────────────
   Показ держат обычные radio, а не скрипт: без JS видны все десять
   вопросов, а не ни одного. Чипу «Все» правило не нужно — она и есть
   состояние по умолчанию, когда ни одно из четырёх не выбрано. */

.faq-scope:has(#faq-fin:checked) [data-cat]:not([data-cat='fin']),
.faq-scope:has(#faq-treat:checked) [data-cat]:not([data-cat='treat']),
.faq-scope:has(#faq-kids:checked) [data-cat]:not([data-cat='kids']),
.faq-scope:has(#faq-guar:checked) [data-cat]:not([data-cat='guar']) {
  display: none;
}

/* Группа вопросов — один лист, а не стопка отдельных плашек: крупные углы
   принадлежат ей, а не первому и последнему пункту. Так фильтр не может
   оставить прямой край — скрытый пункт просто выпадает из потока, и щель
   между соседями (gap по цвету линии) схлопывается сама.
   Вариант со скруглением у крайних пунктов здесь не работает:
   :first-of-type считает по типу элемента и скрытые не пропускает. */
.faq-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow: hidden;
  border-radius: var(--radius-card);
  background-color: var(--color-line);
}

/* ── Ведомость цен на Детской ──────────────────────────────────
   Лист прайса на детской странице держит все суммы в одном правом
   краю, и строка обязана оставаться однострочной. На 19px имя услуги
   ниже 640px переносилось у двух третей позиций: сумма отрывалась
   от названия на строку вверх, а лист вырастал на треть. Ступень
   ниже (16 / 19 вместо 19 / 21) укладывает почти все имена в строку.
   Ступени заданы здесь, а не вариантом в разметке: `t-lg` и `t-xl` —
   классы этого файла, и `sm:`-вариант Tailwind их не порождает. */

@media (max-width: 639px) {
  .price-sheet dt {
    font-size: calc(16px * var(--k));
    line-height: 1.4;
  }
  .price-sheet dd {
    font-size: calc(19px * var(--k));
  }
}

/* ── Линейка профилей врачей (Главная) ─────────────────────────
   Сегменты названы работой («Коронки и протезы»), а не должностью:
   посетитель ищет свою нужду, а не строку из диплома. Переключение
   держат обычные radio и `:has()`, как фильтр вопросов и прайса, —
   скрипта здесь нет, и без JS панель остаётся открытой, а не пустой.

   Flowbite сюда не позван намеренно: его вкладки ставят серые дефолты
   `bg-gray-100 text-gray-900` и требуют перечисления состояний
   в data-атрибутах, а radio даёт то же поведение разметкой и клавиатурой
   (стрелки внутри группы) бесплатно. */

/* Скругления живут в разметке, а не здесь: правила этого файла лежат вне
   слоёв Tailwind и потому бьют утилиты при любой специфичности. С
   `border-radius` в `.prof-tab` наружные `rounded-tl-max` не применялись
   вовсе, и группа теряла крупные углы — все четыре сегмента выходили
   по 6px. Свойства, у которых в разметке есть утилита, сюда не пишем. */
.prof-tab {
  display: block;
  cursor: pointer;
  padding: 16px 12px;
  text-align: center;
  background-color: var(--color-paper-elev);
  color: var(--color-ink-600);
  font-size: calc(16px * var(--k));
  line-height: 1.4;
  transition: background-color 160ms ease-out, color 160ms ease-out;
}
.prof-tab:hover {
  background-color: var(--color-mint-200);
}
.prof-tab:has(input:checked) {
  background-color: var(--color-brand-700);
  color: var(--color-paper-elev);
  font-weight: 500;
}
/* Обводка на самом сегменте: радиокнопка спрятана `sr-only`, и её
   собственное кольцо фокуса стояло бы в углу экрана, а не на кнопке. */
.prof-tab:has(input:focus-visible) {
  outline: 2px solid var(--color-brand-700);
  outline-offset: 3px;
}

.prof-scope [data-prof-panel] {
  display: none;
}
.prof-scope:has(#prof-1:checked) [data-prof-panel='1'],
.prof-scope:has(#prof-2:checked) [data-prof-panel='2'],
.prof-scope:has(#prof-3:checked) [data-prof-panel='3'],
.prof-scope:has(#prof-4:checked) [data-prof-panel='4'],
.prof-scope:has(#prof-5:checked) [data-prof-panel='5'] {
  display: block;
}

/* Появление секций живёт целиком в dp.js: без него ничего не скрыто. */

/* ── Реестр документов («Сведения») ────────────────────────────
   Строка документа — ссылка во всю ширину листа, а не подчёркнутое
   слово внутри абзаца: цель нажатия здесь высотой в строку, и попасть
   в неё пальцем обязано быть проще, чем промахнуться.

   Стрелка отъезжает по диагонали, в ту же сторону, куда смотрит:
   единственное движение раздела и единственный отклик, который
   отличает выложенный документ от строки «Готовится». Сдвиг задан
   через translate, а не transform: сама стрелка уже повёрнута
   утилитой -rotate-45, и transform в наведении стёр бы поворот. */
.doc-row svg {
  transition: translate 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.doc-row:hover svg,
.doc-row:focus-visible svg {
  translate: 3px -3px;
}

/* Текущий раздел в оглавлении документов. То же правило, что у разделов
   прайса, и тот же атрибут: отметку ставит scrollspy из dp.js, читает
   её и глаз, и скринридер. Без скрипта оглавление остаётся обычными
   якорными ссылками — оно и есть его основная работа. */
[data-docs-nav] [aria-current='true'] {
  background-color: var(--color-mint-200);
  color: var(--color-brand-700);
  font-weight: 500;
}

/* ── Уважение к настройке пользователя ─────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── Док связи ─────────────────────────────────────────────────
   Кнопка в углу и панель, которая из неё вырастает. Разметка —
   tools/parts/dock.html, поведение — блок «Док связи» в dp.js.

   Фигура одна на оба элемента (силуэт зуба, путь #dp-tooth-d),
   и переход между ними — не своя анимация, а масштаб той же фигуры.
   Поэтому размеры коробок здесь заданы числами, а не «на глаз»:
   поле содержимого панели считается от контура — 34px по бокам,
   52px сверху и 170px снизу, ровно до развилки корней (390 из 560).
   Меняется путь — пересчитывается и поле, иначе строки поедут
   на скруглённые плечи коронки. */

.dock {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 45; /* под модалками (50), над липкой шапкой (40) */
  display: flex;
  /* Ни появления, ни ухода: док стоит в углу постоянно — и на первом
     экране, и у секции записи. Прежде он выходил после 240px прокрутки
     и прятался над формой; оба правила сняты 22 августа 2026 по просьбе
     клиники. Кнопка связи, которую надо сначала найти прокруткой,
     кнопкой связи работает наполовину. */
}
.dock[hidden] {
  display: none;
}

/* Док уступает место секции записи и подвалу. Над ними он не добавляет
   ничего — телефон, запись, Telegram, MAX и карта там уже стоят строками,
   — зато перекрывает их собой: на 390px кнопка ложилась на последние
   цифры телефона в секции записи и на итог примерной сметы. Атрибут
   ставит dp.js по видимости этих блоков; без скрипта док просто стоит
   в углу, как стоял.

   Уходит и возвращается сама кнопка, а не контейнер: точка отсчёта —
   правый нижний угол, тот же, из которого растёт панель. Движение одно
   на весь док, второго не заводим. */
.dock-btn {
  transform-origin: 100% 100%;
  transition: opacity 200ms ease-out,
    transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}
.dock[data-tucked] .dock-btn {
  opacity: 0;
  transform: scale(0.72) translateY(10px);
  pointer-events: none;
}


.dock-fit {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

/* ── Панель ───────────────────────────────────────────────────── */

/* Масштаб раскрытия и масштаб посадки перемножаются в одном
   transform: иначе низкое окно и анимация роста дрались бы за одно
   свойство. Точка отсчёта — правый корень, он же место кнопки:
   панель растёт оттуда, куда человек нажал. */
.dock-panel {
  --fit: 1;
  position: relative;
  width: 348px;
  height: 560px;
  transform-origin: 88% 100%;
  transform: scale(calc(0.14 * var(--fit)));
  opacity: 0;
  transition: transform 340ms cubic-bezier(0.16, 1, 0.3, 1), opacity 180ms ease-out;
}
.dock-panel.is-open {
  transform: scale(var(--fit));
  opacity: 1;
}

.dock-shape {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.dock-panel > .dock-shape {
  fill: var(--color-paper-elev);
  stroke: var(--color-line);
  stroke-width: 1;
  filter: drop-shadow(0 22px 40px rgb(18 51 42 / 0.2))
    drop-shadow(0 4px 10px rgb(18 51 42 / 0.1));
}

/* Поле содержимого задано долями, а не пикселями, и это не манера
   записи: доли считаны от того же viewBox 320×560, в котором нарисован
   контур (34 / 52 / 170 единиц), и держатся за него при любом размере
   панели. В пикселях они разъехались бы с фигурой на первом же
   изменении ширины — строки уехали бы на скруглённые плечи коронки. */
.dock-body {
  position: absolute;
  top: 9.3%;
  right: 10.6%;
  bottom: 30.4%;
  left: 10.6%;
  display: flex;
  flex-direction: column;
  /* Заголовка в панели нет, и содержимое стало короче поля коронки.
     По верхнему краю оно жалось бы к изгибу, а снизу оставалась бы
     пустая полка над развилкой корней; по центру обе кромки ровные. */
  justify-content: center;
}

.dock-hours {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  /* На ступень ниже .t-sm: в коронке 15px переносило строку
     «сегодня до 20:00» на второй ряд, и та отъедала место у пунктов. */
  font-size: calc(14px * var(--k));
  line-height: 1.35;
  color: var(--color-ink-400);
}

/* Точка повторяет то, что говорит строка: скрипт ставит на док
   data-shut, когда клиника закрыта. Зелёный огонёк над словами
   «сейчас закрыто» был бы вторым голосом, который врёт. */
.dock-dot {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  border-radius: 999px;
  background-color: var(--color-mint-500);
}
.dock[data-shut] .dock-dot {
  background-color: var(--color-line-input);
}

.dock-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 12px;
}

/* Список сужается вслед за зубом. Ниже середины коронка уходит внутрь
   почти на тридцать единиц, и прямоугольный столбец строк вылезал бы
   за контур последними двумя пунктами. Это не приём оформления: то же
   поле содержимого, посчитанное там, где фигура уже сузилась. */
.dock-list > li:nth-child(4) {
  margin: 0 5px;
}
.dock-list > li:nth-child(5) {
  margin: 0 16px;
}

.dock-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 22px;
  background-color: var(--color-mint-100);
  color: var(--color-ink-900);
  transition: background-color 160ms ease-out, transform 160ms ease-out;
}
@media (hover: hover) {
  .dock-row:hover {
    background-color: var(--color-mint-200);
    transform: translateX(-3px);
  }
}

.dock-row-key {
  min-height: 52px;
  background-color: var(--color-brand-700);
  color: var(--color-paper-elev);
}
@media (hover: hover) {
  .dock-row-key:hover {
    background-color: var(--color-brand-800);
  }
}

.dock-row-t {
  display: block;
  font-size: calc(17px * var(--k));
  font-weight: 500;
  line-height: 1.2;
}

/* Телефонов у клиники два, и в коронке они делят одну клавишу: шестым
   пунктом список выходит из фигуры — поле содержимого посчитано от
   контура зуба, а не от числа строк. Номера равны, поэтому набраны
   одним кеглем и разделены только просветом. */
.dock-row-pair .dock-row-t + .dock-row-t {
  margin-top: 4px;
}

.dock-row-s {
  display: block;
  margin-top: 3px;
  font-size: calc(13.5px * var(--k));
  line-height: 1.25;
  color: var(--color-ink-400);
}
.dock-row-key .dock-row-s {
  color: var(--color-mint-300);
}

/* Строки въезжают следом за фигурой, лесенкой по 34мс. Задержка
   стоит только на открытии: при закрытии панель уходит целиком,
   и лесенка наоборот выглядела бы поломкой. */
.dock-body > p,
.dock-list > li {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 200ms ease-out, transform 200ms ease-out;
}
.dock-panel.is-open .dock-body > p,
.dock-panel.is-open .dock-list > li {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 120ms;
}
.dock-panel.is-open .dock-list > li:nth-child(1) { transition-delay: 154ms; }
.dock-panel.is-open .dock-list > li:nth-child(2) { transition-delay: 188ms; }
.dock-panel.is-open .dock-list > li:nth-child(3) { transition-delay: 222ms; }
.dock-panel.is-open .dock-list > li:nth-child(4) { transition-delay: 256ms; }
.dock-panel.is-open .dock-list > li:nth-child(5) { transition-delay: 290ms; }

/* ── Кнопка ───────────────────────────────────────────────────── */

.dock-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

/* Подпись словами: голый силуэт в углу читается украшением,
   а не управлением. На узком экране места на неё нет — там
   кнопку называет aria-label. */
.dock-btn-cap {
  display: none;
  padding: 9px 16px;
  border: 1px solid var(--color-line);
  border-radius: 999px;
  background-color: var(--color-paper-elev);
  color: var(--color-ink-900);
  font-size: calc(15px * var(--k));
  font-weight: 500;
  line-height: 1;
  box-shadow: 0 10px 22px -14px rgb(18 51 42 / 0.5);
  transition: opacity 160ms ease-out, transform 160ms ease-out;
}
@media (min-width: 768px) {
  .dock-btn-cap {
    display: block;
  }
}
.dock-btn[aria-expanded='true'] .dock-btn-cap {
  opacity: 0;
  transform: translateX(8px);
  pointer-events: none;
}

.dock-btn-tooth {
  position: relative;
  display: block;
  width: 56px;
  height: 72px;
  transition: transform 200ms ease-out;
}
@media (hover: hover) {
  .dock-btn:hover .dock-btn-tooth {
    transform: translateY(-3px);
  }
}
/* На узком экране док занимает меньше: 56×72 с полями по 20px забирали
   в углу полосу 96×92 — на 373px это четверть строки. Меньше 44px
   кнопке нельзя, и 48×62 её не нарушают. */
@media (max-width: 439px) {
  .dock {
    right: 12px;
    bottom: 12px;
  }
  .dock-btn-tooth {
    width: 48px;
    height: 62px;
  }
}

.dock-btn .dock-shape {
  fill: var(--color-brand-700);
  filter: drop-shadow(0 12px 24px rgb(18 51 42 / 0.34))
    drop-shadow(0 2px 6px rgb(18 51 42 / 0.16));
  transition: fill 180ms ease-out;
}
@media (hover: hover) {
  .dock-btn:hover .dock-shape {
    fill: var(--color-brand-800);
  }
}

/* Знак стоит в коронке, а не в середине коробки: середина у зуба
   приходится на корни. Точки и крест лежат в одной ячейке сетки
   и меняются поворотом друг через друга. */
.dock-glyph {
  position: absolute;
  top: 13px;
  left: 0;
  display: grid;
  width: 100%;
  color: var(--color-paper-elev);
}
.dock-glyph > svg {
  grid-area: 1 / 1;
  width: 26px;
  height: 26px;
  justify-self: center;
  transition: opacity 180ms ease-out, transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
}
.dock-cross {
  opacity: 0;
  transform: rotate(-90deg) scale(0.6);
}
.dock-btn[aria-expanded='true'] .dock-dots {
  opacity: 0;
  transform: rotate(90deg) scale(0.6);
}
.dock-btn[aria-expanded='true'] .dock-cross {
  opacity: 1;
  transform: rotate(0) scale(1);
}

/* Посадка в низкое и узкое окно. Уменьшается только панель:
   кнопка остаётся полного размера, потому что по ней попадают
   пальцем, а 56×72 — это уже минимум цели касания. */
@media (max-height: 760px) {
  .dock-panel {
    --fit: 0.86;
  }
}
@media (max-height: 660px) {
  .dock-panel {
    --fit: 0.74;
  }
}
@media (max-width: 400px) {
  .dock {
    right: 14px;
    bottom: 14px;
  }
  .dock-panel {
    --fit: 0.88;
  }
}
@media (max-width: 400px) and (max-height: 720px) {
  .dock-panel {
    --fit: 0.8;
  }
}

/* ── Версия для слабовидящих ───────────────────────────────────
   Включается кнопкой-глазом в шапке, живёт на четырёх атрибутах
   у <html> и запоминается в localStorage (assets/js/bvi.js).

       data-bvi              версия включена
       data-bvi-scheme       bw | wb | bc — пара «текст на фоне»
       data-bvi-size         m | l | xl  — множитель кегля
       data-bvi-track        n | m | l   — межбуквенный интервал
       data-bvi-img          on | off    — снимки

   Схема не перекрашивает элементы по одному, а переобъявляет саму
   палитру: Tailwind разложил тему в :root как --color-*, и утилиты
   в разметке читают значения оттуда. Поэтому шесть страниц, дюжина
   секций и два десятка компонентов переходят в контраст одним
   блоком переменных — и переходят целиком, без пропущенной карточки
   где-нибудь на «Ценах».

   Работает это потому, что палитра делится ровно на две роли:
   всё, что заливка (бумага, мята, лимон), становится фоном схемы;
   всё, что тушь, линия или действие, — её текстом. Роли меняются
   местами сами, когда плоскость тёмная: подвал залит brand-800
   и набран mint-300, то есть «текстом по фону» — после подстановки
   это по-прежнему текст по фону, только уже белым по чёрному.
   Ни одного отдельного правила на тёмные секции не нужно.

   Единственные hex-значения этого файла — здесь, и это не исключение
   из правила «палитра объявлена один раз», а вторая палитра: три пары
   ГОСТ Р 52872 брендом не управляются и в @theme им делать нечего.
   Объявлены они переменными, а не вписаны в правила, потому что
   образцы в панели обязаны показывать ровно те цвета, которые
   включатся. */

:root {
  --bvi-bw-bg: #ffffff;
  --bvi-bw-fg: #000000;
  --bvi-wb-bg: #000000;
  --bvi-wb-fg: #ffffff;
  --bvi-bc-bg: #9dd1ff;
  --bvi-bc-fg: #063462;
}

/* Подстановка палитры. Список полный: пропущенная переменная —
   это элемент, оставшийся в зелёном посреди чёрно-белой страницы. */
html[data-bvi-scheme] {
  --color-paper: var(--bvi-bg);
  --color-paper-tint: var(--bvi-bg);
  --color-paper-elev: var(--bvi-bg);
  --color-mint-100: var(--bvi-bg);
  --color-mint-200: var(--bvi-bg);
  --color-mint-300: var(--bvi-bg);
  --color-sun-200: var(--bvi-bg);
  --color-sun-400: var(--bvi-bg);
  --color-alert-bg: var(--bvi-bg);

  --color-ink-900: var(--bvi-fg);
  --color-ink-600: var(--bvi-fg);
  --color-ink-400: var(--bvi-fg);
  --color-brand-600: var(--bvi-fg);
  --color-brand-700: var(--bvi-fg);
  --color-brand-800: var(--bvi-fg);
  --color-mint-500: var(--bvi-fg);
  --color-sun-ink: var(--bvi-fg);
  --color-sun-ink-2: var(--bvi-fg);
  --color-sun-line: var(--bvi-fg);
  --color-alert: var(--bvi-fg);
  --color-line: var(--bvi-fg);
  --color-line-input: var(--bvi-fg);
  --color-dark-backdrop: var(--bvi-fg);
}

html[data-bvi-scheme='bw'] {
  --bvi-bg: var(--bvi-bw-bg);
  --bvi-fg: var(--bvi-bw-fg);
}
html[data-bvi-scheme='wb'] {
  --bvi-bg: var(--bvi-wb-bg);
  --bvi-fg: var(--bvi-wb-fg);
}
html[data-bvi-scheme='bc'] {
  --bvi-bg: var(--bvi-bc-bg);
  --bvi-fg: var(--bvi-bc-fg);
}

/* Кегль. Множитель тот же, что держит всю типографику файла. */
html[data-bvi-size='l'] {
  --k: 1.25;
}
html[data-bvi-size='xl'] {
  --k: 1.5;
}
html[data-bvi] {
  scroll-padding-top: calc(112px * var(--k));
}

/* Антиква уходит. Prata — дидон с волосяными соединительными штрихами:
   на 150% они не растут вместе с основными, а остаются нитью и
   пропадают первыми. Гротеск в полужирном читается там, где она
   рассыпается, и это тот случай, когда функция важнее почерка. */
html[data-bvi] {
  --font-display: var(--font-sans);
}
html[data-bvi] :where(.h1, .h2, .h3, .h1-hero, .card-t, .seal-num, .price-hero, .quote, .font-display) {
  font-weight: 600;
}

/* Межбуквенный интервал. Отрицательный трекинг заголовков снимается
   всегда, даже на «обычном» шаге: -0.03em на 56px это слипшиеся
   «ЛИ» и «ТЬ». `:where(*)` весит ноль, весь селектор — (0,1,1),
   и потому перебивает и классы файла, и утилиты tracking-* разметки,
   не трогая при этом само тело: у него правило специфичнее. */
html[data-bvi] {
  --bvi-track: normal;
}
html[data-bvi-track='m'] {
  --bvi-track: 0.045em;
}
html[data-bvi-track='l'] {
  --bvi-track: 0.09em;
}
html[data-bvi] body {
  letter-spacing: var(--bvi-track);
}
html[data-bvi] :where(*) {
  letter-spacing: inherit;
}
html[data-bvi] :where(.h1, .h2, .h3, .h1-hero) {
  text-wrap: pretty;
}

/* Иконки становятся одноцветными. Двухцветная пара собрана из подложки
   и рисунка (см. tools/tw-pages.mjs), и в контрастной схеме подложка
   получает либо цвет фона, либо цвет рисунка — в первом случае она
   исчезает, во втором заливает иконку сплошным пятном. Поэтому
   подложка снимается явно, а рисунок берёт цвет текста рядом:
   на любой плоскости, светлой или тёмной, иконка оказывается того же
   цвета, что подпись под ней. */
html[data-bvi] svg > path.fill-mint-300,
html[data-bvi] svg > path.fill-mint-500,
html[data-bvi] svg > path.fill-paper,
html[data-bvi] svg > path.fill-sun-200 {
  fill: none;
}
html[data-bvi] svg > path.fill-brand-700,
html[data-bvi] svg > path.fill-ink-900,
html[data-bvi] svg > path.fill-paper-elev,
html[data-bvi] svg > path.fill-sun-ink {
  fill: currentColor;
}

/* Логотип — растровая марка, и подставить ей цвет нельзя. На чёрном
   фоне тёмный парусник пропадает целиком, поэтому там он печатается
   белым силуэтом: узнаваемость формы важнее оттенка, которого
   в контрастной схеме всё равно нет. */
html[data-bvi-scheme='wb'] header svg image {
  filter: brightness(0) invert(1);
}

/* Границы там, где их держал только оттенок. Карточка на белом среди
   белого — не потеря: контрастная схема и есть плоский документ.
   А вот пилюля и сегмент возраста без границы перестают быть
   элементами управления, и это уже поломка. */
html[data-bvi] :where(.eyebrow, .prof-tab, .ruler [role='tab'], [data-accordion-target]) {
  border: 1px solid var(--color-line);
}
html[data-bvi] .ghost-num {
  display: none;
}

/* Снимки. Отключённая иллюстрация не оставляет пустой плашки: <img>
   здесь всегда сам элемент сетки, и сетка смыкается. На его месте
   скрипт печатает alt — человеку, который убрал картинки ради
   спокойного экрана, описание нужнее пустоты. */
html[data-bvi-img='off'] img {
  display: none;
}
.bvi-alt {
  border: 1px solid var(--color-line);
  border-radius: 10px;
  padding: 14px 16px;
  font-size: calc(15px * var(--k));
  line-height: 1.5;
  color: var(--color-ink-600);
}

/* ── Панель настроек ───────────────────────────────────────────
   Раскрывается под шапкой второй свёрнутой областью — тем же приёмом,
   что и мобильное меню, и той же деталью шапки, что кнопка-трубка.
   Высоту раскрытия ставит скрипт, измеряя содержимое: угаданный
   max-height обрезал бы панель на крупном шрифте (на 150% она вдвое
   выше, чем на 100%). Пробовалось и `grid-template-rows: 0fr → 1fr` —
   приём известный, но переход к `1fr` в контейнере с автоматической
   высотой воспроизводимо застревал на нуле, и панель открывалась
   пустой полосой в 1px. Измеренная высота такого состояния не имеет:
   после перехода скрипт ставит `auto`, и панель следует за кеглем
   и шириной окна сама.
   Закрытая панель невидима по visibility, поэтому её органы
   управления не ловят табуляцию. */

.bvi-panel {
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  border-bottom: 1px solid transparent;
  background-color: var(--color-mint-100);
  transition: height 300ms cubic-bezier(0.16, 1, 0.3, 1),
    visibility 0s linear 300ms;
}
.bvi-panel.is-open {
  visibility: visible;
  border-bottom-color: var(--color-line);
  transition: height 300ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}

/* Высота панели ограничена окном, и это не про эстетику. Панель лежит
   внутри sticky-шапки: прилипшая шапка выше окна не прокручивается —
   её низ недостижим в принципе. На телефоне раскрытая панель занимала
   977px при экране в 700–800, и «Снимки» вместе с кнопкой возврата
   оставались за нижним краем: человек, включивший версию, выйти из неё
   не мог. Прокручивается теперь не шапка, а сама панель.
   Точное значение ставит скрипт в --bvi-max: высота шапки не постоянна,
   на крупном кегле полоса выше, и отмерять от 88px нельзя. Строки с vh
   и dvh остаются запасом на случай, если скрипт не отработал: dvh
   перекрывает vh там, где поддержан, — vh в мобильных браузерах считает
   от окна с убранной адресной строкой и отдаёт высоту больше видимой.
   Меряет скрипт ту же внутреннюю часть (bvi.js, height()), так что
   ограничение попадает и в анимацию раскрытия.
   overscroll-behavior не даёт докрутке уводить страницу под панелью. */
.bvi-panel > div {
  max-height: calc(100vh - 88px);
  max-height: calc(100dvh - 88px);
  max-height: var(--bvi-max, calc(100dvh - 88px));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Число колонок считается и от ширины, и от кегля. На 125% четыре
   группы в ряд наезжают друг на друга, а auto-fit раскладывал их
   3 + 1 — строка из одной группы читается остатком, а не строкой.
   Поэтому потолок на крупном шрифте — две колонки: 2 + 2. */
.bvi-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 640px) {
  .bvi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 1280px) {
  .bvi-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  html[data-bvi-size='l'] .bvi-grid,
  html[data-bvi-size='xl'] .bvi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Док связи на время настройки уходит. Он висит в правом нижнем углу
   поверх всего, и это правильно для кнопки связи — но на узком экране
   раскрытая панель достаёт до низа окна, и зуб накрывал собой клавишу
   «Большой». Настройка чтения, часть которой закрыта плавающей кнопкой,
   хуже, чем кнопка связи, которой минуту не видно. */
html:has(.bvi-panel.is-open) .dock {
  display: none;
}

.bvi-legend {
  display: block;
  font-size: calc(15px * var(--k));
  font-weight: 500;
  line-height: 1.3;
  color: var(--color-ink-900);
}

/* Сегменты равной ширины: подписи разной длины не должны давать
   клавиши разного размера — по ним попадают, а не читают их. */
.bvi-seg {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 6px;
}

.bvi-key {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-height: 104px;
  padding: 12px 6px;
  overflow-wrap: anywhere;
  border: 1px solid var(--color-line);
  border-radius: 10px;
  background-color: var(--color-paper-elev);
  color: var(--color-ink-600);
  font-size: calc(13.5px * var(--k));
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  transition: background-color 160ms ease-out, color 160ms ease-out,
    border-color 160ms ease-out;
}
.bvi-key:hover {
  background-color: var(--color-mint-200);
  border-color: var(--color-mint-500);
}
.bvi-key:has(input:checked) {
  background-color: var(--color-brand-700);
  border-color: var(--color-brand-700);
  color: var(--color-paper-elev);
  font-weight: 500;
}
.bvi-key:has(input:focus-visible) {
  outline: 3px solid var(--color-brand-700);
  outline-offset: 3px;
}

/* На телефоне сетка идёт одной колонкой, и четыре группы по 104px
   складывались в панель на два экрана. Клавиша ужимается до 88 —
   это всё ещё вдвое больше пальца и хватает образцу с подписью, —
   а зазор между группами до 20. Прокрутки внутри панели остаётся
   меньше половины экрана. */
@media (max-width: 639px) {
  .bvi-grid {
    gap: 20px;
  }
  .bvi-key {
    min-height: 88px;
  }
}

/* Образец показывает ровно то, что включает клавиша, и потому набран
   вне общей шкалы: множитель --k на него не действует, иначе три
   ступени кегля выглядели бы одинаково крупными на крупном шрифте
   и переставали бы отличаться друг от друга. */
.bvi-demo {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  font-weight: 500;
  line-height: 1;
}
.bvi-demo-16 {
  font-size: 16px;
}
.bvi-demo-21 {
  font-size: 21px;
}
.bvi-demo-27 {
  font-size: 27px;
}
.bvi-demo-t0 {
  font-size: 16px;
  letter-spacing: normal;
}
.bvi-demo-t1 {
  font-size: 16px;
  letter-spacing: 0.045em;
}
.bvi-demo-t2 {
  font-size: 16px;
  letter-spacing: 0.09em;
}

/* Образец схемы — буква в своей паре цветов. Обводка обязательна:
   белый образец на белой карточке иначе не виден вовсе, а именно
   он и означает «чёрным по белому». */
.bvi-swatch {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 30px;
  border: 1px solid var(--color-line);
  border-radius: 8px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1;
}
.bvi-sw-bw {
  background-color: var(--bvi-bw-bg);
  color: var(--bvi-bw-fg);
}
.bvi-sw-wb {
  background-color: var(--bvi-wb-bg);
  color: var(--bvi-wb-fg);
}
.bvi-sw-bc {
  background-color: var(--bvi-bc-bg);
  color: var(--bvi-bc-fg);
}
.bvi-key:has(input:checked) .bvi-swatch {
  border-color: var(--color-paper-elev);
}

/* Кнопка-глаз в шапке. Включённая версия видна по самой кнопке:
   иначе на странице, где человек уже привык к контрасту, нечем
   отличить «включено» от «выключено». */
.bvi-btn[aria-expanded='true'],
html[data-bvi] .bvi-btn {
  background-color: var(--color-brand-700);
  border-color: var(--color-brand-700);
  color: var(--color-paper-elev);
}

/* Длинное слово на крупном кегле. Колонка сетки шириной в свою долю
   не умеет быть уже своего содержимого: «Генерала Кузнецова, 12» набрано
   через неразрывные пробелы, на 150% этот кусок шире колонки, и блок
   выходил за неё — прямо на строку про налоговый вычет справа. Двух
   правил хватает на весь сайт: элемент сетки получает право сжиматься,
   слово — право переноситься внутри себя. Ни то, ни другое не нужно
   на обычном кегле и потому не трогает обычную версию. */
html[data-bvi] :where(.flex, .grid) > * {
  min-width: 0;
}
/* .h1-hero из списка исключён (правка 24.08.2026): единственное длинное
   «слово» в нём — адрес с номером дома, и break-word рвал сам номер
   на «1» и «2». Номер держит nowrap в разметке, длинных слов, ради
   которых правило писалось, в этой ступени нет. */
html[data-bvi] :where(p, li, dd, dt, a, span, .h1, .h2, .h3, .card-t, .lead) {
  overflow-wrap: break-word;
}

/* Ведомость гарантий в режиме слабовидящих распадается на один столбец.
   Колонка знаков 200px посчитана под обычные 34–38px антиквы: на ступени
   xl тот же «2 из 100» набирается 57px и занимает 257px — знак ложился
   на описание справа ровно так же, как до правки 24.08.2026, просто
   на другом кегле. Расширять колонку под самый крупный шаг нельзя:
   на обычном кегле рядом со словом «1 год» повисло бы полполосы пустоты.
   Столбец решает оба случая — знак и описание встают друг под друга,
   как это уже происходит ниже sm. row-gap здесь, а не margin на dd:
   у dd стоит sm:mt-0, и на этой ширине отступа у него нет. */
html[data-bvi] .seal-row {
  grid-template-columns: 1fr;
  row-gap: 10px;
}

/* Печать «0 ₽» при отключённых снимках. Она висит на кадре, а кадра
   больше нет: рамка схлопывается до высоты подписи, и печать ложится
   на саму подпись и на заголовок слева. Здесь она перестаёт висеть
   и встаёт в поток под описанием снимка — единственное место, где
   для неё точно есть воздух. Селектор с двумя атрибутами, чтобы
   правило не зависело от порядка строк в этом файле. */
html[data-bvi][data-bvi-img='off'] .seal {
  position: relative;
  inset: auto;
  /* translate, а не transform: Tailwind 4 пишет -translate-y-1/2
     в отдельное свойство translate, и transform: none его не снимает.
     Печать оставалась поднятой на половину своей высоты и ложилась
     на подпись сверху — при том, что в потоке стояла ниже неё. */
  translate: none;
  transform: none;
  width: 176px;
  height: 212px;
  margin-top: 20px;
}

/* Полупрозрачность шапки снимается. bg-paper/95 задуман как лёгкая
   дымка над уезжающим содержимым, но в контрастной схеме сквозь неё
   читается текст под шапкой — прямо за пунктами меню. Пять процентов
   прозрачности не стоят строки, наложенной на строку. */
html[data-bvi] header {
  background-color: var(--color-paper);
}

/* Шапка на крупном кегле. Высота 88px задана числом и на 150% держать
   логотип, номер и «Записаться» перестаёт: строка переносится, а полоса
   растёт под содержимое.

   Селектор именной, а не «первый div внутри шапки»: под header > div
   попадали ещё две области — свёрнутое меню и панель настроек. Панель
   получала от этого min-height: 88px и в закрытом виде оставляла под
   шапкой пустую полосу, а в открытом зажималась в те же 88px, обрезая
   собственные настройки. */
html[data-bvi] .dp-bar {
  height: auto;
  min-height: 88px;
  flex-wrap: wrap;
  padding-top: 10px;
  padding-bottom: 10px;
}

/* Правило подложки для клавиш панели: выбранная клавиша залита хвоей,
   и пара по умолчанию (мята + хвоя) на ней исчезает. В контрастной схеме
   перекраска не нужна и вредна — там иконка уже взяла цвет текста, —
   поэтому правило снято с html[data-bvi]. */
html:not([data-bvi]) .bvi-key:has(input:checked) svg > path.fill-mint-300,
html:not([data-bvi]) .bvi-btn[aria-expanded='true'] svg > path.fill-mint-300 {
  fill: var(--color-brand-800);
}
html:not([data-bvi]) .bvi-key:has(input:checked) svg > path.fill-brand-700,
html:not([data-bvi]) .bvi-btn[aria-expanded='true'] svg > path.fill-brand-700 {
  fill: var(--color-paper-elev);
}

/* ── Марка в шапке на узком экране ─────────────────────────────
   Слово рядом со знаком стоит 133px, знак — 52, кнопки связи,
   глаза и меню — четыре круга по 48 с зазорами, поля контейнера —
   40. Вместе 425px: ниже этой ширины слово выдавливает бургер
   за край экрана, и остаётся один знак. Порог здесь, а не утилитой
   max-[439px]: в браузерной сборке Tailwind произвольный max-вариант
   правила не даёт — класс в разметке есть, медиазапроса в документе
   нет, и слово оставалось на месте. */
@media (max-width: 439px) {
  /* Слово снимается с экрана, но не из документа. Было display: none —
     и вместе со словом пропадало единственное имя ссылки на главную:
     знак рядом стоит с aria-hidden, и читалка ниже 440px объявляла
     ссылку по адресу файла. Теперь слово просто уходит с глаз:
     имя у ссылки есть на любой ширине, а position: absolute снимает
     его и с раскладки — flex-зазор рядом со знаком не появляется. */
  .dp-brand-word {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border-width: 0;
  }
}

/* ── Крупный кегль: что не влезает в свою коробку ──────────────

   Меню. Шапка на 125% в одну строку не собирается, и перенос сам по
   себе её не чинит: без указания порядка настольное меню оставалось
   в первой строке, а блок связи уезжал во вторую, к правому краю,
   мимо всего. Меню уходит целой строкой под логотип — тогда обе
   строки читаются как строки, а не как последствие переноса.
   На узких экранах правило ничего не меняет: там меню и так свёрнуто
   в бургер. */
html[data-bvi] .dp-bar > nav {
  order: 3;
  width: 100%;
  margin-top: 4px;
}
html[data-bvi] .dp-bar > nav > ul {
  flex-wrap: wrap;
}

/* Печать «0 ₽». Она висит слева от снимка и заходит на колонку
   с текстом — запас в 88px рассчитан на обычный кегль. На крупном
   лид дотягивается до неё и уходит под коронку, поэтому печать
   сдвигается на край кадра. Ниже 1024px она и так лежит на снимке. */
@media (min-width: 1024px) {
  html[data-bvi] .seal {
    left: 0;
  }
}

/* Подложечная цифра. В контрастной схеме полутонов нет: «97» под
   строкой «позиций в нашем прайсе» либо совпадает с фоном, либо
   становится чёрным щитом на пол-экрана. В потоке она перестаёт быть
   подложкой и печатается обычным числом — строка без него повисает
   без подлежащего. Абсолютные же цифры на «Детской» и «Акциях»
   повторяют число из соседнего заголовка и снимаются: показывать
   «20» вторым чёрным знаком рядом с «20 минут» незачем. */
html[data-bvi] .ghost-num {
  color: var(--color-ink-900);
  font-size: calc(56px * var(--k));
}
html[data-bvi] .ghost-num.absolute {
  display: none;
}
