/* Budin — Light 02. Палитра утверждена протоколом COLOR-AUDIT-LIGHT-02.md.
   Заголовки: Unbounded (OFL), веса 600 и 800, самохостится. CoFo Sans выведен.
   Гарнитура заголовков меняется одним токеном --font-display. */

/* Заголовочная гарнитура взята с боевого сайта: там h1 набран Unbounded 800,
   файлы уже самохостятся в budin-site-main/public/fonts. Причина замены -
   в пробном CoFo Sans подменена буква Ф: у Ф и ф одинаковые метрики
   (594, габарит 12/2/532/522), единственная такая пара во всей кириллице,
   и вместо буквы рисуется кружок с латинской F. Unbounded открытый (OFL),
   лицензионного долга не остаётся. Кириллица и латиница разнесены по
   unicode-range, как на бою: браузер тянет только нужный кусок. */
@font-face {
  font-family: "Unbounded";
  src: url("assets/fonts/unbounded-800-cyrillic-v2.woff2") format("woff2");
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Unbounded";
  src: url("assets/fonts/unbounded-800-latin-v2.woff2") format("woff2");
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122;
}

@font-face {
  font-family: "Unbounded";
  src: url("assets/fonts/unbounded-600-cyrillic-v2.woff2") format("woff2");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Unbounded";
  src: url("assets/fonts/unbounded-600-latin-v2.woff2") format("woff2");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122;
}

/* IBM Plex приезжает сабсетами woff2, а не исходными ttf.

   До этого пять начертаний ехали файлами .ttf на 881 КБ. TTF внутри не
   сжат, а шрифты сервер gzip-ом не жмёт, так что все 881 КБ доезжали как
   есть: больше, чем весь остальной первый экран. На медленном мобильном
   канале это секунды до первого текста.

   Файлы порезаны под кириллицу с латиницей и упакованы в woff2 скриптом
   tools/subset-fonts.py, из 881 КБ осталось 86. Шрифт при этом ТОТ ЖЕ
   самый: рез убирает лишние глифы, но не трогает рисунок буквы, метрики и
   кернинг, поэтому строки не поехали.

   Исходные .ttf остаются в ветке: они источник для повторного реза, если
   понадобится ещё начертание. В сборку они не попадают сами собой -
   copy_assets в scripts/newdesign_to_dist.py берёт только те файлы, на
   которые есть ссылка из разметки или css. Убрал ссылку - файл не поехал. */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("assets/fonts/IBMPlexSans-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("assets/fonts/IBMPlexSans-Medium.woff2") format("woff2");
  font-style: normal;
  font-weight: 500;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("assets/fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("assets/fonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("assets/fonts/IBMPlexMono-SemiBold.woff2") format("woff2");
  font-style: normal;
  font-weight: 600;
  font-display: swap;
}

/* ── Токены ─────────────────────────────────────────────── */

:root {
  /* Цвет: Light 02, 11 токенов. Протокол COLOR-AUDIT-LIGHT-02.md */
  --bg: #ffffff;
  --surface: #fbf7f2;
  --surface-2: #ffffff;
  --ink: #12100f;
  --ink-invert: #f4eee6;
  --muted: #6b5a50;
  --dim: #8a786e;
  --line: #e0d3c4;
  --accent: #f05a3f;
  --accent-ink: #b23718;
  --accent-hover: #d84e33;

  /* Поверхности внутри тёмных вставок: прозрачности, не новые цвета */
  --ink-surface: color-mix(in srgb, var(--ink-invert) 8%, transparent);
  --ink-line: color-mix(in srgb, var(--ink-invert) 16%, transparent);

  /* Шкала отступов 4 / 8 */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 128px;

  /* Радиусы. Ступени равномерные: между соседними значениями примерно
     полуторный шаг. Раньше между карточкой (14) и карточкой кейса (40)
     не было ничего, из-за чего одни блоки читались круглыми, другие
     угловатыми. --r-lg добавлен как недостающая середина. */
  --r-sm: 14px;
  --r-md: 24px;
  --r-lg: 32px;
  --r-xl: 40px;
  --r-pill: 999px;

  /* Подъём: тёплые тени */
  --e-1: 0 1px 2px rgba(40, 33, 30, .06), 0 2px 8px rgba(40, 33, 30, .04);
  --e-2: 0 4px 12px rgba(40, 33, 30, .08);
  --e-3: 0 12px 32px rgba(40, 33, 30, .1);

  /* Стекло. Полупрозрачная белая плоскость плюс размытие подложки.
     Внутренняя световая грань даёт кромку, внешняя граница отделяет от фона. */
  --glass: rgba(255, 255, 255, .62);
  --glass-strong: rgba(255, 255, 255, .8);
  --glass-edge: rgba(40, 33, 30, .1);
  --glass-sheen: inset 0 1px 0 rgba(255, 255, 255, .85);
  --glass-blur: blur(18px) saturate(1.5);
  --glass-dark: rgba(244, 238, 230, .07);
  --glass-dark-edge: rgba(244, 238, 230, .14);
  --glass-dark-sheen: inset 0 1px 0 rgba(244, 238, 230, .1);

  /* Акцентная поверхность. Единый рецепт для всех оранжевых элементов:
     градиент сверху вниз, световая грань, тёмная грань, ореол со смещением
     вниз. Ореол не должен подсвечивать фон над элементом, иначе верхняя
     кромка теряет 3:1. Крупный вариант для кнопок, малый для круглых
     контролов и бейджей. */
  --accent-grad: linear-gradient(180deg, #ee5a3e 0%, #df4c2b 100%);
  /* При наведении градиент не темнеет, а становится ровнее и ярче снизу:
     затемнение низа роняло текст до 4.41, осветление верха — кромку до 3.03. */
  --accent-grad-hover: linear-gradient(180deg, #ee5a3e 0%, #e2532f 100%);
  --accent-lift:
    inset 0 1px 0 rgba(255, 255, 255, .34),
    inset 0 -1px 0 rgba(112, 26, 8, .3),
    0 1px 2px rgba(140, 45, 20, .2),
    0 8px 20px -10px rgba(176, 55, 24, .5),
    0 20px 38px -14px rgba(226, 80, 47, .5),
    0 18px 46px -18px rgba(240, 90, 63, .45);
  --accent-lift-hover:
    inset 0 1px 0 rgba(255, 255, 255, .4),
    inset 0 -1px 0 rgba(112, 26, 8, .32),
    0 2px 4px rgba(140, 45, 20, .2),
    0 10px 24px -10px rgba(176, 55, 24, .55),
    0 24px 46px -14px rgba(226, 80, 47, .6),
    0 22px 56px -18px rgba(240, 90, 63, .55);
  --accent-lift-sm:
    inset 0 1px 0 rgba(255, 255, 255, .34),
    inset 0 -1px 0 rgba(112, 26, 8, .3),
    0 1px 2px rgba(140, 45, 20, .2),
    0 10px 20px -8px rgba(226, 80, 47, .5),
    0 10px 26px -12px rgba(240, 90, 63, .45);

  /* Типографическая шкала, отношение 1.25 */
  --t-xs: 12px;
  --t-sm: 14px;
  --t-base: 16px;
  --t-md: 20px;
  --t-lg: 25px;
  --t-xl: 32px;
  --t-2xl: 40px;
  --t-3xl: 50px;
  --t-4xl: clamp(34px, 3.5vw, 49px);
  --t-5xl: clamp(56px, 6.2vw, 84px);

  /* Вторая ступень заголовка секции. Кривая та же, что у --t-4xl, поэтому
     соотношение 1.28 держится на любой ширине. Брать вместо неё --t-3xl
     нельзя: он фиксированный (50px) и на 980 оказался бы КРУПНЕЕ узлового
     заголовка, который там сжимается до 45px. */
  --t-4xl-sub: clamp(28px, 2.7vw, 38px);

  --font-display: "Unbounded", Arial, sans-serif;
  --font-body: "IBM Plex Sans", Arial, sans-serif;
  --font-meta: "IBM Plex Mono", Consolas, monospace;

  --shell: 1320px;
  --gutter: var(--s-6);
  --grid-gap: var(--s-5);
  --ease: cubic-bezier(.2, .72, .2, 1);
}

/* ── База ───────────────────────────────────────────────── */

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

html {
  scroll-behavior: auto;
  scroll-padding-top: 80px;
}

body {
  margin: 0;
  /* Минимума ширины больше нет. Пока стоял `min-width: 980px`, макет был
     честно десктопным: ниже этой ширины страница не перестраивалась, а
     просто ехала вбок. Всё, что сворачивает двенадцать колонок в одну,
     собрано в конце файла, в разделе «Адаптив». */
  /* Засветки нужны стеклу: на плоском белом backdrop-filter не даёт эффекта.
     Это прозрачности существующего акцента, новых цветовых семейств нет. */
  background:
    radial-gradient(1000px 700px at 88% -8%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 62%),
    radial-gradient(760px 560px at -8% 34%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    radial-gradient(900px 620px at 65% 90%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 62%),
    var(--bg);
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* height: auto здесь обязателен и идёт в паре с max-width.
   Без него HTML-атрибут height становится РЕАЛЬНОЙ высотой картинки, и
   CSS-ширина его не отменяет. На этом 02.09.2026 поплыл весь блог: у
   аватарки автора в рельсе при ширине 128px высота встала в 949px, у
   декора ответа при ширине 190px в 760px, и он лёг поверх текста.
   Правила с собственной высотой (.form-operator-body и подобные) выигрывают
   у этого по специфичности, их height: auto не трогает. */
img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: inherit;
}

button,
input,
textarea {
  font: inherit;
}

h1,
h2,
h3 {
  /* Страховка от длинного слова: на 360px «Продвижение» заголовочным
     начертанием шире экрана целиком и тянуло горизонтальную прокрутку.
     Правило срабатывает только когда слово действительно не помещается. */
  overflow-wrap: break-word;
  font-family: var(--font-display);
  /* Запрашиваем тот вес, который реально загружен. Unbounded подключён в 600
     и 800; запрос 700 браузер разрешал в 600, то есть код обещал одно, а
     рендер давал другое. 600 фиксирует фактическое начертание без сдвига
     ширин: переход на 800 менял бы длину строк и требовал нового замера. */
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.04;
  margin: 0;
}

p {
  margin: 0;
}

::selection {
  background: var(--accent);
  color: var(--ink);
}

.sprite {
  display: none;
}

.skip-link {
  position: fixed;
  top: var(--s-3);
  left: var(--s-3);
  z-index: 200;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--ink-invert);
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

.shell {
  width: min(calc(100% - (var(--gutter) * 2)), var(--shell));
  margin-inline: auto;
}

/* ── Примитивы ──────────────────────────────────────────── */

/* Контентные глифы: линейные, сетка 24, штрих 2. */
.ico {
  flex: none;
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ico-sm {
  width: 18px;
  height: 18px;
}

/* Глифы управления: пиксельные, сетка 16×16, заливка вместо штриха.
   Пиксель помечает то, с чем человек взаимодействует: стрелки перехода,
   плюс и крестик. Размер только 16px, клетка ровно в пиксель.
   Другие размеры и поворот запрещены — сетка развалится. */
.ico-px {
  flex: none;
  width: 16px;
  height: 16px;
  fill: currentColor;
  stroke: none;
  shape-rendering: crispEdges;
}

/* Удвоенный размер: клетка ровно в 2 пикселя, сетка не плывёт. */
.ico-px-lg {
  width: 32px;
  height: 32px;
}

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

/* Помощник Budin: персонаж обозначает точку входа, а не конкурирует
   с главным CTA. Акцент появляется только при намерении взаимодействовать. */
.budin-helper {
  position: fixed;
  right: var(--s-6);
  /* Нижняя панель браузера на телефоне лежит поверх страницы и съедает
     фигуру помощника: при чистых 24px от низа он оказывался наполовину
     под ней. safe-area-inset даёт высоту этой панели там, где браузер её
     сообщает; где не сообщает, остаются прежние 24px. */
  bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  z-index: 120;
}

.budin-helper-launcher {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

/* Подпись — тот же чип, что и везде на сайте, с пиксельным глифом терминала */
.budin-helper-caption {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-4) var(--s-1) var(--s-1);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-1), var(--glass-sheen);
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: opacity .2s ease, background .2s ease, box-shadow .2s ease;
}

.budin-helper-launcher:is(:hover, :focus-visible) .budin-helper-caption {
  background: var(--glass-strong);
  box-shadow: var(--e-2), var(--glass-sheen);
}

/* Пропорция сцены равна пропорции холста слоёв, иначе фигура исказится */
.budin-helper-stage {
  position: relative;
  display: block;
  width: 136px;
  aspect-ratio: 1073 / 949;
}

.budin-helper-mascot {
  position: absolute;
  inset: 0;
  transition: transform .28s var(--ease);
}

.budin-helper-figure,
.budin-helper-arm {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  pointer-events: none;
  user-select: none;
}

/* Точка вращения совпадает с плечом на холсте, замерена при сборке слоёв */
.budin-helper-arm {
  z-index: 2;
  transform-origin: 63.8% 54.6%;
}

.budin-helper.is-open .budin-helper-caption {
  background: var(--accent-grad);
  border-color: transparent;
  box-shadow: var(--accent-lift-sm);
}

.budin-helper.is-open .budin-helper-mascot {
  transform: translate(-5px, -3px) rotate(-3deg);
}

.budin-helper.is-caption-muted:not(:hover):not(:focus-within) .budin-helper-caption {
  opacity: 0;
  transform: translateX(8px);
}

.budin-helper.is-intro .budin-helper-mascot {
  animation: helper-rise .6s var(--ease) both;
}

.budin-helper.is-waving .budin-helper-arm {
  animation: helper-wave .9s var(--ease) both;
}

@keyframes helper-rise {
  from { opacity: 0; transform: translateY(34px) rotate(5deg); }
  to { opacity: 1; transform: none; }
}

@keyframes helper-wave {
  0%, 100% { transform: rotate(0deg); }
  26% { transform: rotate(-13deg); }
  52% { transform: rotate(9deg); }
  78% { transform: rotate(-6deg); }
}

.budin-helper-panel {
  position: absolute;
  right: 0;
  bottom: 160px;
  width: 360px;
  max-height: calc(100vh - 140px);
  overflow: auto;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-3), var(--glass-sheen);
  color: var(--ink);
  animation: helper-panel-in .24s var(--ease) both;
}

.budin-helper-panel[hidden] {
  display: none;
}

@keyframes helper-panel-in {
  from { opacity: 0; transform: translateY(12px) scale(.98); }
  to { opacity: 1; transform: none; }
}

.budin-helper-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--s-4);
  padding: var(--s-5);
  border-bottom: 1px solid var(--line);
}

.budin-helper-head h2 {
  margin-top: var(--s-2);
  font-size: var(--t-lg);
}

.budin-helper-kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
}

.budin-helper-kicker > span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
}

.budin-helper-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}

.budin-helper-close:is(:hover, :focus-visible) {
  border-color: var(--ink);
}

.budin-helper-content {
  padding: var(--s-5);
}

.budin-helper-content > p {
  max-width: 31ch;
  color: var(--muted);
  font-size: var(--t-sm);
}

.budin-helper-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-4);
}

.budin-helper-actions a {
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
}

.budin-helper-actions a:is(:hover, :focus-visible) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, var(--surface-2));
}

/* Плашка ответа собрана из тех же материалов, что и сама панель: рамка в
   один пиксель тоном --line, скругление и белая подложка, как у круглой
   кнопки закрытия и у пилюль быстрых действий. Прежняя версия осталась от
   старого сайта - прямоугольник без скруглений с жёсткой коралловой гранью
   в три пикселя и кремовой заливкой; в стеклянной панели она читалась
   вставкой из другого макета. Коралл в этом углу и так занят: он метка
   живого статуса в шапке помощника, и вторая коралловая деталь рядом
   спорила с ней. */
