/*
 * Компоненты АвтоПорт. Только компоненты — значения берутся из tokens.css.
 */

/* ============ Контейнер ============ */
/*
 * Одна левая вертикаль на всей странице. Ширина 1200 включает боковое
 * поле (border-box), поэтому содержимое любого блока начинается на одной
 * и той же линии: и текст героя, и заголовки, и карточки, и подвал.
 *
 * Раньше формул было три: у героя ширина включала поле, у крошек и
 * подвала — нет, а прозу WordPress центрировал отдельной колонкой 760px.
 * На одной странице получалось три левых края: 152, 120 и 340.
 *
 * main переведён на раскладку default ради этого же: constrained
 * центрирует каждого потомка auto-полями через !important, и договориться
 * с ним об общей вертикали нельзя. Длину строки контейнер и задаёт:
 * своего потолка у прозы нет (см. «Текст» в style.css).
 */
main.wp-block-group,
.ap-hero__inner,
.ap-tags,
.ap-crumbs,
.ap-footer__grid,
.ap-footer__bottom {
  max-width: var(--ap-container);
  margin-inline: auto;
  padding-inline: var(--ap-gutter);
  box-sizing: border-box;
}

/* Между контентом и полосой CTA нужен ритм секции: без него последний
   абзац упирался прямо в её верхнюю рамку. На главной такой же просвет
   нужен и сверху — там над контентом стоит герой, а не крошки. */
main.wp-block-group { padding-block-end: var(--ap-section); }
/* Просвет после героя меньше ритма секции: герой — сплошная тёмная полоса
   со своим нижним полем под плашками, и полный ритм поверх него читался
   как провал. Замерено: было 120px до заголовка на десктопе, стало 72. */
body.home main.wp-block-group { padding-block-start: var(--ap-s6); }
@media (min-width: 1024px) {
  body.home main.wp-block-group { padding-block-start: var(--ap-s7); }
}

/* ============ Шапка ============ */

/* Sticky обязан висеть на прямом потомке .wp-site-blocks, а не на самой
   шапке: WordPress оборачивает template part в .wp-block-template-part,
   высота обёртки равна высоте шапки ровно, и sticky внутри неё некуда
   ехать — прокрутка уносила шапку вместе с обёрткой (замерено: top = -600
   при scrollY = 600 на 320, 390 и 1440), а с ней телефон и бургер. Своего
   класса у обёртки нет: шаблоны страниц отдаёт родительская тема, className
   в её wp:template-part не пропишешь, поэтому берём первого потомка —
   шапка идёт первой во всех шаблонах.

   Побочное следствие, которого раньше не было видно: при сжатии шапки
   (is-stuck, 92 → 80px) обёртка сжимается вместе с ней и контент ниже
   уезжает на 12px вверх. Сжатие требует спека; убрать сдвиг можно только
   отказом от него — одна строка в .is-stuck. */
.wp-site-blocks > .wp-block-template-part:first-child {
  position: sticky;
  top: 0;
  z-index: 100;
}

/* Sticky на самой шапке оставлен запасом: если обёртка когда-нибудь
   исчезнет (своя блочная тема вне этой итерации), липкость переедет сюда. */
.ap-header {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: var(--ap-s5);
  padding: var(--ap-s4) var(--ap-gutter);
  background: var(--ap-black);
  border-bottom: 1px solid transparent;
  transition: padding var(--ap-speed) ease, border-color var(--ap-speed) ease;
}

/* Twenty Twenty-Five вешает на детей .ap-header (is-layout-flow)
   margin-block-start: 1.2rem. Из-за него шапка была на 19px выше честной
   высоты, а логотип стоял на 9,6px выше телефона и кнопки — замерено по
   оптическим центрам. Обнуляем: выравнивание в строке делает flex. */
.ap-header > * { margin-block-start: 0; }

/* Класс вешает скрипт после 120px прокрутки */
.ap-header.is-stuck {
  padding-block: 10px;
  border-bottom-color: var(--ap-line);
}

.ap-header__logo img {
  height: 30px;
  width: auto;
  display: block;
}

.ap-header__nav {
  display: flex;
  gap: var(--ap-s5);
  font-size: 15px;
}

/* Все правила строки обходят .ap-btn через :not(). Селектор
   `.ap-header__nav a` — это класс плюс элемент (0,1,1) и по специфичности
   бьёт `.ap-btn` (0,1,0) независимо от порядка в файле. Без обхода кнопка
   «Записаться» внутри панели получала цвет ссылки #C6C6CE на жёлтом фоне
   (контраст ~1,5:1, подпись нечитаема), вес 500 вместо 700 и
   padding-block: 4px вместо 10px. */

/* Подчёркивание выезжает слева на 150 мс — движение минимальное, как и
   требует спека: ничего не появляется по скроллу, реагирует только то, на
   что навели. transform, а не изменение height/width, чтобы браузер не
   пересчитывал раскладку на каждый кадр. */
