/* Zubok & Partners — общая основа сайта
   Требует styles/tokens.css, подключённого раньше.
   Здесь ровно словарь .zp-* из .autopilot/home-variants/interfaces.md, плюс
   одна добавка ремонта формы — .zp-fields, сетка ряда полей внутри формы (§10).
   Постраничный CSS живёт под своим корневым классом (у главной — .v-chapters)
   и .zp-* глобально не переопределяет.

   Три правила, из-за которых страница выглядит референсом, а не «тёмной темой»:
   заливок нет — карточку отделяет волосяная линия, её фон равен фону страницы;
   теней нет — box-shadow только у фокусного кольца;
   радиус 2 px — скругления принадлежали рамке презентации, а не макету. */

/* ═══ 1. Сброс и корпус страницы ═══════════════════════════════════════ */

*,
*::before,
*::after { box-sizing: border-box; }

/* Синтетики нет нигде и по умолчанию. Libre Caslon Display существует только
   в прямом 400: попроси у него 500 или курсив — браузер дорисует их сам,
   размазав контур. Запрет стоит на корне, чтобы это не могло вернуться
   ни одной новой строкой стиля. Настоящие начертания (Archivo 500, курсив
   Libre Caslon Text) правилом не затрагиваются. */
html {
  font-synthesis-weight: none;
  font-synthesis-style: none;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: var(--header-h);
  scrollbar-color: var(--ink-600) var(--ink-900);
  scrollbar-width: thin;
}

body {
  margin: 0;
  background-color: var(--ink-800);
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img,
svg,
video,
canvas { display: block; max-width: 100%; }

img { height: auto; }

button,
input,
select,
textarea { font: inherit; color: inherit; }

/* Поверхности, которые рисует браузер, — тоже часть системы. */
::selection {
  background-color: var(--brass-500);
  color: var(--ink-900);
}

:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ═══ 2. Служебное ═════════════════════════════════════════════════════ */

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

.zp-skip {
  position: fixed;
  z-index: 300;
  inset-block-start: var(--s-4);
  inset-inline-start: var(--s-4);
  padding: var(--s-3) var(--s-5);
  border: var(--border) solid var(--brass-500);
  border-radius: var(--radius);
  background-color: var(--ink-900);
  color: var(--brass-400);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  translate: 0 calc(-100% - var(--s-8));
  transition: translate var(--dur-hover) var(--ease);
}

.zp-skip:focus { translate: 0 0; }

/* ═══ 3. Контейнер и мера строки ═══════════════════════════════════════ */

/* ≥1440 → контент 1320 при полях 60; ниже поля сужаются по брейкпоинтам (2.3). */
.zp-container {
  width: 100%;
  max-width: calc(var(--container) + var(--margin) * 2);
  margin-inline: auto;
  padding-inline: var(--margin);
}

.zp-measure { max-width: var(--measure); }

/* ═══ 4. Типографика ═══════════════════════════════════════════════════ */

/* Капслок разрешён ровно в двух местах: .zp-label и .zp-card-title (2.2). */
.zp-label {
  margin: 0;
  color: var(--brass-500);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}

.zp-h1,
.zp-h2,
.zp-h3 {
  margin: 0;
  color: var(--text-hi);
  font-family: var(--font-display);
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-display);
  text-wrap: balance;
}

.zp-h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); }
.zp-h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); }
.zp-h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); }

.zp-card-title {
  margin: 0;
  color: var(--champagne);
  font-family: var(--font-display);
  font-size: var(--fs-card-title);
  font-weight: var(--fw-display);
  line-height: var(--lh-card-title);
  letter-spacing: var(--ls-card-title);
  text-transform: uppercase;
}

/* У Каслона нет табличных цифр: «0» шире «1», и «01» рядом с «05» дают разный
   отступ до заголовка. Ширина дорожки номера фиксируется 2ch — тем же способом,
   каким это решено для номеров глав на главной. */
.zp-num {
  display: block;
  min-inline-size: 2ch;
  margin: 0;              /* на <p> браузер иначе даёт 48 px сверху и снизу */
  color: var(--brass-500);
  font-family: var(--font-display);
  font-size: var(--fs-num);
  font-weight: var(--fw-display);
  line-height: var(--lh-num);
  font-variant-numeric: tabular-nums;
}

.zp-lead,
.zp-body,
.zp-small {
  margin: 0;
  font-family: var(--font-text);
  font-weight: 400;
  text-wrap: pretty;
}

.zp-lead  { color: var(--text-hi); font-size: var(--fs-lead);  line-height: var(--lh-lead); }
.zp-body  { color: var(--text-hi); font-size: var(--fs-body);  line-height: var(--lh-body); }
.zp-small { color: var(--text-lo); font-size: var(--fs-small); line-height: var(--lh-small); }

/* Единственный курсив на сайте. Набирается не дисплейным Каслоном — у него
   курсива нет вовсе, — а Libre Caslon Text: тот же рисунок, настоящее italic.
   Шрифт текстовый, а не титульный, поэтому на кегле цитаты он чуть плотнее
   дисплейного; это и нужно — цитата читается как речь, а не как заголовок. */
.zp-quote {
  margin: 0;
  padding-inline-start: var(--s-5);
  border-inline-start: var(--border-accent) solid var(--brass-500);
  color: var(--text-hi);
  font-family: var(--font-quote);
  font-size: var(--fs-quote);
  font-style: italic;
  font-weight: 400;
  line-height: var(--lh-quote);
}

/* Кавычки не рисуются (2.6): гасим и авторские, и браузерные. */
.zp-quote > p { margin: 0; }
.zp-quote q::before,
.zp-quote q::after { content: none; }

/* ═══ 5. Линии и разделитель ═══════════════════════════════════════════ */

.zp-rule {
  block-size: var(--border);
  margin: 0;
  border: 0;
  background-color: var(--hairline-dark);
}

/* Линия во всю ширину с разрывом посередине; в разрыве — шеврон из знака Z.
   SVG подаётся маской, поэтому латунь остаётся токеном, а не запечена в файл. */
.zp-divider {
  position: relative;
  display: block;
  block-size: var(--s-4);
  margin: 0;
  border: 0;
  background-color: transparent;
  background-image: linear-gradient(
    to right,
    var(--hairline-dark) 0 calc(50% - var(--s-5)),
    transparent calc(50% - var(--s-5)) calc(50% + var(--s-5)),
    var(--hairline-dark) calc(50% + var(--s-5)) 100%
  );
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% var(--border);
}