.budin-helper-response {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--t-sm);
}

.budin-helper-response[hidden] {
  display: none;
}

/* Ответом идёт список найденных страниц, а не одна ссылка: на запрос вроде
   «сайт для кофейни» подходящих страниц обычно несколько, и выбор делает
   человек. Больше трёх не показываем - панель узкая. */
.budin-helper-results {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Строки разделены линией, а не полем: три отдельные карточки в панели
   шириной 360px дробят её на кубики, а список читается списком. */
.budin-helper-results li + li {
  border-top: 1px solid var(--line);
}

.budin-helper-results a {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px var(--s-3);
  width: auto;
  padding: var(--s-3) var(--s-2);
  margin: 0 calc(var(--s-2) * -1);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  transition: background .2s ease, color .2s ease;
}

.budin-helper-results a:is(:hover, :focus-visible) {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent-ink);
}

/* Пометка раздела уходит под название и набирается мелко: три «РАЗДЕЛ»
   подряд капсом в узкой панели перебивали сами названия, ради которых
   список и показан. */
.budin-helper-results b {
  grid-column: 1;
  font-weight: 600;
}

.budin-helper-results span {
  grid-column: 1;
  grid-row: 2;
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .08em;
}

.budin-helper-results svg {
  grid-column: 2;
  grid-row: 1 / span 2;
  color: var(--muted);
}

.budin-helper-results a:is(:hover, :focus-visible) svg {
  color: var(--accent-ink);
}

.budin-helper-form {
  display: grid;
  grid-template-columns: 1fr 48px;
  gap: var(--s-2);
  padding: 0 var(--s-5) var(--s-5);
}

.budin-helper-form input {
  min-width: 0;
  height: 48px;
  padding: 0 var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-size: var(--t-sm);
}

.budin-helper-form button {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--ink-invert);
  cursor: pointer;
}

.budin-helper-form button:is(:hover, :focus-visible) {
  background: var(--accent-grad);
  color: var(--ink);
}

/* Иконочный бейдж: круг с монохромным глифом */
.badge {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--ink-invert);
}

.badge-sm {
  width: 28px;
  height: 28px;
}

.badge-lg {
  width: 52px;
  height: 52px;
}

/* Чип-тег: бейдж плюс подпись в пилюле */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 40px;
  padding: var(--s-1) var(--s-4) var(--s-1) var(--s-1);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-1), var(--glass-sheen);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

a.chip {
  transition: box-shadow .2s ease, background .2s ease;
}

/* Никакого сдвига по вертикали: чипы стоят плотным рядом, и подъём одного
   на фоне неподвижных соседей ломает общую линию. Отклик даём плотностью
   стекла и тенью. */
a.chip:is(:hover, :focus-visible) {
  background: var(--glass-strong);
  box-shadow: var(--e-2), var(--glass-sheen);
}

.chip-accent {
  background: var(--accent-grad);
  border-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: var(--accent-lift-sm);
  color: var(--ink);
}

/* Служебная метка */
.eyebrow {
  display: block;
  margin-bottom: var(--s-4);
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* Кнопки */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 56px;
  padding: var(--s-4) var(--s-6);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--t-base);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease, color .2s ease, box-shadow .2s ease, transform .2s var(--ease);
}

.btn .badge {
  width: 40px;
  height: 40px;
  background: var(--ink);
  color: var(--ink-invert);
}

/* Объёмная акцентная кнопка без обводки: градиент задаёт форму,
   верхняя световая грань и нижняя тень дают толщину, подъём отделяет от фона.
   Кромка держит порог 3:1 за счёт градиента, поэтому граница не нужна. */
.btn-accent {
  background: var(--accent-grad);
  border-color: transparent;
  color: var(--ink);
  box-shadow: var(--accent-lift);
}

.btn-accent:is(:hover, :focus-visible) {
  background: var(--accent-grad-hover);
  transform: translateY(-1px);
  box-shadow: var(--accent-lift-hover);
}

.btn-accent:active {
  transform: translateY(0);
  box-shadow:
    inset 0 2px 4px rgba(112, 26, 8, .3),
    0 1px 2px rgba(140, 45, 20, .2);
}

.btn-ghost {
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-color: var(--dim);
  color: var(--ink);
  box-shadow: var(--e-1), var(--glass-sheen);
}

.btn-ghost:is(:hover, :focus-visible) {
  border-color: var(--accent-ink);
  color: var(--accent-ink);
  box-shadow: var(--e-2);
}

.btn-ghost .badge {
  background: var(--ink);
}

.btn-dark {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ink-invert);
}

.btn-dark:is(:hover, :focus-visible) {
  background: #241f1c;
  transform: translateY(-1px);
}

.btn-dark .badge {
  background: var(--ink-surface);
  color: var(--ink-invert);
}

/* Текстовое действие второго уровня */
.link-action {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--accent-ink);
  font-weight: 600;
  white-space: nowrap;
  /* Подчёркивание постоянное: ссылка не должна опознаваться только по цвету.
     При дейтеранопии пара accent-ink / bg падает до 3.0. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  transition: gap .2s var(--ease);
}

.link-action:is(:hover, :focus-visible) {
  gap: var(--s-3);
  text-decoration-thickness: 2px;
}

/* Карточка */
.card {
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-1), var(--glass-sheen);
}

/* Стекло на тёмных вставках */
.section-dark .card,
.manifesto-note {
  border-color: var(--glass-dark-edge);
  background: var(--glass-dark);
  box-shadow: var(--glass-dark-sheen);
}

/* ── Секции ─────────────────────────────────────────────── */

/* Вертикальный ритм.

   Было по 96px сверху и снизу у каждой секции без исключений: между любыми
   двумя блоками ровно 192px, и страница читалась лентой равных пауз — все
   переходы одинаково значимы, значит ни один не значим.

   Ритм задаётся рангом секции, тем же, что и ступень заголовка:
     опора        64  → между двумя опорными секциями 128;
     тёмный блок  96  → у него внутренний воздух это ещё и поле рамки,
                        на 64 контент прижимался бы к скруглению;
     узел        128  → вокруг услуг и кейсов пауза 192–224.

   Смысл: опорные секции идут плотной группой, а перед узлом воронки пауза
   заметно шире — она и сообщает, что начинается новое. */
.section {
  padding: var(--s-8) 0;
}

#services,
#cases {
  padding-top: var(--s-10);
  padding-bottom: var(--s-10);
}

.section-dark {
  /* Тёмному блоку нужен внутренний воздух: он ещё и поле рамки, на 64px
     контент прижимался бы к скруглению. */
  padding: var(--s-9) 0;

  /* Тёмные секции — тоже блоки, а не фон во всю страницу: небольшой отступ
     по бокам делает скругление видимым, иначе радиус срезался бы кромкой окна. */
  margin-inline: var(--s-5);
  border-radius: var(--r-xl);
  background: var(--ink);
  color: var(--ink-invert);
}

/* Контейнер внутри тёмной секции обязан стоять на ОБЩЕЙ оси страницы.
   Обычный .shell считает ширину от родителя, а родитель здесь уже сужен на
   два --s-5 — из-за этого контент уезжал вправо: на 1280 заголовки светлых
   секций стояли на x=32, а тёмных на x=56, на 980 — 24 против 48. На 1440
   оба значения совпадали случайно, поэтому проблема и не была видна.

   Возвращаем в расчёт ширины те же два --s-5, что забрал margin-inline, —
   формула становится дословно равна светлой, а margin-inline: auto ставит
   контейнер ровно на общую вертикаль. Никаких подобранных чисел. */
.section-dark > .shell {
  width: min(calc(100% + var(--s-5) * 2 - var(--gutter) * 2), var(--shell));
  margin-inline: auto;
}

/* Подвал закругляется только сверху: снизу страница заканчивается. */
.site-footer {
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}

.section-dark .eyebrow {
  color: color-mix(in srgb, var(--ink-invert) 68%, transparent);
}

.section-head {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: end;
  margin-bottom: var(--s-8);
}

.section-head > div {
  grid-column: 1 / 8;
}

/* Две ступени заголовка секции.

   Было девять h2 одного кегля 64px подряд: страница читалась лентой
   равнозначных блоков, а h1 выигрывал у них всего 9%. Первую ступень
   получают только узлы воронки — услуги и кейсы. Остальные секции опорные:
   они поддерживают решение, а не требуют его, и стоят на ступень ниже. */
.section-head h2 {
  font-size: var(--t-4xl-sub);
}

#services .section-head h2,
#cases .section-head h2 {
  font-size: var(--t-4xl);
}

/* Ссылка на полный список рядом с заголовком секции: карусель на главной
   показывает выборку, весь список живёт на отдельной странице. */
.section-more {
  margin-top: var(--s-4);
}

/* max-width убран: колонки 9–12 дают ровно 424px на 1440, а ограничение в
   420 срезало 4px и выбивало правый край интро из сетки. Меру строки теперь
   держит сама колонка. */
.section-intro {
  grid-column: 9 / 13;
  color: var(--muted);
  font-size: var(--t-md);
}

.section-dark .section-intro {
  color: color-mix(in srgb, var(--ink-invert) 72%, transparent);
}

/* ── Шапка ──────────────────────────────────────────────── */

/* Шапка — плавающая стеклянная пилюля: логотип, навигация и кнопка внутри
   одного скруглённого контейнера, контент прокручивается под ним. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 80;
  padding: var(--s-2) 0;
  background: transparent;
}

.header-inner {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  padding: var(--s-1) var(--s-1) var(--s-1) var(--s-5);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-2), var(--glass-sheen);
}

.brand {
  flex: none;
  display: flex;
  align-items: center;
  text-decoration: none;
}

.brand img {
  width: 100px;
  height: auto;
}

/* Навигация живёт внутри шапки-пилюли, своей подложки не имеет */
.nav-pill {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  margin-left: auto;
}

.nav-pill a {
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background .2s ease, color .2s ease;
}

.nav-pill a:is(:hover, :focus-visible) {
  background: rgba(255, 255, 255, .9);
  color: var(--ink);
}

/* ── Услуги в шапке ─────────────────────────────────────────
   Направлений тридцать семь, в строку они не помещаются, а одна ссылка
   «Услуги» прячет весь товар студии за лишним переходом. Поэтому ключевые
   страницы вынесены в панель под шапкой, сгруппированные так же, как на
   странице услуг, а ссылка на полный список стоит в подвале панели. */

/* Панель считает координаты от самой шапки, а не от кнопки: привязанная к
   кнопке, она уезжала левее пилюли и висела над пустотой. Правый край
   совпадает с правым краем шапки, и меню читается её продолжением. */
.header-inner {
  position: relative;
}

.nav-drop {
  position: static;
}

/* Кнопка обязана совпадать с соседними ссылками до пикселя: рядом с ними
   она читается пунктом меню, а не элементом формы. */
.nav-drop-btn {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background .2s ease, color .2s ease;
}

.nav-drop-btn:is(:hover, :focus-visible),
.nav-drop-btn[aria-expanded="true"] {
  background: rgba(255, 255, 255, .9);
  color: var(--ink);
}

/* Своей стрелки вниз в наборе нет, поэтому берём правую и поворачиваем:
   отдельная иконка ради одного пункта меню того не стоит. */
.nav-drop-mark {
  transform: rotate(90deg);
  transition: transform .2s ease;
}

.nav-drop-btn[aria-expanded="true"] .nav-drop-mark {
  transform: rotate(-90deg);
}

.nav-drop-panel {
  position: absolute;
  top: calc(100% + var(--s-3));
  right: 0;
  z-index: 90;
  width: min(880px, 92vw);
  padding: var(--s-6);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  /* Заливка почти непрозрачная, и это не оплошность: на стекле шапки
     (.8 при blur 18) сквозь панель читался заголовок первого экрана
     кеглем 96px, и пункты меню терялись в его контурах. Меню обязано
     быть читаемым в любом месте страницы, поэтому подложка своя. */
  background: color-mix(in srgb, var(--bg) 97%, transparent);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-3), var(--glass-sheen);
}

.nav-drop-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-5);
}