.ap-header__nav a:not(.ap-btn) {
  position: relative;
  padding-block: 4px;
  font-weight: 500;
  color: var(--ap-text-2);
  text-decoration: none;
  transition: color var(--ap-speed) ease;
}
.ap-header__nav a:not(.ap-btn)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 2px;
  background: var(--ap-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--ap-speed) ease;
}
.ap-header__nav a:not(.ap-btn):hover { color: var(--ap-accent); }
.ap-header__nav a:not(.ap-btn):hover::after { transform: scaleX(1); }

/* Текущий раздел: белый текст и подчёркивание без анимации. Белый, а не
   акцентный, чтобы отметка места не спорила с подсветкой наведения.
   aria-current ставит скрипт в подвале — без JS теряется только отметка,
   сами ссылки работают. */
.ap-header__nav a:not(.ap-btn)[aria-current="page"] { color: var(--ap-white); }
.ap-header__nav a:not(.ap-btn)[aria-current="page"]::after { transform: scaleX(1); }

/* Кнопка живёт только в мобильной панели. Селектор специфичнее одного
   класса намеренно: .ap-btn ниже в файле ставит display:inline-block и по
   порядку победил бы. */
.ap-header__nav a.ap-header__nav-cta { display: none; }

.ap-header__right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--ap-s3);
}

.ap-header__tel {
  /* 44px — тач-цель. Была 23px, ниже минимума WCAG 2.5.8 (24×24), и это
     единственная работающая конверсия при преимущественно мобильном
     трафике. Кегль не менялся, выросла только область нажатия. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 600;
  font-size: 15px;
  color: var(--ap-white);
  text-decoration: none;
  white-space: nowrap;
}
.ap-header__tel:hover { color: var(--ap-accent); }

/* ============ Бургер ============ */
/* Показывается только под 1024px и только когда скрипт жив: без JS кнопка,
   которая ничего не открывает, хуже её отсутствия. */

.ap-burger {
  display: none;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--ap-line);
  border-radius: var(--ap-radius);
  color: var(--ap-white);
  cursor: pointer;
  position: relative;
}
.ap-burger:hover { border-color: var(--ap-accent); color: var(--ap-accent); }