.zp-divider::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 50%;
  inline-size: var(--s-5);
  translate: -50% 0;
  background-color: var(--brass-500);
  -webkit-mask: var(--mark-chevron) no-repeat center / contain;
  mask: var(--mark-chevron) no-repeat center / contain;
}

/* ═══ 6. Действия ══════════════════════════════════════════════════════ */

/* Единственное место в системе, где латунь становится заливкой, — ховер этой кнопки. */
.zp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-6);
  border: var(--border) solid var(--brass-500);
  border-radius: var(--radius);
  background-color: transparent;
  color: var(--brass-400);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--dur-hover) var(--ease),
    border-color var(--dur-hover) var(--ease),
    color var(--dur-hover) var(--ease);
}

.zp-btn:hover {
  border-color: var(--brass-400);
  background-color: var(--brass-400);
  color: var(--ink-900);
}

/* Фокус читается иначе, чем ховер: ховер заливает кнопку латунью, фокус оставляет её
   спокойной и обводит кольцом. Кольцо приходит из общего правила :focus-visible. */

.zp-btn:disabled,
.zp-btn[aria-disabled="true"] {
  border-color: var(--hairline-card);
  color: var(--text-lo);
  cursor: not-allowed;
}

.zp-btn:disabled:hover,
.zp-btn[aria-disabled="true"]:hover {
  background-color: transparent;
  color: var(--text-lo);
}

.zp-link {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-2);
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}

.zp-link::after {
  content: "\27F6";           /* длинная стрелка вправо, 2.6 */
  color: var(--brass-500);
  transition: color var(--dur-hover) var(--ease);
}

.zp-link:hover {
  color: var(--brass-400);
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.3em;
}

.zp-link:hover::after { color: var(--brass-400); }

/* ═══ 7. Шапка и навигация ═════════════════════════════════════════════ */

.zp-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 100;
  border-block-end: var(--border) solid var(--hairline-dark);
}

/* Подложка --ink-900 92 % + blur 12 живёт в псевдоэлементе, а не на самой шапке:
   backdrop-filter делает элемент containing block для fixed-потомков, и панель
   мобильного меню тогда раскрывается не на экран, а внутрь шапки. */
.zp-header::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background-color: color-mix(in srgb, var(--ink-900) 92%, transparent);
  -webkit-backdrop-filter: blur(var(--blur-header));
  backdrop-filter: blur(var(--blur-header));
}

.zp-header > .zp-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-6);
  min-block-size: var(--header-h);
}

/* Знак в шапке. Один и тот же во всех трёх: шапка — общая, и подменять
   в ней марку на другой знак значит менять общее, а не своё. */
.zp-brand {
  display: flex;
  flex: none;
  align-items: center;
  line-height: 0;
}

.zp-brand img {
  block-size: var(--s-6);
  inline-size: auto;
}

@media (max-width: 767px) { .zp-brand img { block-size: var(--s-5); } }

/* Кнопка шапки. Класс живёт в базе, а не в вариантах: пороги её появления —
   часть общей шапки, и три страницы обязаны вести себя одинаково. */
.zp-header__cta { flex: none; }

.zp-nav {
  display: flex;
  align-items: center;
  gap: var(--s-6);
  margin: 0;
  padding: 0;
  list-style: none;
}

.zp-nav a {
  display: inline-block;
  padding-block: var(--s-2);
  border-block-end: var(--border) solid transparent;
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  transition:
    color var(--dur-hover) var(--ease),
    border-color var(--dur-hover) var(--ease);
}

.zp-nav a:hover,
.zp-nav a[aria-current] {
  border-block-end-color: var(--brass-500);
  color: var(--brass-400);
}

/* ═══ 8. Мобильное меню — <details> + <summary>, без единой строки JS ═══
   Разметка: <details class="zp-menu"><summary class="zp-menu-toggle"></summary>
             <div> … ссылки … </div></details>
   Панель — всё, что внутри .zp-menu кроме .zp-menu-toggle. */

.zp-menu { position: relative; }

.zp-menu-toggle {
  position: relative;
  display: block;
  inline-size: var(--s-7);
  block-size: var(--s-7);
  margin-inline-end: calc(var(--s-3) * -1);
  list-style: none;
  cursor: pointer;
  background-color: transparent;
  background-image: linear-gradient(var(--brass-500), var(--brass-500));
  background-repeat: no-repeat;
  background-position: center;
  background-size: var(--s-5) var(--border);
  transition: background-size var(--dur-hover) var(--ease);
}

.zp-menu-toggle::-webkit-details-marker { display: none; }

.zp-menu-toggle::before,
.zp-menu-toggle::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: var(--s-5);
  block-size: var(--border);
  background-color: var(--brass-500);
  transition:
    translate var(--dur-hover) var(--ease),
    rotate var(--dur-hover) var(--ease);
}

.zp-menu-toggle::before { translate: -50% calc(-50% - var(--s-2)); }
.zp-menu-toggle::after  { translate: -50% calc(-50% + var(--s-2)); }

.zp-menu[open] > .zp-menu-toggle { background-size: 0 var(--border); }
.zp-menu[open] > .zp-menu-toggle::before { translate: -50% -50%; rotate: 45deg; }
.zp-menu[open] > .zp-menu-toggle::after  { translate: -50% -50%; rotate: -45deg; }

/* safe center, а не center: с восемью практиками под Services панель на 375
   выше экрана, а у центрированной сетки с переполнением верхние пункты уезжают
   за верхний край и не доскролливаются. safe отдаёт их обратно.

   Ритм панели считан, а не подобран. В панели семнадцать строк: семь пунктов
   верхнего уровня, восемь практик под Services, кнопка и телефон. При padding
   --s-8 и gap --s-5 они складывались в 911 px против 752 доступных на 375 × 812,
   и «Discuss Your Matter» — единственный призыв сайта — начинался на 825-й,
   то есть за нижней кромкой панели: его приходилось доскролливать.
   Отступ --s-5 и промежуток --s-4 (вместе с --s-2 в списке практик ниже)
   снимают 180 px: содержимое становится 731 px и умещается целиком, кнопка
   заканчивается на 739-й. Кегли и состав панели не тронуты — сжата вертикаль. */
.zp-menu > :not(.zp-menu-toggle) {
  position: fixed;
  z-index: 90;
  inset: var(--header-h) 0 0 0;
  display: grid;
  align-content: safe center;
  justify-items: center;
  gap: var(--s-4);
  padding: var(--s-5) var(--margin);
  overflow-y: auto;
  border-block-start: var(--border) solid var(--hairline-dark);
  background-color: var(--ink-900);
  overscroll-behavior: contain;
}