.nav-drop-col {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.nav-drop-head {
  margin-bottom: var(--s-2);
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.nav-drop-col a {
  padding: 6px 8px;
  margin-left: -8px;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background .2s ease;
}

.nav-drop-col a:is(:hover, :focus-visible) {
  background: rgba(255, 255, 255, .9);
}

.nav-drop-all {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--line);
  color: var(--accent-ink);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  width: 100%;
}

.nav-drop-all:is(:hover, :focus-visible) {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* Кнопка меню. На десктопе её нет: там навигация помещается в пилюлю
   целиком.

   Кнопка собрана из тех же материалов, что круглая кнопка закрытия у
   помощника: 44px, светлая подложка, рамка в пиксель тоном --line. Полосы
   разной длины — верхняя короче, это подпись набора: в макете так же
   устроены метки и засечки. Открытое состояние заливается акцентом, знак
   становится светлым и складывается в косой крест, а не в ровный: тот же
   наклон, что у полос. */
.nav-burger {
  display: none;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  margin-left: auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease, color .2s ease;
  -webkit-tap-highlight-color: transparent;
}

.nav-burger:is(:hover, :focus-visible) {
  border-color: var(--ink);
}

.nav-burger:active {
  transform: scale(.96);
}

.nav-burger span,
.nav-burger span::before,
.nav-burger span::after {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform .26s var(--ease), width .26s var(--ease),
    opacity .18s ease;
}

/* Средняя полоса — сам элемент, крайние — его псевдоэлементы. Так группа
   центрируется по кнопке сама, без подобранных отступов. */
.nav-burger span {
  position: relative;
  width: 18px;
}

.nav-burger span::before,
.nav-burger span::after {
  content: "";
  position: absolute;
  left: 0;
}

/* Верхняя полоса короче остальных: знак читается меткой набора, а не
   стандартным гамбургером из трёх одинаковых линий. */
.nav-burger span::before {
  top: -6px;
  width: 12px;
}

.nav-burger span::after {
  top: 6px;
  width: 18px;
}

.site-header.is-open .nav-burger {
  border-color: transparent;
  background: var(--accent-grad);
  color: var(--ink);
  box-shadow: var(--accent-lift-sm);
}

/* Средняя полоса гасится ЦВЕТОМ, а не шириной и не прозрачностью.

   Прозрачность нельзя: крайние полосы — её псевдоэлементы, и `opacity: 0`
   на родителе прячет весь знак, кнопка оставалась пустым кружком.
   Ширину нельзя тоже: полосы позиционированы от левого края средней, и при
   схлопывании до нуля их опора уезжала в центр кнопки — крест смещался
   вправо и вниз на девять пикселей. Ширина остаётся, меняется только
   заливка, и крест собирается ровно по центру. */
.site-header.is-open .nav-burger span {
  background: transparent;
}

.site-header.is-open .nav-burger span::before {
  width: 18px;
  transform: translateY(6px) rotate(45deg);
}

.site-header.is-open .nav-burger span::after {
  transform: translateY(-6px) rotate(-45deg);
}

/* Кнопка заявки, подписи и контакты внутри выезжающего меню: в строке шапки
   на десктопе им места нет, там навигация это ряд ссылок. */
.nav-sheet-cta,
.nav-sheet-head,
.nav-sheet-contacts {
  display: none;
}

.header-cta {
  flex: none;
  min-height: 40px;
  padding: var(--s-2) var(--s-5);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* Один акцент первого уровня в кадре.

   На первом экране их было два: кнопка шапки 163×40 и кнопка героя 200×56,
   обе с коралловой заливкой и ореолом. Пока у экрана есть своё главное
   действие, кнопка шапки говорит тише — контур вместо заливки.

   Условие на `body:has(.hero)` держит правило только на главной: на статьях
   и лендингах первого экрана с кнопкой нет, и шапка сразу акцентная.
   Состояние по умолчанию тихое, поэтому вспышки заливки при загрузке не
   бывает; `is-loud` возвращает акцент, когда кнопка героя ушла из кадра.
   Если :has() не поддержан, шапка просто остаётся акцентной — как раньше. */
body:has(.hero) .header-cta:not(.is-loud) {
  background: none;
  border-color: var(--dim);
  color: var(--ink);
  box-shadow: none;
}

body:has(.hero) .header-cta:not(.is-loud):is(:hover, :focus-visible) {
  background: none;
  border-color: var(--accent-ink);
  color: var(--accent-ink);
  transform: none;
  box-shadow: none;
}

/* ── Первый экран ───────────────────────────────────────── */

.hero {
  position: relative;
  overflow: clip;
}

.hero-shell {
  position: relative;
  min-height: 560px;
  min-height: calc(100svh - 206px);
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  /* Четвёртая строка пустая: свободная высота делится между воздухом НАД
     заголовком и воздухом ПОД действием, а не копится между заголовком и
     кнопкой. Доли, а не пиксели, потому что высота первого экрана зависит
     от окна: пропорция держится на любом. */
  grid-template-rows: auto 1fr auto .28fr;
  column-gap: var(--grid-gap);
  padding: var(--s-6) 0 var(--s-8);
}

.hero-kicker {
  grid-column: 1 / 13;
  display: flex;
  gap: var(--s-2);
}

.hero-title {
  position: relative;
  z-index: 2;
  grid-column: 1 / 9;
  /* Заголовок прижат к низу своей строки, а под ним ровно один шаг шкалы до
     лида с кнопкой: оффер и действие должны читаться одной группой. */
  align-self: end;
  margin: calc(var(--s-7) - .15em) 0 var(--s-8);
  /* Заливка идёт через background-clip: text, а фон рисуется только внутри
     коробки элемента. При интерлиньяже .96 верх первой строки выходит за неё,
     и всё, что поднимается над прописной высотой, просто не закрашивалось:
     бреве у Й и точки у Ё срезало наполовину. Замер по метрикам шрифта дал
     7.6px при кегле 55, то есть .138em, поэтому поле в .15em с запасом и в
     em - чтобы держалось на всей вилке clamp. Верхнее поле уменьшено на
     столько же: положение строки не меняется. */
  padding-top: .15em;
  /* Капс шире строчных, поэтому кегль ниже общего --t-5xl,
     а трекинг менее отрицательный: иначе строки не влезают в 8 колонок. */
  font-size: clamp(34px, 3.85vw, 55px);
  text-transform: uppercase;
  letter-spacing: -.02em;
  line-height: .96;
}

.hero-title span {
  display: block;
  width: max-content;
  max-width: 100%;
}

/* Маскот занимает 4 колонки, как и заявлено в композиции 8 + 4. Абсолютный
   элемент с заданной grid-позицией отсчитывается от своей ячейки, поэтому
   ширину задаёт колонка, а не подобранное число: раньше фиксированные 400px
   промахивались мимо сетки на 24px при 1440 и на 10.6px при 1280. */
.hero-mascot-stage {
  position: absolute;
  grid-column: 9 / 13;
  grid-row: 1 / -1;
  z-index: 1;
  top: var(--s-8);
  right: 0;
  left: 0;
  height: 600px;
  pointer-events: none;
}

.hero-mascot {
  position: absolute;
  inset: 0;
  animation: mascot-enter .7s var(--ease) backwards;
  transition: transform .34s var(--ease);
}

.hero-mascot-body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.hero-shell:has(.hero-actions a:is(:hover, :focus-visible)) .hero-mascot {
  transform: translate3d(-6px, 0, 0);
}

@keyframes mascot-enter {
  from {
    opacity: 0;
    transform: translate3d(18px, 0, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

/* Лид и действия стояли во flex, поэтому кнопки начинались там, где кончался
   лид: x=548 при колонке 5 на x=508. Здесь своя сетка из восьми колонок —
   ровно тех же, что и у страницы, потому что блок занимает 1/9 и межколонник
   общий. Кнопки садятся на колонку 5. */
.hero-bottom {
  position: relative;
  z-index: 3;
  grid-column: 1 / 9;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: center;
}

.hero-bottom .hero-lead {
  grid-column: 1 / 5;
}

.hero-bottom .hero-actions {
  grid-column: 5 / 9;
}

.hero-lead {
  max-width: 440px;
  color: var(--muted);
  font-size: var(--t-md);
}

/* Перенос обязателен: кнопка и ссылка рядом занимают 206px плюс отступ, и
   на экране в 360 второй элемент уезжал за край, утаскивая всю страницу. */
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4) var(--s-5);
}

/* Полоса направлений */
.hero-strip {
  display: flex;
  gap: var(--s-3);
  padding-bottom: var(--s-9);
}

/* Полоса ниш на главной. От .hero-strip отличается переносом: чипов
   здесь два десятка, в одну строку они не встают ни на одном экране. */
.niche-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

/* ── Чипы первого экрана: капсула liquid glass ──────────────
   Форма взята с референса: бейдж почти во всю высоту капсулы, вокруг него
   равный зазор со всех сторон. Поверхность — стекло с преломлением:
   верхний блик, нижний отсвет от прошедшего света, внутреннее кольцо кромки.
   Применяется только на первом экране; чипы-теги внутри карточек остаются
   мелкими, иначе они перевесят заголовки. */

.hero .chip {
  min-height: 42px;
  gap: var(--s-3);
  /* 2px — оптический зазор вокруг бейджа, а не отступ раскладки: он взят
     с референса (бейдж ≈0.9 высоты капсулы) и в шкалу 4/8 не входит.
     Габарит намеренно ниже кнопки: чипы — третий уровень внимания после
     заголовка и основного действия, они не должны читаться наравне с CTA. */
  padding: 2px var(--s-5) 2px 2px;
  border-color: rgba(255, 255, 255, .62);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .6) 0%, rgba(255, 255, 255, .28) 48%, rgba(255, 255, 255, .46) 100%),
    rgba(255, 255, 255, .34);
  backdrop-filter: blur(22px) saturate(1.9);
  -webkit-backdrop-filter: blur(22px) saturate(1.9);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    inset 0 -1px 0 rgba(255, 255, 255, .5),
    inset 0 0 14px rgba(255, 255, 255, .34),
    0 1px 2px rgba(40, 33, 30, .07),
    0 8px 18px -10px rgba(40, 33, 30, .18);
  font-size: var(--t-sm);
}

.hero .chip .badge {
  width: 38px;
  height: 38px;
}

.hero a.chip:is(:hover, :focus-visible) {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .78) 0%, rgba(255, 255, 255, .42) 48%, rgba(255, 255, 255, .62) 100%),
    rgba(255, 255, 255, .5);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 1),
    inset 0 -1px 0 rgba(255, 255, 255, .62),
    inset 0 0 18px rgba(255, 255, 255, .45),
    0 1px 2px rgba(40, 33, 30, .08),
    0 14px 30px -10px rgba(40, 33, 30, .24);
}

/* ── Опыт ───────────────────────────────────────────────── */

/* Отступ сверху даёт .section-head своим margin-bottom, дублировать не нужно. */
/* Межколонник системный: при gap 32 три колонки давали 418.7px и мимо
   сетки, при 24 каждая ровно 4 колонки. */
.proof-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--grid-gap);
}

/* Факты выровнены по центру, поэтому линия над цифрой стала симметричной:
   растворяется к обоим краям, коралл держит середину под цифрой. */
.proof-fact {
  text-align: center;
}

.proof-fact::before {
  content: "";
  display: block;
  height: 2px;
  margin-bottom: var(--s-5);
  background: linear-gradient(90deg,
    transparent 0%, var(--ink) 20%, var(--accent) 50%, var(--ink) 80%, transparent 100%);
}

/* inline-block, а не block: у блока во всю ширину градиент растягивается на
   пустые поля по бокам, и на самих цифрах видна только середина заливки.

   Цифры слегка вытянуты по вертикали: ряд из трёх одинаковых блоков читался
   статично. Тянем трансформом, а не узким начертанием - узкие начертания в
   системе не используются, и лишнюю гарнитуру ради трёх чисел не заводим.
   Потолок 1.12: выше горизонтальные штрихи заметно толстеют относительно
   вертикальных. На поток это не влияет, transform не меняет раскладку. */
.proof-fact strong {
  display: inline-block;
  transform: scaleY(1.12);
  transform-origin: 50% 100%;
  font-family: var(--font-display);
  /* Ступень ниже узлового заголовка, и она обязана быть клэмпом. Фиксированные
     40px не сжимались вместе с заголовками: на 980 цифра (40) обгоняла и
     узловой h2 (34.3), и h1 (37.7), то есть факт оказывался крупнее раздела и
     крупнее оффера. Кривая подобрана так, чтобы цифра оставалась ниже опорного
     заголовка на всех трёх ширинах. */
  font-size: clamp(24px, 2.5vw, 36px);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
  line-height: 1;
}

.proof-fact strong i {
  font-style: normal;
  color: var(--accent-ink);
}

/* Цифры заливаются градиентом тем же приёмом, что и крупные заголовки, но
   уходят не в приглушённый тон, а в акцент: коралл забирает только хвост
   числа и приписку, поэтому масса остаётся тёмной и не спорит с основным
   CTA. Направление совпадает с линией над цифрой: тёмное начало, тёплый
   конец. Фолбэк обязателен, без background-clip текст стал бы прозрачным. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .proof-fact strong {
    /* Угол почти горизонтальный, а не 112deg как у заголовков: цифры это одна
       короткая строка, и при сильном наклоне доля тёплого конца зависела от
       ширины числа - «100+» оставалось почти чёрным, а «3 дня» уходило в
       коралл целиком. */
    background: linear-gradient(96deg, var(--ink) 22%, var(--accent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }

  .proof-fact strong i {
    color: inherit;
  }

  /* Пустой слот остаётся плоским: заглушку нельзя красить как факт. */
  .proof-fact.is-empty strong {
    background: none;
    color: var(--dim);
  }
}

.proof-fact span {
  display: block;
  margin-top: var(--s-3);
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* Слот без данных: приглушён целиком, чтобы заглушку нельзя было принять
   за настоящий факт. Заполняется только реальными цифрами. */
.proof-fact.is-empty strong,
.proof-fact.is-empty span {
  color: var(--dim);
}

.proof-fact.is-empty::before {
  background: linear-gradient(90deg,
    transparent 0%, var(--dim) 25%, var(--dim) 75%, transparent 100%);
  opacity: .55;
}

/* Градиентная заливка крупных заголовков: по диагонали от основного тона
   к приглушённому. На светлых секциях идёт от ink к dim, на тёмных — от
   ink-invert к его приглушённому варианту, иначе на чёрном фоне заголовок
   просто исчезнет. Обязателен фолбэк через @supports: без поддержки
   background-clip прозрачный текст пропал бы совсем. */
h1,
.section-head h2,
.open-task-copy h2,
.manifesto-grid h2,
.faq-head h2,
.contact-copy h2,
.design-lab h2 {
  color: var(--ink);
}

.section-dark .section-head h2,
.manifesto-grid h2,
.design-lab h2 {
  color: var(--ink-invert);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
  h1,
  .section-head h2,
  .open-task-copy h2,
  .manifesto-grid h2,
  .faq-head h2,
  .contact-copy h2,
  .design-lab h2 {
    background: linear-gradient(112deg, var(--ink) 30%, var(--dim) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }

  .section-dark .section-head h2,
  .manifesto-grid h2,
  .design-lab h2 {
    background: linear-gradient(112deg,
      var(--ink-invert) 30%,
      color-mix(in srgb, var(--ink-invert) 58%, var(--ink)) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}


/* ── Слайдеры ───────────────────────────────────────────── */

/* Ширина карточки задаётся колонками страницы, а не круглым числом пикселей.
   Было 380 / 400 / 340px — это 3.61, 3.79 и 3.09 колонки, то есть правый край
   карточки промахивался мимо колонки на 24–44px на каждой ширине окна.
   Теперь во всех каруселях один модуль: ровно 4 колонки. Межколонник ленты
   уже равен --grid-gap, поэтому карточки встают на сетку без исключений. */
.slider {
  position: relative;
  --col: calc((100% - 11 * var(--grid-gap)) / 12);
  --span-4: calc(var(--col) * 4 + 3 * var(--grid-gap));
}

.slider-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--s-2);
}

.slider-count {
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: .1em;
}

.section-dark .slider-count {
  color: color-mix(in srgb, var(--ink-invert) 72%, transparent);
}

.slider-count i {
  padding: 0 var(--s-1);
  font-style: normal;
}

.slider-buttons {
  display: flex;
  gap: var(--s-2);
}

.slider-buttons button {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid var(--dim);
  border-radius: 50%;
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--e-1), var(--glass-sheen);
  transition: background .2s ease, border-color .2s ease, color .2s ease, opacity .2s ease;
}

.slider-buttons button:hover:not(:disabled),
.slider-buttons button:focus-visible:not(:disabled) {
  background: var(--accent-grad);
  border-color: transparent;
  color: var(--ink);
  box-shadow: var(--accent-lift-sm);
}

.slider-buttons button:disabled {
  opacity: .35;
  cursor: default;
}

.section-dark .slider-buttons button {
  background: var(--glass-dark);
  border-color: var(--glass-dark-edge);
  color: var(--ink-invert);
  box-shadow: var(--glass-dark-sheen);
}

.slider-viewport {
  display: grid;
  grid-auto-flow: column;
  gap: var(--s-5);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  /* overflow-x: auto превращает overflow-y в auto, поэтому подъём карточки
     и её тень обрезаются верхней кромкой. Запас сверху решает это. */
  padding-top: var(--s-5);
  /* Оба края растворяются, а не режутся. Ширина растворения у каждой стороны
     своя и включается только когда за этим краем реально есть контент:
     иначе первая карточка выглядела бы выцветшей уже в покое.
     Класс проставляет app.js в той же функции, что гасит стрелки. */
  --fade-l: 0px;
  --fade-r: 0px;
  -webkit-mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--fade-l),
    #000 calc(100% - var(--fade-r)), transparent 100%);
  mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--fade-l),
    #000 calc(100% - var(--fade-r)), transparent 100%);
  transition: --fade-l .25s ease, --fade-r .25s ease;
  padding-bottom: var(--s-5);
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  cursor: grab;
}

.slider-viewport::-webkit-scrollbar {
  display: none;
}

/* Регистрация нужна, чтобы ширина растворения плавно анимировалась:
   обычные пользовательские свойства не участвуют в переходах. */
@property --fade-l {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@property --fade-r {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* Слева карточки прилипают к началу (scroll-snap-align: start), поэтому
   после каждого шага крайняя левая карточка видна целиком. Широкое
   растворение выедало её насквозь и читалось как баг, а не как эффект:
   оставляем узкую кромку, она нужна только во время самого перетаскивания. */
.slider-viewport.is-fade-start {
  --fade-l: 40px;
}

/* Справа карточка всегда обрезана краем сетки, здесь растворение и работает.
   На светлых секциях оно шире: белая плашка на белом фоне растворяется
   незаметно, и глазу нужен более длинный переход, чем на тёмной секции. */
.slider-viewport.is-fade-end {
  --fade-r: 190px;
}

.section-dark .slider-viewport.is-fade-end {
  --fade-r: 130px;
}

.slider-viewport.is-dragging {
  cursor: grabbing;
  scroll-behavior: auto;
  scroll-snap-type: none;
  user-select: none;
}

.slider-viewport > * {
  scroll-snap-align: start;
}

/* ── Услуги ─────────────────────────────────────────────── */

.service-track {
  grid-auto-columns: var(--span-4);
}

/* Бейдж «N работ» есть не у каждой услуги: привязка кейсов берётся из
   данных, а не придумывается. Из-за этого карточки наполнены по-разному, и
   ссылка прижимается к низу — иначе действие в каждой карточке оказывалось
   бы на своей высоте. */
.service-card > .link-action {
  margin-top: auto;
}

.service-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--s-6);
  transition: box-shadow .25s ease, transform .25s var(--ease);
}