.ap-burger__box,
.ap-burger__box::before,
.ap-burger__box::after {
  position: absolute;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform var(--ap-speed) ease, opacity var(--ap-speed) ease;
}
.ap-burger__box {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.ap-burger__box::before { content: ""; left: 0; transform: translateY(-6px); }
.ap-burger__box::after  { content: ""; left: 0; transform: translateY(6px); }

/* Открыто: средняя полоса под 45°, верхняя доезжает до центра и встаёт под
   90° — вместе даёт крест; нижняя гасится. */
.ap-header.is-nav-open .ap-burger__box { transform: translate(-50%, -50%) rotate(45deg); }
.ap-header.is-nav-open .ap-burger__box::before { transform: rotate(90deg); }
.ap-header.is-nav-open .ap-burger__box::after { opacity: 0; }

/* ============ Фокус клавиатурой ============ */
/* Спека фокус не описывает, но без видимой рамки шапкой нельзя
   пользоваться с клавиатуры. На жёлтой кнопке рамка белая — жёлтая на
   жёлтом невидима. */

.ap-header__nav a:not(.ap-btn):focus-visible,
.ap-header__tel:focus-visible,
.ap-burger:focus-visible,
.ap-header__logo a:focus-visible {
  outline: 2px solid var(--ap-accent);
  outline-offset: 3px;
  border-radius: 2px;
}
.ap-btn:focus-visible {
  outline: 2px solid var(--ap-white);
  outline-offset: 2px;
}

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

.ap-btn {
  /* border-box обязателен: без него min-height задаёт высоту содержимого, и
     кнопка становится 44 + 24 паддинга = 68px. Паддинг 12/20 по спеке
     сохранён, 44px работает как пол, а не как замена. */
  min-height: 44px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 20px;
  border-radius: var(--ap-radius);
  background: var(--ap-accent);
  color: var(--ap-black);
  font-family: "Golos Text", system-ui, sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid var(--ap-accent);
  transition: background var(--ap-speed) ease;
}
.ap-btn:hover { background: var(--ap-accent-hover); color: var(--ap-black); }

.ap-btn--ghost {
  background: transparent;
  color: var(--ap-accent);
}
.ap-btn--ghost:hover { background: var(--ap-accent-soft); color: var(--ap-accent); }

/* ============ Мобильная шапка ============ */
/* Телефон виден всегда — это единственная работающая конверсия.
   В бургер уезжает только меню.
   Всё, что превращает меню в панель, висит на классе .ap-js, который
   ставит скрипт в <head>. Без JS панель не собирается, ссылки остаются
   обычной строкой и переносятся — шапка станет выше, но навигация
   доступна. Раньше здесь стоял display:none без бургера, и под 1024px
   навигации не было вообще. */

@media (max-width: 1023px) {
  .ap-header { gap: var(--ap-s3); flex-wrap: wrap; }
  .ap-js .ap-header { flex-wrap: nowrap; }
  .ap-js .ap-burger { display: inline-flex; }

  /* Панель под шапкой. Абсолютное позиционирование берёт за точку отсчёта
     саму .ap-header — она sticky, то есть уже образует содержащий блок. */
  .ap-js .ap-header__nav {
    position: absolute;
    top: 100%;
    inset-inline: 0;
    /* Twenty Twenty-Five вешает на детей .ap-header (is-layout-flow)
       margin-block-start: 1.2rem. У абсолютно позиционированного элемента
       margin не исчезает: top: 100% ставил панель по низу шапки, а margin
       толкал её ещё на 19.2px вниз — между шапкой и панелью просвечивала
       страница (замерено: низ шапки 80.19, верх панели 98.38). */
    margin-block-start: 0;
    flex-direction: column;
    gap: 0;
    padding: var(--ap-s2) var(--ap-s4) var(--ap-s4);
    background: var(--ap-black);
    border-bottom: 1px solid var(--ap-line);
    visibility: hidden;
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity var(--ap-speed) ease,
                transform var(--ap-speed) ease,
                visibility var(--ap-speed);
  }
  .ap-js .ap-header.is-nav-open .ap-header__nav {
    visibility: visible;
    opacity: 1;
    transform: none;
  }

  /* Строки панели. Молния перед пунктом — тот же приём, что у .ap-tag. */
  .ap-js .ap-header__nav a:not(.ap-btn) {
    padding-block: var(--ap-s3);
    font-size: 16px;
    border-bottom: 1px solid var(--ap-line);
  }
  .ap-js .ap-header__nav a:not(.ap-btn)::before {
    content: "";
    display: inline-block;
    width: .62em;
    height: .88em;
    margin-inline-end: var(--ap-s2);
    background: var(--ap-bolt) center / contain no-repeat;
  }
  /* Подчёркивание — приём десктопной строки, в вертикальной панели оно
     спорит с разделителями. */
  .ap-js .ap-header__nav a:not(.ap-btn)::after { display: none; }
}

/* Ниже ~338px ряд шапки не помещается: 16 (паддинг) + 96 (логотип 24px) +
   12 (зазор) + 145.8 (телефон) + 12 + 40 (бургер) + 16 = 337.8px, и сжаться
   нечему — у телефона white-space: nowrap. На 320px это давало 2px
   горизонтальной прокрутки и съеденный правый паддинг. Логотип до 20px и
   зазор до 8px освобождают 20px: 317.8px, паддинг возвращается.
   Телефон не трогаем — это единственная работающая конверсия, его кегль
   важнее размера логотипа. */
@media (max-width: 359px) {
  .ap-header { gap: var(--ap-s2); }
  .ap-header__logo img { height: 20px; }
}

@media (max-width: 639px) {
  .ap-header__logo img { height: 24px; }
  .ap-header__tel { font-size: 14px; }
  .ap-btn { padding: var(--ap-s2) var(--ap-s4); font-size: 13px; }

  /* Кнопка переезжает из шапки в панель: логотип, телефон, кнопка и бургер
     в 390px одной строкой не помещаются. Видна всегда ровно одна. */
  .ap-js .ap-header__right a.ap-header__cta { display: none; }
  /* flex, а не block: у .ap-btn стоит min-height: 44px, и при блочной
     раскладке строка в 13px ложится сразу под верхний паддинг, а весь
     остаток высоты копится снизу — отступы сверху и снизу выходят разными
     (замерено: строка 13px в боксе содержимого 26px, лишние 13px под ней).
     flex возвращает align-items и justify-content из .ap-btn, оставаясь
     блочным элементом на всю ширину панели. text-align при этом не нужен —
     подпись центрирует justify-content. */
  .ap-js .ap-header__nav a.ap-header__nav-cta {
    display: flex;
    margin-top: var(--ap-s4);
  }
}

/* ============ Герой ============ */

.ap-hero {
  position: relative;
  background: var(--ap-black);
  /* Секция — не первый прямой потомок .wp-site-blocks (первый — шапка),
     а глобальный стиль темы вешает на такие margin-block-start: 1.2rem.
     Без сброса герой проваливался бы на ~19px ниже первого экрана. */
  margin-block-start: 0;
  /* Весь первый экран за вычетом высоты sticky-шапки, иначе низ героя
     уходит под неё.
     dvh, а не svh: svh — это высота экрана при ПОКАЗАННОЙ адресной строке,
     самая маленькая из трёх. Когда строка при скролле уезжает, окно
     становится выше, а герой остаётся прежним — снизу открывается полоса
     следующей секции, и баннер перестаёт занимать экран целиком. Это и было
     замечание «на телефоне баннер не растягивается на всю высоту».
     vh первой строкой — фолбэк для браузеров без *vh-единиц. */
  min-height: calc(100vh - var(--ap-header-h));
  min-height: calc(100dvh - var(--ap-header-h));
  /* Колонка: содержимое и плашки — два элемента потока. Раньше плашки были
     выведены из потока (position: absolute), места под них никто не
     резервировал, и на невысоких окнах они ложились поверх кнопок —
     замерено: 720×480 и 720×560, перекрытие 40px и 35,4px, хит-тест по
     центру обеих кнопок возвращал SPAN.ap-tag. «Позвонить» — основная
     конверсия, а 720×480 это и телефон в ландшафте, и десктоп при зуме
     200% (WCAG 1.4.4). */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  overflow: hidden;
}

/* <picture> сам выведен из потока, чтобы не участвовать во flex-строке
   .ap-hero наравне с .ap-hero__inner — иначе пустая обёртка (у неё
   единственный ребёнок — abs.-позиционированный img) вставала бы лишним
   flex-элементом. */
.ap-hero picture {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Карусель кадров. Слайды лежат стопкой и сменяются кроссфейдом на чистом
   CSS: JS ради трёх картинок — лишняя зависимость в теме без сборки.
   Цикл 21 с, каждый кадр виден 6,5 с, переход 0,5 с.
   Проценты в кадрах: 2,4% = 0,5 с, 33,3% = 7 с, 35,7% = 7,5 с. */
.ap-hero__slide { opacity: 0; animation: ap-hero-fade 21s linear infinite; }

@keyframes ap-hero-fade {
  0%     { opacity: 0; }
  2.4%   { opacity: 1; }
  33.3%  { opacity: 1; }
  35.7%  { opacity: 0; }
  100%   { opacity: 0; }
}

/* Отрицательная задержка у первого кадра: без неё анимация стартует с
   opacity: 0 и первый экран полсекунды показывает чёрный фон вместо фото.
   -0,5 с = те самые 2,4%, то есть кадр уже проявлен на момент загрузки. */
.ap-hero__slide:nth-child(1) { animation-delay: -.5s; }
.ap-hero__slide:nth-child(2) { animation-delay: 6.5s; }
.ap-hero__slide:nth-child(3) { animation-delay: 13.5s; }

/* Смена кадров — движение без запроса пользователя, ровно тот случай,
   ради которого существует этот медиазапрос. Остаётся первый кадр. */
@media (prefers-reduced-motion: reduce) {
  .ap-hero__slide { animation: none; }
  .ap-hero__slide:nth-child(1) { opacity: 1; }
}

.ap-hero__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .85;
}

/* Градиент держит текст читаемым слева и открывает кадр справа */
.ap-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(0,0,0,.93) 0%,
    rgba(0,0,0,.75) 45%,
    rgba(0,0,0,.25) 100%);
}