/* Пункты списком по центру экрана крупным кеглем (2.9).
   Кнопка внутри панели под это правило не попадает — она остаётся кнопкой. */
.zp-menu a:not(.zp-btn) {
  color: var(--text-hi);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-display);
  line-height: var(--lh-h3);
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}

.zp-menu a:not(.zp-btn):hover,
.zp-menu a:not(.zp-btn)[aria-current] { color: var(--brass-400); }

/* Телефон под пунктами (2.9): не пункт меню, а строка контакта. */
.zp-menu__tel {
  margin: 0;
  color: var(--brass-400);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
}

/* Вес селектора здесь не украшение: .zp-menu a:not(.zp-btn) выше — это (0,2,1),
   и набирает ссылку дисплейным Каслоном прямо на элементе. Наследование от абзаца
   до неё не доходит, поэтому телефон возвращает себе шрифт сам и таким же весом. */
.zp-menu .zp-menu__tel a { font: inherit; color: inherit; text-decoration: none; }

.zp-menu__tel a:hover {
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.3em;
}

/* ── Порог шапки, один на все три варианта ─────────────────────────────
   Замер на реальной строке: знак 106 + навигация 795 + кнопка 245 и два
   промежутка по 32 — это 1210 px. При полях 48 такая строка требует окна
   в 1306 px, поэтому прежний порог и стоял на 1440: ниже него навигация
   уходила в гамбургер целиком. Цена оказалась не та: на ноутбуках 1280
   и 1366 корпоративный сайт встречал гамбургером, хотя места хватало.

   Ужимается промежуток навигации, а не состав шапки. Ниже 1440 он идёт
   шагом --s-5 вместо --s-6: навигация садится с 795 до 747, вся строка —
   до 1146, и ей довольно окна в 1242. Порог поставлен на 1260, а не на
   1280: медиазапрос меряет вьюпорт без классического скроллбара, и окно
   1280 в Windows отдаёт 1263 px. Запас 21 px — цена того, чтобы 1280
   попадало в порог наверняка.
   Кнопка шапки живёт до 768; ниже её место в панели меню — иначе на 375
   знак, кнопка и гамбургер не встают в строку. Правило одно и общее:
   вариантам переопределять его нечем и незачем. */
@media (min-width: 1260px) { .zp-menu { display: none; } }

@media (max-width: 1439px) {
  /* Семь пунктов на шаг теснее: промежуток внутри группы остаётся вдвое
     меньше промежутка между группами, и строка перестаёт упираться в поля. */
  .zp-nav { gap: var(--s-5); }
}

@media (max-width: 1259px) {
  .zp-nav { display: none; }
  /* Навигации нет — space-between развёл бы кнопку в середину строки. */
  .zp-header__cta { margin-inline-start: auto; }
}

/* Кнопка в панели меню — замена кнопке шапки, а не вторая такая же:
   пороги ровно дополняют друг друга, и одновременно их не бывает никогда. */
@media (max-width: 767px) { .zp-header__cta { display: none; } }
@media (min-width: 768px)  { .zp-menu a.zp-btn { display: none; } }

/* ── Восемь практик в панели меню (ТЗ 12) ──────────────────────────────
   Ниже 1260 подменю десктопа не существует, и практики живут здесь — списком
   под пунктом Services, отбитым линией и отступом, чтобы вложенность читалась
   без раскрытия. Кегль текстовый, а не дисплейный: восемь названий Каслоном
   в h3 не помещаются на 375 ни при каком раскладе. */

/* Промежуток внутри списка практик вдвое меньше промежутка панели, а вычет
   сверху ровно на столько же: подсписок держится под своим пунктом Services
   теснее, чем пункты держатся друг друга, и вложенность читается расстоянием.
   Пропорция та же, что была при --s-5 в панели; уменьшены обе величины. */
.zp-menu__sub {
  display: grid;
  gap: var(--s-2);
  justify-self: center;
  margin: calc(var(--s-2) * -1) 0 0;
  padding-inline-start: var(--s-5);
  border-inline-start: var(--border) solid var(--hairline-dark);
  list-style: none;
  text-align: start;
}

.zp-menu .zp-menu__sub a:not(.zp-btn) {
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 400;
  line-height: var(--lh-small);
}

.zp-menu .zp-menu__sub a:not(.zp-btn):hover,
.zp-menu .zp-menu__sub a:not(.zp-btn)[aria-current] { color: var(--brass-400); }

/* ── Короткий экран: панель ужимается ещё на шаг ───────────────────────
   Порог по высоте, а не по ширине: состав панели один и тот же, не помещается
   он именно в высоту. 375 × 667 — iPhone SE и 8 — даёт панели 606 px, и даже
   ужатого выше ритма ей мало: кнопка кончалась на 744-й. Здесь шаг ритма ещё
   меньше, а пункты набираются кеглем h4 вместо h3 — оба размера свои, из шкалы,
   ни одного зашитого числа. На 375 × 667 панель после этого не прокручивается
   вовсе: содержимое 590 px против 606 доступных, кнопка кончается на 603-й.
   Восемь практик остаются на месте: их требует ТЗ 12, и прятать их нельзя. */

@media (max-height: 700px) {
  .zp-menu > :not(.zp-menu-toggle) {
    gap: var(--s-2);
    padding-block: var(--s-4);
  }

  .zp-menu a:not(.zp-btn) {
    font-size: var(--fs-h4);
    line-height: var(--lh-h4);
  }

  .zp-menu__sub {
    gap: var(--s-1);
    margin-block-start: calc(var(--s-1) * -1);
  }
}

/* ── Подменю практик на десктопе ───────────────────────────────────────
   <details> по клику, а не раскрытие по ховеру: на ховере оно открывается
   мимоходом и закрывается, пока к нему ведут курсор. Панель позиционируется
   от своего пункта, поэтому длинные названия не рвут строку навигации.
   Esc и клик вне закрывают её — это единственные два обработчика в меню,
   и живут они в scripts/site.js, а не в разметке. */

.zp-subnav-host {
  position: relative;
  display: flex;
  align-items: center;
}

.zp-subnav__toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding-block: var(--s-2);
  border-block-end: var(--border) solid transparent;
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  list-style: none;
  cursor: pointer;
  transition:
    color var(--dur-hover) var(--ease),
    border-color var(--dur-hover) var(--ease);
}