.service-card:hover {
  box-shadow: var(--e-2);
  transform: translateY(-3px);
}

.service-card h3 {
  font-size: var(--t-lg);
}

.service-card p {
  flex: 1;
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
}

#services .section-head {
  margin-bottom: var(--s-5);
}

[data-slider="services"] .slider-toolbar {
  min-height: 240px;
  align-items: flex-end;
}


/* ── Дизайн: тёмная редакционная вставка ────────────────────
   Секция перенесена из отдельного прототипа design-specimen.html.
   Своих шрифтов, ресета и цветовых токенов у неё нет: всё берётся из
   общих переменных выше. Локальные значения — только то, чего в системе
   не было: более глубокая подложка витрины и раскладка колонок.

   Дизайн-секция и кейсы идут подряд и обе тёмные. Чтобы между ними не
   возникал шов из двух скруглений, радиус разрезан на две половины:
   верхние углы у дизайна, нижние у кейсов. Тёмная глава читается одним
   блоком внутри светлой страницы. */

.design-lab {
  position: relative;
  overflow: hidden;
  /* Нижний отступ меньше обычного: следом идёт такая же тёмная секция со
     своим верхним отступом, полная пара давала между ними пустой провал. */
  padding-bottom: var(--s-7);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}

.design-lab + .section-dark {
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

.design-lab > .shell {
  position: relative;
  z-index: 1;
}

/* Верхняя служебная линейка блока */
.design-lab-head {
  display: flex;
  justify-content: space-between;
  gap: var(--s-5);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--ink-line);
  color: color-mix(in srgb, var(--ink-invert) 68%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* Текстовая колонка уже витрины: читаемая мера строки против широкого кадра */
.design-lab-grid {
  display: grid;
  grid-template-columns: minmax(360px, 5fr) minmax(560px, 7fr);
  /* Зазор из шкалы, а не плавающий: clamp давал на 1440 ровно 72px, значение
     вне шкалы отступов, и колонка дизайна жила по своим правилам. */
  gap: var(--s-8);
  align-items: center;
  padding-top: var(--s-7);
}

.design-lab-copy {
  position: relative;
  z-index: 2;
}

.design-lab-kicker {
  margin: 0 0 var(--s-5);
  color: var(--accent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.design-lab h2 {
  max-width: 13ch;
  font-size: var(--t-4xl-sub);
}

.design-lab-lead {
  max-width: 42ch;
  margin-top: var(--s-6);
  color: color-mix(in srgb, var(--ink-invert) 72%, transparent);
  font-size: var(--t-base);
  line-height: 1.55;
}

/* Три направления: список, а не сетка карточек. Разделители тонкие,
   вес несут номер и название. */
.design-services {
  display: grid;
  margin: var(--s-7) 0 0;
  padding: 0;
  list-style: none;
}

.design-services li {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: var(--s-1) var(--s-4);
  padding: var(--s-4) 0;
  border-top: 1px solid var(--ink-line);
}

.design-services span,
.design-article small {
  color: color-mix(in srgb, var(--ink-invert) 58%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .14em;
  line-height: 1.5;
  text-transform: uppercase;
}

.design-services span {
  grid-row: 1 / span 2;
  /* Было 3px: единственное значение вне шкалы отступов на всей странице. */
  padding-top: var(--s-1);
}

.design-services strong {
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: -.01em;
}

.design-services small {
  color: color-mix(in srgb, var(--ink-invert) 66%, transparent);
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.45;
}

.design-article {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--s-5);
  margin-top: var(--s-5);
  padding: var(--s-4) 0;
  border-top: 1px solid var(--ink-line);
  border-bottom: 1px solid var(--ink-line);
  color: var(--ink-invert);
  font-size: var(--t-base);
  font-weight: 600;
  line-height: 1.35;
  text-decoration: none;
}

.design-article span {
  display: grid;
  gap: var(--s-1);
}

.design-article .ico-px {
  flex: none;
  color: var(--accent);
  transition: transform .2s var(--ease);
}

.design-article:is(:hover, :focus-visible) .ico-px {
  transform: translate(3px, -3px);
}

/* ── Дизайн: витрина ────────────────────────────────────────
   Подложка глубже секции: не новый цвет, а тот же ink, уведённый в чёрный.
   Кадр скруглён по общей лестнице, внутренний радиус на ступень ниже
   внешнего — иначе рамка и кадр читались бы как два разных языка. */

.specimen-window {
  position: relative;
  min-width: 0;
  padding: var(--s-4);
  border: 1px solid var(--ink-line);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, #000 58%, var(--ink));
}

.specimen-stage {
  position: relative;
  /* Кадр шире и ниже, чем в прототипе (было 1.28): секция дизайна была самой
     высокой на странице — 1251px против 1070 у кейсов, то есть услуга
     перевешивала доказательство. Детали внутри расставлены в процентах и
     переносят смену пропорции без правок. */
  aspect-ratio: 1.5 / 1;
  overflow: hidden;
  border-radius: var(--r-sm);
  background:
    linear-gradient(color-mix(in srgb, var(--ink-invert) 3%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--ink-invert) 3%, transparent) 1px, transparent 1px),
    radial-gradient(circle at 53% 47%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 38%),
    color-mix(in srgb, #000 45%, var(--ink));
  background-size: 8.333% 8.333%, 8.333% 8.333%, auto, auto;
  isolation: isolate;
}

/* Виньетка собирает внимание в центр кадра */
.specimen-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  background: radial-gradient(circle at center, transparent 54%, rgba(0, 0, 0, .62) 100%);
}

/* Уголки стоят на прямых участках кромки: отступ больше радиуса кадра,
   иначе прямой угол метки спорил бы с дугой. */
.specimen-corner {
  position: absolute;
  z-index: 30;
  width: 22px;
  height: 22px;
  border-color: var(--ink-invert);
  opacity: .8;
  pointer-events: none;
}

.specimen-corner-a { top: 10px; left: 10px; border-top: 1px solid; border-left: 1px solid; }
.specimen-corner-b { top: 10px; right: 10px; border-top: 1px solid; border-right: 1px solid; }
.specimen-corner-c { bottom: 10px; left: 10px; border-bottom: 1px solid; border-left: 1px solid; }
.specimen-corner-d { bottom: 10px; right: 10px; border-bottom: 1px solid; border-right: 1px solid; }

/* Детали висят в кадре и медленно дышат. Анимация стоит на паузе,
   пока секция не попала в экран: app.js вешает is-running. */
.specimen-part {
  position: absolute;
  z-index: 3;
  max-width: none;
  height: auto;
  pointer-events: none;
  user-select: none;
  will-change: transform;
}

.specimen-cable {
  left: -7%;
  top: 28%;
  width: 62%;
  opacity: .86;
  transform-origin: 56% 55%;
  animation: specimen-cable 8.6s ease-in-out infinite alternate both paused;
}

.specimen-panel {
  left: 5%;
  top: 8%;
  width: 30%;
  animation: specimen-panel 7.4s ease-in-out infinite alternate both paused;
}

.specimen-joint {
  right: 6%;
  top: 10%;
  width: 25%;
  animation: specimen-joint 6.8s ease-in-out infinite alternate both paused;
}

.specimen-glove {
  right: 4%;
  bottom: 3%;
  width: 27%;
  transform-origin: 45% 84%;
  animation: specimen-glove 7.9s ease-in-out infinite alternate both paused;
}

.specimen-core {
  left: 35%;
  top: 18%;
  z-index: 5;
  width: 34%;
  transform-origin: 52% 66%;
  animation: specimen-core 8.2s ease-in-out infinite alternate both paused;
}

.specimen-core-body {
  display: block;
  width: 100%;
  height: auto;
}

/* Метки замера: квадратные намеренно, это технические точки, а не блоки */
.specimen-mark {
  position: absolute;
  z-index: 11;
  width: 7px;
  height: 7px;
  border: 1px solid color-mix(in srgb, var(--ink-invert) 72%, transparent);
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 9%, transparent);
}

.specimen-mark-a { left: 38%; top: 45%; }
.specimen-mark-b { left: 65%; top: 29%; }
.specimen-mark-c { left: 74%; top: 68%; }

.specimen-readout {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-1) 0;
  color: color-mix(in srgb, var(--ink-invert) 58%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.specimen-readout-live {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--ink-invert);
}

.specimen-readout-live i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent);
}

.design-lab.is-running .specimen-part {
  animation-play-state: running;
}

@keyframes specimen-cable {
  from { transform: rotate(-17deg) scale(.94) translate3d(-1%, 2%, 0); }
  to { transform: rotate(-13deg) scale(.96) translate3d(2%, -1%, 0); }
}

@keyframes specimen-panel {
  from { transform: rotate(-12deg) scale(.86) translate3d(-2%, 2%, 0); opacity: .66; }
  to { transform: rotate(-8deg) scale(.88) translate3d(3%, -4%, 0); opacity: .78; }
}

@keyframes specimen-joint {
  from { transform: rotate(13deg) scale(.88) translate3d(2%, 2%, 0); }
  to { transform: rotate(8deg) scale(.9) translate3d(-3%, -5%, 0); }
}

@keyframes specimen-glove {
  from { transform: rotate(23deg) scale(.9) translate3d(2%, 3%, 0); }
  to { transform: rotate(18deg) scale(.92) translate3d(-4%, -4%, 0); }
}

@keyframes specimen-core {
  from { transform: rotate(-3deg) scale(.97) translate3d(-1%, 2%, 0); }
  to { transform: rotate(1deg) scale(.99) translate3d(2%, -2%, 0); }
}

/* ── Дизайн: крупная надпись с проявлением под курсором ──────
   Контур виден всегда, тёплая заливка живёт в маске radial-градиента и
   идёт за курсором. Две одинаковые надписи лежат друг на друге: нижняя
   держит контур, верхняя проявляется. Цвета только из тёплой линейки. */

.design-lab-display {
  margin-top: var(--s-7);
  padding-top: var(--s-6);
  border-top: 1px solid var(--ink-line);
}

/* Надпись не тянется во всю ширину блока: на 1320px она спорила с
   заголовком секции и читалась вторым заголовком, а не фоновым жестом.
   Ширина ограничена и отбита по центру — сам кадр подгоняется в app.js
   под реальные габариты строки, поэтому кегль здесь не задаётся. */
.design-lab-display-svg {
  display: block;
  width: min(100%, 980px);
  margin-inline: auto;
  overflow: visible;
}

.design-lab-display text {
  font-family: var(--font-display);
  font-size: 120px;
  font-weight: 600;
  letter-spacing: -.02em;
}

/* Надпись залита, а не обведена контуром.

   Контур пришлось снять вместе со сменой гарнитуры: глифы Unbounded собраны
   из перекрывающихся контуров. При заливке перекрытия не видны, а при
   fill:none со штрихом обводится КАЖДЫЙ контур, включая внутренние швы, —
   буквы читались как перечёркнутые и слипшиеся. У прежней гарнитуры
   контуры были слиты, поэтому обводка там работала. Заливка держит тот же
   приём: слово-призрак на фоне, тёплый свет проявляется под курсором. */
.design-lab-display-base {
  fill: color-mix(in srgb, var(--ink-invert) 9%, transparent);
  stroke: none;
}

/* Проявленная надпись со свечением: маска обрезает и заливку, и ореол,
   поэтому свет живёт ровно в пятне под курсором. */
.design-lab-display-fill {
  fill: url(#lab-text-grad);
  stroke: none;
  filter: drop-shadow(0 0 9px color-mix(in srgb, var(--accent) 55%, transparent));
  opacity: 0;
  transition: opacity .3s var(--ease);
}

.design-lab-display.is-lit .design-lab-display-fill {
  opacity: 1;
}

/* Контур прорисовывается один раз, когда секция входит в экран */
.design-lab.is-running .design-lab-display-base {
  stroke-dasharray: 3200;
  stroke-dashoffset: 3200;
  animation: lab-draw 3.6s var(--ease) forwards;
}

@keyframes lab-draw {
  to { stroke-dashoffset: 0; }
}

/* На узком десктопе витрина уходит под текст: колонка в 560px рядом с
   текстом перестаёт помещаться в сетку без переносов в заголовке. */
@media (max-width: 1180px) {
  .design-lab-grid {
    grid-template-columns: 1fr;
    gap: var(--s-8);
  }

  .design-lab h2 {
    max-width: 18ch;
  }

  /* В одну колонку витрина занимала всю ширину, и при 1.5:1 секция дизайна
     разгонялась до 1842px на 980 против 982 у кейсов: опорный блок становился
     вдвое тяжелее узла воронки. Кадр здесь шире и ниже, а три услуги встают
     в ряд вместо столбца. На широких ширинах пропорции не меняются. */
  .specimen-stage {
    aspect-ratio: 2.1 / 1;
  }

  .design-services {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--grid-gap);
    margin-top: var(--s-6);
  }
}

/* ── Кейсы ──────────────────────────────────────────────── */

/* ── Кейсы: декоративный объект ─────────────────────────────
   Веер пластин на CSS-трансформах: ни картинок, ни 3D-движка.
   Каждая пластина повёрнута и поднята пропорционально своему индексу,
   грань подсвечена сверху и затемнена снизу — объём даёт свет, а не сетка.
   Объект уходит за правый край секции и частично уводится под стекло. */

#cases {
  position: relative;
  overflow: hidden;
}

#cases > .shell {
  position: relative;
  z-index: 1;
}

/* Веер обязан уходить ЗА кромку секции, а не висеть внутри неё: секции
   обрезают содержимое (`overflow: hidden`), и объект читается продолжением
   композиции за краем. Прежние 60px оставляли зазор до низа блока — веер
   повисал в воздухе и выглядел вставленной картинкой. Теперь низ уходит на
   200px под кромку, как верхний уходит на 286px за верхнюю. */
.cases-object {
  position: absolute;
  right: -40px;
  bottom: -200px;
  z-index: 0;
  width: 760px;
  height: 560px;
  pointer-events: none;
}

/* Свет за объектом вместо плашки: перекрытие даёт мягкое пятно, а не
   прямоугольник, который налезал на текст карточек. */
.cases-object::before {
  content: "";
  position: absolute;
  right: 60px;
  bottom: 40px;
  width: 560px;
  height: 460px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(240, 90, 63, .26), rgba(240, 90, 63, .07) 55%, transparent 72%);
  filter: blur(26px);
}

/* Веер — рендер на прозрачном фоне, а не четырнадцать блоков с градиентом.
   Прежняя сборка держала геометрию (нарастающий шаг угла, ось за краем
   пластины, наклон), но пластины оставались плоскими: ни фаски, ни
   материала, ни настоящего света. Геометрия раскрытия в рендере повторена,
   поэтому координаты, повороты и обрезка краем секции не менялись. */