.ap-hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--ap-container);
  /* margin-block: auto центрирует блок в свободном месте над плашками.
     justify-content: center здесь не годится — auto-поля забирают свободное
     место раньше него, и содержимое прижалось бы к верху. */
  margin: auto;
  /* border-box: без него max-width 1200 плюс паддинги давали бокс 1248 и
     контейнер переставал совпадать со спекой. */
  box-sizing: border-box;
  padding: var(--ap-s8) var(--ap-gutter);
}

.ap-hero h1 {
  /* Потолок 60px, а не 66 из спеки: при 66 строка «Всё, что нужно вашему
     авто,» занимает 1213px при колонке в 1136 и уходит на третью строку.
     60px дают 1103px — запас 33px. Разница в кегле на глаз незаметна,
     лишняя строка в заголовке заметна сразу. Порог тесный: 62px уже не
     хватает трёх пикселей, поэтому слоган длиннее нынешнего потребует
     не подгонки кегля, а другой формулировки. */
  font-size: clamp(30px, 6.4vw, 60px);
  line-height: .99;
  letter-spacing: -.025em;
  margin: 0 0 var(--ap-s4);
}
/* Была <em> ради одного цвета: семантическое усиление там не подразумевалось.
   Заменена на <span> в patterns/hero.php. */
.ap-hero h1 .ap-accent { color: var(--ap-accent); }

/* :not(.ap-eyebrow) обязателен: `.ap-hero p` — класс плюс элемент (0,1,1)
   и перебивает `.ap-eyebrow` (0,1,0) независимо от порядка файлов. Из-за
   этого надзаголовок рендерился 17px цветом --ap-text-2 вместо 11px и
   --ap-text-3, а на 390px переносился в две строки (бокс 56,1px против
   18,1 после починки) и читался как подзаголовок, а не как цитата из
   логотипа. */
.ap-hero p:not(.ap-eyebrow) {
  color: var(--ap-text-2);
  font-size: 17px;
  max-width: 52ch;
  margin: 0 0 var(--ap-s5);
}

.ap-hero__cta { display: flex; gap: var(--ap-s3); flex-wrap: wrap; }

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