.zp-subnav__toggle::-webkit-details-marker { display: none; }

/* Шеврон из знака Z, развёрнутый вниз, — тот же, что у селекта формы.
   Из потока он вынут, и это не приём, а замер: строка шапки на пороге 1260
   сходится ровно, с запасом в три пикселя (см. «Порог шапки» ниже). Шеврон
   в потоке добавлял двенадцать, и «THE FIRM» с «OUR TEAM» уезжали на вторую
   строку. Вынесенный за край подписи, он живёт в промежутке между пунктами:
   четыре пикселя от своего слова, двенадцать до следующего — принадлежность
   читается, ширина навигации не меняется. */
.zp-subnav__toggle::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 100%;
  margin-inline-start: var(--s-1);
  inline-size: var(--s-2);
  block-size: var(--s-2);
  translate: 0 -50%;
  rotate: 180deg;
  background-color: var(--brass-500);
  -webkit-mask: var(--mark-chevron) no-repeat center / contain;
  mask: var(--mark-chevron) no-repeat center / contain;
  transition:
    rotate var(--dur-hover) var(--ease),
    background-color var(--dur-hover) var(--ease);
}

.zp-subnav[open] > .zp-subnav__toggle::after { rotate: 0deg; }

.zp-subnav__toggle:hover,
.zp-subnav__toggle[aria-current] {
  border-block-end-color: var(--brass-500);
  color: var(--brass-400);
}

.zp-subnav__toggle:hover::after,
.zp-subnav__toggle[aria-current]::after { background-color: var(--brass-400); }

.zp-subnav__panel {
  position: absolute;
  z-index: 10;
  inset-block-start: calc(100% + var(--s-5));
  inset-inline-start: calc(var(--s-5) * -1);
  inline-size: max-content;
  max-inline-size: min(26rem, 70vw);
  padding: var(--s-5) var(--s-6);
  border: var(--border) solid var(--hairline-card);
  border-radius: var(--radius);
  background-color: var(--ink-900);
}

.zp-subnav__panel ul {
  display: grid;
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Вес селектора здесь не украшение: .zp-nav a выше набирает ссылку капслоком
   и вешает на неё нижнюю линию. В панели ссылка — обычная строка списка. */
.zp-nav .zp-subnav__panel a {
  display: block;
  padding-block: 0;
  border: 0;
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 400;
  line-height: var(--lh-small);
  letter-spacing: normal;
  text-transform: none;
  text-decoration: none;
}

.zp-nav .zp-subnav__panel a:hover,
.zp-nav .zp-subnav__panel a[aria-current] { color: var(--brass-400); }

/* Первая строка панели — сам раздел: без неё страница-концентратор
   с десктопа недостижима, потому что ярлык Services стал <summary>. */
.zp-nav .zp-subnav__panel .zp-subnav__all {
  padding-block-end: var(--s-3);
  border-block-end: var(--border) solid var(--hairline-dark);
  color: var(--champagne);
}

/* ═══ 9. Футер ═════════════════════════════════════════════════════════ */

.zp-footer {
  border-block-start: var(--border) solid var(--hairline-dark);
  background-color: var(--ink-900);
  color: var(--text-lo);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

/* Разделитель-шеврон садится ровно на верхнюю линию футера, чтобы линий
   было по-прежнему одна, а не две. Правило общее: футер один на все страницы. */
.zp-footer__divider {
  inline-size: calc(100% - var(--margin) * 2);
  max-inline-size: var(--container);
  margin-block-start: calc(var(--s-2) * -1);
  margin-inline: auto;
}

.zp-footer > .zp-container {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-7) var(--gutter);
  padding-block: var(--s-9);
}

/* Колонка ссылок футера: заголовок .zp-label, под ним список. */
.zp-footer__col ul {
  display: grid;
  gap: var(--s-2);
  margin: var(--s-4) 0 0;
  padding: 0;
  list-style: none;
}

.zp-footer__col a[aria-current] { color: var(--brass-400); }

/* Первая колонка футера — знак, название и адрес (copy-deck §9). */
.zp-footer__brand {
  display: grid;
  align-content: start;   /* иначе колонка растягивает строки на всю высоту футера */
  gap: var(--s-4);
  justify-items: start;
}

.zp-footer__brand img {
  inline-size: var(--s-10);
  max-inline-size: 100%;
  block-size: auto;
}

.zp-footer__brand p { margin: 0; max-width: 30ch; }

/* Фолбэк без JavaScript. Шапка и футер собираются скриптом, поэтому у страницы
   с выключенным JS остаются четыре ссылки: домой, в услуги, в контакты и телефон.
   Класс висит на абзаце внутри <noscript>, а не на самом элементе: display
   у <noscript> при включённом JS ставит браузер, и спорить с ним нельзя. */
.zp-noscript {
  margin: 0;
  padding: var(--s-4) var(--margin);
  border-block-end: var(--border) solid var(--hairline-dark);
  background-color: var(--ink-900);
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  text-align: center;
}

.zp-noscript a {
  color: var(--brass-400);
  text-decoration: none;
}

.zp-noscript a:hover {
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.3em;
}

/* Полоса о нарушенном контракте разметки. Ставится скриптом первым потомком
   <body> и в нормально собранной странице не существует вовсе. Кричит намеренно:
   опечатка в data-page или data-root не должна выглядеть как рабочая страница. */
.zp-fault {
  position: relative;
  z-index: 400;
  margin: 0;
  padding: var(--s-4) var(--margin);
  border-block-end: var(--border-accent) solid var(--danger);
  background-color: var(--ink-900);
  color: var(--danger);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

/* Полужирного в системе нет: у Archivo подключены 400 и 500, и на html стоит
   font-synthesis-weight: none. Вес назван честным 500, а не оставлен на bold. */
.zp-fault strong { font-weight: 500; }

/* Строка дисклеймера и копирайта — четвёртый ряд футера во всю ширину.
   Правило живёт в базе: три варианта стилизовали её у себя по-разному,
   а это часть общего футера. */
.zp-footer__legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-4) var(--s-7);
  grid-column: 1 / -1;
  padding-block-start: var(--s-6);
  border-block-start: var(--border) solid var(--hairline-dark);
}

.zp-footer__legal p { margin: 0; max-width: var(--measure); }

.zp-footer a {
  color: var(--text-lo);
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}

.zp-footer a:hover {
  color: var(--brass-400);
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.3em;
}