.cases-object img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* contain, а не cover: пропорция файла (1.343) чуть отличается от
     пропорции коробки (1.357), и растягивание правило бы фаски. */
  object-fit: contain;
  /* Прежние пластины занимали не всю коробку, а её нижне-правую часть, и
     веер читался углом композиции. Рендер заполняет кадр целиком, поэтому
     ужимается к тому же углу — иначе он выезжает на карточки. */
  transform: scale(.72);
  transform-origin: 100% 100%;
}

/* Та же гармошка, продублированная в светлой секции в тех же координатах
   страницы. Каждая секция обрезает свою часть по своей кромке, поэтому пластины
   продолжаются без разрыва, а цвет переключается ровно на границе секций.
   Геометрия здесь не меняется — только цвет. Сдвинув тёмную половину,
   обязательно пересчитать top у светлой: bottom(60) + height(560) = 620, со знаком минус в top светлой. */

.open-task {
  position: relative;
  overflow: hidden;
}

.open-task > .shell {
  position: relative;
  z-index: 1;
}

.cases-object-light {
  right: -40px;
  top: -620px;
  bottom: auto;
}

/* Зеркальная пара в верхнем левом углу секции кейсов: поворот на 180°
   даёт точечную симметрию с нижним правым веером и уравновешивает блок.
   Светлая копия живёт в секции услуг в тех же координатах страницы,
   поэтому пластины так же продолжаются через верхнюю границу. */
.cases-object-tl {
  right: auto;
  bottom: auto;
  left: -40px;
  /* Веер опущен в блок: у самой кромки он читался обрезком чужого объекта.
     Ниже начинается заголовок, а светлый текст поверх коралла даёт 2.13 при
     пороге 3.0 — поэтому нижний край объекта обязан остаться ВЫШЕ заголовка,
     и координата проверяется замером, а не на глаз. С рендером вместо
     пластин объект стал плотнее и заезжал на «Настраиваем визуальный язык»,
     поэтому поднят со -70 до -260: низ картинки встаёт на 117px, а ниже идут
     метка раздела (153) и заголовок (195). Метку «02 / Дизайн» на 70–88 он
     перекрывает по-прежнему — так было и с пластинами, она служебная.
     Менять только с повторным замером. */
  top: -260px;
  transform: rotate(180deg);
}

.cases-object-tl-light {
  right: auto;
  top: auto;
  left: -40px;
  bottom: -400px;
  transform: rotate(180deg);
}

#services {
  position: relative;
  overflow: hidden;
}

#services > .shell {
  position: relative;
  z-index: 1;
}

.cases-object-light::before {
  background: radial-gradient(closest-side, rgba(240, 90, 63, .1), rgba(240, 90, 63, .03) 55%, transparent 72%);
}

/* Светлая половина держится тише тёмной.

   Она лежит под карточками услуг и просвечивает сквозь стекло: при полной
   насыщенности коралла веер по массе и цвету перебивал кнопки, то есть декор
   выигрывал у действия. Отсчёт градиента сдвинут на ступень светлее —
   пластина начинается там, где раньше была её середина. Геометрия и
   координаты не тронуты: они связаны с тёмной половиной. */
/* Светлая половина держится тише: под ней лежат карточки услуг, и при полной
   насыщенности веер перебивал бы кнопки. Раньше это делалось своим набором
   градиентов для пластин; с рендером хватает приглушения самой картинки. */
.cases-object-light img {
  opacity: .5;
}

/* ── Кейсы: карточки по черновому макету ────────────────────
   Подложка светлее секции, внутри белый кадр с равным зазором, под ним
   категория и название по центру. Пропорции сняты с макета: кадр 1.277
   по сторонам, зазор 2% ширины карточки, радиус подложки ~10% ширины.
   Класс .card здесь не используется: у карточки своя поверхность, а не стекло. */

.case-track {
  grid-auto-columns: var(--span-4);
}

.case-card {
  display: flex;
  flex-direction: column;
  padding: var(--s-2);
  border-radius: 40px;
  /* Непрозрачная: в макете подложка сплошная, а сквозь прозрачную
     просвечивал декоративный объект секции. */
  background: color-mix(in srgb, var(--ink-invert) 17%, var(--ink));
  text-align: center;
  text-decoration: none;
  /* Карточку «достают из колоды»: она выходит вперёд и отрывается тенью.
     Запас сверху под этот подъём задан в .slider-viewport. */
  transition: transform .34s cubic-bezier(.2, .9, .25, 1), box-shadow .34s ease;
}

.case-card:is(:hover, :focus-visible) {
  transform: translateY(-10px);
  box-shadow: 0 22px 40px -16px rgba(0, 0, 0, .72);
}

.case-shot {
  margin: 0;
  aspect-ratio: 1.277;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--surface-2);
}

.case-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

.case-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-5) var(--s-4) var(--s-4);
}

.case-cat {
  color: color-mix(in srgb, var(--ink-invert) 66%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.case-card h3 {
  font-size: var(--t-lg);
}

.case-card p {
  max-width: 300px;
  color: color-mix(in srgb, var(--ink-invert) 74%, transparent);
  font-size: var(--t-sm);
  font-weight: 600;
}

.case-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-1);
  color: var(--ink-invert);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* ── Иная задача ────────────────────────────────────────── */

/* Опорная секция: тот же ранг, что у остальных опор. */
.open-task {
  padding: var(--s-8) 0;
}

.open-task-grid {
  position: relative;
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: var(--s-8);
  align-items: center;
  padding: var(--s-9) var(--s-8);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-2), var(--glass-sheen);
}

/* Пульс задач: правый нижний угол панели был пустым. Столбики набраны теми
   же блоками, что пиксельные глифы управления, и оживают волной слева
   направо. Пик подкрашивается приглушённым кораллом, а не чистым акцентом:
   рядом стоит основная кнопка, и второй яркой точки в кадре быть не должно.
   Декор не кликается и снимается при prefers-reduced-motion. */
.task-pulse {
  position: absolute;
  right: var(--s-8);
  bottom: var(--s-8);
  display: flex;
  align-items: flex-end;
  gap: var(--s-2);
  pointer-events: none;
}

.task-pulse i {
  display: block;
  width: 10px;
  height: calc(var(--k) * 16px);
  color: var(--ink);
  opacity: .14;
  background-image: repeating-linear-gradient(180deg, currentColor 0 10px, transparent 10px 16px);
  animation: task-pulse 5.6s var(--ease) infinite;
  animation-delay: calc(var(--k) * .12s);
}

@keyframes task-pulse {
  0%, 46%, 100% { opacity: .14; color: var(--ink); }
  12% { opacity: .62; color: color-mix(in srgb, var(--accent) 72%, var(--ink)); }
  30% { opacity: .14; color: var(--ink); }
}

.open-task-visual {
  position: relative;
  min-height: 560px;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--surface);
  isolation: isolate;
}

.open-task-question {
  position: absolute;
  z-index: 0;
  right: -2%;
  bottom: -18%;
  color: color-mix(in srgb, var(--accent) 9%, transparent);
  font-family: var(--font-display);
  font-size: clamp(300px, 30vw, 430px);
  line-height: .8;
  pointer-events: none;
  user-select: none;
}

.open-task-mascot-rig {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: -24px;
  width: min(82%, 410px);
  aspect-ratio: 2 / 3;
  transform: translateX(-50%);
  transform-origin: 50% 88%;
  transition: transform .42s var(--ease);
  will-change: transform;
}

.open-task-mascot-body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.open-task-connector-signal {
  position: absolute;
  z-index: 3;
  top: 23.3%;
  left: 63.1%;
  width: 34px;
  aspect-ratio: 1;
  border: 2px solid var(--accent-ink);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}

.has-motion .open-task-visual:not(.is-visible) .open-task-mascot-rig {
  opacity: 0;
  transform: translate3d(-50%, 20px, 0) rotate(-2deg);
}

.has-motion .open-task-visual.is-visible .open-task-mascot-rig {
  animation: open-task-arrive .72s var(--ease);
}

.open-task-grid:has(.open-task-copy .btn:is(:hover, :focus-visible)) .open-task-mascot-rig {
  transform: translate3d(calc(-50% + 7px), -5px, 0) rotate(1.5deg);
}

.open-task-grid:has(.open-task-copy .btn:is(:hover, :focus-visible)) .open-task-connector-signal {
  animation: connector-confirm .52s ease-out both;
}

@keyframes open-task-arrive {
  0% {
    opacity: 0;
    transform: translate3d(-50%, 20px, 0) rotate(-2deg);
  }
  68% {
    opacity: 1;
    transform: translate3d(-50%, -5px, 0) rotate(.7deg);
  }
  100% {
    opacity: 1;
    transform: translate3d(-50%, 0, 0) rotate(0);
  }
}

@keyframes connector-confirm {
  0% {
    opacity: 0;
    transform: scale(.72);
  }
  38% {
    opacity: .8;
  }
  100% {
    opacity: 0;
    transform: scale(1.45);
  }
}

.open-task-copy h2 {
  margin-bottom: var(--s-5);
  font-size: var(--t-4xl-sub);
}

.open-task-copy p {
  max-width: 560px;
  margin-bottom: var(--s-6);
  color: var(--muted);
  font-size: var(--t-md);
}

/* ── Процесс ────────────────────────────────────────────── */

.process-track {
  grid-auto-columns: var(--span-4);
}

/* Этапы читаются как последовательность, а не как пять одинаковых плашек.
   Три приёма, все без наклонов и без подъёма при наведении:
   1. Номер этапа вынесен вправо крупной моно-цифрой: он держит угол карточки
      и даёт ряду счётный ритм, а мелкая метка «Этап» уходит над ним.
   2. Бейджи соединены трассой: отрезок продолжается в межколонник и упирается
      в следующую карточку, поэтому ряд читается слева направо.
   3. По верхней кромке идёт полоса прогресса, заполнение растёт по шагам.
   Прогресс и трасса нарисованы псевдоэлементами, разметку не трогают. */
.process-card {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  /* Свободную высоту забирает только строка абзаца. Без явного 1fr пустое
     место делится поровну между всеми строками, и в карточке с более длинным
     текстом заголовок уезжал на 11px ниже, чем у соседей. */
  grid-template-rows: auto auto 1fr;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-6);
}

.process-card .badge {
  grid-column: 1;
  grid-row: 1;
}

.process-card h3,
.process-card > p {
  grid-column: 1 / -1;
}

/* Полоса прогресса: доля закрашенного растёт от этапа к этапу. Переход к
   серому размазан, а не встык: на жёсткой границе полоса читалась как две
   отдельные линии, приставленные друг к другу. Коралл держится плотным до
   45% своей длины и дальше гаснет в серый ровно к отметке шага. */
.process-card::before {
  content: "";
  position: absolute;
  /* Полоса лежит на самой обводке карточки, а не отдельной чертой под ней,
     и начинается там, где кончается скругление угла. Оба конца растворяются
     в прозрачность: дальше видно тонкую границу карточки, поэтому линия
     переходит в обводку, а не обрывается тупым концом. */
  top: -1px;
  left: var(--r-md);
  right: var(--r-md);
  height: 2px;
  background: linear-gradient(90deg,
    transparent 0,
    var(--accent) 16px,
    var(--accent) calc(var(--step, 20%) * .45),
    var(--glass-edge) var(--step, 20%),
    var(--glass-edge) calc(100% - 16px),
    transparent 100%);
}

.process-track > .process-card:nth-child(1) { --step: 20%; }
.process-track > .process-card:nth-child(2) { --step: 40%; }
.process-track > .process-card:nth-child(3) { --step: 60%; }
.process-track > .process-card:nth-child(4) { --step: 80%; }
.process-track > .process-card:nth-child(5) { --step: 100%; }

/* Трасса приходит к бейджу СЛЕВА, из межколонника: отрезок начинается у
   кромки предыдущей карточки и упирается в бейдж. Вправо линию вести
   нельзя, там стоит номер этапа и она бы его перечеркнула. У первой
   карточки приходить неоткуда, отрезок снимается. */
.process-card::after {
  content: "";
  position: absolute;
  top: calc(var(--s-6) + 26px);
  left: calc(var(--grid-gap) * -1);
  width: calc(var(--grid-gap) + var(--s-6));
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%, color-mix(in srgb, var(--ink) 16%, transparent) 45%,
    color-mix(in srgb, var(--ink) 16%, transparent) 100%);
}

.process-track > .process-card:first-child::after {
  display: none;
}

.process-no {
  grid-column: 2;
  grid-row: 1;
  display: grid;
  justify-items: end;
  gap: var(--s-1);
  /* 26% давали композит rgb(193,193,193) и 1.81:1 против порога 3.0 для
     крупного текста. Цифра этапа это текст, а не фон, поэтому доля чернил
     поднята до 48%: композит rgb(141,140,139), проба даёт запас к порогу. */
  color: color-mix(in srgb, var(--ink) 48%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  line-height: 1;
}

.process-no i {
  color: var(--muted);
  font-size: var(--t-xs);
  font-style: normal;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.process-card h3 {
  font-size: var(--t-lg);
}

.process-card > p {
  flex: 1;
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
}

.process-result {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--accent-ink);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* ── Манифест ───────────────────────────────────────────── */

/* Веер в правом нижнем углу: тот же объект, что в дизайне и кейсах, но
   целиком внутри блока. Секции нужен overflow, иначе пластины вылезут на
   белую страницу, а контенту - свой слой поверх декора. */
.manifesto-section {
  position: relative;
  overflow: hidden;
}

.manifesto-section > .shell {
  position: relative;
  z-index: 1;
}

/* Веер уменьшен трансформом, а не координатами: правые колонки давали 42-80%
   плотности против 17-21% у левых, центр масс секции уезжал на 0.671 при
   норме 0.5. Трансформ не двигает раскладку и не ломает связку координат
   с копиями веера в других блоках. */
.manifesto-object {
  right: -200px;
  /* Тот же приём, что у веера в работах: объект уходит под нижнюю кромку
     секции, а не заканчивается внутри неё. */
  bottom: -180px;
  transform: scale(.88);
  transform-origin: 100% 100%;
}

.manifesto-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: start;
}

.manifesto-grid h2 {
  grid-column: 1 / 8;
  font-size: var(--t-4xl-sub);
}

/* Текст о студии под заголовком. Строка задана явно: декоративная петля
   тоже стоит в сетке и без явной строки вклинивалась между заголовком и
   текстом, разрывая их на пол-экрана. Мера — пять колонок, при восьми
   строка на 16px кегле уходила за 90 знаков. */
.manifesto-lead {
  grid-column: 1 / 6;
  grid-row: 2;
  display: grid;
  gap: var(--s-4);
  margin-top: var(--s-6);
  color: color-mix(in srgb, var(--ink-invert) 72%, transparent);
  font-size: var(--t-base);
  line-height: 1.6;
}

.manifesto-notes {
  /* Колонка сдвинута влево на одну: при 9/13 между текстом слева и
     карточками справа оставалась пустая полоса в две колонки, и блок
     читался разорванным на две несвязанные половины. Ближе к тексту
     подвинуть нельзя: заголовок занимает колонки 1-7, и старт раньше
     восьмой линии наложил бы карточки на него в первой строке. */
  grid-column: 8 / 13;
  grid-row: 1 / 4;
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}

.manifesto-note {
  padding: var(--s-5);
  border: 1px solid var(--glass-dark-edge);
  border-radius: var(--r-md);
  background: var(--glass-dark);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-dark-sheen);
}

.manifesto-note strong {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
}

.manifesto-note p {
  color: color-mix(in srgb, var(--ink-invert) 74%, transparent);
  font-size: var(--t-sm);
  font-weight: 600;
}