.ap-tags {
  position: relative;
  z-index: 2;
  /* Своего margin-top: auto здесь быть не должно. Плашки прижимает к низу
     нижнее auto-поле у .ap-hero__inner; если добавить auto и сюда, свободное
     место делится на три части вместо двух и содержимое героя встаёт заметно
     выше центра — замерено на 390: смещение около 100px. */
  width: 100%;
  max-width: var(--ap-container);
  margin-inline: auto;
  box-sizing: border-box;
  display: flex;
  gap: var(--ap-s2);
  flex-wrap: wrap;
  /* То же боковое поле, что у .ap-hero__inner: раньше у плашек было своё,
     и на 1440 их левый край отставал от края заголовка на 96px. */
  padding: var(--ap-s4) var(--ap-gutter);
}

.ap-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--ap-s2);
  font-size: 13px;
  color: var(--ap-text-2);
  background: rgba(0, 0, 0, .55);
  border: 1px solid var(--ap-line);
  border-radius: 100px;
  padding: var(--ap-s1) var(--ap-s3);
}

/* Плашки ведут на карточку в Яндекс Картах — рейтинг, знак и адрес там и
   живут, проверить их можно только там. text-decoration гасим: подчёркивание
   внутри капсулы читается как брак вёрстки, признак кликабельности —
   ховер и курсор. Цвет ссылок из theme.json перебиваем явно. */
a.ap-tag {
  color: var(--ap-text-2);
  text-decoration: none;
  transition: border-color var(--ap-speed) ease, color var(--ap-speed) ease;
}
a.ap-tag:hover { color: var(--ap-white); border-color: var(--ap-accent); }
a.ap-tag:focus-visible {
  outline: 2px solid var(--ap-accent);
  outline-offset: 2px;
}
.ap-tag::before {
  content: "";
  flex: none;
  width: .62em;
  height: .88em;
  background: var(--ap-bolt) center / contain no-repeat;
}

@media (max-width: 639px) {
  .ap-hero__inner { padding: var(--ap-s7) var(--ap-gutter); }
  .ap-hero::after {
    background: linear-gradient(180deg, rgba(0,0,0,.75) 0%, rgba(0,0,0,.92) 60%);
  }
}

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

.ap-grid {
  display: grid;
  gap: var(--ap-s3);
  grid-template-columns: 1fr;
}
@media (min-width: 640px)  { .ap-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .ap-grid { grid-template-columns: repeat(3, 1fr); } }