@media (max-width: 1023px) {
  .zp-footer > .zp-container { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  .zp-footer > .zp-container {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-6);
    padding-block: var(--s-8);
  }
}

/* ═══ 10. Форма ════════════════════════════════════════════════════════
   Поле без рамки: подпись капслоком сверху, ввод, волосяная линия снизу.
   Ошибка — <p role="alert"> внутри .zp-field, отдельного класса не заводим.

   Три дорожки поля — подпись / ввод / строка под полем — берутся из сетки
   ряда (grid-template-rows: subgrid). Поэтому у всех полей одного ряда
   подписи, линии ввода и подстрочные строки стоят на общих горизонталях:
   ни двухстрочная подпись у соседа, ни «Optional» под одним из полей
   больше не двигают чужой ввод. Ряды считает сетка, а не глазомер.

   Сетка ряда — .zp-form или .zp-fields (группа полей внутри формы).
   Промежутки внутри поля держат поля элементов, а не row-gap: пустая
   третья дорожка обязана быть нулевой высоты, иначе ряды без подстрочника
   разъезжаются с рядом, где он есть. */

.zp-form,
.zp-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5) var(--gutter);
}

/* Поля тянутся во всю ширину формы, кнопка — нет. */
.zp-form > .zp-btn { justify-self: start; }

.zp-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  row-gap: 0;
}