/* Правая колонка манифеста была короче левой на 291px при ширине 1280:
   заголовок, текст и кабель слева набирают 667, три карточки справа только
   376, и под ними оставалась пустая треть экрана. Колонка занимает строки
   1-3, но `align-items: start` у сетки не давал ей растянуться, и она стояла
   по содержимому.

   Колонка тянется на всю высоту строки, карточки делят её поровну. Кегль и
   поля растут вслед за коробкой: в карточке высотой под 200px прежние 14px
   с полем 24 читались бы потерянными, и вместо пустоты под колонкой
   получилась бы пустота внутри карточек. Содержимое центрируется по
   вертикали - при равной высоте карточек прижатый вверх текст оставляет
   разные по величине хвосты. */
@media (min-width: 901px) {
  .manifesto-notes {
    align-self: stretch;
  }

  .manifesto-note {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--s-6);
  }

  .manifesto-note strong {
    font-size: var(--t-md);
  }

  .manifesto-note p {
    font-size: var(--t-base);
  }
}

/* ── Блог ───────────────────────────────────────────────── */

.blog-track {
  grid-auto-columns: var(--span-4);
}

.blog-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--s-6);
  /* Карточка — ссылка на статью, поэтому подчёркивание снимаем явно:
     иначе браузер подчеркнёт весь текст внутри, включая чип и заголовок. */
  text-decoration: none;
  color: var(--ink);
  transition: box-shadow .25s ease, transform .25s var(--ease);
}

.blog-card:hover {
  box-shadow: var(--e-2);
  transform: translateY(-3px);
}

.blog-card h3 {
  font-size: var(--t-md);
  line-height: 1.2;
}

.blog-card p {
  flex: 1;
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
}

.blog-status {
  /* Правило проекта: на 12px кегле метка берёт --muted, а не --dim. Здесь оно
     было не применено, и метка шла на 4.21:1 при пороге 4.5. */
  color: var(--muted);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* ── Вопросы ────────────────────────────────────────────── */

/* Было 4fr 8fr с межколонником 64: доли считались от остатка и список
   начинался на x=543 при колонке 6 на x=508. Теперь честные 12 колонок,
   колонка 5 оставлена пустой — она и держит разрыв между заголовком и
   списком вместо произвольного gap. */
.faq-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: start;
}

.faq-head {
  grid-column: 1 / 5;
}

.faq-list {
  grid-column: 6 / 13;
}

.faq-head h2 {
  margin-bottom: var(--s-5);
  font-size: var(--t-4xl-sub);
}

.faq-head p {
  max-width: 320px;
  color: var(--muted);
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

.faq-list details {
  overflow: hidden;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-1), var(--glass-sheen);
}

.faq-list summary {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  cursor: pointer;
  list-style: none;
  font-size: var(--t-md);
  font-weight: 600;
}

.faq-list summary::-webkit-details-marker {
  display: none;
}

.faq-list summary > span:not(.badge) {
  flex: 1;
}

.faq-list summary .badge {
  margin-left: auto;
  transition: background .25s ease, box-shadow .25s ease;
}

/* Плюс меняется на крестик подменой глифа, а не поворотом: повёрнутый
   пиксельный глиф теряет сетку и мылится. */
.faq-list summary .badge .i-shut {
  display: none;
}

.faq-list details[open] summary .badge {
  background: var(--accent-grad);
  border: 0;
  color: var(--ink);
  box-shadow: var(--accent-lift-sm);
}

.faq-list details[open] summary .badge .i-open {
  display: none;
}

.faq-list details[open] summary .badge .i-shut {
  display: block;
}

.faq-list details p {
  max-width: 720px;
  padding: 0 var(--s-6) var(--s-5);
  color: var(--muted);
}

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

.contact-grid {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: var(--s-8);
  padding: var(--s-8);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--glass-strong);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-2), var(--glass-sheen);
}

.contact-copy {
  display: flex;
  flex-direction: column;
}

.contact-copy h2 {
  /* Форма — узел воронки, а заголовок был самым мелким на странице (40px
     против 64 у всех остальных). Поднят до опорной ступени, а не до узловой:
     колонка карточки всего 5 из 12, и 64px разошлись бы там на пять строк.
     Вес узла форма добирает акцентной кнопкой, маскотом и штампом. */
  margin-bottom: var(--s-5);
  font-size: var(--t-4xl-sub);
}

.contact-copy > p {
  max-width: 380px;
  color: var(--muted);
}

.form-operator {
  position: relative;
  z-index: 2;
  width: 180px;
  height: 270px;
  align-self: flex-start;
  margin-top: auto;
  pointer-events: none;
}

.form-operator-rig {
  position: absolute;
  inset: 0;
  will-change: transform;
}

.form-operator-body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.form-stamp-result {
  position: absolute;
  z-index: 3;
  right: calc(var(--s-5) * -1);
  bottom: var(--s-6);
  display: grid;
  gap: var(--s-1);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--accent-grad);
  box-shadow: var(--accent-lift-sm);
  color: var(--ink);
  opacity: 0;
}

.form-stamp-result span {
  font-size: var(--t-sm);
  font-weight: 600;
}

.form-stamp-result small {
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .12em;
}

.contact.is-stamped .form-operator-rig {
  animation: stamp .86s cubic-bezier(.22, .78, .18, 1);
}

.contact.is-stamped .form-stamp-result {
  animation: stamp-appear .44s .34s var(--ease) both;
}

@keyframes stamp {
  0%, 100% { transform: translate3d(0, 0, 0); }
  40% { transform: translate3d(0, 7px, 0); }
}

@keyframes stamp-appear {
  from { opacity: 0; transform: translate3d(0, -8px, 0) scale(.96); }
  to { opacity: 1; transform: none; }
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}

.contact-form label {
  display: grid;
  gap: var(--s-2);
}

.contact-form label > span {
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 600;
}

.contact-form input,
.contact-form textarea {
  width: 100%;
  padding: var(--s-4);
  border: 1px solid var(--dim);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-size: var(--t-base);
  resize: vertical;
}

.contact-form textarea {
  min-height: 120px;
}

.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: var(--dim);
}

.contact-form input:focus,
.contact-form textarea:focus {
  border-color: var(--accent-ink);
  outline: 2px solid var(--accent-ink);
  outline-offset: 1px;
}

/* Сноска и кнопка в одну строку помещаются только на широком экране: на 360
   кнопка шириной 206px уезжала за край и тянула прокрутку всей страницы. */
.form-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4) var(--s-6);
  margin-top: auto;
}

.form-footer p {
  max-width: 340px;
  color: var(--muted);
  font-size: var(--t-xs);
  font-weight: 600;
}

/* Поле-приманка от ботов. Не display:none: часть спам-скриптов такие
   поля пропускает намеренно. Убираем из потока и с экрана иначе. */
.form-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  min-height: 20px;
  color: var(--accent-ink);
  font-size: var(--t-sm);
  font-weight: 600;
}

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

/* Фон подвала по черновому макету владельца: подсвеченный верхний край,
   тёмные силуэты толпы головами в свет, всё размыто. Картинка ложится
   вторым слоем поверх --ink и растворяется в него на 146px — раньше, чем
   начинается контент, поэтому логотип, навигация и телефон лежат на ровном
   тёмном фоне и не теряют контраст. Собирается tools/make-footer-crowd.py.

   Верхний отступ вырос до 192px: полосе со светом нужна своя высота, при
   прежних 64px толпа шла прямо по тексту.

   Размер задан по высоте, а не `100%`: растянутая по ширине картинка
   превращает головы в овалы. Ниже лежит градиент с тем же профилем света —
   на мониторах шире 2025px, где картинка кончается, полоса продолжается
   без силуэтов вместо резкого обрыва. Под картинкой он не виден: там
   кремовый слой почти непрозрачен. */
.site-footer {
  padding: calc(var(--s-10) + var(--s-8)) 0 var(--s-6);
  background:
    url("assets/decor/budin-footer-crowd-v1.webp") top center / auto 428px no-repeat,
    linear-gradient(180deg,
      var(--ink-invert) 0,
      color-mix(in srgb, var(--ink-invert) 66%, transparent) 73px,
      color-mix(in srgb, var(--ink-invert) 24%, transparent) 107px,
      transparent 146px),
    var(--ink);
  color: var(--ink-invert);
}

/* Те же 12 колонок, что и у страницы: бренд 5, две колонки ссылок по 3,
   колонка 6 пустая как разделитель. Доли fr с межколонником 64 давали
   произвольные ширины. */
.footer-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--grid-gap);
  padding-bottom: var(--s-8);
}

/* Бренд занимает первую четверть, колонки ссылок делят остаток по три.
   `span 3` вместо поимённых координат: колонок в подвале три, и раскладка
   не обязана знать, сколько их. Прежнее правило адресовало их через
   `nth-of-type`, а тот считает ВСЕ соседние div, а не только `.footer-nav`:
   первой колонке доставалось правило второй, вторая не получала ничего и
   падала следующим рядом под бренд. Половина подвала стояла пустой. */
.footer-brand {
  grid-column: 1 / 4;
}

.footer-nav {
  grid-column: span 3;
}

.footer-brand img {
  width: 200px;
}

.footer-brand p {
  max-width: 380px;
  margin-top: var(--s-5);
  color: color-mix(in srgb, var(--ink-invert) 72%, transparent);
  font-size: var(--t-sm);
  font-weight: 600;
}

.footer-nav {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

/* Контакты повторяют боевой подвал: канал, адрес и имя, с кем говорить.
   Подпись слева служебная, поэтому кегль меньше и цвет приглушён, а сам
   контакт остаётся полноценной ссылкой. */
.footer-contacts {
  display: grid;
  gap: var(--s-3);
  margin-top: var(--s-6);
  padding: 0;
  list-style: none;
}

.footer-contacts li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
}