.ap-card {
  background: var(--ap-card);
  border: 1px solid var(--ap-line);
  border-radius: var(--ap-radius);
  padding: var(--ap-s4) var(--ap-s5) var(--ap-s5);
  transition: border-color var(--ap-speed) ease, background var(--ap-speed) ease;
}
.ap-card:hover { border-color: #3C3C44; background: var(--ap-elevated); }

/* Уровень заголовка карточки зависит от того, что стоит над сеткой: на
   /uslugi/ группы — h2, значит карточки h3; на главной группы сами h3,
   значит h4. Выглядеть при этом карточки обязаны одинаково, поэтому вид
   задан явно, а не унаследован от уровня. */
.ap-card :is(h3, h4) {
  margin-top: 0;
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 600;
  font-size: 19px;
  line-height: 1.25;
  color: var(--ap-white);
}
/* color: inherit — иначе ссылку в заголовке красит глобальное правило
   ссылок из theme.json в акцентный жёлтый, и в сетке заголовки карточек
   со ссылкой отличаются цветом от заголовков без ссылки. */
.ap-card :is(h3, h4) a { text-decoration: none; color: inherit; }

/* Карточка-ссылка: кликабельна вся, а не только заголовок. Рамка, фон и
   ховер уже описаны у .ap-card, здесь только то, что добавляет ссылка. */
a.ap-card {
  display: block;
  text-decoration: none;
  color: inherit;
}
/* В покое заголовки карточек со ссылкой и без — одного цвета, поэтому
   единственный признак кликабельности — акцент при ховере.
   Специфичность: (0,2,2) против (0,1,1) у .ap-card :is(h3, h4). */
a.ap-card:hover :is(h3, h4) { color: var(--ap-accent); }
a.ap-card:focus-visible {
  outline: 2px solid var(--ap-accent);
  outline-offset: 3px;
}
/* :not(.ap-price) — цена может быть свёрстана абзацем, а `.ap-card p`
   (0,1,1) перебивает `.ap-price` (0,1,0) и красит её в серый. Проверено:
   <p class="ap-price"> внутри карточки отдавал rgb(154,154,162). */
.ap-card p:not(.ap-price) { color: var(--ap-text-3); font-size: 15px; max-width: none; }

/* Карточка «Работы» — с фото. Базовая .ap-card кладёт паддинг на всю
   карточку; здесь верхний паддинг снят и добавлен отрицательный боковой
   у картинки, чтобы фото легло вплотную к скруглённым углам карточки. */
.ap-card--rabota { padding-top: 0; overflow: hidden; }
.ap-card--rabota .ap-card__img {
  display: block;
  width: calc(100% + var(--ap-s5) * 2);
  /* WordPress пишет width/height прямо в атрибуты <img>; если не обнулить
     здесь height, оба размера окажутся не auto и aspect-ratio браузер
     проигнорирует целиком — участок спеки CSS Sizing, не баг вёрстки. */
  height: auto;
  margin: 0 calc(var(--ap-s5) * -1) var(--ap-s4);
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* ============ Пагинация ленты работ ============ */

.pagination {
  display: flex;
  justify-content: center;
  gap: var(--ap-s3);
  margin-top: var(--ap-s6);
}
.pagination .page-numbers {
  color: var(--ap-text-2);
  text-decoration: none;
  padding: var(--ap-s2) var(--ap-s3);
  border-radius: var(--ap-radius);
}
.pagination .page-numbers:hover { color: var(--ap-accent); }
.pagination .page-numbers.current { color: var(--ap-black); background: var(--ap-accent); }

/* На телефоне колонка узкая (~350px) — вертикальное фото на всю ширину
   там и так умещается нормально, рамка и ограничение высоты не нужны.
   Проблема была только на десктопе (~1136px колонка): width:100%
   растягивало портретный кадр до неоправданной высоты, повисавшей в
   пустом фоне страницы. Поэтому рамка и max-height — только от 768px. */
.ap-rabota__frame { display: block; }
.ap-rabota__photo {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--ap-radius);
}
@media (min-width: 768px) {
  .ap-rabota__frame {
    display: flex;
    justify-content: center;
    background: var(--ap-card);
    border: 1px solid var(--ap-line);
    border-radius: var(--ap-radius);
    padding: var(--ap-s4);
  }
  .ap-rabota__photo {
    max-width: 100%;
    max-height: 70vh;
    width: auto;
  }
}

/* ============ Карусель фото на странице работы ============ */

/* Свайп нативным скроллом со snap — без JS: у героя на главной карусель
   ждёт фиксированные три кадра с кроссфейдом по расписанию (см.
   .ap-hero__slide), здесь же кадров переменное число и нужна ручная
   пролистка, а не автопрокрутка. */
/* .ap-carousel — внешний, не скроллится: только он даёт координаты для
   position:absolute у стрелок, поэтому они остаются на месте при любом
   положении прокрутки. Сама прокрутка и scroll-snap — на .ap-carousel__track
   внутри. Стрелка внутри самого скролл-контейнера позиционировалась бы от
   начала ленты и уезжала вместе с ней — так было до этой правки. */
.ap-carousel { position: relative; }
.ap-carousel__track {
  display: flex;
  /* Без stretch (умолчание у флекса по вертикали) все слайды на мобилке
     тянулись бы под высоту самого высокого — у более «квадратных» фото
     снизу висело бы пустое поле. Каждый слайд — своей естественной
     высоты. На десктопе высоту всё равно задаёт aspect-ratio ниже. */
  align-items: flex-start;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: var(--ap-s3);
  /* Навигация уже есть — свайп и стрелки; системный скроллбар снизу только
     торчит нестилизованной полосой поверх тёмной темы. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.ap-carousel__track::-webkit-scrollbar { display: none; }
.ap-carousel__slide {
  position: relative;
  /* z-index:0 замыкает контекст наложения на самом слайде: без него
     z-index:1 у .ap-carousel__img (нужен, чтобы фото легло поверх
     размытой подложки на десктопе) утекал наружу и оказывался выше
     стрелок — клик по кнопке перехватывала картинка. */
  z-index: 0;
  flex: 0 0 100%;
  scroll-snap-align: start;
  border-radius: var(--ap-radius);
  overflow: hidden;
}
/* На мобильной колонке (~350px) вертикальное фото во всю ширину
   умещается нормально без рамки и обрезки — как и одиночное фото
   (.ap-rabota__photo). Фикс-бокс 4:3 с размытой подложкой (иначе портрет
   обрезался бы по object-fit:cover) нужен только на широких экранах,
   где иначе разные фото в ленте скакали бы по высоте при пролистывании. */
.ap-carousel__bg { display: none; }
.ap-carousel__img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
}
@media (min-width: 768px) {
  .ap-carousel__slide {
    aspect-ratio: 4 / 3;
    background: var(--ap-card);
  }
  /* inset:-16px и соответствующая ширина/высота с запасом — иначе
     размытие обнажает свой же край по контуру кадра. */
  .ap-carousel__bg {
    display: block;
    position: absolute;
    inset: -16px;
    width: calc(100% + 32px);
    height: calc(100% + 32px);
    object-fit: cover;
    filter: blur(24px) brightness(.45);
  }
  .ap-carousel__img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
}

/* Стрелки — поверх свайпа, не вместо него: на тач-устройствах пролистывают
   тем же жестом, кнопки нужны для мыши/клавиатуры. Жёлтый фон и чёрная
   иконка — тот же контраст, что у .ap-btn, стрелка не теряется на фото. */