@supports (grid-template-rows: subgrid) {
  .zp-form > .zp-field,
  .zp-fields > .zp-field {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

/* Подпись прижата к низу своей дорожки: короткая подпись стоит вплотную
   к своему полю, а не висит под потолком чужой двухстрочной. */
.zp-field__label {
  grid-row: 1;
  grid-column: 1;
  align-self: end;
  color: var(--brass-500);
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  transition: color var(--dur-hover) var(--ease);
}

.zp-field:focus-within .zp-field__label { color: var(--brass-400); }

.zp-field__control {
  grid-row: 2;
  grid-column: 1;
  inline-size: 100%;
  margin-block-start: var(--s-2);
  padding: var(--s-3) 0;
  border: 0;
  border-block-end: var(--border) solid var(--hairline-card);
  border-radius: 0;
  background-color: transparent;
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  caret-color: var(--brass-400);
  transition:
    border-color var(--dur-hover) var(--ease),
    box-shadow var(--dur-hover) var(--ease);
}

input.zp-field__control,
textarea.zp-field__control {
  -webkit-appearance: none;
  appearance: none;
}

/* Пол высоты — две строки текста плюс поля ввода: ниже textarea перестаёт
   читаться как место для рассказа о деле. Выше решает атрибут rows. */
textarea.zp-field__control {
  resize: vertical;
  min-block-size: calc(var(--lh-body) * 2em + var(--s-3) * 2);
}

/* Список раскрытого селекта рисует система; там, где цвета всё же наши,
   он обязан остаться тёмным — иначе светлый текст ляжет на светлый фон. */
.zp-field__control option {
  background-color: var(--ink-800);
  color: var(--text-hi);
}

.zp-field__control::placeholder {
  color: var(--text-lo);
  opacity: 1;
}

/* Селект теряет системную стрелку и получает шеврон из знака Z, развёрнутый
   вниз. Реализация одна на весь проект — вариантам её не переобъявлять.
   Шеврон живёт в дорожке ввода, а не приклеен к низу поля: с подстрочником
   «Optional» низ поля уезжает, а линия ввода остаётся на месте.
   Всё это — под :has(): без него снятую стрелку нечем заменить, и селект
   остаётся с родной системной. */
@supports selector(:has(*)) {
  select.zp-field__control {
    -webkit-appearance: none;
    appearance: none;
    padding-inline-end: var(--s-6);
  }

  /* Невыбранный селект читается как подсказка, а не как введённое значение. */
  select.zp-field__control:has(option[value=""]:checked) { color: var(--text-lo); }

  .zp-field:has(select)::after {
    content: "";
    grid-row: 2;
    grid-column: 1;
    justify-self: end;
    align-self: center;
    inline-size: var(--s-3);
    block-size: var(--s-2);
    margin-block-start: var(--s-2);
    rotate: 180deg;
    background-color: var(--brass-500);
    -webkit-mask: var(--mark-chevron) no-repeat center / contain;
    mask: var(--mark-chevron) no-repeat center / contain;
    pointer-events: none;
    transition: background-color var(--dur-hover) var(--ease);
  }

  .zp-field:focus-within:has(select)::after { background-color: var(--brass-400); }
}

.zp-field__control:hover { border-block-end-color: var(--brass-500); }

/* Фокус — линия латунью, утолщённая кольцом; outline не нужен, линия и есть
   индикатор. Ховер даёт линию 1 px --brass-500, фокус — 2 px --brass-400:
   состояния различаются и толщиной, и тоном. */
.zp-field__control:focus-visible {
  outline: none;
  border-block-end-color: var(--brass-400);
  box-shadow: 0 var(--border) 0 0 var(--brass-400);
}

.zp-field__control[aria-invalid="true"] { border-block-end-color: var(--danger); }

/* Подсказка под полем («Optional»): не подпись и не ошибка. Связывается
   с контролем через aria-describedby, а не подсовывается в placeholder —
   placeholder исчезает, как только человек начинает печатать. */
.zp-field__note,
.zp-field [role="alert"] {
  grid-row: 3;
  grid-column: 1;
  margin: var(--s-2) 0 0;
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

.zp-field__note { color: var(--text-lo); }
.zp-field [role="alert"] { color: var(--danger); }

/* Дорожка под полем одна: ошибка приходит — подсказка уходит, иначе обе
   лягут в одну клетку. Ошибка важнее подсказки. */
.zp-field:has([role="alert"]) .zp-field__note { display: none; }

/* Состояние «отправлено»: заголовок Каслоном в шампани, строка Small, шеврон-разделитель. */
.zp-sent {
  display: grid;
  justify-items: center;
  gap: var(--s-4);
  padding: var(--s-7) var(--s-6);
  border: var(--border) solid var(--hairline-card);
  border-radius: var(--radius);
  text-align: center;
}

.zp-sent :is(h1, h2, h3, h4, p) { margin: 0; }

.zp-sent :is(h1, h2, h3, h4) {
  color: var(--champagne);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-display);
  line-height: var(--lh-h3);
}

/* Две строки под заголовком читаются по-разному: ответ фирмы — основным
   текстом, оговорка про прототип — Small и приглушённо. */
.zp-sent p {
  max-width: var(--measure);
  color: var(--text-hi);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

.zp-sent p.zp-small {
  color: var(--text-lo);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

.zp-sent .zp-divider { inline-size: 100%; }

/* ═══ 11. Фотография — единственная реализация дуотона ══════════════════
   grayscale(1) на самом снимке + слой --ink-800 в mix-blend-mode: color при 0.62.
   Вторых реализаций в вариантах быть не должно. */

.zp-photo {
  position: relative;
  display: block;
  overflow: hidden;
  border: var(--border) solid var(--hairline-card);
  border-radius: var(--radius);
  background-color: var(--ink-900);
  isolation: isolate;
}

.zp-photo > img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  filter: grayscale(1);
}

.zp-photo::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  background-color: var(--ink-800);
  mix-blend-mode: color;
  opacity: 0.62;
  pointer-events: none;
}

/* Градиент лежит поверх дуотона и сам не смешивается: иначе текст на светлых
   участках снимка не читается (2.5). Снизу 85 %, кверху 25 %. */
.zp-photo--grad::before {
  content: "";
  position: absolute;
  z-index: 2;
  inset: 0;
  background-image: linear-gradient(
    to top,
    color-mix(in srgb, var(--ink-900) 85%, transparent),
    color-mix(in srgb, var(--ink-900) 25%, transparent)
  );
  pointer-events: none;
}

/* ═══ 12. Водяной знак ═════════════════════════════════════════════════
   Крупный знак Z цветом --ghost, контраст к фону 1.20:1. Один на секцию.
   Родитель должен быть position: relative, содержимое — над знаком.
   Ставить с aria-hidden="true". */

.zp-ghost {
  position: absolute;
  z-index: 0;
  inline-size: var(--s-11);
  aspect-ratio: var(--mark-aspect);
  background-color: var(--ghost);
  -webkit-mask: var(--mark-z) no-repeat center / contain;
  mask: var(--mark-z) no-repeat center / contain;
  pointer-events: none;
}

/* ═══ 13. Светлая секция ═══════════════════════════════════════════════
   Тёмная страница — норма, светлая секция — инструмент для длинного текста.
   Токены переопределяются внутри блока, поэтому весь словарь .zp-* переезжает
   на светлое сам: латунь текстом на --paper не появляется нигде. */

.zp-paper {
  --text-hi: var(--text-ink);
  --text-lo: var(--text-ink-lo);
  --champagne: var(--text-ink);
  --brass-400: var(--brass-800);
  --brass-500: var(--brass-800);
  --ghost: var(--paper-2);
  --hairline-dark: var(--hairline-light);
  --hairline-card: var(--hairline-light);
  --danger: var(--danger-ink);
  --focus: var(--brass-800);   /* --brass-400 на бумаге даёт 1.54:1 — кольца не видно */

  background-color: var(--paper);
  color: var(--text-ink);
}

/* --brass-800 на --paper даёт 5.38:1, а --ink-900 на --brass-800 — только 3.02:1,
   поэтому на светлом заливка ховера подписывается бумагой. */
.zp-paper .zp-btn:hover { color: var(--paper); }

.zp-paper::selection,
.zp-paper ::selection {
  background-color: var(--brass-800);
  color: var(--paper);
}

/* ═══ 14. Словарь внутренних страниц ═══════════════════════════════════
   Компоненты, из которых собираются тридцать три страницы. Всё на действующих
   токенах: ни одного нового цвета, ни одного нового кегля. Раскладка типа
   страницы живёт в page.css под .p-*; здесь — только сами компоненты.
   Имена — часть контракта: на них ссылаются следующие таски. */

/* ── Хлебные крошки ─────────────────────────────────────────────────────
   Стоят в статическом HTML самой страницы, а не в скрипте: путь назад обязан
   быть виден и без JavaScript. Разметка — <nav aria-label="Breadcrumb"><ol>,
   последний элемент не ссылка и несёт aria-current="page".
   Разделитель — псевдоэлементом, чтобы не попасть в поток чтения скринридера. */

.zp-crumbs {
  padding-block: var(--s-6) 0;
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

.zp-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.zp-crumbs li {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
}

.zp-crumbs li + li::before {
  content: "/";
  color: var(--brass-500);
}

.zp-crumbs a {
  color: var(--text-lo);
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease);
}

.zp-crumbs a:hover {
  color: var(--brass-400);
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.3em;
}

.zp-crumbs [aria-current="page"] { color: var(--text-hi); }

/* ── Шапка страницы: метка, <h1>, лид ─────────────────────────────────── */

.zp-pagehead {
  display: grid;
  justify-items: start;
  gap: var(--s-5);
  padding-block: var(--s-8);
  border-block-end: var(--border) solid var(--hairline-dark);
}

.zp-pagehead > .zp-lead,
.zp-pagehead > .zp-body { max-width: var(--measure); }

/* ── Проза: длинный текст разделами ────────────────────────────────────
   Мера строки — на самом блоке, поэтому вкладывать .zp-measure не нужно. */

.zp-prose,
.zp-article { max-width: var(--measure); }

/* Класс удвоен намеренно. Обнуляющие правила ниже — .zp-prose p, .zp-prose
   :is(ul, ol) — весят (0,1,1) и перебивали этот промежуток: два соседних абзаца
   в одном .zp-prose слипались вплотную (D02). Удвоение даёт (0,2,0). */
.zp-prose.zp-prose > * + *,
.zp-article.zp-article > * + * { margin-block-start: var(--s-5); }

.zp-prose :is(h2, h3, h4),
.zp-article :is(h2, h3, h4) {
  margin: 0;
  color: var(--text-hi);
  font-family: var(--font-display);
  font-weight: var(--fw-display);
  letter-spacing: var(--ls-display);
  text-wrap: balance;
}

.zp-prose h2,
.zp-article h2 { font-size: var(--fs-h3); line-height: var(--lh-h3); }

.zp-prose :is(h3, h4),
.zp-article :is(h3, h4) { font-size: var(--fs-h4); line-height: var(--lh-h4); }

/* Заголовок отбивается от предыдущего абзаца сильнее, чем абзац от абзаца:
   иначе раздел не читается как новый. Класс удвоен по той же причине, что выше:
   иначе (0,1,1) проиграет промежутку между соседями, ставшему (0,2,0). */
.zp-prose.zp-prose > * + :is(h2, h3, h4),
.zp-article.zp-article > * + :is(h2, h3, h4) { margin-block-start: var(--s-8); }

.zp-prose p,
.zp-article p {
  margin: 0;
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  text-wrap: pretty;
}

.zp-prose :is(ul, ol),
.zp-article :is(ul, ol) {
  margin: 0;
  padding-inline-start: var(--s-5);
  color: var(--text-hi);
}

.zp-prose li + li,
.zp-article li + li { margin-block-start: var(--s-3); }

.zp-prose :is(ul, ol)::marker,
.zp-prose li::marker,
.zp-article li::marker { color: var(--brass-500); }

.zp-prose a:not(.zp-link):not(.zp-btn),
.zp-article a:not(.zp-link):not(.zp-btn) {
  color: var(--brass-400);
  text-decoration: underline;
  text-decoration-thickness: var(--border);
  text-underline-offset: 0.2em;
  transition: color var(--dur-hover) var(--ease);
}

/* ── Строка «категория · раздел» ──────────────────────────────────────── */

.zp-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2) var(--s-3);
  margin: 0;
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

.zp-meta > * + *::before {
  content: "·";
  margin-inline-end: var(--s-3);
  color: var(--brass-500);
}

/* ── Список подтем практики ────────────────────────────────────────────
   Разграфка вместо заливок: подтему отделяет волосяная линия. */

.zp-topics {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.zp-topics > li {
  display: grid;
  gap: var(--s-2);
  padding-block: var(--s-4);
  border-block-start: var(--border) solid var(--hairline-dark);
  color: var(--text-hi);
}

.zp-topics > li:last-child { border-block-end: var(--border) solid var(--hairline-dark); }

.zp-topics > li > :is(h3, h4, p) { margin: 0; }

/* ── Карточка раздела в концентраторе ──────────────────────────────────
   Заливки нет: карточку держит рамка --hairline-card, фон равен фону страницы.
   Ссылка растягивается на всю карточку псевдоэлементом, поэтому кликается
   вся плитка, а в дереве доступности остаётся одна ссылка с именем заголовка. */

.zp-tile {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--s-4);
  padding: var(--pad-card);
  border: var(--border) solid var(--hairline-card);
  border-radius: var(--radius);
  transition: border-color var(--dur-hover) var(--ease);
}

.zp-tile:hover { border-color: var(--brass-500); }

.zp-tile :is(h2, h3, h4, p) { margin: 0; }

/* Растяжка живёт на ::before, а не на ::after: ::after занят стрелкой ⟶ у
   .zp-link (§2.6), и на общей ссылке a.zp-link.zp-tile__link растяжка её
   затирала — приходилось выбирать между стрелкой и кликом по всей плитке. */
.zp-tile__link::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
}