.footer-contacts span {
  min-width: 92px;
  color: color-mix(in srgb, var(--ink-invert) 55%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.footer-contacts a {
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
}

.footer-contacts a:is(:hover, :focus-visible) {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.footer-contacts i {
  color: color-mix(in srgb, var(--ink-invert) 60%, transparent);
  font-size: var(--t-xs);
  font-style: normal;
  font-weight: 600;
}

/* Ниши идут строкой ссылок, а не пятой колонкой: их девятнадцать, и любой
   вертикальный столбец такой длины перевесил бы весь подвал. Названия
   короткие, без сквозного «Сайт для»: девятнадцать одинаковых зачинов
   подряд читаются как ошибка вёрстки, а раздел над строкой и так говорит,
   о чём речь. */
/* Ниже десктопа четыре колонки не живут: ссылки подвала стоят `max-content`
   и не переносятся, поэтому колонка упирается в самую длинную строку
   («Лид-бот по Самуи и Пангану», 215px) и перестаёт сжиматься — вместо
   переноса страница уезжала горизонтально. Раскладываем в два столбца,
   потом в один. */
/* Порог стоит ровно под `body { min-width: 980px }`: в поддерживаемой полосе
   подвал всегда в четыре колонки (на 980 они по 215px и встают впритык), а
   правила ниже ждут снятия десктопного ограничения. */
@media (max-width: 979px) {
  .footer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer-brand,
  .footer-nav {
    grid-column: span 1;
  }
}

@media (max-width: 620px) {
  .footer-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Строки ниш в подвале нет по решению владельца: девятнадцать пилюль
   перевешивали низ страницы. Ниши остаются на странице услуг, куда ведут
   и панель шапки, и «Все услуги» в нижней полосе. */

.footer-nav span {
  margin-bottom: var(--s-2);
  color: color-mix(in srgb, var(--ink-invert) 60%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
}

/* `max-content` держит подчёркивание по длине текста, а не по всей колонке,
   но без потолка длинная строка («Лид-бот по Самуи и Пангану») вылезала за
   свою колонку на узком десктопе. Потолок разрешает перенос там, где иначе
   был бы наезд на соседнюю. */
.footer-nav a {
  width: max-content;
  max-width: 100%;
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
}

.footer-nav a:is(:hover, :focus-visible) {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  justify-content: space-between;
  align-items: center;
  padding-top: var(--s-5);
  border-top: 1px solid var(--ink-line);
  color: color-mix(in srgb, var(--ink-invert) 60%, transparent);
  font-family: var(--font-meta);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.footer-bottom a {
  text-decoration: none;
}

.footer-bottom a:is(:hover, :focus-visible) {
  color: var(--ink-invert);
}

/* Правый край нижней полосы отдан плавающему помощнику: он приколот к окну
   (`position: fixed`, right 32) и на самом низу страницы ложится ровно на
   «Наверх». Гуттер шириной с виджет уводит ссылку из-под него — но только
   там, где помощник есть: он стоит на главной и не переносится на
   собранные страницы, а пустой отступ в 300px на них выглядел бы обрывом. */
@media (min-width: 1100px) {
  body:has(.budin-helper) .footer-bottom {
    padding-right: 300px;
  }
}

/* Разделы сайта стоят в нижней полосе, а не пятой колонкой сверху: это
   навигация, а не ещё одно направление работ, и рядом с картой сайта она
   читается ровно как подпись. */
.footer-bottom-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3) var(--s-5);
}

/* ── Фокус и движение ───────────────────────────────────── */

:focus-visible {
  outline: 3px solid var(--accent-ink);
  outline-offset: 2px;
}

.section-dark :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--ink-invert);
}

@media (max-width: 1180px) {
  :root {
    --gutter: var(--s-5);
  }

  .hero-mascot-stage {
    height: 480px;
  }

  /* Ширины каруселей здесь больше не переопределяются: модуль в 4 колонки
     сам сжимается вместе с сеткой (295px на 980). Прежние 340 / 360 / 320px
     были ручной подгонкой и сбивали карточки с колонок именно на узком
     десктопе — там, где сетка и так тесная. */

  .open-task-grid,
  .contact-grid {
    padding: var(--s-7);
  }

  .open-task-visual {
    min-height: 500px;
  }

  .open-task-mascot-rig {
    width: min(86%, 360px);
  }

  .budin-helper {
    right: var(--s-5);
  }

  /* Полоса направлений уступает угол плавающему лончеру: на 980 последний чип
     заходил под него на 1.4px, кликабельное перекрывалось кликабельным.
     Одного padding-right мало: чипы не сжимаются и просто вылезают за поле,
     поэтому разрешаем перенос — лишний чип уходит на вторую строку. */
  .hero-strip {
    flex-wrap: wrap;
    row-gap: var(--s-3);
    padding-right: 180px;
  }

  .budin-helper-caption {
    display: none;
  }
}

/* ── Адаптив ─────────────────────────────────────────────────
   Раскладка держится на сетке в двенадцать колонок, и почти каждый блок
   стоит в ней поимённо: заголовок 1/9, интро 9/13, маскот 9/13 и так далее.
   На узком экране такие координаты бессмысленны, поэтому сворачиваем сетку
   в одну колонку и снимаем поимённые координаты у всех её жильцов разом.
   Порог 900 выбран по шапке: навигация с кнопкой занимают 784px и не
   сжимаются, а ниже этой ширины они уже не помещаются рядом с логотипом. */

@media (max-width: 1024px) {
  /* Веер работ в карточке услуги — украшение поверх содержания. На узком
     десктопе он отъедает место у текста, ради которого карточка и стоит.

     Селектор обязан быть составным. Базовое правило `.work-fan` объявлено
     НИЖЕ этого медиазапроса, вес у них равный, и побеждало оно по порядку:
     веер не скрывался вовсе и доезжал до телефона, где три скриншота 680px
     ужимались до 38px и читались грязным пятном. Тот же гоча, что с
     `.decor` - там он лечится составным `.decor.decor-services`. */
  a.work-fan {
    display: none;
  }
}

@media (max-width: 900px) {
  :root {
    --gutter: var(--s-4);
    --grid-gap: var(--s-4);
    /* Вертикальная шкала тоже обязана ужиматься, а не только боковые поля.
       Крупные ступени задумывались для широкого экрана: на телефоне те же
       128px сверху и снизу превращали каждую секцию в четверть экрана
       пустоты, и страница вырастала до двух десятков экранов. Мелкие
       ступени (s-1..s-6) не трогаем: на них держатся внутренние отступы
       кнопок, полей и карточек, там уменьшать нечего. */
    --s-7: 36px;
    --s-8: 48px;
    --s-9: 64px;
    --s-10: 88px;
  }

  /* Заголовки на телефоне переносятся по слогам. Ширины здесь предельные:
     карточка ленты на экране 320 даёт строку в 174px, а «Корпоративный» в
     Unbounded занимает 220 - слово не влезает ни при каком кегле, который
     ещё читается заголовком. Подгонять кегль под самое длинное слово каждой
     ниши бессмысленно: ниш шесть десятков. Перенос ставит обычный дефис по
     слогу, а `overflow-wrap: break-word` остаётся под ним страховкой для
     браузеров без словаря переносов. */
  h1, h2, h3, h4 {
    -webkit-hyphens: auto;
    hyphens: auto;
  }

  /* Шапка на телефоне всё время стоит прямо над строками: колонка одна,
     полей по бокам нет, и под пилюлей постоянно идёт содержимое. При 80%
     белого сквозь неё читались заголовки и счётчик карусели - две строки в
     одном месте. Стекло и размытие остаются, поднята только плотность: на
     десктопе под шапкой пустые поля, там 80% хватало. */
  .header-inner {
    background: rgba(255, 255, 255, .94);
  }

  /* Навигация уезжает в выезжающее меню под шапкой. Кнопка меню занимает
     место кнопки заявки и берёт её оформление: на телефоне это единственное
     действие в шапке. Светлый кружок с рамкой в пиксель пропадал на тёмных
     секциях - шапка там полупрозрачная, и подложка кнопки сливалась с ней. */
  .nav-burger {
    display: grid;
    border-color: transparent;
    background: var(--accent-grad);
    color: var(--ink);
    box-shadow: var(--accent-lift-sm);
  }

  .nav-burger:is(:hover, :focus-visible) {
    border-color: transparent;
    background: var(--accent-grad-hover);
  }

  /* Тот же уговор, что у кнопки заявки на десктопе: пока главное действие
     первого экрана в кадре, второй акцент в шапке не зажигается. Открытое
     меню из уговора исключено - там знак обязан быть заметным всегда. */
  body:has(.hero) .site-header:not(.is-open) .nav-burger:not(.is-loud) {
    border-color: var(--line);
    background: var(--surface-2);
    color: var(--ink);
    box-shadow: none;
  }

  body:has(.hero) .site-header:not(.is-open) .nav-burger:not(.is-loud):is(:hover, :focus-visible) {
    border-color: var(--ink);
    background: var(--surface-2);
  }

  .header-cta {
    display: none;
  }

  .nav-pill {
    display: none;
    position: absolute;
    top: calc(100% + var(--s-3));
    right: 0;
    left: 0;
    z-index: 90;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-2);
    max-height: calc(100dvh - 140px);
    overflow-y: auto;
    padding: var(--s-5);
    border: 1px solid var(--glass-edge);
    border-radius: var(--r-lg);
    /* Лист меню непрозрачен полностью. У выпадающей панели на десктопе стоят
       97%, и там этого хватает: она висит над обычным текстом. Здесь лист
       занимает почти весь экран и приходится ровно на первый экран, где
       заголовок набран кеглем под сотню: сквозь три процента прозрачности
       его контуры читались поверх пунктов меню. */
    background: var(--bg);
    box-shadow: var(--e-3), var(--glass-sheen);
  }

  .site-header.is-open .nav-pill {
    display: flex;
  }

  /* Порядок в меню обратный разметке: подпись, разделы сайта, кнопка заявки,
     список услуг, контакты. В разметке первым идёт выпадающий блок услуг —
     на десктопе он крайний слева, а в открытом меню два десятка ссылок
     отодвигали бы «Кейсы» и заявку под сгиб. */
  .nav-sheet-head,
  .nav-drop-head {
    display: block;
    color: var(--muted);
    font-family: var(--font-meta);
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
  }

  .nav-sheet-head {
    order: 0;
    margin-bottom: var(--s-2);
  }

  /* Подпись списка услуг стоит в том же ряду, что и сам список: при равном
     порядке решает разметка, а в ней подпись идёт выше. */
  .nav-sheet-head-services {
    order: 3;
    margin-top: var(--s-6);
  }

  /* Разделы — строки со стрелкой у правого края: на телефоне ссылка должна
     читаться как строка списка, а не как слово в ряду. */
  .nav-pill > a {
    order: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-3) 0;
    border-bottom: 1px solid var(--line);
    /* Радиус обязателен нулём. Базовое `.nav-pill a` даёт пилюлю в 999px под
       десктопную навигацию, где ссылка это чип в ряду. Здесь мобильное
       правило рисует у той же ссылки ТОЛЬКО нижнюю границу, и пилюльный
       радиус загибает эту единственную линию дугой вверх на обоих концах:
       разделитель читался кривой рамкой, съехавшей с подписи. Мобильное
       правило радиус не переопределяло, поэтому базовое доживало досюда. */
    border-radius: 0;
    font-size: var(--t-base);
    color: var(--ink);
  }

  .nav-pill > a::after {
    content: "";
    flex: none;
    width: 7px;
    height: 7px;
    border-top: 2px solid var(--muted);
    border-right: 2px solid var(--muted);
    transform: rotate(45deg);
  }

  .nav-pill > a:is(:hover, :focus-visible) {
    background: none;
    color: var(--accent-ink);
  }

  .nav-pill > a:is(:hover, :focus-visible)::after {
    border-color: var(--accent-ink);
  }

  .nav-drop {
    order: 3;
    margin-top: var(--s-5);
  }

  /* Селектор с родителем обязателен: правило `.nav-pill > a` выше по
     весу, и кнопка забирала его `order: 1`, уезжая в начало списка. */
  .nav-pill > .nav-sheet-cta {
    order: 2;
    display: inline-flex;
    justify-content: center;
    margin-top: var(--s-5);
    padding: var(--s-3) var(--s-5);
    border-bottom: 0;
    /* Кнопке пилюлю возвращаем явно: `.nav-pill > a` выше по весу, чем
       `.btn`, и обнуление радиуса строкой выше сделало бы её прямоугольной. */
    border-radius: var(--r-pill);
    color: var(--ink);
  }

  .nav-pill > .nav-sheet-cta::after {
    display: none;
  }

  /* Контакты внизу меню: на телефоне это самый короткий путь к разговору,
     и он не должен требовать возврата в подвал. */
  .nav-sheet-contacts {
    order: 4;
    display: grid;
    gap: var(--s-2);
    margin-top: var(--s-6);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
  }

  .nav-sheet-contacts span {
    color: var(--muted);
    font-family: var(--font-meta);
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
  }

  .nav-sheet-contacts a {
    min-height: 40px;
    display: inline-flex;
    align-items: center;
    color: var(--ink);
    font-size: var(--t-sm);
    font-weight: 600;
    text-decoration: none;
  }

  /* Группы услуг в меню раскрыты сразу: прятать их за второй нажим внутри
     и так открытого меню незачем, поэтому переключатель убран, а панель
     теряет всю оболочку выпадающего блока и становится обычным списком. */
  .nav-drop {
    position: static;
  }

  .nav-drop-btn {
    display: none;
  }

  .nav-drop-panel,
  .nav-drop-panel[hidden] {
    display: block;
    position: static;
    width: auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
  }

  .nav-drop-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-5);
  }

  .nav-drop-all {
    margin-top: var(--s-4);
    padding-top: var(--s-4);
  }

  /* Сетки страницы в одну колонку. Координаты снимаются у жильцов, иначе
     блок, поставленный в колонки 9–13, уедет за пределы единственной. */
  .section-head,
  .hero-shell,
  .hero-bottom,
  .manifesto-grid,
  .faq-grid,
  .open-task-grid,
  .contact-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .section-head > div,
  .section-intro,
  .hero-kicker,
  .hero-title,
  .hero-mascot-stage,
  .hero-bottom,
  .hero-bottom .hero-lead,
  .hero-bottom .hero-actions,
  .manifesto-grid h2,
  .manifesto-lead,
  .manifesto-notes,
  /* Кабель в манифесте тоже жилец сетки, и его забыли в этом списке. Он
     держал десктопные `grid-column: 1 / 4` на сетке, где колонка одна:
     появлялись две неявные колонки по 0px, а два междуколонника по 16px
     между ними оставались настоящими. Заголовок, лид и карточки фактов
     ходят до `-1`, то есть до конца ЯВНОЙ сетки, и получали 311px вместо
     343 - вся секция стояла 16 слева и 48 справа, блоки читались съехавшими.
     Сам кабель при этом занимал все 343 и выглядел ровно, поэтому смещение
     было видно только по тексту.

     Селектор кабеля обязан быть составным. Базовое `.decor-manifesto`
     объявлено НИЖЕ этого медиазапроса, вес равный, и оно побеждало по
     порядку - координаты не снимались вовсе. Тот же гоча, что с
     `.decor.decor-services` и `a.work-fan`. */
  .faq-head,
  .faq-list {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  .decor.decor-manifesto {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  /* Первый экран перестаёт быть экраном: высота по содержанию, маскот
     встаёт в поток отдельной строкой, а не висит абсолютом справа. */
  .hero-shell {
    min-height: 0;
    grid-template-rows: none;
    padding-bottom: var(--s-7);
  }

  /* `relative`, а не `static`: маскот внутри — абсолютный элемент, и без
     позиционированной обёртки он отсчитывался от всего первого экрана и
     ложился поверх заголовка. `inset: auto` снимает координаты, которыми
     сцена была прижата к правым колонкам.

     Пропорция сцены обязана совпадать с пропорцией картинки маскота.
     Знак «B.» позиционируется долями СЦЕНЫ (top 6.65%, left 21.2%,
     width 28.6%), а тело рисуется с `object-fit: contain` по своей
     пропорции. Пока сцена 4:6, как файл, доли совпадают с телом. На
     мобильном сцена была 358x320, тело внутри рисовалось 213x320 — и знак
     из 34% ширины тела превращался в 54%: вместо головы получался
     огромный квадрат. */
  /* Панель с маскотом занимала 500px при экране в 812: почти две трети
     экрана под декор, внутри которого нет ни одной строки текста.
     340 это нижняя граница: при 300 фигура переставала помещаться и
     теряла верх экрана с буквой, замерено. Снизу она по-прежнему уходит
     под кромку на 24px, как задумано. */
  .open-task-visual {
    min-height: 340px;
  }

  /* Запас в панели слайдера услуг держал место под деталь, которая ниже
     1024px скрыта: деталь ушла, 240px остались. Внутри только счётчик и
     две стрелки, им хватает 48. */
  [data-slider="services"] .slider-toolbar {
    min-height: 0;
  }

  /* Деталь у вопросов не прячем: это часть характера страницы. Мешал не
     сам факт её присутствия, а вынос влево за край колонки на 24px при
     ширине 184: на узком экране она читалась как случайный предмет рядом
     с текстом. Ставим по левому краю колонки и чуть уменьшаем. */
  .decor.decor-faq {
    width: 132px;
    margin-left: 0;
  }

  .hero-mascot-stage {
    position: relative;
    inset: auto;
    width: min(260px, 68vw);
    height: auto;
    aspect-ratio: 1024 / 1536;
    margin: var(--s-5) auto 0;
  }

  .hero-bottom {
    gap: var(--s-5);
    align-items: start;
  }

  .hero-lead {
    max-width: none;
  }

  /* Карточка ленты меряется колонками страницы, и на телефоне четыре
     колонки давали 98px — столбик, в котором не читается ни заголовок, ни
     описание. На узком экране карточка меряется экраном, а не сеткой. */
  .slider {
    --span-4: min(82vw, 380px);
  }

  /* Растворение у краёв ленты считалось под десктоп: 190px это 14% от ленты
     в 1320px и больше половины ленты в 343px. На телефоне мягкий край
     начинался на 153-й точке, то есть ПОСРЕДИ текущей карточки - её правая
     половина выцветала, и карточка читалась расфокусированной.

     Пропорцию сюда переносить нельзя. Растворение работает не по доле ленты,
     а по выглядывающему краю соседней карточки: край этот 51px, и если
     растворение шире, оно съедает его целиком - признак, что блок листается,
     пропадает, а на его месте остаётся мутное пятно. Держим растворение
     заметно уже края. */
  .slider-viewport.is-fade-start {
    --fade-l: 16px;
  }

  .slider-viewport.is-fade-end {
    --fade-r: 18px;
  }

  .section-dark .slider-viewport.is-fade-end {
    --fade-r: 14px;
  }

  /* Заголовок карточки услуги рвался посреди слова: «SEO-продвижени / е
     сайта». Карточка на телефоне 281px, поля по 32 съедали 64, и на строку
     оставалось 215 при слове «Корпоративный» шириной 250. Ломал его не баг,
     а страховка `overflow-wrap: break-word` - без неё слово вылезало бы за
     карточку и тянуло горизонтальную прокрутку. Страховку оставляем, но
     убираем повод: поля до 24px даёт строке 233, кегль до 22px ужимает
     самое длинное слово до 220. Замерено по всем семи карточкам.

     Карточки процесса и кейсов набраны тем же кеглем и той же ширины:
     «мессенджере» не влезало в них на два пикселя. Поля правим только у
     услуг и процесса, у карточки кейса своя подложка и свой радиус. */
  .service-card,
  .process-card {
    padding: var(--s-5);
  }

  .service-card h3,
  .process-card h3,
  .case-card h3 {
    font-size: 22px;
  }

  /* Тёмная секция во всю ширину экрана. Её боковые поля намеренно
     возвращаются в расчёт ширины контейнера, чтобы контент тёмных и светлых
     секций стоял на одной вертикали. Пока поле (24px) меньше гуттера, это
     работает; на телефоне гуттер стал 16px, и контейнер получился ШИРЕ самой
     секции — заголовок и карусель вылезали за тёмный блок и обрезались.
     Поля убраны, формула контейнера становится обычной, вертикаль сходится. */
  .section-dark {
    margin-inline: 0;
  }

  .section-dark > .shell {
    width: min(calc(100% - var(--gutter) * 2), var(--shell));
  }

  /* Две детали рассчитаны на пустые правые колонки, которых на телефоне
     нет: панель ложилась на цифру «3 дня» с подписью, перчатка — на абзац
     под заголовком услуг. Обе непрозрачные, то есть просто закрывали текст,
     и потому их прятали совсем.

     Прятать не обязательно, достаточно увести с текста. Обе уходят к
     правой кромке секции и наполовину за неё: у секций уже стоит
     overflow: clip, поэтому лишнее срезается и прокрутку не тянет.
     z-index 0 держит их под содержимым, как гребёнку: если деталь всё же
     заденет строку, читаемым останется текст, а не картинка.

     Селектор составной не для красоты: базовое правило `.decor` объявлено
     НИЖЕ этого медиазапроса и при равном весе побеждает по порядку. */
  /* Панель на телефоне убрана целиком, как и перчатка ниже. На одной
     колонке полоса над счётчиком схлопывается: заголовок, ссылка «Все
     услуги» и счётчик «01 / 07» идут подряд, свободного места под деталь
     не остаётся, и она легла бы прямо на строку.

     Прежде деталь стояла в «Коротко о нас» и на телефоне жалась в правый
     нижний угол секции, ради чего секции добавляли нижнее поле в 72px.
     С переездом обе подпорки не нужны: поле снято, угол свободен. */
  .decor.decor-services {
    display: none;
  }

  /* Перчатка на телефоне убрана целиком. Её сдвигали вниз и ужимали до
     104px, считая пересечение с ТЕКСТОМ секции: с текстом ноль, а с панелью
     слайдера нет. Правый верхний угол секции на одной колонке занимают
     счётчик и стрелки, и деталь ложилась прямо на кнопку «вперёд» - тот же
     случай, что и с пульсом задач. Наклон делает всё хуже: поворот
     увеличивает габаритный прямоугольник со 104 до 141px. */
  .decor.decor-glove {
    display: none;
  }

  /* Пульс задач стоял в углу панели и накладывался на кнопку: на телефоне
     колонка одна, и угол панели — это как раз место под действием. */
  .task-pulse {
    display: none;
  }

  /* Витрина дизайн-блока на телефоне сплющивалась до 358x170: маскот и
     четыре детали внутри расставлены в процентах, и на такой высоте они
     превращались в неразличимую полосу. Кадр возвращается к почти
     квадратному — детали снова читаются. */
  .specimen-stage {
    aspect-ratio: 1.12 / 1;
  }

  /* Веер на телефоне занимает почти всю ширину: коробка в 760px шире экрана.
     Ужимаем до угла — деталь остаётся, но перестаёт быть фоном страницы. */
  /* На телефоне карточка занимала 323px при экране 375: следующая не
     выглядывала вовсе, и ряд читался как одна карточка, а не как лента.
     Сужаем колонку, чтобы справа торчал край соседней: это единственный
     честный признак, что блок листается. Стрелки сверху его не заменяют,
     их принимают за навигацию всей секции. */
  .slider-viewport {
    grid-auto-columns: 78%;
  }

  .cases-object {
    width: 420px;
    height: 310px;
  }

  /* Вылет за кромку рассчитан на высокую секцию широкого экрана. На
     телефоне секции ниже, и веер уходил за край почти целиком: в кейсах
     от него оставалось 32%, в блоке дизайна 14%. Приём «деталь уходит под
     кромку» сохраняем, но вылет сокращаем, чтобы веер был виден.
     Замерено: так видно 83% и 84% соответственно. */
  #cases .cases-object {
    bottom: -20px;
  }

  .cases-object-tl {
    top: -40px;
  }

  /* В манифесте под веером стоят стеклянные карточки фактов: сквозь них
     просвечивал коралл, и текст «Дизайн, вёрстка, публикация» ложился на
     светлое пятно. В кадре и так есть кабель, второй крупной детали тут не
     нужно. */
  .manifesto-object {
    display: none;
  }

  /* Крупный декор уходит за край секции. На десктопе его придерживает
     обрезка у первого экрана, а у остальных секций её нет — на узком экране
     объекты тянули горизонтальную прокрутку через всю страницу. */
  #cases,
  #services,
  .manifesto-section,
  .section-proof,
  .open-task {
    overflow: clip;
  }
}