.ap-carousel__arrow {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: var(--ap-s3);
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: var(--ap-accent);
  color: var(--ap-black);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: filter var(--ap-speed) ease;
}
.ap-carousel__arrow:hover { filter: brightness(0.9); }
.ap-carousel__arrow:focus-visible { outline: 2px solid var(--ap-white); outline-offset: 2px; }
.ap-carousel__arrow--next { left: auto; right: var(--ap-s3); }

.ap-price {
  display: block;
  margin-top: var(--ap-s3);
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 600;
  font-size: 15px;
  color: var(--ap-accent);
  font-variant-numeric: tabular-nums;
}

/* ============ Фото в тексте страницы ============ */
/* Кадр из цеха на странице услуги: во всю ширину контейнера, вводный
 * абзац под ним.
 *
 * Прошлые подходы и почему их нет: float с обтеканием и две колонки с
 * квадратом 210px. Оба держали кадр узким рядом с текстом, и оба зависели
 * от длины вводки — на сход-развале она в две строки, на ТО в шесть, и
 * обтекать было нечем. Владелец: растянуть кадры на всю ширину. Ширину
 * задаёт контейнер main, своего потолка здесь нет — общего ограничения
 * строки в проекте больше нет (см. «Текст» в style.css), поэтому и абзац
 * под кадром идёт во всю ширину.
 */

.ap-lead {
  margin-bottom: var(--ap-s5);
}

.ap-lead p {
  margin: var(--ap-s4) 0 0;
}

.ap-figure {
  display: block;
  border: 1px solid var(--ap-line);
  border-radius: var(--ap-radius);
  overflow: hidden;
}

/* display: block — у inline-картинки под базовой линией остаётся просвет
   строки, и снизу внутри рамки появлялась бы полоска фона. */