/* Кольцо приходит только от :focus-visible — политика проекта. Раньше здесь
   стояло :focus-within, и плитка обводилась кольцом на обычный клик мышью:
   ссылка получает фокус и от мыши тоже. :has() переносит кольцо со ссылки на
   всю плитку, но исключительно в том случае, когда браузер сам решил показать
   индикатор. Где :has() нет, кольцо остаётся на ссылке — по общему правилу
   :focus-visible из §1, и снимать его тогда нечем и незачем. */
@supports selector(:has(*)) {
  .zp-tile:has(.zp-tile__link:focus-visible) {
    outline: var(--focus-w) solid var(--focus);
    outline-offset: var(--focus-offset);
  }

  .zp-tile__link:focus-visible { outline: none; }
}

.zp-tile__body {
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

/* ── Карточка человека ─────────────────────────────────────────────────
   Портрет — дуотон из §11, второй реализации нет. */

.zp-person {
  display: grid;
  align-content: start;
  gap: var(--s-4);
}

.zp-person .zp-photo { aspect-ratio: 4 / 5; }

.zp-person :is(h2, h3, h4, p) { margin: 0; }

.zp-person__role {
  color: var(--text-lo);
  font-family: var(--font-text);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
}

/* ── Врезка-оговорка ───────────────────────────────────────────────────
   Для строк вроде «мы не обещаем, что счёт откроют»: отделена от прозы
   рамкой и латунной линией слева, но остаётся текстом, а не плашкой. */

.zp-note {
  display: grid;
  gap: var(--s-3);
  padding: var(--pad-card);
  border: var(--border) solid var(--hairline-card);
  border-inline-start: var(--border-accent) solid var(--brass-500);
  border-radius: var(--radius);
}

.zp-note :is(h2, h3, h4, p) {
  margin: 0;
  max-width: var(--measure);
}

.zp-note p {
  color: var(--text-hi);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

/* ── Полоса призыва в конце страницы ───────────────────────────────────
   Единственная кнопка-CTA на странице (ТЗ 1: Discuss Your Matter). */

.zp-cta {
  display: grid;
  justify-items: start;
  gap: var(--s-5);
  margin-block-start: var(--s-9);
  padding-block: var(--s-8) 0;
  border-block-start: var(--border) solid var(--hairline-dark);
}

.zp-cta :is(h2, h3, p) { margin: 0; }

.zp-cta > p { max-width: var(--measure); }

/* ── Карта, которая грузится только по нажатию ─────────────────────────
   До нажатия — рамка с адресом и кнопкой [data-zp-map-load], ни одного
   запроса на внешний домен и ни одной куки Google. Разметку ставит страница,
   iframe подставляет scripts/site.js. */

.zp-map {
  position: relative;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--s-4);
  min-block-size: calc(var(--s-11) * 2);
  padding: var(--s-6);
  overflow: hidden;
  border: var(--border) solid var(--hairline-card);
  border-radius: var(--radius);
  text-align: center;
}

.zp-map :is(h2, h3, p) { margin: 0; }

.zp-map[data-zp-map-on] {
  display: block;
  padding: 0;
}

.zp-map iframe {
  display: block;
  inline-size: 100%;
  block-size: calc(var(--s-11) * 2);
  border: 0;
  filter: grayscale(1);
}

/* ── Графический слой страницы ─────────────────────────────────────────
   Дизайн-система 2.5 держит четыре графических приёма: разграфка, разделитель
   с шевроном, водяной знак и фотография. На внутренних страницах не работал
   ни один — тридцать три страницы шли чистой типографикой. Здесь они собраны
   в один слой, и включается он ровно одним признаком: страница, у которой
   в шапке стоит кадр, получает слой целиком. Ни атрибута, ни класса на <body> —
   выключателем служит сама фотография.

   Сначала слой включала гильошная плита — розетка под маской вместо снимка.
   Приём был отвергнут пользователем как не отвечающий дизайн-системе, и он
   прав: 2.5 называет фотографию, а гильоша в системе нет. Плита, её генератор
   и рисунки удалены; восстановить их можно из коммита b3ea2d5. */

/* ── Кадр в шапке страницы ─────────────────────────────────────────────
   Тот же разворот, что у героя главной: текст слева, снимок справа выпущен
   в правый край экрана, и шов между ними — одна волосяная линия. Компонент
   не новый: .zp-photo и дуотон те же самые (§11), новое только место.

   Выпуск в край считается без единого нового токена. У абсолютного блока
   проценты в inset-inline-end берутся от ширины родителя, а родитель —
   .zp-pagehead, то есть содержимое контейнера. Значит calc(50% - 50vw) —
   это ровно расстояние от правой кромки контейнера до кромки экрана,
   на любой ширине: при 1440 это -60, при 1200 это -48.

   50vw считается вместе с полосой прокрутки, и там, где полоса занимает
   место (Windows), кадр вылез бы за экран и дал горизонтальную прокрутку.
   Поэтому у <main> стоит overflow-x: clip — clip, а не hidden: hidden сделал
   бы <main> контейнером прокрутки со всеми последствиями для sticky. */

.p-page:has(.zp-pagehead > .zp-photo) > main { overflow-x: clip; }

@media (min-width: 1024px) {
  /* Высота шапки растёт вместе с шириной кадра, и это не вкус, а арифметика.
     Кадр занимает от 54 % шапки до кромки экрана, поэтому его ширина тянется
     за окном, а высота была прибита к 30rem — на 1440 коробка выходила 1.39,
     на 2560 уже 2.56 при исходнике 3:2. object-fit: cover честно резал разницу
     по высоте: на 2560 сверху и снизу уходило по 21 %, на 3440 — по 28.

     33vw держит коробку около полутора на любой ширине. Потолок 62svh нужен
     ультраширокому монитору: там 33vw даёт шапку выше экрана, и лекарство
     оказалось бы хуже болезни. Пол 30rem оставляет 1440 ровно таким, каким он был. */
  .zp-pagehead:has(> .zp-photo) {
    position: relative;
    align-content: center;
    min-block-size: max(30rem, min(33vw, 62svh));
    padding-inline-end: calc(46% + var(--s-8));
  }

  .zp-pagehead > .zp-photo {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 54%;
    inset-inline-end: calc(50% - 50vw);

    /* Выпуск в край: шов остаётся только слева, одной линией — как у героя
       главной. Скругление и правая рамка на выпущенном кадре читались бы
       как оторванная от края карточка. */
    border-block: 0;
    border-inline-end: 0;
    border-radius: 0;
  }
}

/* Ниже 1024 разворота нет: кадр становится полосой над текстом, как герой
   главной на узком экране. Полоса живёт в контейнере и в край не выпускается —
   двусторонний выпуск здесь стоил бы горизонтальной прокрутки, а выигрыш
   на 375 нулевой. */
@media (max-width: 1023px) {
  .zp-pagehead:has(> .zp-photo) { position: relative; }

  .zp-pagehead > .zp-photo {
    justify-self: stretch;
    inline-size: 100%;
    block-size: 34svh;
    min-block-size: 14rem;
  }
}

/* Разделитель системы (2.5) — волосяная линия с разрывом и шевроном из знака Z
   по центру — стоит на нижней границе шапки страницы: это единственный шов
   страницы, отделяющий её представление от тела. Реализация повторяет
   .zp-divider (§5) не копией правил, а тем же рецептом на границе блока:
   линия обязана остаться границей .zp-pagehead, а не встать отдельным <hr>
   в разметке тридцати трёх страниц.

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

   На полосу призыва этот же разделитель не идёт, и это замер, а не вкус:
   такой же шеврон уже стоит на разделителе футера (.zp-footer__divider),
   а от полосы призыва до футера меньше двухсот пикселей — два одинаковых
   орнамента подряд читаются как сбой печати. Полосе достаётся только
   водяной знак. */
.p-page:has(.zp-pagehead > .zp-photo) .zp-pagehead:not(:has(> .zp-photo)) {
  border-block-end-color: transparent;
  background-image: linear-gradient(
    to right,
    var(--hairline-dark) 0 calc(50% - var(--s-6)),
    transparent calc(50% - var(--s-6)) calc(50% + var(--s-6)),
    var(--hairline-dark) calc(50% + var(--s-6)) 100%
  );
  background-repeat: no-repeat;
  background-position: bottom center;
  background-size: 100% var(--border);
}

.p-page:has(.zp-pagehead > .zp-photo) .zp-pagehead:not(:has(> .zp-photo))::after {
  content: "";
  position: absolute;
  inset-block-end: calc(var(--s-4) * -0.5);
  inset-inline-start: 50%;
  inline-size: var(--s-5);
  block-size: var(--s-4);
  translate: -50% 0;
  background-color: var(--brass-500);
  -webkit-mask: var(--mark-chevron) no-repeat center / contain;
  mask: var(--mark-chevron) no-repeat center / contain;
}

.p-page:has(.zp-pagehead > .zp-photo) .zp-cta { position: relative; }

/* Водяной знак за полосой призыва — филигрань системы 2.5, одна на секцию.
   Псевдоэлементом, а не <span class="zp-ghost">: знак не несёт смысла, и его
   отсутствие в разметке избавляет от aria-hidden на тридцати страницах. */
.p-page:has(.zp-pagehead > .zp-photo) .zp-cta::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset-block-start: var(--s-7);
  inset-inline-end: 0;
  inline-size: var(--s-11);
  aspect-ratio: var(--mark-aspect);
  background-color: var(--ghost);
  -webkit-mask: var(--mark-z) no-repeat center / contain;
  mask: var(--mark-z) no-repeat center / contain;
  pointer-events: none;
}

.p-page:has(.zp-pagehead > .zp-photo) .zp-cta > * {
  position: relative;
  z-index: 1;
}

/* ═══ 15. Хвост ════════════════════════════════════════════════════════ */

/* Компоненты выше задают display, поэтому обычного [hidden] (0,1,0) им не хватает.
   Удвоенный атрибут даёт (0,2,0) и перебивает их без форсирования важности. */
[hidden][hidden] { display: none; }

/* Страховка на случай, если вариант всё же зашьёт длительность числом. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: var(--dur-instant);
    animation-delay: 0s;
    animation-iteration-count: 1;
    transition-duration: var(--dur-instant);
    transition-delay: 0s;
  }
}