/* Пальцем в строку высотой 20px не попасть. Действиям — логотипу, ссылкам
   «смотреть», возврату из рельсы — даётся высота нажатия. Обычные ссылки
   внутри абзацев не трогаем: там растянутая строка развалила бы текст. */
@media (max-width: 768px) {
  .brand,
  .link-action,
  .section-more a,
  .rail-back,
  .blog-status,
  .footer-nav a,
  .footer-contacts a,
  /* Нижняя строка подвала в список не попала, и девять ссылок в ней
     оставались высотой 18px: «Все услуги», «Блог», «Кейсы», «Процесс»,
     «О студии», «Вопросы», «Оставить заявку», «Политика данных» и «Наверх».
     Пальцем в такую строку не попасть, а стоят они вплотную друг к другу. */
  .footer-bottom-nav a,
  .footer-bottom > a {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }

  /* Строка контакта перестаёт быть строкой: подпись сверху, сам контакт
     под ней во всю ширину — так по нему проще попасть. */
  .footer-contacts li {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
  }

  /* Плавающий помощник приколот к окну и на телефоне ложился прямо на поля
     формы: фигура в 136px занимает треть ширины экрана. Ужимаем до размера
     обычной круглой кнопки. */
  .budin-helper-stage {
    width: 76px;
  }

  /* Под маскотом нет ни плашки, ни тени. Пробовали оба: белое стекло
     превращалось на тёмных секциях в яркий прямоугольник, а тень в 55%
     чёрного с размытием 14px давала вокруг вырезанной фигуры грязный ореол
     и на светлом фоне читалась полупрозрачностью самого маскота. Плюс
     свечение в этой системе положено только акцентной кнопке. Фигура стоит
     чистой вырезкой, как задумано в макете. */

  /* Контейнер помощника обязан растянуться на ширину экрана. Он приколот
     только правым краем, поэтому сжимался по кнопке, а панель внутри —
     абсолютная с `left: 0; right: 0` — получала ширину этой кнопки: на
     телефоне вместо окна выходила полоска в 76px, где поле ввода было
     шириной 26px. Сама кнопка при этом должна остаться у правого края. */
  .budin-helper {
    right: var(--s-4);
    left: var(--s-4);
    bottom: var(--s-4);
    display: flex;
    justify-content: flex-end;
    /* Растянутый контейнер обязан быть прозрачным для нажатий. Он фиксирован
       и занимает всю ширину экрана внизу, а видна из него только кнопка в
       правом углу - остальное пустое место. При `pointer-events: auto` это
       пустое место всё равно ловило палец: ссылка «Наверх» в подвале лежит
       ровно под этой полосой, и нажатие уходило в контейнер помощника.
       Ссылка не работала не потому, что сломан якорь, а потому, что до неё
       не доходило касание. Нажатия принимают только сама кнопка и панель. */
    pointer-events: none;
  }

  .budin-helper-launcher,
  .budin-helper-panel {
    pointer-events: auto;
  }

  /* Пока открыто меню, помощник уходит. Он приколот к правому нижнему углу
     окна, а выезжающее меню занимает почти весь экран: кнопка помощника
     оказывалась поверх списка услуг и перекрывала ссылки. Двух плавающих
     слоёв одновременно на телефоне быть не должно. */
  body:has(.site-header.is-open) .budin-helper {
    display: none;
  }

  /* Панель помощника в 360px шире экрана вместе с полями: разворачиваем её
     от края до края. */
  .budin-helper-panel {
    right: 0;
    left: 0;
    bottom: 96px;
    width: auto;
    max-height: calc(100dvh - 160px);
  }
}

@media (max-width: 620px) {
  .proof-facts {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-5);
  }

  .design-services {
    grid-template-columns: minmax(0, 1fr);
  }

  .slider {
    --span-4: 86vw;
  }

  /* Подписи под витриной дизайна.

     Три метки дают в сумме 36 знаков, при 12px моно с трекингом .14em это
     333px, а строка на телефоне около 309px. В один ряд они не встанут ни
     при каком разумном кегле - и раньше это лечили тем, что каждую метку
     ломали на две строки в равных колонках, подбирая междуколонник под
     длину конкретных слов.

     Подход не пережил смены ширины экрана: на 390px «WEB DESIGN» перестало
     ломаться и осталось в одну строку, пока соседи ломались на две, а
     «LANGUAGE» вылезло за свою колонку и обрезалось краем карточки.
     Вёрстка, посчитанная под конкретные слова, ломается от любой правки
     текста или появления нового телефона.

     Поэтому переносим МЕТКАМИ, а не словами. Каждая метка неразрывна,
     ряд переносится сам: на телефоне выходит «LOGO SYSTEM  WEB DESIGN»
     первой строкой и «VISUAL LANGUAGE» второй, обе по центру. Ширины
     считать не нужно, при любом экране и любом тексте останется ровно. */
  .specimen-readout {
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--s-2) var(--s-4);
    text-align: center;
  }

  .specimen-readout > * {
    white-space: nowrap;
  }

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

     Сама метка остаётся выделенной цветом, для акцента этого достаточно. */
  .specimen-readout-live i {
    display: none;
  }
}

/* Отдельного правила под самые узкие экраны здесь больше нет. Оно
   подгоняло междуколонник под ширину слова «LANGUAGE» и было нужно ровно
   потому, что метки ломались на две строки внутри равных колонок. Метки
   переносятся целиком, и первая строка «LOGO SYSTEM  WEB DESIGN» занимает
   202px - помещается и в 320px экран. Подгонять нечего. */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }

  .open-task-mascot-rig {
    opacity: 1 !important;
    transform: translateX(-50%) !important;
  }

  .open-task-connector-signal {
    display: none;
  }

  .budin-helper-mascot,
  .budin-helper-arm,
  .budin-helper-caption,
  .budin-helper-panel {
    animation: none !important;
  }
}

/* ── Декоративные детали ────────────────────────────────────
   Объекты в материале маскота, заполняют пустоты и не участвуют в чтении:
   всегда aria-hidden, всегда без событий мыши. Тень мягкая и тёплая,
   на тёмных секциях глубже. Ассеты обрезаны по содержимому и переведены
   в WebP, поэтому позиционируются предсказуемо. */

/* Высота обязана считаться от ширины. У картинок проставлены атрибуты
   width/height — иначе до загрузки блок схлопывается в ноль и вёрстка
   прыгает, — но при заданной в CSS только ширине браузер берёт высоту из
   атрибута как есть: детали растягивались до 760px, накрывая цифры и
   карточки. */
.decor {
  display: block;
  max-width: none;
  height: auto;
  pointer-events: none;
  user-select: none;
}

/* ── Гребёнка ───────────────────────────────────────────────
   Тонкие штрихи с острыми концами на тёмном фоне. Шаг штриха 24px — тот же,
   что межколонник сетки, поэтому фон не спорит с раскладкой, а повторяет её
   ритм. Всегда под содержимым: z-index 0 против 1 у оболочки блока. */
.rake {
  position: absolute;
  z-index: 0;
  height: auto;
}

/* Дизайн: справа сверху и зеркально слева снизу под крупной надписью. */
/* Гребёнка поднята на уровень витрины: панель непрозрачна и перекрывает
   середину штрихов, поэтому видны верхние и нижние концы — фактура, на
   которой лежит картинка. Ширина натуральная (552px): при уменьшении шаг
   штриха сжимается и перестаёт совпадать с межколонником 24px. */
.rake-design-tr {
  right: 60px;
  top: 200px;
  width: 552px;
}


/* Манифест: за карточками фактов, справа. */
/* Ширина натуральная: масштабирование сжимает или растягивает шаг штриха,
   а он должен совпадать с межколонником 24px, иначе фон спорит с сеткой. */
.rake-manifesto {
  top: 40px;
  right: 40px;
  width: 552px;
}

/* Сустав в пустой левой колонке вопросов */
/* Размер выставлен по замеру баланса: на 232px деталь давала левым колонкам
   22-32% плотности против 3-9% у списка вопросов, и центр масс секции уезжал
   на 0.364 при норме 0.5. Декор не должен весить больше содержания. */
.decor-faq {
  width: 184px;
  margin: var(--s-8) 0 0 calc(var(--s-5) * -1);
  filter: drop-shadow(0 26px 38px rgba(40, 33, 30, .2));
}

/* Кабельная петля под заголовком манифеста, на тёмном фоне */
/* Ширину задаёт колонка: при фиксированных 252px правый край детали не
   доходил до края своей полосы на 28px при 1440 и на 34px при 1280. */
.decor-manifesto {
  grid-column: 1 / 4;
  grid-row: 3;
  width: 100%;
  margin-top: var(--s-6);
  filter: drop-shadow(0 32px 46px rgba(0, 0, 0, .55));
}

/* Панель корпуса под шапкой блока услуг.

   Раньше стояла в правом нижнем углу «Коротко о нас». Перенесена под
   заголовок «Что можем собрать»: там под ссылкой «Все услуги» пустует
   целая полоса до счётчика карусели, и деталь её занимает.

   Именно СЛЕВА: правую зону секции уже держит перчатка над стрелками, и
   две детали в одном углу спорили бы между собой. Слева панель, справа
   кулак - секция уравновешивается по горизонтали.

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

   Деталь стоит ВНУТРИ слайдера, у верхней кромки, а не над ним. Между
   шапкой секции и слайдером всего 24px: пустая полоса, которую видно на
   макете, принадлежит панели счётчика высотой 240px, а сам счётчик
   «01 / 07» прижат к её низу. Поэтому деталь занимает верх этой полосы.
   Привязка к верху слайдера, а не к низу шапки, ещё и не зависит от
   переносов заголовка на разной ширине. */
.decor-services {
  position: absolute;
  left: 0;
  top: 0;
  width: 146px;
  filter: drop-shadow(0 22px 32px rgba(40, 33, 30, .2));
}

/* Перчатка занимает место убранного маскота-оператора. Ширина ровно две
   колонки с междуколонником (200px при shell 1320), смещение справа — одна
   колонка с междуколонником (112px). Итого деталь занимает 10-ю и 11-ю
   колонки и не наезжает на стрелки карусели, которые стоят в 12-й. */
.decor-glove {
  position: absolute;
  right: 112px;
  top: -40px;
  z-index: 1;
  /* Размер по замеру баланса: на 200px деталь тянула центр масс секции на
     0.630 при норме 0.5 - правые колонки давали 17-19% плотности против
     1-7% у левых, то есть украшение перевешивало содержание. */
  width: 156px;
  /* Наклон в сторону стрелок: кулак смотрит на управление каруселью.
     Поворот увеличивает габаритный прямоугольник, поэтому запас до стрелок
     и до карточек проверяется замером, а не на глаз. */
  transform: rotate(16deg);
  transform-origin: 50% 40%;
  filter: drop-shadow(0 26px 38px rgba(40, 33, 30, .22));
}

/* ── Веер работ ─────────────────────────────────────────────
   Стопка миниатюр проектов, раскрывающаяся при наведении. Техника та же,
   что у гармошки: поворот и сдвиг пропорционально индексу, без JS.
   Две вещи проверены на прототипе и менять их нельзя без повторной проверки:
   миниатюры при раскрытии вырастают вдвое (в 38px скриншот не читается),
   и веер раскрывается строго вправо (влево крайняя уезжает за край карточки). */

.work-fan {
  position: relative;
  display: inline-flex;
  /* Карточка — колоночный флекс, поэтому inline-flex всё равно растянулся бы
     на всю ширину: прижимаем бейдж к содержимому явно. */
  align-self: flex-start;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-2);
  padding: var(--s-1) var(--s-4) var(--s-1) var(--s-1);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--e-1), var(--glass-sheen);
  color: var(--ink);
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
}

.work-fan-stack {
  position: relative;
  flex: none;
  width: 44px;
  height: 38px;
}

.work-fan-stack img {
  position: absolute;
  left: 50%;
  top: 0;
  width: 38px;
  height: 38px;
  margin-left: -19px;
  object-fit: cover;
  object-position: top;
  border: 2px solid var(--surface-2);
  border-radius: 12px;
  box-shadow: 0 2px 6px rgba(40, 33, 30, .18);
  transform-origin: 50% 50%;
  transform: rotate(calc((var(--i) - 1) * 6deg)) translateX(calc((var(--i) - 1) * 4px));
  transition: transform .36s cubic-bezier(.2, .9, .25, 1), box-shadow .3s ease;
}

.work-fan:is(:hover, :focus-visible) .work-fan-stack img {
  transform:
    translate(calc(var(--i) * 58px + 14px), -64px)
    rotate(calc(var(--i) * 8deg - 6deg))
    scale(2.05);
  box-shadow: 0 12px 24px -6px rgba(40, 33, 30, .4);
}

.work-fan:is(:hover, :focus-visible) {
  background: var(--glass-strong);
  box-shadow: var(--e-2), var(--glass-sheen);
}
