/* ============================================================
   UniBuilder — ИП Кириенко С.В. (kirienko-theme)
   Задача Bitrix24 87390. Тип: ЦПП.

   Гамма подобрана скиллом site-color-palette, вариант Б
   «Современный консультант». Фирстиля и лого у заказчика нет —
   строить вокруг было нечего.

     #0F5257 — глубокий сине-зелёный, основной  → --p (шапка, кнопки, ссылки)
     #8A6520 — охра, акцент                     → --a (вторая CTA, иконки, линии)
     #DEEBEA — светлая плашка бренда            → --pl
     #0B3A3E — тёмные секции и футер            → --dk

   Почему не эталонный синий ниши (#1B365D, строка «Юр. услуги, финансы,
   бухгалтерия» в references/palettes.md): он проходит по контрасту, но
   читается как банк. Клиент — частный финансовый консультант по ОКВЭД
   66.19, то есть ПОСРЕДНИК, и треть услуг адресована физлицам с долгами.
   Сходство сайта посредника с банком — не стилистическая, а
   репутационная и юридическая проблема. Сине-зелёный держит то же
   доверие без учрежденческого тона.
   Почему не зелёный (#1F6B4A): зелёная кнопка на финансовом сайте
   читается как «одобрено» — ложное обещание результата в теме, где
   обещать результат нельзя.

   Контраст (scripts/contrast.py скилла, WCAG 2.1):
     текст --n900 на фоне ......... 15.29:1  AAA
     белый на --p ................. 8.88:1   AAA
     --p ссылкой на фоне .......... 8.71:1   AAA
     белый на --a ................. 5.30:1   AA   ← акцент годится под кнопку
     --a на фоне страницы ......... 5.20:1   AA
     текст на светлой плашке ...... 12.75:1  AAA
     белый на --ph (hover) ........ 11.95:1  AAA
     белый на --ah (hover акцента)  7.43:1   AAA
     белый в футере ............... 12.43:1  AAA
     ссылка футера на --footer-bg .. 7.33:1   AAA
     приглушённый текст футера ..... 5.32:1   AA
   Провалов нет.

   Level-A: один :root override поверх templates/_base/global.css плюс
   несколько правил на стабильных hook-классах. Своей вёрстки блоков нет.

   Шрифт: Onest — коробка уже раздаёт его self-hosted из /fonts/fonts.css
   (400/500/600/700, latin+cyrillic). Google Fonts не подключаем.
   ============================================================ */