.ap-figure img {
  display: block;
  width: 100%;
  height: auto;
  /* Пропорция задана и в атрибутах width/height, но при 100% ширины и
     height: auto браузер резервирует место только по ним — дублируем
     свойством, чтобы кадр не прыгал при догрузке. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* ============ Списки с молнией ============ */
/* Молния — знак из логотипа, тот же приём, что на баннере у входа.
   Фоном, а не эмодзи: у ::before с content: "" нет текста, поэтому знак
   не читается скринридером как «high voltage sign» перед каждым пунктом. */

.entry-content ul,
.wp-block-post-content ul {
  list-style: none;
  padding-left: 0;
}
.entry-content ul > li,
.wp-block-post-content ul > li {
  position: relative;
  padding-left: var(--ap-s5);
  margin-bottom: var(--ap-s2);
}
.entry-content ul > li::before,
.wp-block-post-content ul > li::before {
  content: "";
  position: absolute;
  left: 0;
  /* Прижимаем к оптической середине первой строки: интерлиньяж 1.65 при
     кегле 17px даёт строку 28px, знак высотой 15px. */
  top: 7px;
  width: .62em;
  height: .88em;
  background: var(--ap-bolt) center / contain no-repeat;
}

/* Нумерованные оставляем цифрами: в них порядок несёт смысл */
.entry-content ol,
.wp-block-post-content ol { padding-left: var(--ap-s5); }
.entry-content ol > li::marker,
.wp-block-post-content ol > li::marker {
  color: var(--ap-accent);
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 600;
}

/* ============ Цепочка шагов ============ */

.ap-steps { counter-reset: ap-step; list-style: none; padding: 0; }
.ap-steps li {
  counter-increment: ap-step;
  position: relative;
  padding-left: var(--ap-s7);
  margin-bottom: var(--ap-s5);
}
.ap-steps li::before {
  content: counter(ap-step);
  position: absolute;
  left: 0;
  top: 0;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: var(--ap-radius);
  background: var(--ap-accent-soft);
  border: 1px solid var(--ap-accent);
  color: var(--ap-accent);
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 600;
  font-size: 14px;
}
/* У пунктов цепочки своя нумерация — молния из общего правила списков тут
   лишняя, иначе перед номером появится второй знак.

   content обязателен: без него правило молний `.entry-content ul > li::before`
   (0,1,3) перебивает `.ap-steps li::before` (0,1,2) и подставляет content: "",
   то есть кружок остаётся пустым. Проверено в браузере на живой странице:
   с <ul class="ap-steps"> вычисленный content был "" вместо counter. */
.entry-content .ap-steps > li::before,
.wp-block-post-content .ap-steps > li::before {
  content: counter(ap-step);
  background: var(--ap-accent-soft);
  width: 30px;
  height: 30px;
  left: 0;
  top: 0;
}

/* ============ CTA перед подвалом ============ */

.ap-cta {
  background: var(--ap-black);
  border-top: 1px solid var(--ap-line);
  border-bottom: 1px solid var(--ap-line);
  padding: var(--ap-section) var(--ap-gutter);
  text-align: center;
}
.ap-cta h2 { margin-top: 0; }
.ap-cta p { margin-inline: auto; }
.ap-cta__tel {
  display: inline-block;
  margin-top: var(--ap-s4);
  font-family: Unbounded, system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(24px, 4vw, 38px);
  color: var(--ap-accent);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.ap-cta__tel:hover { color: var(--ap-accent-hover); }

/* ============ Подвал ============ */

/* Обёртка подвала — прямой потомок .wp-site-blocks, и Twenty Twenty-Five
   вешает на неё тот же margin-block-start: 1.2rem, что и на шапку. Полоса
   фона страницы над рамкой CTA выглядит случайной чертой. */
.wp-site-blocks > .wp-block-template-part:last-child { margin-block-start: 0; }

.ap-footer {
  background: var(--ap-black);
  /* боковое поле — на внутренних контейнерах, иначе оно удваивается */
  padding-block: var(--ap-section) var(--ap-s6);
  color: var(--ap-text-3);
  font-size: 15px;
}
.ap-footer__grid {
  max-width: var(--ap-container);
  margin-inline: auto;
  display: grid;
  gap: var(--ap-s6);
  grid-template-columns: 1fr;
}
/* Колонок четыре (адрес, часы, связь, услуги). На планшете они встают
   в две пары: при трёх колонках в 640px последняя оставалась одна в
   следующем ряду, а «Часы работы» переносились в разрыв. */
@media (min-width: 640px)  { .ap-footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .ap-footer__grid { grid-template-columns: repeat(4, 1fr); } }
.ap-footer h3 {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ap-text-3);
  font-family: "Golos Text", system-ui, sans-serif;
  margin: 0 0 var(--ap-s3);
}
/* Подчёркнуты все ссылки подвала: на тёмном фоне разница между
   --ap-text-2 у ссылки и --ap-text-3 у обычного текста слишком мала,
   чтобы по ней опознать кликабельное. */
.ap-footer a {
  color: var(--ap-text-2);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ap-footer a:hover { color: var(--ap-accent); }
/* Тач-цель: ссылки подвала стоят плотным столбиком, промахнуться легко. */
.ap-footer a { display: inline-block; padding-block: 4px; }
.ap-footer__bottom {
  max-width: var(--ap-container);
  margin: var(--ap-s6) auto 0;
  padding-top: var(--ap-s4);
  border-top: 1px solid var(--ap-line);
  font-size: 13px;
}

/* ============ Соцсети ============ */
/* Иконки вместо текстовых ссылок «ВКонтакте» и «Телеграм»: логотипы обеих
   сетей узнаются быстрее слова, а в подвале и на контактах они стоят
   рядом и раньше сливались с остальным столбиком ссылок.
   Разметка иконок — inline SVG в parts/footer.html и на /kontakty/:
   спрайта и внешнего файла ради двух знаков заводить незачем. */

.ap-social {
  list-style: none;
  display: flex;
  gap: var(--ap-s2);
  margin: var(--ap-s3) 0 0;
  padding: 0;
}

/* Селектор из двух классов (0,2,0), а не одного: `.ap-footer a` (0,1,1)
   подчёркивает все ссылки подвала, добавляет им вертикальный паддинг и
   ставит display: inline-block. Последнее особенно важно перебить —
   inline-block вместо inline-flex сносит центровку, и иконка садится в
   левый верхний угол рамки (видно на скриншоте подвала до правки). */
.ap-social .ap-social__link {
  /* 44px — тач-цель. Иконка внутри 20px: полем добирается до цели, а не
     кеглем, иначе логотип на телефоне выглядит кнопкой приложения. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--ap-line);
  border-radius: var(--ap-radius);
  color: var(--ap-text-2);
  text-decoration: none;
  transition: color var(--ap-speed), border-color var(--ap-speed);
}
.ap-social .ap-social__link:hover { color: var(--ap-accent); border-color: var(--ap-accent); }

.ap-social__icon {
  width: 20px;
  height: 20px;
  display: block;
  fill: currentColor;
}

/* Молния из общего правила списков перед иконкой не нужна — тот же случай,
   что у .ap-steps выше: без content правило молний перебивает и рисует
   пустой блок в отступе. */
.entry-content .ap-social > li,
.wp-block-post-content .ap-social > li {
  padding-left: 0;
  margin-bottom: 0;
  max-width: none;
}
.entry-content .ap-social > li::before,
.wp-block-post-content .ap-social > li::before {
  content: none;
}

/* ============ Хлебные крошки ============ */

.ap-crumbs {
  max-width: var(--ap-container);
  margin: var(--ap-s5) auto 0;
  padding-inline: var(--ap-gutter);
  font-size: 13px;
  color: var(--ap-text-3);
}
.ap-crumbs a { color: var(--ap-text-3); text-decoration: none; }
.ap-crumbs a:hover { color: var(--ap-accent); }
.ap-crumbs span[aria-current] { color: var(--ap-text-2); }

/* ============ Карта на контактах ============ */

.ap-map {
  margin-top: var(--ap-s3);
  border-radius: var(--ap-radius);
  overflow: hidden;
  aspect-ratio: 16 / 9;
}
.ap-map iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
@media (min-width: 768px) {
  .ap-map { aspect-ratio: 21 / 9; }
}