:root{
  /* — Бренд: сине-зелёный. Дублируем через var(--ub-*, fallback), чтобы
       владельческое «оставить свои цвета» в админке продолжало работать. — */
  --p:var(--ub-primary,#0f5257);--ph:var(--ub-primary-hover,#0a3d41);
  --pl:var(--ub-primary-light,#deebea);--p50:#f1f7f6;

  /* — Акцент: охра. Проходит AA под белым текстом (5.30:1), поэтому им
       можно красить вторую кнопку, а не только иконки и линии. — */
  --a:var(--ub-accent,#8a6520);--ah:var(--ub-accent-hover,#6e5019);

  /* — 🔴 --al задаём ВЫЧИСЛЕНИЕМ, а не через var(--ub-accent-light, …).
       Коробка выводит --ub-accent-light из theme_accent сама и на нашей
       охре #8A6520 даёт #D0B88B — это средний тон, а не бледный тинт.
       Раз переменная задана, фолбэк в var() не применяется НИКОГДА, и
       наше #F6EFE0 просто не срабатывало. Отдельной настройки под
       светлый тинт в админке нет (есть только theme_color и theme_accent).
       Результат был виден в градиенте --gs: под формой лежала бежевая
       плита, а описание на её охряном конце давало 2.28:1.

       color-mix оставляет связь с админкой: сменят акцент в настройках —
       тинт поедет за ним, но останется бледным. Функция в коробке уже
       используется (фокус-кольца в blocks-base.css), так что опора
       безопасная. — */
  --al:color-mix(in srgb, var(--a) 14%, var(--surface));

  /* — Градиенты бренда: следуют за --p/--ph, переживают смену цвета в админке. — */
  --gp:linear-gradient(135deg,var(--p),var(--ph));
  --gs:linear-gradient(135deg,var(--surface-3),var(--al));

  /* — Тёмные секции (features/dark, футер) и тёмный hero. — */
  --dk:#0b3a3e;--dc:#0f5257;--ds:#1a5f64;
  --gd:linear-gradient(135deg,#0b3a3e,#0f5257,#1a5f64);
  --gh:linear-gradient(160deg,#0b3a3e 0%,#0f5257 60%,#1a5f64 100%);

  /* — Поверхности. Чистый #fff большими плоскостями не берём: тёплый
       белый мягче на светлых экранах. — */
  --surface:#fcfdfc;--surface-2:#f2f6f5;--surface-3:#f8fbfa;

  /* — Нейтрали с сине-зелёным подтоном (не «серые из коробки») — именно это
       держит гамму цельной. Текст — --n900, не #000. — */
  --n50:#f5f8f7;--n100:#eaf0ef;--n200:#dbe4e3;--n300:#bcc9c8;
  --n400:#8fa09f;--n500:#627271;--n600:#516260;--n700:#394846;
  --n800:#283534;--n900:#17262a;

  /* — 🔴 --n500 затемнён на 8% против исходного #6b7c7b (правка 07.09).
       Им покрашены подзаголовки секций (18px) и описания форм (16px) —
       это текст обычного размера, порог AA 4.5. Исходное значение не
       проходило НИ НА ОДНОЙ светлой поверхности сайта: 4.38 на белом,
       4.30 на --surface, 4.10 на --n50, 4.02 на --surface-2. Провал был
       незаметен на глаз именно потому, что везде почти дотягивал.
       #627271 даёт 5.04 / 4.95 / 4.72 / 4.63 — проходит везде.
       Светлее делать нельзя: связывающее ограничение — --surface-2,
       самая тёмная из светлых поверхностей. — */

  /* — 🔴 Токены, которые должны приходить из templates/_base/global.css,
       но :root базы не доезжает до CSSOM (болячка вскрыта на rki-theme и
       подтверждена на kononov-theme). Без них правила вида
       `border:var(--bw-2) solid var(--p)` становятся невалидными целиком,
       и контурные кнопки теряют рамку. Значения взяты из базы 1-в-1.
       Это обход, а не лечение. — */
  --bw-2:2px;
  --s1:4px;--s2:8px;--s3:16px;--s4:24px;--s5:40px;--s6:64px;
  --ok:#10B981;--ok50:#ECFDF5;
  --fs-body:1rem;
  --tr:transform var(--transition-duration,.2s) var(--transition-easing,ease),
       box-shadow var(--transition-duration,.2s) var(--transition-easing,ease),
       border-color var(--transition-duration,.2s) var(--transition-easing,ease),
       background-color var(--transition-duration,.2s) var(--transition-easing,ease),
       color var(--transition-duration,.2s) var(--transition-easing,ease),
       opacity var(--transition-duration,.2s) var(--transition-easing,ease);

  /* — Радиусы: 🔴 анти-клон. У всех соседних ЦПП-сайтов карточки на
       радиусе 12–16 с мягкими тенями — силуэт страницы получается один
       и тот же независимо от цвета. Здесь карточки почти прямоугольные:
       для финансового консультанта «документная» строгость уместнее
       скруглённой мягкости, и сайт перестаёт читаться как соседний. — */
  --rs:4px;--rm:6px;--rl:4px;--rxl:8px;--rf:9999px;
  --r-btn:4px;--r-card:4px;--r-input:4px;
  --bw:1px;

  /* — Тени тонированы бренд-цветом, а не чистым чёрным. — */
  --sh-sm:rgba(15,82,87,0.06) 0px 0px 0px 1px,rgba(15,82,87,0.06) 0px 1px 2px 0px;
  --sh-brand:rgba(15,82,87,0.08) 0px 0px 0px 1px,rgba(15,82,87,0.08) 0px 2px 6px 0px;
  --sh-modal:rgba(15,82,87,0.10) 0px 0px 0px 1px,rgba(11,58,62,0.14) 0px 12px 32px 0px;
  --sh-focus:rgba(138,101,32,0.32) 0px 0px 0px 3px;
  --sh-btn:rgba(15,82,87,0.18) 0px 2px 8px 0px;
  --sh-btn-hover:rgba(15,82,87,0.28) 0px 4px 14px 0px;

  /* — Шрифт: Onest (self-hosted коробкой). — */
  --fh:'Onest',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --fb:'Onest',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;

  /* — Заголовки: сплошным тёмным. `none` здесь ломает clip-текст.

       🔴 Анти-клон: шкала и начертание отличаются от соседних ЦПП-сайтов
       осознанно. Там h1 доходит до 3.25rem при весе 700 — это интонация
       лендинга, который громко продаёт. Здесь консультант по долгам:
       человек приходит с проблемой, и крик на первом экране работает
       против доверия. Шкала спокойнее (до 2.75rem), вес 600, межбуквенное
       плотнее, интерлиньяж свободнее — текст читается как разговор,
       а не как оффер. — */
  --h1:clamp(2rem,3.4vw,2.75rem);--h2:clamp(1.5rem,2.5vw,1.875rem);
  --h3:clamp(1.1875rem,2.2vw,1.4375rem);--h4:clamp(1.0625rem,1.7vw,1.1875rem);
  --heading-weight:600;--heading-tracking:-.022em;--lh-heading:1.22;--lh-body:1.7;
  --heading-gradient:var(--n900);

  /* — Featured-карта: тонкий бренд-бордер, без цветной планки сверху. — */
  --card-featured-border:1px solid var(--p);--card-featured-accent:transparent;

  /* — Эмфатика: цифры в stats крупно. — */
  --title-weight:700;--stat-value-size:clamp(2.25rem,4.5vw,3rem);
  --stat-value-size-sm:clamp(1.75rem,3.5vw,2.25rem);
  --price-size:clamp(1.875rem,4vw,2.5rem);

  /* — Вертикальный ритм. Базовые 80/96 дают ~180px пустоты между соседними
       блоками, сайт разваливается на несвязанные экраны. Держим тот же
       зажатый ритм, что и структурный слой lib/theme_overrides.css. — */
  --section-py:clamp(32px,3.5vw,50px);
  --section-py-lg:clamp(44px,5vw,72px);
  --section-py-md:clamp(24px,2.8vw,40px);
  --section-py-sm:clamp(24px,3vw,40px);

  /* — Иконки красятся одним цветом, а не «каждая своим». — */
  --icon:var(--a);

  /* — Футер: тёмный сине-зелёный. Ссылка #B6CBCA на #0B3A3E — 7.33:1. — */
  --footer-bg:#0b3a3e;--footer-text:#ffffff;--footer-muted:#8fb0ae;--footer-heading:#ffffff;
  --footer-link:#b6cbca;--footer-link-hover:#ffffff;--footer-border:#1a5f64;
}

/* — Базовая типографика вешает на заголовки margin-top (~33px у h2). Когда
     заголовок стоит ПЕРВЫМ в секции, он складывается с её padding-top и между
     блоками набегает лишнее, причём в инспекторе паддинги выглядят нормальными.
     Первому заголовку в контейнере верхний отступ не нужен — вертикаль задаёт
     padding секции. Последующие заголовки свой margin сохраняют. — */
h1:first-child,h2:first-child,h3:first-child,h4:first-child{margin-top:0}
.ct > *:first-child > h1:first-child,
.ct > *:first-child > h2:first-child,
.ct > *:first-child > h3:first-child{margin-top:0}

/* — Заголовок и лид секции должны читаться как один блок: 33px от margin-bottom
     у h2 разрывают пару. Якорь `.sh` — общая обёртка шапки секции у features,
     services, steps и остальных, ритм остаётся единым. — */
.sh h2{margin-bottom:12px}

/* — Фирменная деталь: короткая охряная линия над заголовком секции.
     Дешёвый, но заметный признак — он есть на каждой странице и его нет
     ни на одном соседнем ЦПП-сайте, где секции отличаются только цветом
     заголовка.
     ⚠️ Линия центрируется автополями: на этом сайте ВСЕ пять .sh
     выровнены по центру (проверено на витрине). Появится блок с левым
     выравниванием шапки — линию нужно будет выравнивать по нему,
     иначе повиснет посередине над левым заголовком. — */
.sh h2::before{
  content:"";display:block;width:44px;height:3px;
  background:var(--a);border-radius:2px;margin:0 auto 18px;
}
/* У faq/accordion-text заголовок секции лежит НЕ в .sh, а прямо в левой
   колонке .bm-side, и выровнен по левому краю — правило выше его не ловило,
   и единственная секция на сайте оставалась без фирменной линии.
   Здесь та же линия, но прижатая влево. */
.bm-faq-acctext .bm-side h2::before{
  content:"";display:block;width:44px;height:3px;
  background:var(--a);border-radius:2px;margin:0 0 18px;
}

/* — 🔴 Кнопка на тёмном hero. --gp построен на том же сине-зелёном, что и фон
     секции: бренд-кнопка на нём сливается с подложкой. Здесь за это отвечает
     охра — белый на ней 5.30:1 (AA), и она чисто отделяется от фона.
     Якорь — класс вариации. 🔴 Он бывает БЕЗ id-суффикса (`bm-hero-large`,
     а не `bm-hero-large-<id>`), поэтому селектор с дефисом на конце сам по
     себе не срабатывает. Ловим оба написания. Светлые hero
     (standard/centered-clean) правило не трогает: там бренд на белом
     читается сам по себе. — */
.bm-hero-large .btn-p,.bm-hero-video .btn-p,
.bm-hero-standard .btn-p,[class*="bm-hero-cclean"] .btn-p,
[class*="bm-hero-large-"] .btn-p,
[class*="bm-hero-video-"] .btn-p{
  background:var(--a);color:#fff;border:none;box-shadow:var(--sh-btn);
}
.bm-hero-large .btn-p:hover,.bm-hero-video .btn-p:hover,
.bm-hero-standard .btn-p:hover,[class*="bm-hero-cclean"] .btn-p:hover,
[class*="bm-hero-large-"] .btn-p:hover,
[class*="bm-hero-video-"] .btn-p:hover{
  background:var(--ah);color:#fff;box-shadow:var(--sh-btn-hover);
}
/* Вторичная кнопка там же — контурная белым по тёмному фону. */
.bm-hero-large .btn-o,.bm-hero-video .btn-o,
[class*="bm-hero-large-"] .btn-o,
[class*="bm-hero-video-"] .btn-o{
  background:transparent;color:#fff;border:2px solid rgba(255,255,255,.55);
}
.bm-hero-large .btn-o:hover,.bm-hero-video .btn-o:hover,
[class*="bm-hero-large-"] .btn-o:hover,
[class*="bm-hero-video-"] .btn-o:hover{
  background:rgba(255,255,255,.12);color:#fff;border-color:#fff;
}

/* — Подпись под логотипом в подвале. Движок печатает туда site_name отдельным
     абзацем, а site_name убрать нельзя — из него собираются <title> и
     og:site_name. Отдельной настройки под эту строку в подвале нет, поэтому
     гасим сам элемент. Селектор бьёт ровно по абзацу сразу за логотипом
     подвала; копирайт лежит в <div class="ub-footer-muted"> и под правило не
     попадает — он остаётся, это пункт чек-листа приёмки. — */
.ub-footer-logo + p.ub-footer-muted{display:none}

/* — Меню в шапке по центру страницы.

     Ряд шапки — три flex-ячейки: логотип (48px), <nav class="ub-header-nav
     flex-1"> и правая зона с телефоном и кнопкой (335px). У nav стоит
     flex-1, поэтому ссылки прижаты к левому краю середины.

     🔴 Одного justify-content:center на nav НЕ хватает: он центрирует
     ссылки внутри собственного бокса nav, а бокс несимметричен относительно
     страницы — слева от него 48px логотипа, справа 335px правой зоны.
     Меню вставало на ~144px левее настоящего центра, и на глаз это
     читалось как «почти по центру, но криво».

     🔴 Вариант «дать обеим боковым ячейкам flex:1 1 0» даёт настоящий
     центр только пока правая зона помещается в свою половину остатка.
     Она nowrap и ужаться ниже своего содержимого не может: добавят один
     пункт меню — и центрирование молча сломается, а выглядеть будет как
     «съехало само». Поэтому не он.

     🔴 Вариант «вывести nav из потока и центрировать по ряду» тоже не
     подошёл, хотя даёт математически точный центр. Замеры: меню 563px,
     правая зона 335px, а контейнер шапки зажат в 1136px на ЛЮБОЙ ширине
     экрана (1440, 1920 — всё равно 1136). Меню, поставленное в центр
     страницы, занимает 286..849, правая зона начинается на 801 — наложение
     на 49px, телефон уезжает под ссылки. Чтобы точный центр помещался,
     ряду нужно ≥1233px, которых у него не бывает.

     Поэтому здесь меню стоит симметрично между логотипом и правой зоной:
     свободное место (190px) делится поровну автополями самого nav. Ровно
     по центру страницы его не поставить — правая зона в семь раз шире
     логотипа, и для истинного центра ей пришлось бы налезть на меню.
     Освободить место можно только убрав телефон из шапки — это решение
     заказчика, не тема.

     Гейт 1200px: ниже правая зона и меню начинают сходиться, а под md
     (768px) nav вообще скрыт и уходит в бургер — там правило не нужно. — */
@media (min-width:1200px){
  .ub-header-nav{
    flex:0 0 auto;justify-content:center;
    margin-left:auto;margin-right:auto;
  }
  /* Боковым ячейкам растяжение запрещаем: иначе слack уходит им, а не
     автополям nav, и меню снова прилипает к логотипу. */
  div:has(> .ub-header-nav) > div:first-child{flex:0 0 auto}
  .ub-header-nav ~ div{flex:0 0 auto}
  /* Без запрета переноса min-content правой зоны считается по самому
     длинному слову и «Оставить заявку» ломается на две строки. */
  .ub-header-nav ~ div a,
  .ub-header-nav ~ div button,
  .ub-header-nav ~ div span{white-space:nowrap}
}

/* — 🔴 services/overlay: штатная вуаль под текстом карточки слишком слабая.

     У вариации своя градиентная вуаль (.bm-lay), построенная на --dk:
     снизу 85% прозрачности, сверху 10%. Заголовок карточки стоит на 48%
     высоты снизу, то есть попадает примерно на 49% плотности — и на наших
     фотографиях белый заголовок давал 3.26–4.24:1 при норме AA 4.5.
     Формально «крупный текст» проходит по 3.0, но заголовок карточки идёт
     весом 600, а не 700, и полагаться на послабление для крупного текста
     здесь не стоит.

     Стопы пересчитаны под эти три кадра: снизу 93%, сверху 35% — в точке
     заголовка выходит ~65% и худший кадр даёт 4.86:1. Считалось так же,
     как для hero: 90-й перцентиль яркости в полосе, где лежит текст.
     Меняются картинки карточек — пересчитать. — */
.bm-srv-overlay .bm-lay{
  background-image:linear-gradient(to top,
    rgba(11,58,62,.93) 0%, rgba(11,58,62,.35) 100%);
}

/* — 🔴 Описание карточки было практически невидимым.

     Заголовок карточки вариация красит белым, а описание оставляет
     базовым тёмным (--n800, #283534) — и кладёт его на ту же тёмную
     вуаль поверх фотографии. Замер на витрине: 1.67–1.90:1 при норме
     AA 4.5, то есть текст физически не читался. Ровно в этом описании
     стоит подпись аудитории («Для бизнеса» / «Для частных лиц»), ради
     которой карточки и разводились по двум аудиториям, — потеря не
     косметическая.

     Описание лежит в БЕЗЫМЯННОМ <div> внутри .bm-lay (класса у него
     нет), поэтому цепляем его как единственного не-заголовочного
     ребёнка. Белый даёт 6.70–7.62:1 на наших трёх кадрах. — */
.bm-srv-overlay .bm-lay > *:not(.bm-it){color:#fff}

/* — 🔴 Форма внутри тёмного блока контактов.

     После объединения блоков (07.09) форма обратной связи переехала внутрь
     contacts/map-right, то есть с белой страницы на градиент --gh. Сами
     поля коробка под тёмное оформила (заливка белым 5%, белый текст), но
     два элемента остались от светлой версии:

     1. Подписи полей .fl шли цветом --n400 #8FA09F при кегле 12.8px — это
        текст обычного размера, порог 4.5. По стопам градиента выходило
        4.56 / 3.25 / 2.69, то есть на двух из трёх — провал.
        --n300 тоже не спасает (7.30 / 5.21 / 4.30), берём --n200:
        9.60 / 6.86 / 5.66.

     2. Рамка поля rgba(255,255,255,.12) против фона секции давала 1.18–1.24
        при пороге 3.0 для границ элементов управления — поля не читались
        как поля. Полупрозрачный белый тут не годится в принципе: при 45%
        всё ещё 2.49, а выше начинается «молоко». Сплошной --n300 даёт
        7.30 / 5.21 / 4.30 — единственный вариант, проходящий на всех стопах.

     Правило узкое — только форма внутри блока контактов; отдельные
     form/* блоки стоят на светлом и своих цветов не меняют. — */
.bm-con-map-right .fl{color:var(--n200)}
.bm-con-map-right .fi{border-color:var(--n300)}

/* — 🔴 Адаптив для сетки, заданной пропом columns.

     Проп `columns` вариаций features/* пишет grid-template-columns
     ИНЛАЙНОМ и БЕЗ единого медиазапроса: `repeat(3,1fr)` остаётся
     тремя колонками и на 375px, где карточка ужимается до ~110px.
     Коробочный дефолт (`auto-fit, minmax(260px,1fr)`) адаптивен, но
     при нашей ширине контейнера даёт четыре колонки, а шесть пунктов
     ложатся 4+2 рвано — поэтому колонки и заданы явно.

     Селектор бьёт ТОЛЬКО по блокам, которым columns=3 реально проставлен,
     поэтому остальные features/* (например «Принципы работы» на «О нас»,
     где 4 пункта) продолжают жить на коробочном auto-fit.
     !important нужен, чтобы перебить инлайновый стиль. — */
@media (max-width:899px){
  .bm-g[style*="repeat(3"]{grid-template-columns:repeat(2,1fr) !important}
}
@media (max-width:599px){
  .bm-g[style*="repeat(3"]{grid-template-columns:1fr !important}
}

/* — Шапка модальной формы. Сам элемент дописывается скриптом (см.
     MODAL_HEADER_SCRIPT в scripts/build_site.py — рендерер модалки
     заголовок не выводит), здесь только его оформление. Отступ снизу
     отделяет шапку от первого поля, чтобы окно не читалось сплошняком. — */
.ub-modal-header{margin-bottom:20px}
.ub-modal-title{
  font-family:var(--fh);font-size:1.25rem;font-weight:var(--heading-weight);
  letter-spacing:var(--heading-tracking);line-height:1.25;
  color:var(--n900);margin:0 0 8px;
}
.ub-modal-subtitle{
  font-size:.875rem;line-height:1.55;color:var(--n500);margin:0;
}

/* — 🔴 Иконки контактов сливались с фоном.

     Иконки почты, телефона и адреса — инлайновые SVG, у которых обводка
     прибита к переменным темы прямо в атрибуте style: контур идёт
     `stroke:var(--p)`, внутренняя линия — `stroke:var(--a)`. На светлых
     блоках это работает, но contacts/map-left стоит на градиенте --gh,
     собранном из ТЕХ ЖЕ цветов. Замер по трём стопам градиента
     (#0B3A3E / #0F5257 / #1A5F64) при пороге 3.0 для нетекстовых элементов:

       --p  #0F5257 (контур) ..... 1.40 / 1.00 / 1.21  ← на среднем стопе
                                                         цвет в цвет
       --a  #8A6520 (акцент) ..... 2.35 / 1.68 / 1.38

     То есть контур не «плохо виден», его буквально нет.

     Решение: ореховая плашка под иконкой + белая иконка на ней.

     Плашка — это .bm-ci-i (36×36, радиус 6), у коробки она залита
     rgba(--p,.15), то есть той же бирюзой в 15% и на тёмном фоне не видна
     вовсе. Заливаем брендовой охрой --a #8A6520.

     ⚠️ Сама плашка против фона секции даёт 2.35 / 1.68 / 1.38 — ореховый
     и тёмно-бирюзовый близки по светлоте, сильнее развести их, не выходя
     из гаммы, нельзя (--ah #6E5019 ещё хуже: 1.67 / 1.19 / 1.01). Поэтому
     читаемость несёт ИКОНКА на плашке, а не граница плашки с фоном.
     Требование 1.4.11 к нетекстовому контрасту выполняется на паре
     «иконка ↔ её фон», и это корректно: смысл несёт значок.

     Иконка — белая: на охре 5.30:1. Прежняя светлая охра #E3B85C здесь
     уже не годится — на плашке она даёт 2.85, ниже порога 3.0.
     Красим целиком одним цветом: двухцветный значок на цветной плашке
     читается как грязь.

     🔴 !important обязателен: обводка задана ИНЛАЙНОВЫМ style
     (`stroke:var(--p)` и `stroke:var(--a)` прямо в атрибуте), обычное
     правило таблицы стилей его не перебьёт.
     Правило намеренно узкое — только map-left: в остальных местах эти же
     иконки лежат на светлом, там исходные цвета верны. — */
.bm-con-map-left .bm-ci-i,
.bm-con-map-right .bm-ci-i{background:var(--a)}
.bm-con-map-left .bm-ci-i svg [style*="stroke"],
.bm-con-map-right .bm-ci-i svg [style*="stroke"]{stroke:#fff !important}

/* — 🔴 hero/slider: затемнение под текстом.

     Вариация берёт картинку слайда из props.imageUrl и кладёт её в
     style="background-image" на .bm-hs-slide, но настройки блока
     overlay / overlayColor / overlayOpacity при этом ИГНОРИРУЕТ — в
     разметке слайдера никакого оверлейного слоя нет (проверено на
     витрине), из секции приезжает только color:#ffffff. В итоге белый
     заголовок ложится прямо на фотографию.

     Рисуем слой сами. Плотность 67% — по худшему из трёх кадров слайдера
     (hero-refinancing, самый светлый; см. scripts/check_hero_contrast.py).
     Одно значение на все слайды: разные плотности у соседних слайдов
     читались бы как мигание при перелистывании. — */
/* — form/minimal: согласие над кнопкой, оба слева.

     Вариация рендерит форму одним flex-контейнером с переносом
     (form.bm-row), а кнопку и чекбокс согласия кладёт последними —
     они попадали на одну перенесённую строку: кнопка слева, согласие
     справа от неё. Порядок чтения выходил обратный смыслу: сначала
     «Отправить», потом то, с чем соглашаешься.

     Переставляем порядком, а не перестановкой в разметке: `order`
     у флекс-элементов, плюс flex-basis 100% чекбоксу, чтобы он занял
     строку целиком и вытолкнул кнопку на следующую. Кнопке —
     margin-right:auto, иначе её центрирует justify-content контейнера.

     Правило только для minimal: form/standard и форма внутри
     contacts/map-right свёрстаны иначе, там кнопка и согласие уже
     стоят как надо. — */
.bm-frm-minimal form.bm-row{justify-content:flex-start}
.bm-frm-minimal .bm-consent{
  order:1;flex:0 0 100%;justify-content:flex-start;
  text-align:left;margin-top:4px;
}
.bm-frm-minimal .bm-row > .btn{
  order:2;flex:0 0 auto;margin-right:auto;margin-top:4px;
}

/* — 🔴 Мобильный вертикальный ритм.

     Отступы на мобильной совпадали с десктопными один в один: hero 72/72,
     обычные секции 48/48, FAQ 56/56. Зазор между блоками (сумма нижнего
     и верхнего паддингов соседей) выходил 96px везде и 120px после первого
     экрана — на высоте 812px это 12–15% экрана на каждый стык.

     Причина: --section-py:clamp(32px,3.5vw,50px) из :root НЕ РАБОТАЕТ для
     контентных блоков вообще. add_block пишет pt/pb инлайном в style
     каждой секции, а инлайн перебивает любое правило темы; медиазапросов
     у инлайна нет по определению. Поэтому здесь !important — единственный
     способ, и это обход, а не лечение: правильно было бы не писать
     инлайновый padding в ядре.

     Исключения намеренные:
       • .bm-hero-slider — у него инлайн 0/0, фото должно идти встык
         с шапкой (иначе возвращаются тёмные полосы, см. build_site.py);
       • .bm-cta-withimage — у него свои 24/24 по отдельной правке.

     Боковой отступ контейнера на мобильной был 14px против 24px на
     десктопе — текст почти упирался в края. 16px — нижняя граница нормы. — */
@media (max-width:767px){
  section[data-block-id]:not([class*="bm-hero"]):not(.bm-cta-withimage){
    padding-top:32px !important;padding-bottom:32px !important;
  }
  section[data-block-id][class*="bm-hero"]:not(.bm-hero-slider){
    padding-top:48px !important;padding-bottom:48px !important;
  }
  .ct{padding-left:16px !important;padding-right:16px !important}
}

/* — cta/with-image на мобильной: без фотографии и с зажатыми отступами.

     На 375px блок занимал 784px, из них 320px — обёртка .bm-img. Кадр там
     чисто декоративный (тот же, что фоном на «Услугах»), смысла не несёт,
     а треть экрана съедает и отодвигает кнопку. Прячем обёртку целиком,
     а не сам <img>: иначе от неё останется пустая коробка на 320px.
     С единственным оставшимся ребёнком .bm-txt flex-контейнер сам
     разложится на всю ширину.

     🔴 Отступы 48/48 приходят ИНЛАЙНОМ из settings блока
     (style="padding-top:48px;padding-bottom:48px"), поэтому !important
     обязателен. 24px = --s4, оставшийся блок — просто заголовок, абзац
     и кнопка, ему полка в 48px уже не нужна. — */
@media (max-width:767px){
  .bm-cta-withimage .bm-img{display:none}
  .bm-cta-withimage{padding-top:24px !important;padding-bottom:24px !important}
}

/* — Стрелки слайдера на мобильной.

     Swiper рисует .swiper-button-prev/next по 44×44 у самых краёв
     (на 375px это x=10 и x=321) — они ложатся прямо на заголовок и
     подзаголовок слайда. На узком экране это лишний орган управления:
     слайды листаются свайпом, а точки пагинации остаются кликабельными
     (.swiper-pagination-clickable), так что способ переключения не
     теряется.

     Гейт 767px — под коробочный md. Правило узкое, только hero-слайдер:
     другим swiper-каруселям (галереи, отзывы) стрелки не трогаем. — */
@media (max-width:767px){
  .bm-hero-slider .swiper-button-prev,
  .bm-hero-slider .swiper-button-next{display:none}
}

.bm-hero-slider .bm-hs-slide{position:relative}
.bm-hero-slider .bm-hs-slide::before{
  content:"";position:absolute;inset:0;background:#0b3a3e;opacity:.67;
  pointer-events:none;
}
.bm-hero-slider .bm-hs-slide > .ct{position:relative;z-index:1}

/* — 🔴 FAQ: ответы не раскрывались вообще.

     Клик по вопросу вешал на .bm-i класс open и ставил aria-expanded=true,
     но .bm-aw оставался нулевой высоты — на витрине это выглядело как
     «аккордеон не работает», причём в разметке всё правильное.

     Причина в css/blocks-base.css: там на .bm-aw объявлен
     `grid-template-rows:0fr` плюс `transition:grid-template-rows .35s`,
     а .open переводит трек в 1fr. Проверено прямо на странице
     (Chrome 148): с `transition:none` ответ раскрывается на 106px, с
     transition вычисленное значение так и остаётся 0px — интерполяция
     0fr → 1fr не стартует. Форсировать `grid-template-rows:1fr !important`
     при живом transition тоже не помогает, то есть дело именно в переходе,
     а не в специфичности.

     Правило в коробке общее для всех трёх аккордеонных вариаций
     (accordion / acctext / accexpert), поэтому гасим переход у всех —
     иначе тот же блок сломается при смене вариации. Это обход в теме;
     чинить надо в ядре, в blocks-base.css.

     Раскрытие становится мгновенным. Чтобы не выглядело рывком, добавляем
     проявление самого текста — opacity анимируется без всяких оговорок.
     max-height намеренно НЕ используем: он требует потолка в пикселях,
     и длинный ответ им обрежется. — */
.bm-faq-accordion .bm-aw,
.bm-faq-acctext .bm-aw,
.bm-faq-accexpert .bm-aw{transition:none}
.bm-faq-accordion .bm-a-inner,
.bm-faq-acctext .bm-a-inner,
.bm-faq-accexpert .bm-a-inner{opacity:0;transition:opacity .2s ease}
.bm-faq-accordion .bm-i.open .bm-a-inner,
.bm-faq-acctext .bm-i.open .bm-a-inner,
.bm-faq-accexpert .bm-i.open .bm-a-inner{opacity:1}

/* — Левая колонка FAQ липнет при прокрутке.

     🔴 Одного position:sticky мало: .bm-side — грид-ячейка, и по умолчанию
     она растянута на всю высоту строки (align-self:stretch). Растянутому
     элементу некуда «липнуть» — он и так занимает всю строку, поэтому
     sticky визуально ничего не делает. Нужен align-self:start, тогда
     колонка сжимается до своей высоты по содержимому и получает ход.

     top считаем от липкой шапки: она position:sticky высотой 80px, плюс
     24px воздуха.

     Гейт 900px: ниже .bm-tl схлопывается в одну колонку, и липкий заголовок
     над списком вопросов только мешал бы. — */
@media (min-width:900px){
  .bm-faq-accordion .bm-tl > .bm-side,
  .bm-faq-acctext .bm-tl > .bm-side,
  .bm-faq-accexpert .bm-tl > .bm-side{
    position:sticky;top:104px;align-self:start;
  }
}

/* — Аватары-инициалы в карточках людей и отзывов. Движок раздаёт им случайные
     градиенты (синий, голубой, оранжевый вперемешку) — на сине-зелёной теме
     это читается как чужие цвета. Приводим к бренду. — */
.bm-tst-standard .bm-av,.bm-tst-centered .bm-av,.bm-tst-slider .bm-av{
  background:var(--gp) !important;color:#fff
}

/* — Согласие на обработку ПД в form/minimal: на мобиле чекбокс отрывался
     от своего текста и висел отдельно у левого края.

     🔴 Виноват не сам .bm-consent, а сумма трёх правил blocks-base.css:
     .bm-w{text-align:center}, .bm-consent{display:flex;justify-content:center}
     и мобильный .bm-row{flex-direction:column}. В колоночном flex label
     растягивается на всю ширину формы (align-items:stretch), чекбокс как
     первый flex-элемент остаётся у левого края, а <span> забирает весь
     остаток и центрирует текст внутри себя унаследованным text-align.
     justify-content:center при этом не работает — свободного места в
     строке уже нет, центрировать нечего.

     Лечим не флексом, а шириной: max-width + auto-поля по бокам. Авто-поля
     в поперечной оси отменяют stretch, label сжимается по содержимому и
     центрируется целиком, а text-align:left возвращает текст вплотную к
     чекбоксу. Только ≤768px — на десктопе .bm-row остаётся строкой и
     auto-поля съели бы свободное место главной оси.

     Правится в теме, а не в blocks-base.css: последний перезаписывается
     обновлением ядра. Баг заведён на исправление в самой вариации. — */
@media (max-width:768px){
  .bm-frm-minimal .bm-consent{
    max-width:420px;margin-left:auto;margin-right:auto;text-align:left;
  }
}

/* — Кнопка мобильного меню: зона нажатия.

     Замер на 375px: .ub-burger — 21×32px. Ядро рисует её текстовым «☰» в
     text-2xl без паддингов, поэтому ширину кнопки задаёт ширина самого
     глифа. WCAG 2.1 (2.5.8, уровень AA) требует минимум 24×24 CSS-пикселя,
     то есть по ширине она не проходит; практический ориентир для пальца —
     44×44 (iOS HIG) или 48×48 (Material).

     Это единственный способ открыть меню на телефоне, и промах по ней
     означает, что посетитель не попадёт никуда, кроме главной. Даём
     44×44 через min-width/min-height и центрируем глиф внутри; margin
     отрицательный по правому краю, чтобы выросшая зона нажатия не
     сдвинула ряд шапки и не оторвала иконку от края экрана. — */
@media (max-width:767px){
  .ub-burger{
    min-width:44px;min-height:44px;
    display:inline-flex;align-items:center;justify-content:center;
    margin-right:-10px;
  }
}

/* — Иконка соцсети в подвале: была не видна.

     В подвале одна ссылка — мессенджер Max (social_max в настройках сайта),
     иконка «M» в круглой полупрозрачной плашке. Базовый global.css ядра
     задаёт этой ссылке ТОЛЬКО фон (--footer-social-bg), цвет глифа он не
     трогает вообще — и глиф наследовал бренд-бирюзовый #0F5257 от
     родителя, оказавшись бирюзовым по тёмно-бирюзовому подвалу #0B3A3E.

     Замер: контраст 1.04:1 на плашке кружка. Это не «тускло» — это
     невидимо: порог различимости нетекстового элемента по WCAG 1.4.11 —
     3.0:1, то есть иконка не дотягивала втрое. На скриншоте заказчика от
     неё виден только силуэт.

     Белый: 9.21:1 на плашке, 6.80:1 на наведении (плашка там светлеет до
     rgba(255,255,255,.2)). Оба значения с запасом.

     currentColor у Font Awesome — глиф это текст, поэтому достаточно
     color; заливать fill не нужно. — */
.ub-footer-social{color:#fff}
.ub-footer-social:hover{color:#fff}

/* — Карточки статей блога: кнопки «Читать» на одной линии.

     Карточки в сетке уже одной высоты (замер: 495px у всех трёх), а вот
     кнопки стояли на разной: заголовки и описания разной длины, и кнопка
     шла сразу за текстом. На узком экране разница доходила до строки
     заголовка — три кнопки лесенкой.

     Прижимаем их к низу карточки. Тянем не кнопку, а ОПИСАНИЕ:
     .bm-bd становится колоночным флексом, описание получает flex:1 и
     забирает свободное место, кнопка уезжает вниз сама.

     🔴 Почему не margin-top:auto на кнопке — так было бы короче, но auto
     заменяет её собственный margin-top:14px, и в карточке, где текст занял
     всю высоту, кнопка прилипла бы к описанию вплотную. Через растяжение
     описания отступ 14px остаётся минимальным гарантированным.

     align-self:flex-start кнопке обязателен: в колоночном флексе
     align-items по умолчанию stretch, и кнопка растянулась бы на всю
     ширину карточки вместо того, чтобы обнимать слово «Читать».

     Описание в разметке — <div> без класса, поэтому :not([class]).
     Ослабить селектор до .bm-bd > div нельзя: под него попадёт любой
     будущий блок внутри тела карточки. — */
.bm-srv-image-top .bm-c{display:flex;flex-direction:column}
.bm-srv-image-top .bm-bd{display:flex;flex-direction:column;flex:1 1 auto}
.bm-srv-image-top .bm-bd > div:not([class]){flex:1 1 auto}
.bm-srv-image-top .bm-bd > .btn{align-self:flex-start}

/* — Подпись под логотипом в подвале: убрана (правка 22.09).

     Движок выводит под логотипом подвала название сайта отдельным
     абзацем. До появления фирменного знака это было осмысленно. Сначала
     стало мешать расхождением («ДеньгиТУТ» на картинке и «Светлана
     Кириенко» строкой ниже), а после переименования сайта 22.09 —
     повтором: в локапе подвала слово «ДеньгиТУТ» уже написано, и абзац
     дублировал бы его вплотную. Прячем в обоих случаях.

     Отключить настройкой нельзя: абзац берёт site_name, а он же идёт в
     <title>, og:site_name и шапку — обнулить его значит сломать их.
     Поэтому прячем именно этот абзац.

     🔴 Цепляться за соседство с логотипом (.ub-footer-logo + p) НЕЛЬЗЯ,
     хотя в отдаваемой сервером разметке img и абзац — соседи. Наш же
     скрипт apply_footer_logo_link() оборачивает логотип в <a>, и после
     его срабатывания соседом абзаца становится ссылка, а не картинка:
     правило перестаёт совпадать, и подпись возвращается. Поймано замером
     — в статике селектор работал, в браузере абзац был на месте.

     🔴 По классу .ub-footer-muted тоже нельзя: его носят ещё адрес и
     строка копирайта, правило снесло бы «© 2026 ИП Кириенко С.В. · ИНН …».

     Цепляемся за тип элемента: подпись — единственный <p> с этим классом
     в подвале, адрес и копирайт лежат в <div> (проверено по разметке).

     Имя никуда не делось: оно осталось в копирайте, в <title>, в
     og:site_name и в alt логотипа. — */
footer p.ub-footer-muted{display:none}


/* ============================================================
   cpp_lib — структурные дефолты поверх темы сайта (Фаза 2).
   Грузится ПОСЛЕ per-site global.css → перебивает :root каскадом.
   Только структура (ритм секций, размер форм), НЕ цвета/шрифты —
   их задаёт тема клиента. Закрывает повторяющиеся правки:
   «большие отступы между блоками» (4/8) и «форма слишком большая».
   Подключать: c.upload_theme(open('lib/theme_overrides.css').read())
   уже ПОСЛЕ заливки темы сайта, либо конкатенацией в хвост global.css.
   ============================================================ */
:root{
  /* Зажатый вертикальный ритм секций (дефолт базы 80/96/64 давал «воздух»,
     который правился вручную). Значения — то, к чему приходили фиксы. */
  --section-py:clamp(48px,6vw,72px);
  --section-py-lg:clamp(56px,7vw,88px);
  --section-py-md:clamp(40px,5vw,56px);
  --section-py-sm:clamp(32px,4vw,48px);
}

/* Формы не во всю ширину секции — «форма слишком большая/гигантская»
   встречалась не раз. Центрируем и ограничиваем ширину. */
.ub-form,.ub-modal-body .ub-form{max-width:520px;margin-left:auto;margin-right:auto}

/* Единый разумный отступ подзаголовка секции (иногда «дубль/большой gap»). */
.sh{margin-bottom:clamp(28px,4vw,44px)}

.ub-footer-logo{content:url("/storage/media/media_6ab69121959203.04384494.png")}
